From fbeadf2ec9bda2cd78bdc04caabe7caa7a2ccdac Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Tue, 12 Mar 2019 16:40:18 -0700 Subject: [PATCH 01/15] pkg/test/context.go: use nanoseconds instead of seconds --- pkg/test/context.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/test/context.go b/pkg/test/context.go index 9427faf2457..53276526cde 100644 --- a/pkg/test/context.go +++ b/pkg/test/context.go @@ -41,6 +41,7 @@ type cleanupFn func() error func NewTestCtx(t *testing.T) *TestCtx { var prefix string if t != nil { + // Use the name of the test as the prefix // TestCtx is used among others for namespace names where '/' is forbidden prefix = strings.TrimPrefix( strings.Replace( @@ -52,10 +53,11 @@ func NewTestCtx(t *testing.T) *TestCtx { "test", ) } else { - prefix = "main" + prefix = "operator-sdk" } - id := prefix + "-" + strconv.FormatInt(time.Now().Unix(), 10) + // add a creation timestamp to the ID + id := prefix + "-" + strconv.FormatInt(time.Now().UnixNano(), 10) return &TestCtx{ id: id, t: t, From 5df3a9ebf0fd7e1ae9c39fb99bc04044975f3476 Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Wed, 13 Mar 2019 11:09:55 -0700 Subject: [PATCH 02/15] doc/test-framework/writing...: update doc --- doc/test-framework/writing-e2e-tests.md | 34 +++++++++++++------------ 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/doc/test-framework/writing-e2e-tests.md b/doc/test-framework/writing-e2e-tests.md index 411e59b66eb..3de7046167c 100644 --- a/doc/test-framework/writing-e2e-tests.md +++ b/doc/test-framework/writing-e2e-tests.md @@ -106,7 +106,7 @@ defer ctx.Cleanup() ``` Now that there is a `TestCtx`, the test's Kubernetes resources (specifically the test namespace, -Service Account, RBAC, and Operator deployment in `local` testing; just the Operator deployment +Service Account, Role, Role Binding, and Operator deployment in `local` testing; just the Operator deployment in `cluster` testing) can be initialized: ```go @@ -226,9 +226,9 @@ functions will automatically be run since they were deferred when the TestCtx wa ## Running the Tests To make running the tests simpler, the `operator-sdk` CLI tool has a `test` subcommand that can configure -default test settings, such as locations of your global resource manifest file (by default -`deploy/crd.yaml`) and your namespaced resource manifest file (by default `deploy/service_account.yaml` concatenated with -`deploy/rbac.yaml` and `deploy/operator.yaml`), and allows the user to configure runtime options. There are 2 ways to use the +default test settings, such as locations of your global resource manifest file (by default all CRDs in +`deploy/crds`) and your namespaced resource manifest file (by default `deploy/service_account.yaml` concatenated with +`deploy/role.yaml`, `deploy/role_binding.yaml`, and `deploy/operator.yaml`), and allows the user to configure runtime options. There are 2 ways to use the subcommand: local and cluster. ### Local @@ -295,7 +295,9 @@ will result in undefined behavior. This is an example `go test` equivalent to th # Combine service_account, rbac, operator manifest into namespaced manifest $ cp deploy/service_account.yaml deploy/namespace-init.yaml $ echo -e "\n---\n" >> deploy/namespace-init.yaml -$ cat deploy/rbac.yaml >> deploy/namespace-init.yaml +$ cat deploy/role.yaml >> deploy/namespace-init.yaml +$ echo -e "\n---\n" >> deploy/namespace-init.yaml +$ cat deploy/role_binding.yaml >> deploy/namespace-init.yaml $ echo -e "\n---\n" >> deploy/namespace-init.yaml $ cat deploy/operator.yaml >> deploy/namespace-init.yaml # Run tests @@ -352,24 +354,24 @@ in your cluster. You can do this with `kubectl`: $ kubectl get namespaces Example Output: -NAME STATUS AGE -default Active 2h -kube-public Active 2h -kube-system Active 2h -main-1534287036 Active 23s -memcached-memcached-group-cluster-1534287037 Active 22s -memcached-memcached-group-cluster2-1534287037 Active 22s +NAME STATUS AGE +default Active 2h +kube-public Active 2h +kube-system Active 2h +memcached-memcached-group-cluster-1552500058464380681 Active 5s +memcached-memcached-group-cluster2-1552500058464409898 Active 5s +operator-sdk-1552500057336429125 Active 6s ``` -The names of the namespaces will be either start with `main` or with the name of the tests and the suffix will -be a Unix timestamp (number of seconds since January 1, 1970 00:00 UTC). Kubectl can be used to delete these +The names of the namespaces will be either start with `operator-sdk` or with the name of the tests and the suffix will +be a Unix nanosecond timestamp (number of nanoseconds since January 1, 1970 00:00 UTC). Kubectl can be used to delete these namespaces and the resources in those namespaces: ```shell -$ kubectl delete namespace main-153428703 +$ kubectl delete namespace operator-sdk-1552500057336429125 ``` -Since the CRD is not namespaced, it must be deleted separately. Clean up the CRD created by the tests using the CRD manifest `deploy/crd.yaml`: +Since the CRD is not namespaced, it must be deleted separately. Clean up the CRD created by the tests using the CRD manifest(s) in `deploy/crds`: ```shell $ kubectl delete -f deploy/crds/cache_v1alpha1_memcached_crd.yaml From bb52ce225087a220397db2622cfd98109fd5a5b6 Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Wed, 13 Mar 2019 12:07:39 -0700 Subject: [PATCH 03/15] pkg/test,test/e2e: clean up and expose test framework Setup func --- pkg/test/framework.go | 35 +++++++++++++---------------------- pkg/test/main_entry.go | 6 +++++- pkg/test/resource_creator.go | 2 +- test/e2e/memcached_test.go | 14 +++++++------- 4 files changed, 26 insertions(+), 31 deletions(-) diff --git a/pkg/test/framework.go b/pkg/test/framework.go index ce2be711f67..6d0db19d4c0 100755 --- a/pkg/test/framework.go +++ b/pkg/test/framework.go @@ -55,19 +55,20 @@ type Framework struct { KubeConfig *rest.Config KubeClient kubernetes.Interface Scheme *runtime.Scheme - NamespacedManPath *string + NamespacedManPath string Namespace string LocalOperator bool } -func setup(kubeconfigPath, namespacedManPath *string, localOperator bool) error { - namespace := "" - if *singleNamespace { - namespace = os.Getenv(TestNamespaceEnv) - } +func Setup(kubeconfigPath, namespacedManPath, namespace string, localOperator bool) error { var err error var kubeconfig *rest.Config - if *kubeconfigPath == "incluster" { + if kubeconfigPath == "incluster" { + // when running with an InCluster config, we don't have permission to create new namespaces + *singleNamespace = true + if len(namespace) == 0 { + return fmt.Errorf("namespace must be set for in cluster testing mode") + } // Work around https://github.com/kubernetes/kubernetes/issues/40973 if len(os.Getenv("KUBERNETES_SERVICE_HOST")) == 0 { addrs, err := net.LookupHost("kubernetes.default.svc") @@ -84,14 +85,9 @@ func setup(kubeconfigPath, namespacedManPath *string, localOperator bool) error } } kubeconfig, err = rest.InClusterConfig() - *singleNamespace = true - namespace = os.Getenv(TestNamespaceEnv) - if len(namespace) == 0 { - return fmt.Errorf("test namespace env not set") - } } else { var kcNamespace string - kubeconfig, kcNamespace, err = k8sInternal.GetKubeconfigAndNamespace(*kubeconfigPath) + kubeconfig, kcNamespace, err = k8sInternal.GetKubeconfigAndNamespace(kubeconfigPath) if *singleNamespace && namespace == "" { namespace = kcNamespace } @@ -151,28 +147,23 @@ func AddToFrameworkScheme(addToScheme addToSchemeFunc, obj runtime.Object) error defer mutex.Unlock() err := addToScheme(Global.Scheme) if err != nil { - return err + return fmt.Errorf("failed to update global scheme: %v", err) } restMapper.Reset() - dynClient, err := dynclient.New(Global.KubeConfig, dynclient.Options{Scheme: Global.Scheme, Mapper: restMapper}) - if err != nil { - return fmt.Errorf("failed to initialize new dynamic client: (%v)", err) - } err = wait.PollImmediate(time.Second, time.Second*10, func() (done bool, err error) { if *singleNamespace { - err = dynClient.List(goctx.TODO(), &dynclient.ListOptions{Namespace: Global.Namespace}, obj) + err = Global.Client.List(goctx.TODO(), &dynclient.ListOptions{Namespace: Global.Namespace}, obj) } else { - err = dynClient.List(goctx.TODO(), &dynclient.ListOptions{Namespace: "default"}, obj) + err = Global.Client.List(goctx.TODO(), &dynclient.ListOptions{Namespace: "default"}, obj) } if err != nil { restMapper.Reset() return false, nil } - Global.Client = &frameworkClient{Client: dynClient} return true, nil }) if err != nil { - return fmt.Errorf("failed to build the dynamic client: %v", err) + return fmt.Errorf("failed to update the client restmapper: %v", err) } dynamicDecoder = serializer.NewCodecFactory(Global.Scheme).UniversalDeserializer() return nil diff --git a/pkg/test/main_entry.go b/pkg/test/main_entry.go index ddabe5f38bc..5fd5046d9b5 100644 --- a/pkg/test/main_entry.go +++ b/pkg/test/main_entry.go @@ -57,7 +57,11 @@ func MainEntry(m *testing.M) { if err != nil { log.Fatalf("Failed to change directory to project root: %v", err) } - if err := setup(kubeconfigPath, namespacedManPath, *localOperator); err != nil { + namespace := "" + if *singleNamespace || *kubeconfigPath == "incluster" { + namespace = os.Getenv(TestNamespaceEnv) + } + if err := Setup(*kubeconfigPath, *namespacedManPath, namespace, *localOperator); err != nil { log.Fatalf("Failed to set up framework: %v", err) } // setup local operator command, but don't start it yet diff --git a/pkg/test/resource_creator.go b/pkg/test/resource_creator.go index bf840145405..429ff8c42d9 100644 --- a/pkg/test/resource_creator.go +++ b/pkg/test/resource_creator.go @@ -107,7 +107,7 @@ func (ctx *TestCtx) createFromYAML(yamlFile []byte, skipIfExists bool, cleanupOp func (ctx *TestCtx) InitializeClusterResources(cleanupOptions *CleanupOptions) error { // create namespaced resources - namespacedYAML, err := ioutil.ReadFile(*Global.NamespacedManPath) + namespacedYAML, err := ioutil.ReadFile(Global.NamespacedManPath) if err != nil { return fmt.Errorf("failed to read namespaced manifest: %v", err) } diff --git a/test/e2e/memcached_test.go b/test/e2e/memcached_test.go index 931bbf302e3..ac127450a72 100644 --- a/test/e2e/memcached_test.go +++ b/test/e2e/memcached_test.go @@ -37,7 +37,7 @@ import ( "github.com/operator-framework/operator-sdk/pkg/test/e2eutil" "github.com/prometheus/prometheus/util/promlint" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" @@ -261,7 +261,7 @@ func TestMemcached(t *testing.T) { // hacky way to use createFromYAML without exposing the method // create crd filename := file.Name() - framework.Global.NamespacedManPath = &filename + framework.Global.NamespacedManPath = filename err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) if err != nil { t.Fatal(err) @@ -386,7 +386,7 @@ func memcachedScaleTest(t *testing.T, f *framework.Framework, ctx *framework.Tes } // create memcached custom resource - framework.Global.NamespacedManPath = &filename + framework.Global.NamespacedManPath = filename err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) if err != nil { return err @@ -526,7 +526,7 @@ func MemcachedCluster(t *testing.T) { } // create namespaced resources filename := file.Name() - framework.Global.NamespacedManPath = &filename + framework.Global.NamespacedManPath = filename err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) if err != nil { t.Fatal(err) @@ -563,7 +563,7 @@ func MemcachedClusterTest(t *testing.T) { // create sa filename := "deploy/service_account.yaml" - framework.Global.NamespacedManPath = &filename + framework.Global.NamespacedManPath = filename err := ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) if err != nil { t.Fatal(err) @@ -572,7 +572,7 @@ func MemcachedClusterTest(t *testing.T) { // create rbac filename = "deploy/role.yaml" - framework.Global.NamespacedManPath = &filename + framework.Global.NamespacedManPath = filename err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) if err != nil { t.Fatal(err) @@ -580,7 +580,7 @@ func MemcachedClusterTest(t *testing.T) { t.Log("Created role") filename = "deploy/role_binding.yaml" - framework.Global.NamespacedManPath = &filename + framework.Global.NamespacedManPath = filename err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) if err != nil { t.Fatal(err) From 05535c9110d9014893f4eb4b7090dac9ef0da3c8 Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Wed, 13 Mar 2019 13:25:04 -0700 Subject: [PATCH 04/15] pkg/test,test/e2e: expose CreateFromYAML and clean up e2e tests --- pkg/test/main_entry.go | 2 +- pkg/test/resource_creator.go | 4 +- test/e2e/memcached_test.go | 77 +++++++++++++++++------------------- 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/pkg/test/main_entry.go b/pkg/test/main_entry.go index 5fd5046d9b5..dc5c32379c0 100644 --- a/pkg/test/main_entry.go +++ b/pkg/test/main_entry.go @@ -130,7 +130,7 @@ func MainEntry(m *testing.M) { if err != nil { log.Fatalf("Failed to read global resource manifest: %v", err) } - err = ctx.createFromYAML(globalYAML, true, &CleanupOptions{TestContext: ctx}) + err = ctx.CreateFromYAML(globalYAML, true, &CleanupOptions{TestContext: ctx}) if err != nil { log.Fatalf("Failed to create resource(s) in global resource manifest: %v", err) } diff --git a/pkg/test/resource_creator.go b/pkg/test/resource_creator.go index 429ff8c42d9..7b3f1c5b92f 100644 --- a/pkg/test/resource_creator.go +++ b/pkg/test/resource_creator.go @@ -52,7 +52,7 @@ func (ctx *TestCtx) GetNamespace() (string, error) { return ctx.namespace, nil } -func (ctx *TestCtx) createFromYAML(yamlFile []byte, skipIfExists bool, cleanupOptions *CleanupOptions) error { +func (ctx *TestCtx) CreateFromYAML(yamlFile []byte, skipIfExists bool, cleanupOptions *CleanupOptions) error { namespace, err := ctx.GetNamespace() if err != nil { return err @@ -111,5 +111,5 @@ func (ctx *TestCtx) InitializeClusterResources(cleanupOptions *CleanupOptions) e if err != nil { return fmt.Errorf("failed to read namespaced manifest: %v", err) } - return ctx.createFromYAML(namespacedYAML, false, cleanupOptions) + return ctx.CreateFromYAML(namespacedYAML, false, cleanupOptions) } diff --git a/test/e2e/memcached_test.go b/test/e2e/memcached_test.go index ac127450a72..61947c78d37 100644 --- a/test/e2e/memcached_test.go +++ b/test/e2e/memcached_test.go @@ -258,13 +258,13 @@ func TestMemcached(t *testing.T) { if err != nil { t.Fatal(err) } - // hacky way to use createFromYAML without exposing the method - // create crd - filename := file.Name() - framework.Global.NamespacedManPath = filename - err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) + globalYAML, err := ioutil.ReadFile(file.Name()) if err != nil { - t.Fatal(err) + t.Fatalf("Failed to read global resource manifest: %v", err) + } + err = ctx.CreateFromYAML(globalYAML, false, &framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) + if err != nil { + t.Fatalf("Failed to create global resources: %v", err) } t.Log("Created global resources") @@ -337,7 +337,7 @@ func verifyLeader(t *testing.T, namespace string, f *framework.Framework, labels return true, nil }) if err != nil { - return nil, fmt.Errorf("error getting leader lock configmap: %v\n", err) + return nil, fmt.Errorf("error getting leader lock configmap: %v", err) } t.Logf("Found leader lock configmap %s\n", lockName) @@ -376,21 +376,8 @@ func verifyLeader(t *testing.T, namespace string, f *framework.Framework, labels } func memcachedScaleTest(t *testing.T, f *framework.Framework, ctx *framework.TestCtx) error { - // create example-memcached yaml file - filename := "deploy/cr.yaml" - err := ioutil.WriteFile(filename, - []byte(crYAML), - fileutil.DefaultFileMode) - if err != nil { - return err - } - // create memcached custom resource - framework.Global.NamespacedManPath = filename - err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) - if err != nil { - return err - } + err := ctx.CreateFromYAML([]byte(crYAML), false, &framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) t.Log("Created cr") namespace, err := ctx.GetNamespace() @@ -525,11 +512,13 @@ func MemcachedCluster(t *testing.T) { t.Fatal(err) } // create namespaced resources - filename := file.Name() - framework.Global.NamespacedManPath = filename - err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) + namespacedYAML, err := ioutil.ReadFile(file.Name()) if err != nil { - t.Fatal(err) + t.Fatalf("Failed to read namespaced resource manifest: %v", err) + } + err = ctx.CreateFromYAML(namespacedYAML, false, &framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) + if err != nil { + t.Fatalf("Failed to create namespaced resources: %v", err) } t.Log("Created namespaced resources") @@ -560,30 +549,38 @@ func MemcachedClusterTest(t *testing.T) { // get global framework variables ctx := framework.NewTestCtx(t) defer ctx.Cleanup() + cleanupOptions := &framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval} - // create sa - filename := "deploy/service_account.yaml" - framework.Global.NamespacedManPath = filename - err := ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) + // create service account + serviceAccountYAML, err := ioutil.ReadFile("deploy/service_account.yaml") if err != nil { - t.Fatal(err) + t.Fatalf("Failed to read service account manifest: %v", err) } - t.Log("Created sa") + err = ctx.CreateFromYAML(serviceAccountYAML, false, cleanupOptions) + if err != nil { + t.Fatalf("Failed to create service account: %v", err) + } + t.Log("Created service account") - // create rbac - filename = "deploy/role.yaml" - framework.Global.NamespacedManPath = filename - err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) + // create role + roleYAML, err := ioutil.ReadFile("deploy/role.yaml") if err != nil { - t.Fatal(err) + t.Fatalf("Failed to read role manifest: %v", err) + } + err = ctx.CreateFromYAML(roleYAML, false, cleanupOptions) + if err != nil { + t.Fatalf("Failed to create role: %v", err) } t.Log("Created role") - filename = "deploy/role_binding.yaml" - framework.Global.NamespacedManPath = filename - err = ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) + // create role binding + roleBindingYAML, err := ioutil.ReadFile("deploy/role_binding.yaml") if err != nil { - t.Fatal(err) + t.Fatalf("Failed to read role binding manifest: %v", err) + } + err = ctx.CreateFromYAML(roleBindingYAML, false, cleanupOptions) + if err != nil { + t.Fatalf("Failed to create role binding: %v", err) } t.Log("Created role_binding") From f0fee70e6ec9b0d2a4653960a77b70e8bf9521bc Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Wed, 13 Mar 2019 13:43:11 -0700 Subject: [PATCH 05/15] pkg/test: add godocs --- pkg/test/client.go | 6 ++++++ pkg/test/context.go | 6 ++++++ pkg/test/e2eutil/wait_util.go | 1 + pkg/test/framework.go | 2 ++ pkg/test/main_entry.go | 2 ++ pkg/test/resource_creator.go | 6 ++++++ 6 files changed, 23 insertions(+) diff --git a/pkg/test/client.go b/pkg/test/client.go index 931dce46240..c385cd5a9e3 100644 --- a/pkg/test/client.go +++ b/pkg/test/client.go @@ -30,6 +30,8 @@ type frameworkClient struct { var _ FrameworkClient = &frameworkClient{} +// FrameworkClient is a wrapper for the controller-runtime client with a modified Create function +// that automatically adds a cleanup function for the created resource type FrameworkClient interface { Get(gCtx goctx.Context, key dynclient.ObjectKey, obj runtime.Object) error List(gCtx goctx.Context, opts *dynclient.ListOptions, list runtime.Object) error @@ -89,18 +91,22 @@ func (f *frameworkClient) Create(gCtx goctx.Context, obj runtime.Object, cleanup return nil } +// Get is a simple wrapper for the controller-runtime client's Get function func (f *frameworkClient) Get(gCtx goctx.Context, key dynclient.ObjectKey, obj runtime.Object) error { return f.Client.Get(gCtx, key, obj) } +// List is a simple wrapper for the controller-runtime client's List function func (f *frameworkClient) List(gCtx goctx.Context, opts *dynclient.ListOptions, list runtime.Object) error { return f.Client.List(gCtx, opts, list) } +// Delete is a simple wrapper for the controller-runtime client's Delete function func (f *frameworkClient) Delete(gCtx goctx.Context, obj runtime.Object, opts ...dynclient.DeleteOptionFunc) error { return f.Client.Delete(gCtx, obj, opts...) } +// Update is a simple wrapper for the controller-runtime client's Update function func (f *frameworkClient) Update(gCtx goctx.Context, obj runtime.Object) error { return f.Client.Update(gCtx, obj) } diff --git a/pkg/test/context.go b/pkg/test/context.go index 53276526cde..9d947f322c7 100644 --- a/pkg/test/context.go +++ b/pkg/test/context.go @@ -23,6 +23,7 @@ import ( log "github.com/sirupsen/logrus" ) +// TestCtx contains the state of a test, which includes ID, namespace, and cleanup functions type TestCtx struct { id string cleanupFns []cleanupFn @@ -30,6 +31,7 @@ type TestCtx struct { t *testing.T } +// CleanupOptions allows for configuration of resource cleanup functions type CleanupOptions struct { TestContext *TestCtx Timeout time.Duration @@ -38,6 +40,7 @@ type CleanupOptions struct { type cleanupFn func() error +// NewTestCtx returns a new TestCtx object func NewTestCtx(t *testing.T) *TestCtx { var prefix string if t != nil { @@ -64,10 +67,12 @@ func NewTestCtx(t *testing.T) *TestCtx { } } +// GetID returns the ID of the TestCtx func (ctx *TestCtx) GetID() string { return ctx.id } +// Cleanup runs all the TestCtx's cleanup function in reverse order of their insertion func (ctx *TestCtx) Cleanup() { failed := false for i := len(ctx.cleanupFns) - 1; i >= 0; i-- { @@ -86,6 +91,7 @@ func (ctx *TestCtx) Cleanup() { } } +// AddCleanupFn adds a new cleanup function to the TestCtx func (ctx *TestCtx) AddCleanupFn(fn cleanupFn) { ctx.cleanupFns = append(ctx.cleanupFns, fn) } diff --git a/pkg/test/e2eutil/wait_util.go b/pkg/test/e2eutil/wait_util.go index 9b155bd930b..17dfaf4a238 100644 --- a/pkg/test/e2eutil/wait_util.go +++ b/pkg/test/e2eutil/wait_util.go @@ -71,6 +71,7 @@ func waitForDeployment(t *testing.T, kubeclient kubernetes.Interface, namespace, return nil } +// WaitForDeletion waits for a given object to be fully deleted according to the apiserver before returning func WaitForDeletion(t *testing.T, dynclient client.Client, obj runtime.Object, retryInterval, timeout time.Duration) error { key, err := client.ObjectKeyFromObject(obj) if err != nil { diff --git a/pkg/test/framework.go b/pkg/test/framework.go index 6d0db19d4c0..85a765ae90e 100755 --- a/pkg/test/framework.go +++ b/pkg/test/framework.go @@ -50,6 +50,7 @@ var ( restMapper *restmapper.DeferredDiscoveryRESTMapper ) +// Framework contains all relevant variables needed for running tests with the operator-sdk type Framework struct { Client *frameworkClient KubeConfig *rest.Config @@ -60,6 +61,7 @@ type Framework struct { LocalOperator bool } +// Setup initializes the Global.Framework variable and its fields func Setup(kubeconfigPath, namespacedManPath, namespace string, localOperator bool) error { var err error var kubeconfig *rest.Config diff --git a/pkg/test/main_entry.go b/pkg/test/main_entry.go index dc5c32379c0..0434a2a4823 100644 --- a/pkg/test/main_entry.go +++ b/pkg/test/main_entry.go @@ -44,6 +44,8 @@ const ( LocalOperatorFlag = "localOperator" ) +// MainEntry parses the flags set by the operator-sdk test command, configures the testing environment, and then +// runs the tests func MainEntry(m *testing.M) { projRoot := flag.String(ProjRootFlag, "", "path to project root") kubeconfigPath := flag.String(KubeConfigFlag, "", "path to kubeconfig") diff --git a/pkg/test/resource_creator.go b/pkg/test/resource_creator.go index 7b3f1c5b92f..5af9c208be6 100644 --- a/pkg/test/resource_creator.go +++ b/pkg/test/resource_creator.go @@ -29,6 +29,8 @@ import ( "k8s.io/apimachinery/pkg/util/wait" ) +// GetNamespace returns the namespace for the current context, creating a new namespace +// if one does not exist func (ctx *TestCtx) GetNamespace() (string, error) { if ctx.namespace != "" { return ctx.namespace, nil @@ -52,6 +54,8 @@ func (ctx *TestCtx) GetNamespace() (string, error) { return ctx.namespace, nil } +// CreateFromYAML takes a raw yaml file and creates the resource(s) in it in the cluster and adds cleanup functions +// for the created resource(s) func (ctx *TestCtx) CreateFromYAML(yamlFile []byte, skipIfExists bool, cleanupOptions *CleanupOptions) error { namespace, err := ctx.GetNamespace() if err != nil { @@ -105,6 +109,8 @@ func (ctx *TestCtx) CreateFromYAML(yamlFile []byte, skipIfExists bool, cleanupOp return nil } +// InitializeClusterResources creates all resources in the namespaced manifest file +// using the CreateFromYAML function func (ctx *TestCtx) InitializeClusterResources(cleanupOptions *CleanupOptions) error { // create namespaced resources namespacedYAML, err := ioutil.ReadFile(Global.NamespacedManPath) From 06f4086bc9c0356b1196d054fa27564183636cd8 Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Wed, 13 Mar 2019 13:52:55 -0700 Subject: [PATCH 06/15] pkg/test/resource_creator: add SetNamespace function --- pkg/test/resource_creator.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/test/resource_creator.go b/pkg/test/resource_creator.go index 5af9c208be6..779a8c7d394 100644 --- a/pkg/test/resource_creator.go +++ b/pkg/test/resource_creator.go @@ -30,7 +30,7 @@ import ( ) // GetNamespace returns the namespace for the current context, creating a new namespace -// if one does not exist +// if one does not exist using the context's ID as the new namespace's name func (ctx *TestCtx) GetNamespace() (string, error) { if ctx.namespace != "" { return ctx.namespace, nil @@ -54,6 +54,25 @@ func (ctx *TestCtx) GetNamespace() (string, error) { return ctx.namespace, nil } +// SetNamespace sets a static namespace for the current context. If the specifed namespace does not exist, +// it will be created and a cleanup function for the new namespace will be added to the context +func (ctx *TestCtx) SetNamespace(namespace string) error { + ctx.namespace = namespace + namespaceObj := &core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ctx.namespace}} + _, err := Global.KubeClient.CoreV1().Namespaces().Create(namespaceObj) + // do not add a cleanup function if the namespace already exists + if apierrors.IsAlreadyExists(err) { + return nil + } + if err != nil { + return err + } + ctx.AddCleanupFn(func() error { + return Global.KubeClient.CoreV1().Namespaces().Delete(ctx.namespace, metav1.NewDeleteOptions(0)) + }) + return nil +} + // CreateFromYAML takes a raw yaml file and creates the resource(s) in it in the cluster and adds cleanup functions // for the created resource(s) func (ctx *TestCtx) CreateFromYAML(yamlFile []byte, skipIfExists bool, cleanupOptions *CleanupOptions) error { From bd127031d1b357f20fa9301c3de18a9b077ba863 Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Thu, 14 Mar 2019 16:14:55 -0700 Subject: [PATCH 07/15] pkg/test: expose SingleNamespace and prevent nil pointer --- pkg/test/framework.go | 15 +++++++++++---- pkg/test/main_entry.go | 4 ++-- pkg/test/resource_creator.go | 2 +- test/test-framework/deploy/namespace-init.yaml | 2 ++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/pkg/test/framework.go b/pkg/test/framework.go index 85a765ae90e..3e45b4a574f 100755 --- a/pkg/test/framework.go +++ b/pkg/test/framework.go @@ -43,7 +43,7 @@ var ( // mutex for AddToFrameworkScheme mutex = sync.Mutex{} // whether to run tests in a single namespace - singleNamespace *bool + SingleNamespace *bool // decoder used by createFromYaml dynamicDecoder runtime.Decoder // restMapper for the dynamic client @@ -63,11 +63,18 @@ type Framework struct { // Setup initializes the Global.Framework variable and its fields func Setup(kubeconfigPath, namespacedManPath, namespace string, localOperator bool) error { + if SingleNamespace == nil { + a := false + SingleNamespace = &a + } + if namespace != "" { + *SingleNamespace = true + } var err error var kubeconfig *rest.Config if kubeconfigPath == "incluster" { // when running with an InCluster config, we don't have permission to create new namespaces - *singleNamespace = true + *SingleNamespace = true if len(namespace) == 0 { return fmt.Errorf("namespace must be set for in cluster testing mode") } @@ -90,7 +97,7 @@ func Setup(kubeconfigPath, namespacedManPath, namespace string, localOperator bo } else { var kcNamespace string kubeconfig, kcNamespace, err = k8sInternal.GetKubeconfigAndNamespace(kubeconfigPath) - if *singleNamespace && namespace == "" { + if *SingleNamespace && namespace == "" { namespace = kcNamespace } } @@ -153,7 +160,7 @@ func AddToFrameworkScheme(addToScheme addToSchemeFunc, obj runtime.Object) error } restMapper.Reset() err = wait.PollImmediate(time.Second, time.Second*10, func() (done bool, err error) { - if *singleNamespace { + if *SingleNamespace { err = Global.Client.List(goctx.TODO(), &dynclient.ListOptions{Namespace: Global.Namespace}, obj) } else { err = Global.Client.List(goctx.TODO(), &dynclient.ListOptions{Namespace: "default"}, obj) diff --git a/pkg/test/main_entry.go b/pkg/test/main_entry.go index 0434a2a4823..7525c8a2777 100644 --- a/pkg/test/main_entry.go +++ b/pkg/test/main_entry.go @@ -51,7 +51,7 @@ func MainEntry(m *testing.M) { kubeconfigPath := flag.String(KubeConfigFlag, "", "path to kubeconfig") globalManPath := flag.String(GlobalManPathFlag, "", "path to operator manifest") namespacedManPath := flag.String(NamespacedManPathFlag, "", "path to rbac manifest") - singleNamespace = flag.Bool(SingleNamespaceFlag, false, "enable single namespace mode") + SingleNamespace = flag.Bool(SingleNamespaceFlag, false, "enable single namespace mode") localOperator := flag.Bool(LocalOperatorFlag, false, "enable if operator is running locally (not in cluster)") flag.Parse() // go test always runs from the test directory; change to project root @@ -60,7 +60,7 @@ func MainEntry(m *testing.M) { log.Fatalf("Failed to change directory to project root: %v", err) } namespace := "" - if *singleNamespace || *kubeconfigPath == "incluster" { + if *SingleNamespace || *kubeconfigPath == "incluster" { namespace = os.Getenv(TestNamespaceEnv) } if err := Setup(*kubeconfigPath, *namespacedManPath, namespace, *localOperator); err != nil { diff --git a/pkg/test/resource_creator.go b/pkg/test/resource_creator.go index 779a8c7d394..5ad35c66c53 100644 --- a/pkg/test/resource_creator.go +++ b/pkg/test/resource_creator.go @@ -35,7 +35,7 @@ func (ctx *TestCtx) GetNamespace() (string, error) { if ctx.namespace != "" { return ctx.namespace, nil } - if *singleNamespace { + if *SingleNamespace { ctx.namespace = Global.Namespace return ctx.namespace, nil } diff --git a/test/test-framework/deploy/namespace-init.yaml b/test/test-framework/deploy/namespace-init.yaml index 749ed548a36..fc9b93f90d5 100644 --- a/test/test-framework/deploy/namespace-init.yaml +++ b/test/test-framework/deploy/namespace-init.yaml @@ -65,6 +65,7 @@ roleRef: apiVersion: apps/v1 kind: Deployment metadata: + creationTimestamp: null name: memcached-operator spec: replicas: 1 @@ -74,6 +75,7 @@ spec: strategy: {} template: metadata: + creationTimestamp: null labels: name: memcached-operator spec: From ee6c9dc2f4e5090e5fd56de54aa3fdfdf104fe09 Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Thu, 14 Mar 2019 16:28:37 -0700 Subject: [PATCH 08/15] pkg/test/context: don't make cleanup error fatal --- pkg/test/context.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkg/test/context.go b/pkg/test/context.go index 9d947f322c7..66eb0c22fba 100644 --- a/pkg/test/context.go +++ b/pkg/test/context.go @@ -74,11 +74,9 @@ func (ctx *TestCtx) GetID() string { // Cleanup runs all the TestCtx's cleanup function in reverse order of their insertion func (ctx *TestCtx) Cleanup() { - failed := false for i := len(ctx.cleanupFns) - 1; i >= 0; i-- { err := ctx.cleanupFns[i]() if err != nil { - failed = true if ctx.t != nil { ctx.t.Errorf("A cleanup function failed with error: (%v)\n", err) } else { @@ -86,9 +84,6 @@ func (ctx *TestCtx) Cleanup() { } } } - if ctx.t == nil && failed { - log.Fatal("A cleanup function failed") - } } // AddCleanupFn adds a new cleanup function to the TestCtx From b73cd052dba1cd65a38e3a15a258eeca34abc3f2 Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Mon, 18 Mar 2019 12:21:17 -0700 Subject: [PATCH 09/15] test/test-framework/.../namespace-init: reset file --- test/test-framework/deploy/namespace-init.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test-framework/deploy/namespace-init.yaml b/test/test-framework/deploy/namespace-init.yaml index fc9b93f90d5..749ed548a36 100644 --- a/test/test-framework/deploy/namespace-init.yaml +++ b/test/test-framework/deploy/namespace-init.yaml @@ -65,7 +65,6 @@ roleRef: apiVersion: apps/v1 kind: Deployment metadata: - creationTimestamp: null name: memcached-operator spec: replicas: 1 @@ -75,7 +74,6 @@ spec: strategy: {} template: metadata: - creationTimestamp: null labels: name: memcached-operator spec: From c7b682bf7512638c0407501c3d3fb2be960213f4 Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Mon, 18 Mar 2019 12:34:37 -0700 Subject: [PATCH 10/15] pkg/test: fix godoc punctuation --- pkg/test/client.go | 10 +++++----- pkg/test/context.go | 12 ++++++------ pkg/test/e2eutil/wait_util.go | 4 ++-- pkg/test/framework.go | 10 +++++----- pkg/test/main_entry.go | 2 +- pkg/test/resource_creator.go | 8 ++++---- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pkg/test/client.go b/pkg/test/client.go index c385cd5a9e3..339e4ac1fd3 100644 --- a/pkg/test/client.go +++ b/pkg/test/client.go @@ -31,7 +31,7 @@ type frameworkClient struct { var _ FrameworkClient = &frameworkClient{} // FrameworkClient is a wrapper for the controller-runtime client with a modified Create function -// that automatically adds a cleanup function for the created resource +// that automatically adds a cleanup function for the created resource. type FrameworkClient interface { Get(gCtx goctx.Context, key dynclient.ObjectKey, obj runtime.Object) error List(gCtx goctx.Context, opts *dynclient.ListOptions, list runtime.Object) error @@ -91,22 +91,22 @@ func (f *frameworkClient) Create(gCtx goctx.Context, obj runtime.Object, cleanup return nil } -// Get is a simple wrapper for the controller-runtime client's Get function +// Get is a simple wrapper for the controller-runtime client's Get function. func (f *frameworkClient) Get(gCtx goctx.Context, key dynclient.ObjectKey, obj runtime.Object) error { return f.Client.Get(gCtx, key, obj) } -// List is a simple wrapper for the controller-runtime client's List function +// List is a simple wrapper for the controller-runtime client's List function. func (f *frameworkClient) List(gCtx goctx.Context, opts *dynclient.ListOptions, list runtime.Object) error { return f.Client.List(gCtx, opts, list) } -// Delete is a simple wrapper for the controller-runtime client's Delete function +// Delete is a simple wrapper for the controller-runtime client's Delete function. func (f *frameworkClient) Delete(gCtx goctx.Context, obj runtime.Object, opts ...dynclient.DeleteOptionFunc) error { return f.Client.Delete(gCtx, obj, opts...) } -// Update is a simple wrapper for the controller-runtime client's Update function +// Update is a simple wrapper for the controller-runtime client's Update function. func (f *frameworkClient) Update(gCtx goctx.Context, obj runtime.Object) error { return f.Client.Update(gCtx, obj) } diff --git a/pkg/test/context.go b/pkg/test/context.go index 66eb0c22fba..5991c4729db 100644 --- a/pkg/test/context.go +++ b/pkg/test/context.go @@ -23,7 +23,7 @@ import ( log "github.com/sirupsen/logrus" ) -// TestCtx contains the state of a test, which includes ID, namespace, and cleanup functions +// TestCtx contains the state of a test, which includes ID, namespace, and cleanup functions. type TestCtx struct { id string cleanupFns []cleanupFn @@ -31,7 +31,7 @@ type TestCtx struct { t *testing.T } -// CleanupOptions allows for configuration of resource cleanup functions +// CleanupOptions allows for configuration of resource cleanup functions. type CleanupOptions struct { TestContext *TestCtx Timeout time.Duration @@ -40,7 +40,7 @@ type CleanupOptions struct { type cleanupFn func() error -// NewTestCtx returns a new TestCtx object +// NewTestCtx returns a new TestCtx object. func NewTestCtx(t *testing.T) *TestCtx { var prefix string if t != nil { @@ -67,12 +67,12 @@ func NewTestCtx(t *testing.T) *TestCtx { } } -// GetID returns the ID of the TestCtx +// GetID returns the ID of the TestCtx. func (ctx *TestCtx) GetID() string { return ctx.id } -// Cleanup runs all the TestCtx's cleanup function in reverse order of their insertion +// Cleanup runs all the TestCtx's cleanup function in reverse order of their insertion. func (ctx *TestCtx) Cleanup() { for i := len(ctx.cleanupFns) - 1; i >= 0; i-- { err := ctx.cleanupFns[i]() @@ -86,7 +86,7 @@ func (ctx *TestCtx) Cleanup() { } } -// AddCleanupFn adds a new cleanup function to the TestCtx +// AddCleanupFn adds a new cleanup function to the TestCtx. func (ctx *TestCtx) AddCleanupFn(fn cleanupFn) { ctx.cleanupFns = append(ctx.cleanupFns, fn) } diff --git a/pkg/test/e2eutil/wait_util.go b/pkg/test/e2eutil/wait_util.go index 17dfaf4a238..7d98032364c 100644 --- a/pkg/test/e2eutil/wait_util.go +++ b/pkg/test/e2eutil/wait_util.go @@ -38,7 +38,7 @@ func WaitForDeployment(t *testing.T, kubeclient kubernetes.Interface, namespace, } // WaitForOperatorDeployment has the same functionality as WaitForDeployment but will no wait for the deployment if the -// test was run with a locally run operator (--up-local flag) +// test was run with a locally run operator (--up-local flag). func WaitForOperatorDeployment(t *testing.T, kubeclient kubernetes.Interface, namespace, name string, replicas int, retryInterval, timeout time.Duration) error { return waitForDeployment(t, kubeclient, namespace, name, replicas, retryInterval, timeout, true) } @@ -71,7 +71,7 @@ func waitForDeployment(t *testing.T, kubeclient kubernetes.Interface, namespace, return nil } -// WaitForDeletion waits for a given object to be fully deleted according to the apiserver before returning +// WaitForDeletion waits for a given object to be fully deleted according to the apiserver before returning. func WaitForDeletion(t *testing.T, dynclient client.Client, obj runtime.Object, retryInterval, timeout time.Duration) error { key, err := client.ObjectKeyFromObject(obj) if err != nil { diff --git a/pkg/test/framework.go b/pkg/test/framework.go index 3e45b4a574f..f3c6c33a6e4 100755 --- a/pkg/test/framework.go +++ b/pkg/test/framework.go @@ -38,11 +38,11 @@ import ( ) var ( - // Global framework struct + // Global is a global framework struct that the test can use. Global *Framework // mutex for AddToFrameworkScheme mutex = sync.Mutex{} - // whether to run tests in a single namespace + // SingleNamespace determines whether tests are to be run in a single namespace or not. SingleNamespace *bool // decoder used by createFromYaml dynamicDecoder runtime.Decoder @@ -50,7 +50,7 @@ var ( restMapper *restmapper.DeferredDiscoveryRESTMapper ) -// Framework contains all relevant variables needed for running tests with the operator-sdk +// Framework contains all relevant variables needed for running tests with the operator-sdk. type Framework struct { Client *frameworkClient KubeConfig *rest.Config @@ -61,7 +61,7 @@ type Framework struct { LocalOperator bool } -// Setup initializes the Global.Framework variable and its fields +// Setup initializes the Global.Framework variable and its fields. func Setup(kubeconfigPath, namespacedManPath, namespace string, localOperator bool) error { if SingleNamespace == nil { a := false @@ -150,7 +150,7 @@ type addToSchemeFunc func(*runtime.Scheme) error // } // The List object is needed because the CRD has not always been fully registered // by the time this function is called. If the CRD takes more than 5 seconds to -// become ready, this function throws an error +// become ready, this function throws an error. func AddToFrameworkScheme(addToScheme addToSchemeFunc, obj runtime.Object) error { mutex.Lock() defer mutex.Unlock() diff --git a/pkg/test/main_entry.go b/pkg/test/main_entry.go index 7525c8a2777..2af20240ce9 100644 --- a/pkg/test/main_entry.go +++ b/pkg/test/main_entry.go @@ -45,7 +45,7 @@ const ( ) // MainEntry parses the flags set by the operator-sdk test command, configures the testing environment, and then -// runs the tests +// runs the tests. func MainEntry(m *testing.M) { projRoot := flag.String(ProjRootFlag, "", "path to project root") kubeconfigPath := flag.String(KubeConfigFlag, "", "path to kubeconfig") diff --git a/pkg/test/resource_creator.go b/pkg/test/resource_creator.go index 5ad35c66c53..0981bd8be84 100644 --- a/pkg/test/resource_creator.go +++ b/pkg/test/resource_creator.go @@ -30,7 +30,7 @@ import ( ) // GetNamespace returns the namespace for the current context, creating a new namespace -// if one does not exist using the context's ID as the new namespace's name +// if one does not exist using the context's ID as the new namespace's name. func (ctx *TestCtx) GetNamespace() (string, error) { if ctx.namespace != "" { return ctx.namespace, nil @@ -55,7 +55,7 @@ func (ctx *TestCtx) GetNamespace() (string, error) { } // SetNamespace sets a static namespace for the current context. If the specifed namespace does not exist, -// it will be created and a cleanup function for the new namespace will be added to the context +// it will be created and a cleanup function for the new namespace will be added to the context. func (ctx *TestCtx) SetNamespace(namespace string) error { ctx.namespace = namespace namespaceObj := &core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ctx.namespace}} @@ -74,7 +74,7 @@ func (ctx *TestCtx) SetNamespace(namespace string) error { } // CreateFromYAML takes a raw yaml file and creates the resource(s) in it in the cluster and adds cleanup functions -// for the created resource(s) +// for the created resource(s). func (ctx *TestCtx) CreateFromYAML(yamlFile []byte, skipIfExists bool, cleanupOptions *CleanupOptions) error { namespace, err := ctx.GetNamespace() if err != nil { @@ -129,7 +129,7 @@ func (ctx *TestCtx) CreateFromYAML(yamlFile []byte, skipIfExists bool, cleanupOp } // InitializeClusterResources creates all resources in the namespaced manifest file -// using the CreateFromYAML function +// using the CreateFromYAML function. func (ctx *TestCtx) InitializeClusterResources(cleanupOptions *CleanupOptions) error { // create namespaced resources namespacedYAML, err := ioutil.ReadFile(Global.NamespacedManPath) From f0d12ce5158d415769cfc3a5f175f4d0247f6ec6 Mon Sep 17 00:00:00 2001 From: Lili Cosic Date: Wed, 10 Apr 2019 11:12:25 -0700 Subject: [PATCH 11/15] Update pkg/test/resource_creator.go Co-Authored-By: AlexNPavel --- pkg/test/resource_creator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/test/resource_creator.go b/pkg/test/resource_creator.go index 0981bd8be84..8e775cbf51d 100644 --- a/pkg/test/resource_creator.go +++ b/pkg/test/resource_creator.go @@ -73,7 +73,7 @@ func (ctx *TestCtx) SetNamespace(namespace string) error { return nil } -// CreateFromYAML takes a raw yaml file and creates the resource(s) in it in the cluster and adds cleanup functions +// CreateFromYAML takes a raw YAML file and creates the resource(s) in the cluster and adds cleanup functions // for the created resource(s). func (ctx *TestCtx) CreateFromYAML(yamlFile []byte, skipIfExists bool, cleanupOptions *CleanupOptions) error { namespace, err := ctx.GetNamespace() From 2bb88b1706b5ec0a96e6968b9b9cfd9b5396b774 Mon Sep 17 00:00:00 2001 From: Alex Pavel Date: Wed, 10 Apr 2019 11:45:14 -0700 Subject: [PATCH 12/15] pkg/test: don't make SingleNamespace a pointer --- pkg/test/framework.go | 14 +++++--------- pkg/test/main_entry.go | 4 ++-- pkg/test/resource_creator.go | 2 +- test/test-framework/deploy/namespace-init.yaml | 2 ++ 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/pkg/test/framework.go b/pkg/test/framework.go index 984a8f2fb00..55ca18b429c 100755 --- a/pkg/test/framework.go +++ b/pkg/test/framework.go @@ -45,7 +45,7 @@ var ( // mutex for AddToFrameworkScheme mutex = sync.Mutex{} // SingleNamespace determines whether tests are to be run in a single namespace or not. - SingleNamespace *bool + SingleNamespace bool // decoder used by createFromYaml dynamicDecoder runtime.Decoder // restMapper for the dynamic client @@ -65,18 +65,14 @@ type Framework struct { // Setup initializes the Global.Framework variable and its fields. func Setup(kubeconfigPath, namespacedManPath, namespace string, localOperator bool) error { - if SingleNamespace == nil { - a := false - SingleNamespace = &a - } if namespace != "" { - *SingleNamespace = true + SingleNamespace = true } var err error var kubeconfig *rest.Config if kubeconfigPath == "incluster" { // when running with an InCluster config, we don't have permission to create new namespaces - *SingleNamespace = true + SingleNamespace = true if len(namespace) == 0 { return fmt.Errorf("namespace must be set for in cluster testing mode") } @@ -99,7 +95,7 @@ func Setup(kubeconfigPath, namespacedManPath, namespace string, localOperator bo } else { var kcNamespace string kubeconfig, kcNamespace, err = k8sInternal.GetKubeconfigAndNamespace(kubeconfigPath) - if *SingleNamespace && namespace == "" { + if SingleNamespace && namespace == "" { namespace = kcNamespace } } @@ -162,7 +158,7 @@ func AddToFrameworkScheme(addToScheme addToSchemeFunc, obj runtime.Object) error } restMapper.Reset() err = wait.PollImmediate(time.Second, time.Second*10, func() (done bool, err error) { - if *SingleNamespace { + if SingleNamespace { err = Global.Client.List(goctx.TODO(), &dynclient.ListOptions{Namespace: Global.Namespace}, obj) } else { err = Global.Client.List(goctx.TODO(), &dynclient.ListOptions{Namespace: "default"}, obj) diff --git a/pkg/test/main_entry.go b/pkg/test/main_entry.go index 4ed72df3a20..110871787c4 100644 --- a/pkg/test/main_entry.go +++ b/pkg/test/main_entry.go @@ -51,8 +51,8 @@ func MainEntry(m *testing.M) { kubeconfigPath := flag.String(KubeConfigFlag, "", "path to kubeconfig") globalManPath := flag.String(GlobalManPathFlag, "", "path to operator manifest") namespacedManPath := flag.String(NamespacedManPathFlag, "", "path to rbac manifest") - SingleNamespace = flag.Bool(SingleNamespaceFlag, false, "enable single namespace mode") localOperator := flag.Bool(LocalOperatorFlag, false, "enable if operator is running locally (not in cluster)") + flag.BoolVar(&SingleNamespace, SingleNamespaceFlag, false, "enable single namespace mode") flag.Parse() // go test always runs from the test directory; change to project root err := os.Chdir(*projRoot) @@ -60,7 +60,7 @@ func MainEntry(m *testing.M) { log.Fatalf("Failed to change directory to project root: %v", err) } namespace := "" - if *SingleNamespace || *kubeconfigPath == "incluster" { + if SingleNamespace || *kubeconfigPath == "incluster" { namespace = os.Getenv(TestNamespaceEnv) } if err := Setup(*kubeconfigPath, *namespacedManPath, namespace, *localOperator); err != nil { diff --git a/pkg/test/resource_creator.go b/pkg/test/resource_creator.go index 0981bd8be84..8f8503523eb 100644 --- a/pkg/test/resource_creator.go +++ b/pkg/test/resource_creator.go @@ -35,7 +35,7 @@ func (ctx *TestCtx) GetNamespace() (string, error) { if ctx.namespace != "" { return ctx.namespace, nil } - if *SingleNamespace { + if SingleNamespace { ctx.namespace = Global.Namespace return ctx.namespace, nil } diff --git a/test/test-framework/deploy/namespace-init.yaml b/test/test-framework/deploy/namespace-init.yaml index 749ed548a36..fc9b93f90d5 100644 --- a/test/test-framework/deploy/namespace-init.yaml +++ b/test/test-framework/deploy/namespace-init.yaml @@ -65,6 +65,7 @@ roleRef: apiVersion: apps/v1 kind: Deployment metadata: + creationTimestamp: null name: memcached-operator spec: replicas: 1 @@ -74,6 +75,7 @@ spec: strategy: {} template: metadata: + creationTimestamp: null labels: name: memcached-operator spec: From 3ad807f3da71c71d200b1147fb6b840228358cf1 Mon Sep 17 00:00:00 2001 From: Alex Pavel Date: Wed, 10 Apr 2019 11:45:42 -0700 Subject: [PATCH 13/15] test/e2e: rearrange an import --- test/e2e/memcached_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/memcached_test.go b/test/e2e/memcached_test.go index 833d2380c8f..468c3ecfc96 100644 --- a/test/e2e/memcached_test.go +++ b/test/e2e/memcached_test.go @@ -28,7 +28,6 @@ import ( "testing" "time" - "github.com/ghodss/yaml" "github.com/operator-framework/operator-sdk/internal/pkg/scaffold" "github.com/operator-framework/operator-sdk/internal/util/fileutil" "github.com/operator-framework/operator-sdk/internal/util/projutil" @@ -36,6 +35,7 @@ import ( framework "github.com/operator-framework/operator-sdk/pkg/test" "github.com/operator-framework/operator-sdk/pkg/test/e2eutil" + "github.com/ghodss/yaml" "github.com/prometheus/prometheus/util/promlint" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" From f09bb2bbe1fdc1862fd37e66ee04a6f132956dc2 Mon Sep 17 00:00:00 2001 From: Alex Pavel Date: Wed, 10 Apr 2019 13:59:20 -0700 Subject: [PATCH 14/15] pkg/test: change Setup signature and unexpose singleNamespace --- pkg/test/framework.go | 21 ++++++++++++--------- pkg/test/main_entry.go | 6 +++--- pkg/test/resource_creator.go | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/pkg/test/framework.go b/pkg/test/framework.go index 55ca18b429c..3744b42e000 100755 --- a/pkg/test/framework.go +++ b/pkg/test/framework.go @@ -44,8 +44,8 @@ var ( Global *Framework // mutex for AddToFrameworkScheme mutex = sync.Mutex{} - // SingleNamespace determines whether tests are to be run in a single namespace or not. - SingleNamespace bool + // singleNamespaceInternal determines whether tests are to be run in a single namespace or not. + singleNamespaceInternal bool // decoder used by createFromYaml dynamicDecoder runtime.Decoder // restMapper for the dynamic client @@ -64,15 +64,18 @@ type Framework struct { } // Setup initializes the Global.Framework variable and its fields. -func Setup(kubeconfigPath, namespacedManPath, namespace string, localOperator bool) error { - if namespace != "" { - SingleNamespace = true +func Setup(kubeconfigPath, namespacedManPath, namespace string, singleNamespace, localOperator bool) error { + if namespace != "" && !singleNamespace { + return fmt.Errorf("oneNamespace must be set to true if namespace is set") } + singleNamespaceInternal = singleNamespace var err error var kubeconfig *rest.Config if kubeconfigPath == "incluster" { - // when running with an InCluster config, we don't have permission to create new namespaces - SingleNamespace = true + // when running with an InCluster config, we don't have permission to create new namespaces, so we must be in single namespace mode + if singleNamespaceInternal != true { + return fmt.Errorf("oneNamespace must be set to true for in cluster testing mode") + } if len(namespace) == 0 { return fmt.Errorf("namespace must be set for in cluster testing mode") } @@ -95,7 +98,7 @@ func Setup(kubeconfigPath, namespacedManPath, namespace string, localOperator bo } else { var kcNamespace string kubeconfig, kcNamespace, err = k8sInternal.GetKubeconfigAndNamespace(kubeconfigPath) - if SingleNamespace && namespace == "" { + if singleNamespaceInternal && namespace == "" { namespace = kcNamespace } } @@ -158,7 +161,7 @@ func AddToFrameworkScheme(addToScheme addToSchemeFunc, obj runtime.Object) error } restMapper.Reset() err = wait.PollImmediate(time.Second, time.Second*10, func() (done bool, err error) { - if SingleNamespace { + if singleNamespaceInternal { err = Global.Client.List(goctx.TODO(), &dynclient.ListOptions{Namespace: Global.Namespace}, obj) } else { err = Global.Client.List(goctx.TODO(), &dynclient.ListOptions{Namespace: "default"}, obj) diff --git a/pkg/test/main_entry.go b/pkg/test/main_entry.go index 110871787c4..b9d87bea187 100644 --- a/pkg/test/main_entry.go +++ b/pkg/test/main_entry.go @@ -52,7 +52,7 @@ func MainEntry(m *testing.M) { globalManPath := flag.String(GlobalManPathFlag, "", "path to operator manifest") namespacedManPath := flag.String(NamespacedManPathFlag, "", "path to rbac manifest") localOperator := flag.Bool(LocalOperatorFlag, false, "enable if operator is running locally (not in cluster)") - flag.BoolVar(&SingleNamespace, SingleNamespaceFlag, false, "enable single namespace mode") + flag.BoolVar(&singleNamespaceInternal, SingleNamespaceFlag, false, "enable single namespace mode") flag.Parse() // go test always runs from the test directory; change to project root err := os.Chdir(*projRoot) @@ -60,10 +60,10 @@ func MainEntry(m *testing.M) { log.Fatalf("Failed to change directory to project root: %v", err) } namespace := "" - if SingleNamespace || *kubeconfigPath == "incluster" { + if singleNamespaceInternal || *kubeconfigPath == "incluster" { namespace = os.Getenv(TestNamespaceEnv) } - if err := Setup(*kubeconfigPath, *namespacedManPath, namespace, *localOperator); err != nil { + if err := Setup(*kubeconfigPath, *namespacedManPath, namespace, singleNamespaceInternal, *localOperator); err != nil { log.Fatalf("Failed to set up framework: %v", err) } // setup local operator command, but don't start it yet diff --git a/pkg/test/resource_creator.go b/pkg/test/resource_creator.go index f5ec449fed2..484e0c0b37b 100644 --- a/pkg/test/resource_creator.go +++ b/pkg/test/resource_creator.go @@ -35,7 +35,7 @@ func (ctx *TestCtx) GetNamespace() (string, error) { if ctx.namespace != "" { return ctx.namespace, nil } - if SingleNamespace { + if singleNamespaceInternal { ctx.namespace = Global.Namespace return ctx.namespace, nil } From 735c154b817e8a992f210687dee15780dd601178 Mon Sep 17 00:00:00 2001 From: Alex Pavel Date: Wed, 10 Apr 2019 15:33:22 -0700 Subject: [PATCH 15/15] pkg/test: fix in cluster testing mode --- pkg/test/framework.go | 2 +- pkg/test/main_entry.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/test/framework.go b/pkg/test/framework.go index 3744b42e000..0cb796cf87c 100755 --- a/pkg/test/framework.go +++ b/pkg/test/framework.go @@ -74,7 +74,7 @@ func Setup(kubeconfigPath, namespacedManPath, namespace string, singleNamespace, if kubeconfigPath == "incluster" { // when running with an InCluster config, we don't have permission to create new namespaces, so we must be in single namespace mode if singleNamespaceInternal != true { - return fmt.Errorf("oneNamespace must be set to true for in cluster testing mode") + return fmt.Errorf("singleNamespace must be set to true for in cluster testing mode") } if len(namespace) == 0 { return fmt.Errorf("namespace must be set for in cluster testing mode") diff --git a/pkg/test/main_entry.go b/pkg/test/main_entry.go index b9d87bea187..1b4aa3f6757 100644 --- a/pkg/test/main_entry.go +++ b/pkg/test/main_entry.go @@ -62,6 +62,8 @@ func MainEntry(m *testing.M) { namespace := "" if singleNamespaceInternal || *kubeconfigPath == "incluster" { namespace = os.Getenv(TestNamespaceEnv) + // if kubeconfig is set to incluster, make sure single namespace mode is set + singleNamespaceInternal = true } if err := Setup(*kubeconfigPath, *namespacedManPath, namespace, singleNamespaceInternal, *localOperator); err != nil { log.Fatalf("Failed to set up framework: %v", err)