e2e: support unique namespace each test (#135)

* e2e: support unique namespace each test

* use downward api to convey namespace
This commit is contained in:
Hongchao Deng 2016-09-23 16:09:00 -07:00 коммит произвёл GitHub
Родитель 3296330c73
Коммит 91f009e333
10 изменённых файлов: 137 добавлений и 171 удалений

Просмотреть файл

@ -1,21 +0,0 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
mkdir -p _output/bin || true
option="${1}"
case ${option} in
"setup")
go build -o _output/bin/e2e-setup ./test/e2e/framework/setup/main.go
_output/bin/e2e-setup --kubeconfig ${KUBERNETES_KUBECONFIG_PATH} --controller-image "${2}"
;;
"teardown")
go build -o _output/bin/e2e-teardown ./test/e2e/framework/teardown/main.go
_output/bin/e2e-teardown --kubeconfig ${KUBERNETES_KUBECONFIG_PATH}
;;
*) echo "Unknown option ${option}"
;;
esac

Просмотреть файл

@ -50,11 +50,7 @@ function build_pass {
}
function e2e_pass {
hack/e2e setup $CONTROLLER_IMAGE
test_pass="0"
go test -v ${TEST_PKGS} || test_pass="1"
hack/e2e teardown
return $test_pass
go test -v ${TEST_PKGS} --kubeconfig $KUBERNETES_KUBECONFIG_PATH --controller-image $CONTROLLER_IMAGE
}
for p in $PASSES; do

Просмотреть файл

@ -14,7 +14,6 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/util/wait"
)
const (
@ -139,21 +138,7 @@ func (c *Controller) createTPR() error {
return err
}
err = wait.Poll(3*time.Second, 100*time.Second,
func() (done bool, err error) {
resp, err := k8sutil.WatchETCDCluster(c.masterHost, c.namespace, c.kclient.RESTClient.Client, "0")
if err != nil {
return false, err
}
if resp.StatusCode == 200 {
return true, nil
}
if resp.StatusCode == 404 {
return false, nil
}
return false, errors.New("Invalid status code: " + resp.Status)
})
return err
return k8sutil.WaitEtcdTPRReady(c.kclient.Client, 3*time.Second, 90*time.Second, c.masterHost, c.namespace)
}
func monitorEtcdCluster(host, ns string, httpClient *http.Client, watchVersion string) (<-chan *Event, <-chan error) {

Просмотреть файл

@ -18,6 +18,7 @@ import (
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/wait"
"k8s.io/kubernetes/pkg/watch"
)
@ -92,6 +93,12 @@ func CreateBackupReplicaSetAndService(kclient *unversioned.Client, clusterName,
"--etcd-cluster",
clusterName,
},
Env: []api.EnvVar{
{
Name: "MY_POD_NAMESPACE",
ValueFrom: &api.EnvVarSource{FieldRef: &api.ObjectFieldSelector{FieldPath: "metadata.namespace"}},
},
},
},
},
},
@ -350,3 +357,21 @@ func WatchETCDCluster(host, ns string, httpClient *http.Client, resourceVersion
return httpClient.Get(fmt.Sprintf("%s/apis/coreos.com/v1/namespaces/%s/etcdclusters?watch=true&resourceVersion=%s",
host, ns, resourceVersion))
}
func WaitEtcdTPRReady(httpClient *http.Client, interval, timeout time.Duration, host, ns string) error {
return wait.Poll(interval, timeout, func() (bool, error) {
resp, err := WatchETCDCluster(host, ns, httpClient, "0")
if err != nil {
return false, err
}
switch resp.StatusCode {
case http.StatusOK:
return true, nil
case http.StatusNotFound: // not set up yet. wait.
return false, nil
default:
return false, fmt.Errorf("invalid status code: %v", resp.Status)
}
})
}

Просмотреть файл

@ -6,21 +6,11 @@ End-to-end (e2e) testing is automated testing for real user scenarios.
Prerequisites:
- a running k8s cluster and kube config. We will need to pass kube config as arguments.
- KUBERNETES_KUBECONFIG_PATH env var is required, e.g. `KUBERNETES_KUBECONFIG_PATH=$HOME/.kube/config`
As first step, we need to setup environment for testing:
```
$ ./hack/e2e setup
```
This will do all preparation work such as creating etcd controller.
- Have kubeconfig file ready.
- Have etcd controller image ready.
e2e tests are written as go test. All go test techniques applies, e.g. picking what to run, timeout length.
Let's say I want to run all tests in "test/e2e/":
```
$ go test -v ./test/e2e/
```
Finally, we need to tear down the things we setup before:
```
$ ./hack/e2e teardown
$ go test -v ./test/e2e/ --kubeconfig "$HOME/.kube/config" --controller-image gcr.io/coreos-k8s-scale-testing/kube-etcd-controller:latest
```

Просмотреть файл

@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"net/http"
"os"
"testing"
"time"
@ -19,10 +18,7 @@ import (
)
func TestCreateCluster(t *testing.T) {
f, err := framework.New(os.Getenv("KUBERNETES_KUBECONFIG_PATH"))
if err != nil {
t.Fatal(err)
}
f := framework.Global
myetcd := &cluster.EtcdCluster{
TypeMeta: unversioned.TypeMeta{
Kind: "EtcdCluster",
@ -51,7 +47,7 @@ func TestCreateCluster(t *testing.T) {
}()
err = wait.Poll(5*time.Second, 1*time.Minute, func() (done bool, err error) {
pods, err := f.KubeClient.Pods("default").List(api.ListOptions{
pods, err := f.KubeClient.Pods(f.Namespace.Name).List(api.ListOptions{
LabelSelector: labels.SelectorFromSet(map[string]string{
"etcd_cluster": "my-etcd",
}),
@ -81,7 +77,7 @@ func getPodNames(pods []api.Pod) []string {
func postEtcdCluster(f *framework.Framework, body []byte) error {
resp, err := f.KubeClient.Client.Post(
f.MasterHost+"/apis/coreos.com/v1/namespaces/default/etcdclusters",
fmt.Sprintf("%s/apis/coreos.com/v1/namespaces/%s/etcdclusters", f.MasterHost, f.Namespace.Name),
"application/json", bytes.NewReader(body))
if err != nil {
return err
@ -94,7 +90,7 @@ func postEtcdCluster(f *framework.Framework, body []byte) error {
func deleteEtcdCluster(f *framework.Framework) error {
req, err := http.NewRequest("DELETE",
f.MasterHost+"/apis/coreos.com/v1/namespaces/default/etcdclusters/my-etcd", nil)
fmt.Sprintf("%s/apis/coreos.com/v1/namespaces/%s/etcdclusters/my-etcd", f.MasterHost, f.Namespace.Name), nil)
if err != nil {
return err
}

Просмотреть файл

@ -1,16 +1,25 @@
package framework
import (
"fmt"
"time"
"github.com/Sirupsen/logrus"
"github.com/coreos/kube-etcd-controller/pkg/util/k8sutil"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
)
var Global *Framework
type Framework struct {
KubeClient *unversioned.Client
MasterHost string
Namespace *api.Namespace
}
func New(kubeconfig string) (*Framework, error) {
func New(kubeconfig string, baseName string) (*Framework, error) {
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, err
@ -19,9 +28,67 @@ func New(kubeconfig string) (*Framework, error) {
if err != nil {
return nil, err
}
namespace, err := cli.Namespaces().Create(&api.Namespace{
ObjectMeta: api.ObjectMeta{
GenerateName: fmt.Sprintf("e2e-test-%v-", baseName),
},
})
if err != nil {
return nil, err
}
return &Framework{
MasterHost: config.Host,
KubeClient: cli,
Namespace: namespace,
}, nil
}
func (f *Framework) Setup(ctrlImage string) error {
if err := f.setupEtcdController(ctrlImage); err != nil {
logrus.Errorf("fail to setup etcd controller: %v", err)
return err
}
logrus.Info("setup finished successfully")
return nil
}
func (f *Framework) Teardown() error {
// TODO: delete TPR
return f.KubeClient.Namespaces().Delete(f.Namespace.Name)
}
func (f *Framework) setupEtcdController(ctrlImage string) error {
// TODO: unify this and the yaml file in example/
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "kube-etcd-controller",
Labels: map[string]string{"name": "kube-etcd-controller"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "kube-etcd-controller",
Image: ctrlImage,
Env: []api.EnvVar{
{
Name: "MY_POD_NAMESPACE",
ValueFrom: &api.EnvVarSource{FieldRef: &api.ObjectFieldSelector{FieldPath: "metadata.namespace"}},
},
},
},
},
},
}
_, err := f.KubeClient.Pods(f.Namespace.Name).Create(pod)
if err != nil {
return err
}
err = k8sutil.WaitEtcdTPRReady(f.KubeClient.Client, 5*time.Second, 90*time.Second, f.MasterHost, f.Namespace.Name)
if err != nil {
return err
}
logrus.Info("etcd controller created successfully")
return nil
}

Просмотреть файл

@ -1,76 +0,0 @@
package main
import (
"flag"
"fmt"
"net/http"
"time"
"github.com/Sirupsen/logrus"
"github.com/coreos/kube-etcd-controller/test/e2e/framework"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/wait"
)
const etcdTPRURL = "/apis/coreos.com/v1/etcdclusters"
func main() {
kubeconfig := flag.String("kubeconfig", "", "kube config path, e.g. $HOME/.kube/config")
ctrlImage := flag.String("controller-image", "", "controller image, e.g. gcr.io/coreos-k8s-scale-testing/kube-etcd-controller")
flag.Parse()
f, err := framework.New(*kubeconfig)
if err != nil {
panic(err)
}
if err := setupEtcdController(f, *ctrlImage); err != nil {
panic(err)
}
logrus.Info("setup finished successfully")
}
func setupEtcdController(f *framework.Framework, ctrlImage string) error {
// TODO: unify this and the yaml file in example/
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "kube-etcd-controller",
Labels: map[string]string{"name": "kube-etcd-controller"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "kube-etcd-controller",
Image: ctrlImage,
},
},
},
}
_, err := f.KubeClient.Pods("default").Create(pod)
if err != nil {
return err
}
err = waitTPRReady(f)
if err != nil {
return err
}
logrus.Info("etcd controller created successfully")
return nil
}
func waitTPRReady(f *framework.Framework) error {
return wait.Poll(time.Second*20, time.Minute*5, func() (bool, error) {
resp, err := f.KubeClient.Client.Get(f.MasterHost + etcdTPRURL)
if err != nil {
logrus.Errorf("http GET failed: %v", err)
return false, err
}
switch resp.StatusCode {
case http.StatusOK:
return true, nil
case http.StatusNotFound: // not set up yet. wait.
logrus.Info("TPR not set up yet. Keep waiting...")
return false, nil
default:
return false, fmt.Errorf("unexpected status code: %v", resp.Status)
}
})
}

Просмотреть файл

@ -1,31 +0,0 @@
package main
import (
"flag"
"github.com/Sirupsen/logrus"
"github.com/coreos/kube-etcd-controller/test/e2e/framework"
)
func main() {
kubeconfig := flag.String("kubeconfig", "", "kube config path, e.g. $HOME/.kube/config")
flag.Parse()
f, err := framework.New(*kubeconfig)
if err != nil {
panic(err)
}
// TODO: have unique test namespace (#117) and just delete everything under that.
if err := teardownEtcdController(f); err != nil {
panic(err)
}
logrus.Info("teardown finished successfully")
}
func teardownEtcdController(f *framework.Framework) error {
err := f.KubeClient.Pods("default").Delete("kube-etcd-controller", nil)
if err != nil {
return err
}
logrus.Info("etcd controller deleted successfully")
return nil
}

35
test/e2e/main_test.go Normal file
Просмотреть файл

@ -0,0 +1,35 @@
package e2e
import (
"flag"
"os"
"testing"
"github.com/Sirupsen/logrus"
"github.com/coreos/kube-etcd-controller/test/e2e/framework"
)
func TestMain(m *testing.M) {
kubeconfig := flag.String("kubeconfig", "", "kube config path, e.g. $HOME/.kube/config")
ctrlImage := flag.String("controller-image", "", "controller image, e.g. gcr.io/coreos-k8s-scale-testing/kube-etcd-controller")
flag.Parse()
f, err := framework.New(*kubeconfig, "top-level")
if err != nil {
logrus.Errorf("fail to create new framework: %v", err)
os.Exit(1)
}
if err := f.Setup(*ctrlImage); err != nil {
logrus.Errorf("fail to setup test environment: %v", err)
os.Exit(1)
}
framework.Global = f
code := m.Run()
if err := f.Teardown(); err != nil {
logrus.Errorf("fail to teardown test environment: %v", err)
os.Exit(1)
}
os.Exit(code)
}