From 7fa6b045995df26eab6721cface5ad80bbf411d2 Mon Sep 17 00:00:00 2001 From: Mateusz Urbanek Date: Fri, 2 Aug 2024 11:14:41 +0200 Subject: [PATCH 1/3] test: add e2e scaffolding Signed-off-by: Mateusz Urbanek --- test/e2e/assesments/cosi/cosi.go | 133 ++++++++++++++++++ test/e2e/cosi_test.go | 183 ++++++++++++++++++++++++ test/e2e/envfuncs/common.go | 21 +++ test/e2e/envfuncs/cosi.go | 118 ++++++++++++++++ test/e2e/envfuncs/helpers/helpers.go | 21 +++ test/e2e/envfuncs/kind.go | 88 ++++++++++++ test/e2e/envfuncs/ns.go | 39 +++++ test/e2e/go.mod | 67 +++++++++ test/e2e/go.sum | 203 +++++++++++++++++++++++++++ test/e2e/retry/retry.go | 54 +++++++ 10 files changed, 927 insertions(+) create mode 100644 test/e2e/assesments/cosi/cosi.go create mode 100644 test/e2e/cosi_test.go create mode 100644 test/e2e/envfuncs/common.go create mode 100644 test/e2e/envfuncs/cosi.go create mode 100644 test/e2e/envfuncs/helpers/helpers.go create mode 100644 test/e2e/envfuncs/kind.go create mode 100644 test/e2e/envfuncs/ns.go create mode 100644 test/e2e/go.mod create mode 100644 test/e2e/go.sum create mode 100644 test/e2e/retry/retry.go diff --git a/test/e2e/assesments/cosi/cosi.go b/test/e2e/assesments/cosi/cosi.go new file mode 100644 index 00000000..8a3f1daf --- /dev/null +++ b/test/e2e/assesments/cosi/cosi.go @@ -0,0 +1,133 @@ +package cosi + +import ( + "context" + "testing" + + appsv1 "k8s.io/api/apps/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + cosiv1alpha1 "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" + + "sigs.k8s.io/e2e-framework/klient/k8s/resources" + "sigs.k8s.io/e2e-framework/pkg/envconf" + "sigs.k8s.io/e2e-framework/pkg/types" +) + +// CRDsInstalled checks if the required COSI CRDs are installed in the cluster. +func CRDsInstalled() types.TestEnvFunc { + return func(ctx context.Context, cfg *envconf.Config, t *testing.T) (context.Context, error) { + var crds apiextensionsv1.CustomResourceDefinitionList + + expectedCRDs := []string{ + "bucketaccessclasses.objectstorage.k8s.io", + "bucketaccesses.objectstorage.k8s.io", + "bucketclaims.objectstorage.k8s.io", + "bucketclasses.objectstorage.k8s.io", + "buckets.objectstorage.k8s.io", + } + + if err := cfg.Client().Resources().List(ctx, &crds); err != nil { + t.Fatal(err) + } + + found := 0 + + for _, item := range crds.Items { + for _, expected := range expectedCRDs { + if item.GetObjectMeta().GetName() == expected { + found++ + } + } + } + + if len(expectedCRDs) != found { + t.Fatal("COSI CRDs not installed") + } + + return ctx, nil + } +} + +// ObjectstorageControllerInstalled checks if the COSI object storage controller deployment is installed. +func ObjectstorageControllerInstalled() types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + var deploymentList appsv1.DeploymentList + + selector := resources.WithLabelSelector("app.kubernetes.io/part-of=container-object-storage-interface") + + if err := cfg.Client().Resources().List(ctx, &deploymentList, selector); err != nil { + t.Fatal(err) + } + + if len(deploymentList.Items) == 0 { + t.Fatal("deployment not found") + } + + return ctx + } +} + +// BucketAccessStatusReady checks if the status of BucketAccess is ready. +func BucketAccessStatusReady(ready bool) types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + return ctx + } +} + +// BucketClaimStatusReady checks if the status of BucketClaim is ready. +func BucketClaimStatusReady(ready bool) types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + return ctx + } +} + +// CreateBucket creates a new Bucket resource. +func CreateBucket(bucket *cosiv1alpha1.Bucket) types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + return ctx + } +} + +// BucketExists checks if the Bucket resource exists. +func BucketExists(expected bool) types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + return ctx + } +} + +// CreateBucketClaim creates a new BucketClaim resource. +func CreateBucketClaim(bucketClaim *cosiv1alpha1.BucketClaim) types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + return ctx + } +} + +// DeleteBucketClaim deletes the specified BucketClaim resource. +func DeleteBucketClaim() types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + return ctx + } +} + +// CreateBucketAccess creates a new BucketAccess resource. +func CreateBucketAccess(bucketAccess *cosiv1alpha1.BucketAccess) types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + return ctx + } +} + +// SecretExists checks if the specified Secret resource exists. +func SecretExists(expected bool) types.StepFunc { + // TODO: this should also check if the format of the secret conforms + // to the expectations, so one more arg will be needed + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + return ctx + } +} + +// DeleteBucketAccess deletes the specified BucketAccess resource. +func DeleteBucketAccess() types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + return ctx + } +} diff --git a/test/e2e/cosi_test.go b/test/e2e/cosi_test.go new file mode 100644 index 00000000..0a19a3ed --- /dev/null +++ b/test/e2e/cosi_test.go @@ -0,0 +1,183 @@ +package cositest + +import ( + "e2e/assesments/cosi" + "e2e/envfuncs" + "flag" + "log" + "os" + "testing" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + cosiv1alpha1 "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" + + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" + "sigs.k8s.io/e2e-framework/pkg/env" + "sigs.k8s.io/e2e-framework/pkg/envconf" + "sigs.k8s.io/e2e-framework/pkg/features" +) + +var ( + testenv env.Environment + + noKinD bool + noInstallCRDs bool + noInstallController bool + noInstallSampleDriver bool + + foo string +) + +func init() { + apiextensionsv1.AddToScheme(scheme.Scheme) + cosiv1alpha1.AddToScheme(scheme.Scheme) +} + +func TestMain(m *testing.M) { + flag.BoolVar( + &noKinD, + "no-kind", + false, + "Start new environment with kind", + ) + flag.BoolVar( + &noInstallCRDs, + "cosi.no-install-crds", + false, + "Disable installing CRDs on cluster", + ) + flag.BoolVar( + &noInstallController, + "cosi.no-install-controller", + false, + "Disable installing COSI Controller on cluster", + ) + flag.BoolVar( + &noInstallSampleDriver, + "cosi.no-install-sample-driver", + false, + "Disable installing sample driver on cluster", + ) + flag.Parse() + + cfg, err := envconf.NewFromFlags() + if err != nil { + log.Fatalf("failed to build envconf from flags: %s", err) + } + testenv = env.NewWithConfig(cfg) + + testenv.Setup( + envfuncs.CreateCluster(noKinD), + + envfuncs.InstallCRDs(noInstallCRDs), + envfuncs.InstallController(noInstallController), + envfuncs.InstallDriver(noInstallSampleDriver), + + envfuncs.CreateBucketClass(false), + envfuncs.CreateBucketAccessClass(false), + ) + + testenv.Finish( + envfuncs.DeleteBucketAccessClass(false), + envfuncs.DeleteBucketClass(false), + + envfuncs.UninstallDriver(noInstallSampleDriver), + envfuncs.UninstallController(noInstallController), + envfuncs.UninstallCRDs(noInstallCRDs), + + envfuncs.DeleteCluster(noKinD), + ) + + testenv.BeforeEachTest( + envfuncs.IsClusterReadyTest(), + envfuncs.CreateNSForTest(), + cosi.CRDsInstalled(), + ) + + testenv.AfterEachTest( + envfuncs.DeleteNSForTest(), + ) + + os.Exit(testenv.Run(m)) +} + +func TestBucketProvisioning(t *testing.T) { + testenv.Test(t, + features.New("Greenfield Bucket"). + Assess("BucketClaim is created", + cosi.CreateBucketClaim(&v1alpha1.BucketClaim{})). + Assess("Bucket is created", + cosi.CreateBucket(&v1alpha1.Bucket{})). + Assess("BucketClaim has ready status", + cosi.BucketClaimStatusReady(true)). + Assess("BucketClaim is deleted", + cosi.DeleteBucketClaim()). + Assess("Bucket is deleted", + cosi.BucketExists(false)). + Feature(), + + features.New("Brownfield Bucket"). + Assess("BucketClaim is created", + cosi.CreateBucketClaim(&v1alpha1.BucketClaim{})). + Assess("Bucket is created", + cosi.CreateBucket(&v1alpha1.Bucket{})). + Assess("BucketClaim has ready status", + cosi.BucketClaimStatusReady(true)). + Assess("BucketClaim is deleted", + cosi.DeleteBucketClaim()). + Assess("Bucket is deleted", + cosi.BucketExists(false)). + Feature(), + ) +} + +func TestBucketAccessProvisioning(t *testing.T) { + testenv.Test(t, + features.New("BucketAccess for S3"). + Assess("BucketClaim is created", + cosi.CreateBucketClaim(&v1alpha1.BucketClaim{})). + Assess("Bucket is created", + cosi.CreateBucket(&v1alpha1.Bucket{})). + Assess("BucketClaim has ready status", + cosi.BucketClaimStatusReady(true)). + Assess("BucketAccess is created", + cosi.CreateBucketAccess(&v1alpha1.BucketAccess{})). + Assess("BucketAccess has ready status", + cosi.BucketAccessStatusReady(true)). + Assess("Secret is created", + cosi.SecretExists(true)). + Assess("BucketAccess is deleted", + cosi.DeleteBucketAccess()). + Assess("Secret is deleted", + cosi.SecretExists(false)). + Assess("BucketClaim is deleted", + cosi.DeleteBucketClaim()). + Assess("Bucket is deleted", + cosi.BucketExists(false)). + Feature(), + + features.New("BucketAccess for Azure"). + Assess("BucketClaim is created", + cosi.CreateBucketClaim(&v1alpha1.BucketClaim{})). + Assess("Bucket is created", + cosi.CreateBucket(&v1alpha1.Bucket{})). + Assess("BucketClaim has ready status", + cosi.BucketClaimStatusReady(true)). + Assess("BucketAccess is created", + cosi.CreateBucketAccess(&v1alpha1.BucketAccess{})). + Assess("BucketAccess has ready status", + cosi.BucketAccessStatusReady(true)). + Assess("Secret is created", + cosi.SecretExists(true)). + Assess("BucketAccess is deleted", + cosi.DeleteBucketAccess()). + Assess("Secret is deleted", + cosi.SecretExists(false)). + Assess("BucketClaim is deleted", + cosi.DeleteBucketClaim()). + Assess("Bucket is deleted", + cosi.BucketExists(false)). + Feature(), + ) +} diff --git a/test/e2e/envfuncs/common.go b/test/e2e/envfuncs/common.go new file mode 100644 index 00000000..583cf061 --- /dev/null +++ b/test/e2e/envfuncs/common.go @@ -0,0 +1,21 @@ +// Package envfuncs provides a collection of functions that return closures +// conforming to the types defined in sigs.k8s.io/e2e-framework/pkg/types . +// These functions are designed to be used within the Kubernetes end-to-end +// testing framework to facilitate the creation and management of test +// environments. + +package envfuncs + +import ( + "context" + + "sigs.k8s.io/e2e-framework/pkg/envconf" +) + +// Noop returns an EnvFunc that performs no operation. This can be used as a placeholder +// or default function within an environment configuration. +func Noop() func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} diff --git a/test/e2e/envfuncs/cosi.go b/test/e2e/envfuncs/cosi.go new file mode 100644 index 00000000..cd77b3fe --- /dev/null +++ b/test/e2e/envfuncs/cosi.go @@ -0,0 +1,118 @@ +package envfuncs + +import ( + "context" + + "sigs.k8s.io/e2e-framework/pkg/envconf" + "sigs.k8s.io/e2e-framework/pkg/types" +) + +// InstallCRDs installs the necessary CRDs unless skipping is specified. +func InstallCRDs(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} + +// UninstallCRDs uninstalls the necessary CRDs unless skipping is specified. +func UninstallCRDs(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} + +// InstallController installs the controller unless skipping is specified. +func InstallController(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} + +// UninstallController uninstalls the controller unless skipping is specified. +func UninstallController(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} + +// InstallDriver installs the driver unless skipping is specified. +func InstallDriver(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} + +// UninstallDriver uninstalls the driver unless skipping is specified. +func UninstallDriver(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} + +// CreateBucketClass creates a BucketClass unless skipping is specified. +func CreateBucketClass(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} + +// DeleteBucketClass deletes a BucketClass unless skipping is specified. +func DeleteBucketClass(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} + +// CreateBucketAccessClass creates a BucketAccessClass unless skipping is specified. +func CreateBucketAccessClass(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} + +// DeleteBucketAccessClass deletes a BucketAccessClass unless skipping is specified. +func DeleteBucketAccessClass(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + return ctx, nil + } +} diff --git a/test/e2e/envfuncs/helpers/helpers.go b/test/e2e/envfuncs/helpers/helpers.go new file mode 100644 index 00000000..bc625672 --- /dev/null +++ b/test/e2e/envfuncs/helpers/helpers.go @@ -0,0 +1,21 @@ +package helpers + +import ( + "strings" + "testing" +) + +type ( + NamespaceCtxKey string +) + +// GetNamespaceKey returns the context key for a given test +func GetNamespaceKey(t *testing.T) NamespaceCtxKey { + // When we pass t.Name() from inside an `assess` step, the name is in the form TestName/Features/Assess + if strings.Contains(t.Name(), "/") { + return NamespaceCtxKey(strings.Split(t.Name(), "/")[0]) + } + + // When pass t.Name() from inside a `testenv.BeforeEachTest` function, the name is just TestName + return NamespaceCtxKey(t.Name()) +} diff --git a/test/e2e/envfuncs/kind.go b/test/e2e/envfuncs/kind.go new file mode 100644 index 00000000..2ad89053 --- /dev/null +++ b/test/e2e/envfuncs/kind.go @@ -0,0 +1,88 @@ +package envfuncs + +import ( + "context" + "e2e/retry" + "fmt" + "testing" + + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/e2e-framework/pkg/envconf" + "sigs.k8s.io/e2e-framework/pkg/types" + "sigs.k8s.io/e2e-framework/support/kind" +) + +type ( + ClusterCtxKey string +) + +// CreateCluster creates a new Kubernetes cluster unless skipping is specified. +func CreateCluster(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + name := envconf.RandomName("cosi-e2e-cluster", 32) + cluster := kind.NewCluster(name) + kubeconfig, err := cluster.Create(ctx) + if err != nil { + return ctx, err + } + + // stall a bit to allow most pods to come up + ctx, err = IsClusterReady()(ctx, cfg) + if err != nil { + return ctx, err + } + + // update environment with kubecofig file + cfg.WithKubeconfigFile(kubeconfig) + + // propagate cluster value + return context.WithValue(ctx, ClusterCtxKey("cluster"), cluster), nil + } +} + +// IsClusterReady checks if the Kubernetes cluster is ready. +func IsClusterReady() types.EnvFunc { + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + clientset, err := kubernetes.NewForConfig(cfg.Client().RESTConfig()) + if err != nil { + return ctx, fmt.Errorf("failed to create clientset from klient: %w", err) + } + + r := retry.NewLinearBackoffRetry() + + return ctx, r.Retry(func() error { + _, err := clientset.ServerVersion() + return err + }) + } +} + +func IsClusterReadyTest() types.TestEnvFunc { + return func(ctx context.Context, cfg *envconf.Config, t *testing.T) (context.Context, error) { + return IsClusterReady()(ctx, cfg) + } +} + +// DeleteCluster deletes the Kubernetes cluster unless skipping is specified. +func DeleteCluster(skip bool) types.EnvFunc { + if skip { + return Noop() + } + + return func(ctx context.Context, _ *envconf.Config) (context.Context, error) { + cluster := ctx.Value(ClusterCtxKey("cluster")).(*kind.Cluster) + if cluster == nil { + return ctx, fmt.Errorf("error getting kind cluster from context") + } + + if err := cluster.Destroy(ctx); err != nil { + return ctx, err + } + + return ctx, nil + } +} diff --git a/test/e2e/envfuncs/ns.go b/test/e2e/envfuncs/ns.go new file mode 100644 index 00000000..a399ff4e --- /dev/null +++ b/test/e2e/envfuncs/ns.go @@ -0,0 +1,39 @@ +package envfuncs + +import ( + "context" + "fmt" + "testing" + + "e2e/envfuncs/helpers" + + v1 "k8s.io/api/core/v1" + "sigs.k8s.io/e2e-framework/pkg/envconf" + "sigs.k8s.io/e2e-framework/pkg/types" +) + +// CreateNSForTest creates a random namespace with the runID as a prefix. It is stored in the context +// so that the deleteNSForTest routine can look it up and delete it. +func CreateNSForTest() types.TestEnvFunc { + return func(ctx context.Context, cfg *envconf.Config, t *testing.T) (context.Context, error) { + ns := envconf.RandomName("e2e", 16) + ctx = context.WithValue(ctx, helpers.GetNamespaceKey(t), ns) + + t.Logf("Creating NS %v for test %v", ns, t.Name()) + nsObj := v1.Namespace{} + nsObj.Name = ns + return ctx, cfg.Client().Resources().Create(ctx, &nsObj) + } +} + +// DeleteNSForTest looks up the namespace corresponding to the given test and deletes it. +func DeleteNSForTest() types.TestEnvFunc { + return func(ctx context.Context, cfg *envconf.Config, t *testing.T) (context.Context, error) { + ns := fmt.Sprint(ctx.Value(helpers.GetNamespaceKey(t))) + t.Logf("Deleting NS %v for test %v", ns, t.Name()) + + nsObj := v1.Namespace{} + nsObj.Name = ns + return ctx, cfg.Client().Resources().Delete(ctx, &nsObj) + } +} diff --git a/test/e2e/go.mod b/test/e2e/go.mod new file mode 100644 index 00000000..ea8f2f49 --- /dev/null +++ b/test/e2e/go.mod @@ -0,0 +1,67 @@ +module e2e + +go 1.22.5 + +require ( + k8s.io/api v0.30.1 + k8s.io/apiextensions-apiserver v0.30.0 + k8s.io/client-go v0.30.1 + sigs.k8s.io/container-object-storage-interface-api v0.1.0 + sigs.k8s.io/e2e-framework v0.4.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/imdario/mergo v0.3.15 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/vladimirvivien/gexe v0.2.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apimachinery v0.30.1 // indirect + k8s.io/component-base v0.30.1 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/controller-runtime v0.18.2 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/test/e2e/go.sum b/test/e2e/go.sum new file mode 100644 index 00000000..f99eb680 --- /dev/null +++ b/test/e2e/go.sum @@ -0,0 +1,203 @@ +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= +github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/vladimirvivien/gexe v0.2.0 h1:nbdAQ6vbZ+ZNsolCgSVb9Fno60kzSuvtzVh6Ytqi/xY= +github.com/vladimirvivien/gexe v0.2.0/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6eBht9vGVst8w= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY= +k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM= +k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs= +k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y= +k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U= +k8s.io/apimachinery v0.30.1/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.1 h1:uC/Ir6A3R46wdkgCV3vbLyNOYyCJ8oZnjtJGKfytl/Q= +k8s.io/client-go v0.30.1/go.mod h1:wrAqLNs2trwiCH/wxxmT/x3hKVH9PuV0GGW0oDoHVqc= +k8s.io/component-base v0.30.1 h1:bvAtlPh1UrdaZL20D9+sWxsJljMi0QZ3Lmw+kmZAaxQ= +k8s.io/component-base v0.30.1/go.mod h1:e/X9kDiOebwlI41AvBHuWdqFriSRrX50CdwA9TFaHLI= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/container-object-storage-interface-api v0.1.0 h1:8tB6JFQhbQIC1hwGQ+q4+tmSSNfjKemb7bFI6C0CK/4= +sigs.k8s.io/container-object-storage-interface-api v0.1.0/go.mod h1:YiB+i/UGkzqgODDhRG3u7jkbWkQcoUeLEJ7hwOT/2Qk= +sigs.k8s.io/controller-runtime v0.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q= +sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= +sigs.k8s.io/e2e-framework v0.4.0 h1:4yYmFDNNoTnazqmZJXQ6dlQF1vrnDbutmxlyvBpC5rY= +sigs.k8s.io/e2e-framework v0.4.0/go.mod h1:JilFQPF1OL1728ABhMlf9huse7h+uBJDXl9YeTs49A8= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/test/e2e/retry/retry.go b/test/e2e/retry/retry.go new file mode 100644 index 00000000..f1098b43 --- /dev/null +++ b/test/e2e/retry/retry.go @@ -0,0 +1,54 @@ +package retry + +import ( + "fmt" + "time" +) + +var ( + defaultMaxRetries int64 = 10 + defaultInitialDelay time.Duration = 250 * time.Millisecond + defaultMaxDelay time.Duration = time.Duration(defaultMaxRetries) * defaultInitialDelay +) + +type Retrier interface { + Retry(func() error) error +} + +type LinearBackoffRetry struct { + MaxRetries int64 + InitialDelay time.Duration + MaxDelay time.Duration +} + +var _ Retrier = (*LinearBackoffRetry)(nil) + +func NewLinearBackoffRetry() *LinearBackoffRetry { + return &LinearBackoffRetry{ + MaxRetries: defaultMaxRetries, + InitialDelay: defaultInitialDelay, + MaxDelay: defaultMaxDelay, + } +} + +func (r *LinearBackoffRetry) Retry(fn func() error) error { + retries := int64(0) + + for { + err := fn() + if err == nil { + return nil + } + + retries++ + if retries > r.MaxRetries { + return fmt.Errorf("max retries exceeded, last error: %w", err) + } + + delay := r.InitialDelay * time.Duration(retries) + if delay > r.MaxDelay { + delay = r.MaxDelay + } + time.Sleep(delay) + } +} From c81bff0aa19d5cfb6487747908ee44fa03e142cb Mon Sep 17 00:00:00 2001 From: Mateusz Urbanek Date: Wed, 21 Aug 2024 13:27:37 +0200 Subject: [PATCH 2/3] feat: implementation of assesments Signed-off-by: Mateusz Urbanek --- test/e2e/assesments/cosi/cosi.go | 161 +++++++++++++++++++++++++++++-- test/e2e/cosi_test.go | 44 ++++++--- test/e2e/go.mod | 2 +- test/e2e/setup/setup.go | 44 +++++++++ 4 files changed, 228 insertions(+), 23 deletions(-) create mode 100644 test/e2e/setup/setup.go diff --git a/test/e2e/assesments/cosi/cosi.go b/test/e2e/assesments/cosi/cosi.go index 8a3f1daf..a0649c8a 100644 --- a/test/e2e/assesments/cosi/cosi.go +++ b/test/e2e/assesments/cosi/cosi.go @@ -2,10 +2,15 @@ package cosi import ( "context" + "e2e/retry" + "e2e/setup" + "fmt" "testing" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/api/errors" cosiv1alpha1 "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" "sigs.k8s.io/e2e-framework/klient/k8s/resources" @@ -67,9 +72,38 @@ func ObjectstorageControllerInstalled() types.StepFunc { } } -// BucketAccessStatusReady checks if the status of BucketAccess is ready. -func BucketAccessStatusReady(ready bool) types.StepFunc { +// BucketAccessStatusGranted checks if the status of BucketAccess is granted. +func BucketAccessStatusGranted(granted bool) types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + bucketAccess := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketAccess) + + r := retry.NewLinearBackoffRetry() + + if err := r.Retry(func() error { + actualBucketAccess := &cosiv1alpha1.BucketAccess{} + + err := cfg.Client().Resources().Get(ctx, + bucketAccess.Name, + bucketAccess.Namespace, + actualBucketAccess, + ) + + if err != nil { + return err + } + + if actualBucketAccess.Status.AccessGranted != granted { + return fmt.Errorf("expected: %v, actual: %v", + granted, + actualBucketAccess.Status.AccessGranted, + ) + } + + return nil + }); err != nil { + t.Fatal(err) + } + return ctx } } @@ -77,13 +111,48 @@ func BucketAccessStatusReady(ready bool) types.StepFunc { // BucketClaimStatusReady checks if the status of BucketClaim is ready. func BucketClaimStatusReady(ready bool) types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + bucketClaim := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketClaim) + + r := retry.NewLinearBackoffRetry() + + if err := r.Retry(func() error { + actualBucketClaim := &cosiv1alpha1.BucketClaim{} + + err := cfg.Client().Resources().Get(ctx, + bucketClaim.Name, + bucketClaim.Namespace, + actualBucketClaim, + ) + + if err != nil { + return err + } + + if actualBucketClaim.Status.BucketReady != ready { + return fmt.Errorf("expected: %v, actual: %v", + ready, + actualBucketClaim.Status.BucketReady, + ) + } + + return err + }); err != nil { + t.Fatal(err) + } + return ctx } } // CreateBucket creates a new Bucket resource. -func CreateBucket(bucket *cosiv1alpha1.Bucket) types.StepFunc { +func CreateBucket() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + bucket := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.Bucket) + + if err := cfg.Client().Resources().Create(ctx, bucket); err != nil { + t.Fatal(err) + } + return ctx } } @@ -91,13 +160,46 @@ func CreateBucket(bucket *cosiv1alpha1.Bucket) types.StepFunc { // BucketExists checks if the Bucket resource exists. func BucketExists(expected bool) types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + bucketClaim := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketClaim) + + r := retry.NewLinearBackoffRetry() + + if err := r.Retry(func() error { + bucket := &cosiv1alpha1.Bucket{} + + err := cfg.Client().Resources().Get(ctx, + bucketClaim.Status.BucketName, + bucketClaim.Namespace, + bucket, + ) + + if errors.IsNotFound(err) { + if expected { + return err + } + // else ignore error + } else if err != nil { + return err + } + + return nil + }); err != nil { + t.Fatal(err) + } + return ctx } } // CreateBucketClaim creates a new BucketClaim resource. -func CreateBucketClaim(bucketClaim *cosiv1alpha1.BucketClaim) types.StepFunc { +func CreateBucketClaim() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + bucketClaim := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketClaim) + + if err := cfg.Client().Resources().Create(ctx, bucketClaim); err != nil { + t.Fatal(err) + } + return ctx } } @@ -105,22 +207,61 @@ func CreateBucketClaim(bucketClaim *cosiv1alpha1.BucketClaim) types.StepFunc { // DeleteBucketClaim deletes the specified BucketClaim resource. func DeleteBucketClaim() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + bucketClaim := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketClaim) + + if err := cfg.Client().Resources().Delete(ctx, bucketClaim); err != nil { + t.Fatal(err) + } + return ctx } } // CreateBucketAccess creates a new BucketAccess resource. -func CreateBucketAccess(bucketAccess *cosiv1alpha1.BucketAccess) types.StepFunc { +func CreateBucketAccess() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + bucketAccess := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketAccess) + + if err := cfg.Client().Resources().Create(ctx, bucketAccess); err != nil { + t.Fatal(err) + } + return ctx } } // SecretExists checks if the specified Secret resource exists. func SecretExists(expected bool) types.StepFunc { - // TODO: this should also check if the format of the secret conforms - // to the expectations, so one more arg will be needed return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + bucketAccess := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketAccess) + + r := retry.NewLinearBackoffRetry() + + if err := r.Retry(func() error { + // TODO: this should also check if the format of the secret conforms + // to the expectations, so one more arg will be needed + secret := &corev1.Secret{} + + err := cfg.Client().Resources().Get(ctx, + bucketAccess.Spec.CredentialsSecretName, + bucketAccess.Namespace, + secret, + ) + + if errors.IsNotFound(err) { + if expected { + return err + } + // else ignore error + } else if err != nil { + return err + } + + return nil + }); err != nil { + t.Fatal(err) + } + return ctx } } @@ -128,6 +269,12 @@ func SecretExists(expected bool) types.StepFunc { // DeleteBucketAccess deletes the specified BucketAccess resource. func DeleteBucketAccess() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + bucketAccess := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketAccess) + + if err := cfg.Client().Resources().Delete(ctx, bucketAccess); err != nil { + t.Fatal(err) + } + return ctx } } diff --git a/test/e2e/cosi_test.go b/test/e2e/cosi_test.go index 0a19a3ed..0d5fca71 100644 --- a/test/e2e/cosi_test.go +++ b/test/e2e/cosi_test.go @@ -3,16 +3,17 @@ package cositest import ( "e2e/assesments/cosi" "e2e/envfuncs" + "e2e/setup" "flag" "log" "os" "testing" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" cosiv1alpha1 "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" "sigs.k8s.io/e2e-framework/pkg/env" "sigs.k8s.io/e2e-framework/pkg/envconf" "sigs.k8s.io/e2e-framework/pkg/features" @@ -25,8 +26,6 @@ var ( noInstallCRDs bool noInstallController bool noInstallSampleDriver bool - - foo string ) func init() { @@ -105,10 +104,13 @@ func TestMain(m *testing.M) { func TestBucketProvisioning(t *testing.T) { testenv.Test(t, features.New("Greenfield Bucket"). + Setup(setup.RegisterResourcesForTest( + &v1alpha1.BucketClaim{}, + )). Assess("BucketClaim is created", - cosi.CreateBucketClaim(&v1alpha1.BucketClaim{})). + cosi.CreateBucketClaim()). Assess("Bucket is created", - cosi.CreateBucket(&v1alpha1.Bucket{})). + cosi.BucketExists(true)). Assess("BucketClaim has ready status", cosi.BucketClaimStatusReady(true)). Assess("BucketClaim is deleted", @@ -118,10 +120,14 @@ func TestBucketProvisioning(t *testing.T) { Feature(), features.New("Brownfield Bucket"). + Setup(setup.RegisterResourcesForTest( + &v1alpha1.Bucket{}, + &v1alpha1.BucketClaim{}, + )). Assess("BucketClaim is created", - cosi.CreateBucketClaim(&v1alpha1.BucketClaim{})). + cosi.CreateBucketClaim()). Assess("Bucket is created", - cosi.CreateBucket(&v1alpha1.Bucket{})). + cosi.CreateBucket()). Assess("BucketClaim has ready status", cosi.BucketClaimStatusReady(true)). Assess("BucketClaim is deleted", @@ -135,16 +141,20 @@ func TestBucketProvisioning(t *testing.T) { func TestBucketAccessProvisioning(t *testing.T) { testenv.Test(t, features.New("BucketAccess for S3"). + Setup(setup.RegisterResourcesForTest( + &v1alpha1.BucketClaim{}, + &v1alpha1.BucketAccess{}, + )). Assess("BucketClaim is created", - cosi.CreateBucketClaim(&v1alpha1.BucketClaim{})). + cosi.CreateBucketClaim()). Assess("Bucket is created", - cosi.CreateBucket(&v1alpha1.Bucket{})). + cosi.BucketExists(true)). Assess("BucketClaim has ready status", cosi.BucketClaimStatusReady(true)). Assess("BucketAccess is created", - cosi.CreateBucketAccess(&v1alpha1.BucketAccess{})). + cosi.CreateBucketAccess()). Assess("BucketAccess has ready status", - cosi.BucketAccessStatusReady(true)). + cosi.BucketAccessStatusGranted(true)). Assess("Secret is created", cosi.SecretExists(true)). Assess("BucketAccess is deleted", @@ -158,16 +168,20 @@ func TestBucketAccessProvisioning(t *testing.T) { Feature(), features.New("BucketAccess for Azure"). + Setup(setup.RegisterResourcesForTest( + &v1alpha1.BucketClaim{}, + &v1alpha1.BucketAccess{}, + )). Assess("BucketClaim is created", - cosi.CreateBucketClaim(&v1alpha1.BucketClaim{})). + cosi.CreateBucketClaim()). Assess("Bucket is created", - cosi.CreateBucket(&v1alpha1.Bucket{})). + cosi.BucketExists(true)). Assess("BucketClaim has ready status", cosi.BucketClaimStatusReady(true)). Assess("BucketAccess is created", - cosi.CreateBucketAccess(&v1alpha1.BucketAccess{})). + cosi.CreateBucketAccess()). Assess("BucketAccess has ready status", - cosi.BucketAccessStatusReady(true)). + cosi.BucketAccessStatusGranted(true)). Assess("Secret is created", cosi.SecretExists(true)). Assess("BucketAccess is deleted", diff --git a/test/e2e/go.mod b/test/e2e/go.mod index ea8f2f49..c6304936 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -5,6 +5,7 @@ go 1.22.5 require ( k8s.io/api v0.30.1 k8s.io/apiextensions-apiserver v0.30.0 + k8s.io/apimachinery v0.30.1 k8s.io/client-go v0.30.1 sigs.k8s.io/container-object-storage-interface-api v0.1.0 sigs.k8s.io/e2e-framework v0.4.0 @@ -55,7 +56,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apimachinery v0.30.1 // indirect k8s.io/component-base v0.30.1 // indirect k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect diff --git a/test/e2e/setup/setup.go b/test/e2e/setup/setup.go new file mode 100644 index 00000000..c6a1665c --- /dev/null +++ b/test/e2e/setup/setup.go @@ -0,0 +1,44 @@ +package setup + +import ( + "context" + "fmt" + "testing" + + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" + "sigs.k8s.io/e2e-framework/pkg/envconf" + "sigs.k8s.io/e2e-framework/pkg/types" +) + +type ( + TypeCtxKey string +) + +const ( + BucketTypeCtxKey = TypeCtxKey("cosi.Bucket") + BucketClaimTypeCtxKey = TypeCtxKey("cosi.BucketClaim") + BucketAccessTypeCtxKey = TypeCtxKey("cosi.BucketAccess") +) + +func RegisterResourcesForTest(objects ...runtime.Object) types.StepFunc { + return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + for _, obj := range objects { + switch typedObj := obj.(type) { + case *v1alpha1.Bucket: + ctx = context.WithValue(ctx, BucketTypeCtxKey, typedObj) + + case *v1alpha1.BucketClaim: + ctx = context.WithValue(ctx, BucketClaimTypeCtxKey, typedObj) + + case *v1alpha1.BucketAccess: + ctx = context.WithValue(ctx, BucketAccessTypeCtxKey, typedObj) + + default: + panic(fmt.Sprintf("invalid type: %T (Kind: %s)", typedObj, obj.GetObjectKind())) + } + } + + return ctx + } +} From 93a9311c4242e8518025dd647c638a7106866a72 Mon Sep 17 00:00:00 2001 From: Mateusz Urbanek Date: Fri, 23 Aug 2024 11:19:52 +0200 Subject: [PATCH 3/3] ... Signed-off-by: Mateusz Urbanek --- test/e2e/assesments/{cosi => }/cosi.go | 103 ++++++++++++------ test/e2e/{setup => assesments}/setup.go | 16 +-- test/e2e/cosi_test.go | 132 ++++++++++++++---------- test/e2e/envfuncs/cosi.go | 44 ++++++++ test/e2e/envfuncs/helpers/helpers.go | 26 +++++ test/e2e/envfuncs/kind.go | 7 +- test/e2e/envfuncs/ns.go | 6 +- test/e2e/go.mod | 2 +- 8 files changed, 237 insertions(+), 99 deletions(-) rename test/e2e/assesments/{cosi => }/cosi.go (74%) rename test/e2e/{setup => assesments}/setup.go (70%) diff --git a/test/e2e/assesments/cosi/cosi.go b/test/e2e/assesments/cosi.go similarity index 74% rename from test/e2e/assesments/cosi/cosi.go rename to test/e2e/assesments/cosi.go index a0649c8a..95e34fb0 100644 --- a/test/e2e/assesments/cosi/cosi.go +++ b/test/e2e/assesments/cosi.go @@ -1,17 +1,16 @@ -package cosi +package assesments import ( "context" "e2e/retry" - "e2e/setup" "fmt" "testing" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apps "k8s.io/api/apps/v1" + core "k8s.io/api/core/v1" + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/errors" - cosiv1alpha1 "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" + cosi "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" "sigs.k8s.io/e2e-framework/klient/k8s/resources" "sigs.k8s.io/e2e-framework/pkg/envconf" @@ -21,7 +20,7 @@ import ( // CRDsInstalled checks if the required COSI CRDs are installed in the cluster. func CRDsInstalled() types.TestEnvFunc { return func(ctx context.Context, cfg *envconf.Config, t *testing.T) (context.Context, error) { - var crds apiextensionsv1.CustomResourceDefinitionList + var crds apiextensions.CustomResourceDefinitionList expectedCRDs := []string{ "bucketaccessclasses.objectstorage.k8s.io", @@ -32,7 +31,7 @@ func CRDsInstalled() types.TestEnvFunc { } if err := cfg.Client().Resources().List(ctx, &crds); err != nil { - t.Fatal(err) + t.Error(err) } found := 0 @@ -46,7 +45,7 @@ func CRDsInstalled() types.TestEnvFunc { } if len(expectedCRDs) != found { - t.Fatal("COSI CRDs not installed") + t.Error("COSI CRDs not installed") } return ctx, nil @@ -56,16 +55,16 @@ func CRDsInstalled() types.TestEnvFunc { // ObjectstorageControllerInstalled checks if the COSI object storage controller deployment is installed. func ObjectstorageControllerInstalled() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - var deploymentList appsv1.DeploymentList + var deploymentList apps.DeploymentList selector := resources.WithLabelSelector("app.kubernetes.io/part-of=container-object-storage-interface") if err := cfg.Client().Resources().List(ctx, &deploymentList, selector); err != nil { - t.Fatal(err) + t.Error(err) } if len(deploymentList.Items) == 0 { - t.Fatal("deployment not found") + t.Error("deployment not found") } return ctx @@ -75,12 +74,16 @@ func ObjectstorageControllerInstalled() types.StepFunc { // BucketAccessStatusGranted checks if the status of BucketAccess is granted. func BucketAccessStatusGranted(granted bool) types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - bucketAccess := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketAccess) + bucketAccess, ok := ctx.Value(BucketAccessTypeCtxKey).(*cosi.BucketAccess) + if !ok { + t.Error(ErrKeyNotFound) + return ctx + } r := retry.NewLinearBackoffRetry() if err := r.Retry(func() error { - actualBucketAccess := &cosiv1alpha1.BucketAccess{} + actualBucketAccess := &cosi.BucketAccess{} err := cfg.Client().Resources().Get(ctx, bucketAccess.Name, @@ -101,7 +104,7 @@ func BucketAccessStatusGranted(granted bool) types.StepFunc { return nil }); err != nil { - t.Fatal(err) + t.Error(err) } return ctx @@ -111,12 +114,16 @@ func BucketAccessStatusGranted(granted bool) types.StepFunc { // BucketClaimStatusReady checks if the status of BucketClaim is ready. func BucketClaimStatusReady(ready bool) types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - bucketClaim := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketClaim) + bucketClaim, ok := ctx.Value(BucketAccessTypeCtxKey).(*cosi.BucketClaim) + if !ok { + t.Error(ErrKeyNotFound) + return ctx + } r := retry.NewLinearBackoffRetry() if err := r.Retry(func() error { - actualBucketClaim := &cosiv1alpha1.BucketClaim{} + actualBucketClaim := &cosi.BucketClaim{} err := cfg.Client().Resources().Get(ctx, bucketClaim.Name, @@ -137,7 +144,7 @@ func BucketClaimStatusReady(ready bool) types.StepFunc { return err }); err != nil { - t.Fatal(err) + t.Error(err) } return ctx @@ -147,10 +154,14 @@ func BucketClaimStatusReady(ready bool) types.StepFunc { // CreateBucket creates a new Bucket resource. func CreateBucket() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - bucket := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.Bucket) + bucket, ok := ctx.Value(BucketAccessTypeCtxKey).(*cosi.Bucket) + if !ok { + t.Error(ErrKeyNotFound) + return ctx + } if err := cfg.Client().Resources().Create(ctx, bucket); err != nil { - t.Fatal(err) + t.Error(err) } return ctx @@ -160,12 +171,16 @@ func CreateBucket() types.StepFunc { // BucketExists checks if the Bucket resource exists. func BucketExists(expected bool) types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - bucketClaim := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketClaim) + bucketClaim, ok := ctx.Value(BucketAccessTypeCtxKey).(*cosi.BucketClaim) + if !ok { + t.Error(ErrKeyNotFound) + return ctx + } r := retry.NewLinearBackoffRetry() if err := r.Retry(func() error { - bucket := &cosiv1alpha1.Bucket{} + bucket := &cosi.Bucket{} err := cfg.Client().Resources().Get(ctx, bucketClaim.Status.BucketName, @@ -184,7 +199,7 @@ func BucketExists(expected bool) types.StepFunc { return nil }); err != nil { - t.Fatal(err) + t.Error(err) } return ctx @@ -194,10 +209,14 @@ func BucketExists(expected bool) types.StepFunc { // CreateBucketClaim creates a new BucketClaim resource. func CreateBucketClaim() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - bucketClaim := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketClaim) + bucketClaim, ok := ctx.Value(BucketAccessTypeCtxKey).(*cosi.BucketClaim) + if !ok { + t.Error(ErrKeyNotFound) + return ctx + } if err := cfg.Client().Resources().Create(ctx, bucketClaim); err != nil { - t.Fatal(err) + t.Error(err) } return ctx @@ -207,10 +226,14 @@ func CreateBucketClaim() types.StepFunc { // DeleteBucketClaim deletes the specified BucketClaim resource. func DeleteBucketClaim() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - bucketClaim := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketClaim) + bucketClaim, ok := ctx.Value(BucketAccessTypeCtxKey).(*cosi.BucketClaim) + if !ok { + t.Error(ErrKeyNotFound) + return ctx + } if err := cfg.Client().Resources().Delete(ctx, bucketClaim); err != nil { - t.Fatal(err) + t.Error(err) } return ctx @@ -220,10 +243,14 @@ func DeleteBucketClaim() types.StepFunc { // CreateBucketAccess creates a new BucketAccess resource. func CreateBucketAccess() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - bucketAccess := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketAccess) + bucketAccess, ok := ctx.Value(BucketAccessTypeCtxKey).(*cosi.BucketAccess) + if !ok { + t.Error(ErrKeyNotFound) + return ctx + } if err := cfg.Client().Resources().Create(ctx, bucketAccess); err != nil { - t.Fatal(err) + t.Error(err) } return ctx @@ -233,14 +260,18 @@ func CreateBucketAccess() types.StepFunc { // SecretExists checks if the specified Secret resource exists. func SecretExists(expected bool) types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - bucketAccess := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketAccess) + bucketAccess, ok := ctx.Value(BucketAccessTypeCtxKey).(*cosi.BucketAccess) + if !ok { + t.Error(ErrKeyNotFound) + return ctx + } r := retry.NewLinearBackoffRetry() if err := r.Retry(func() error { // TODO: this should also check if the format of the secret conforms // to the expectations, so one more arg will be needed - secret := &corev1.Secret{} + secret := &core.Secret{} err := cfg.Client().Resources().Get(ctx, bucketAccess.Spec.CredentialsSecretName, @@ -259,7 +290,7 @@ func SecretExists(expected bool) types.StepFunc { return nil }); err != nil { - t.Fatal(err) + t.Error(err) } return ctx @@ -269,10 +300,14 @@ func SecretExists(expected bool) types.StepFunc { // DeleteBucketAccess deletes the specified BucketAccess resource. func DeleteBucketAccess() types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - bucketAccess := ctx.Value(setup.BucketAccessTypeCtxKey).(*cosiv1alpha1.BucketAccess) + bucketAccess, ok := ctx.Value(BucketAccessTypeCtxKey).(*cosi.BucketAccess) + if !ok { + t.Error(ErrKeyNotFound) + return ctx + } if err := cfg.Client().Resources().Delete(ctx, bucketAccess); err != nil { - t.Fatal(err) + t.Error(err) } return ctx diff --git a/test/e2e/setup/setup.go b/test/e2e/assesments/setup.go similarity index 70% rename from test/e2e/setup/setup.go rename to test/e2e/assesments/setup.go index c6a1665c..d340e316 100644 --- a/test/e2e/setup/setup.go +++ b/test/e2e/assesments/setup.go @@ -1,12 +1,14 @@ -package setup +package assesments import ( "context" + "errors" "fmt" "testing" "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" + cosi "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" + "sigs.k8s.io/e2e-framework/pkg/envconf" "sigs.k8s.io/e2e-framework/pkg/types" ) @@ -15,6 +17,8 @@ type ( TypeCtxKey string ) +var ErrKeyNotFound = errors.New("required resource not found") + const ( BucketTypeCtxKey = TypeCtxKey("cosi.Bucket") BucketClaimTypeCtxKey = TypeCtxKey("cosi.BucketClaim") @@ -25,17 +29,17 @@ func RegisterResourcesForTest(objects ...runtime.Object) types.StepFunc { return func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { for _, obj := range objects { switch typedObj := obj.(type) { - case *v1alpha1.Bucket: + case *cosi.Bucket: ctx = context.WithValue(ctx, BucketTypeCtxKey, typedObj) - case *v1alpha1.BucketClaim: + case *cosi.BucketClaim: ctx = context.WithValue(ctx, BucketClaimTypeCtxKey, typedObj) - case *v1alpha1.BucketAccess: + case *cosi.BucketAccess: ctx = context.WithValue(ctx, BucketAccessTypeCtxKey, typedObj) default: - panic(fmt.Sprintf("invalid type: %T (Kind: %s)", typedObj, obj.GetObjectKind())) + panic(fmt.Sprintf("unsupported type: %T (Kind: %s)", typedObj, obj.GetObjectKind())) } } diff --git a/test/e2e/cosi_test.go b/test/e2e/cosi_test.go index 0d5fca71..681d6b5d 100644 --- a/test/e2e/cosi_test.go +++ b/test/e2e/cosi_test.go @@ -1,17 +1,16 @@ package cositest import ( - "e2e/assesments/cosi" + "e2e/assesments" "e2e/envfuncs" - "e2e/setup" + "e2e/envfuncs/helpers" "flag" "log" "os" "testing" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" - cosiv1alpha1 "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + cosi "sigs.k8s.io/container-object-storage-interface-api/apis/objectstorage/v1alpha1" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/e2e-framework/pkg/env" @@ -29,8 +28,8 @@ var ( ) func init() { - apiextensionsv1.AddToScheme(scheme.Scheme) - cosiv1alpha1.AddToScheme(scheme.Scheme) + apiextensions.AddToScheme(scheme.Scheme) + cosi.AddToScheme(scheme.Scheme) } func TestMain(m *testing.M) { @@ -66,7 +65,30 @@ func TestMain(m *testing.M) { } testenv = env.NewWithConfig(cfg) + crds := &apiextensions.CustomResourceDefinitionList{} + if !noInstallCRDs { + crdPaths := []string{ + "../../client/config/crd/objectstorage.k8s.io_bucketaccessclasses.yaml", + "../../client/config/crd/objectstorage.k8s.io_bucketaccesses.yaml", + "../../client/config/crd/objectstorage.k8s.io_bucketclaims.yaml", + "../../client/config/crd/objectstorage.k8s.io_bucketclasses.yaml", + "../../client/config/crd/objectstorage.k8s.io_buckets.yaml", + } + + for _, path := range crdPaths { + + crd, err := helpers.Load(path) + if err != nil { + log.Fatalf("failed to load resource: %s", err) + } + + crds.Items = append(crds.Items, *crd) + } + + } + testenv.Setup( + envfuncs.RegisterResources(crds), envfuncs.CreateCluster(noKinD), envfuncs.InstallCRDs(noInstallCRDs), @@ -91,7 +113,7 @@ func TestMain(m *testing.M) { testenv.BeforeEachTest( envfuncs.IsClusterReadyTest(), envfuncs.CreateNSForTest(), - cosi.CRDsInstalled(), + assesments.CRDsInstalled(), ) testenv.AfterEachTest( @@ -104,36 +126,38 @@ func TestMain(m *testing.M) { func TestBucketProvisioning(t *testing.T) { testenv.Test(t, features.New("Greenfield Bucket"). - Setup(setup.RegisterResourcesForTest( - &v1alpha1.BucketClaim{}, - )). + Assess("Resources are registered", + assesments.RegisterResourcesForTest( + &cosi.BucketClaim{}, + )). Assess("BucketClaim is created", - cosi.CreateBucketClaim()). + assesments.CreateBucketClaim()). Assess("Bucket is created", - cosi.BucketExists(true)). + assesments.BucketExists(true)). Assess("BucketClaim has ready status", - cosi.BucketClaimStatusReady(true)). + assesments.BucketClaimStatusReady(true)). Assess("BucketClaim is deleted", - cosi.DeleteBucketClaim()). + assesments.DeleteBucketClaim()). Assess("Bucket is deleted", - cosi.BucketExists(false)). + assesments.BucketExists(false)). Feature(), features.New("Brownfield Bucket"). - Setup(setup.RegisterResourcesForTest( - &v1alpha1.Bucket{}, - &v1alpha1.BucketClaim{}, - )). + Assess("Resources are registered", + assesments.RegisterResourcesForTest( + &cosi.Bucket{}, + &cosi.BucketClaim{}, + )). Assess("BucketClaim is created", - cosi.CreateBucketClaim()). + assesments.CreateBucketClaim()). Assess("Bucket is created", - cosi.CreateBucket()). + assesments.CreateBucket()). Assess("BucketClaim has ready status", - cosi.BucketClaimStatusReady(true)). + assesments.BucketClaimStatusReady(true)). Assess("BucketClaim is deleted", - cosi.DeleteBucketClaim()). + assesments.DeleteBucketClaim()). Assess("Bucket is deleted", - cosi.BucketExists(false)). + assesments.BucketExists(false)). Feature(), ) } @@ -141,57 +165,59 @@ func TestBucketProvisioning(t *testing.T) { func TestBucketAccessProvisioning(t *testing.T) { testenv.Test(t, features.New("BucketAccess for S3"). - Setup(setup.RegisterResourcesForTest( - &v1alpha1.BucketClaim{}, - &v1alpha1.BucketAccess{}, - )). + Assess("Resources are registered", + assesments.RegisterResourcesForTest( + &cosi.BucketClaim{}, + &cosi.BucketAccess{}, + )). Assess("BucketClaim is created", - cosi.CreateBucketClaim()). + assesments.CreateBucketClaim()). Assess("Bucket is created", - cosi.BucketExists(true)). + assesments.BucketExists(true)). Assess("BucketClaim has ready status", - cosi.BucketClaimStatusReady(true)). + assesments.BucketClaimStatusReady(true)). Assess("BucketAccess is created", - cosi.CreateBucketAccess()). + assesments.CreateBucketAccess()). Assess("BucketAccess has ready status", - cosi.BucketAccessStatusGranted(true)). + assesments.BucketAccessStatusGranted(true)). Assess("Secret is created", - cosi.SecretExists(true)). + assesments.SecretExists(true)). Assess("BucketAccess is deleted", - cosi.DeleteBucketAccess()). + assesments.DeleteBucketAccess()). Assess("Secret is deleted", - cosi.SecretExists(false)). + assesments.SecretExists(false)). Assess("BucketClaim is deleted", - cosi.DeleteBucketClaim()). + assesments.DeleteBucketClaim()). Assess("Bucket is deleted", - cosi.BucketExists(false)). + assesments.BucketExists(false)). Feature(), features.New("BucketAccess for Azure"). - Setup(setup.RegisterResourcesForTest( - &v1alpha1.BucketClaim{}, - &v1alpha1.BucketAccess{}, - )). + Assess("Resources are registered", + assesments.RegisterResourcesForTest( + &cosi.BucketClaim{}, + &cosi.BucketAccess{}, + )). Assess("BucketClaim is created", - cosi.CreateBucketClaim()). + assesments.CreateBucketClaim()). Assess("Bucket is created", - cosi.BucketExists(true)). + assesments.BucketExists(true)). Assess("BucketClaim has ready status", - cosi.BucketClaimStatusReady(true)). + assesments.BucketClaimStatusReady(true)). Assess("BucketAccess is created", - cosi.CreateBucketAccess()). + assesments.CreateBucketAccess()). Assess("BucketAccess has ready status", - cosi.BucketAccessStatusGranted(true)). + assesments.BucketAccessStatusGranted(true)). Assess("Secret is created", - cosi.SecretExists(true)). + assesments.SecretExists(true)). Assess("BucketAccess is deleted", - cosi.DeleteBucketAccess()). + assesments.DeleteBucketAccess()). Assess("Secret is deleted", - cosi.SecretExists(false)). + assesments.SecretExists(false)). Assess("BucketClaim is deleted", - cosi.DeleteBucketClaim()). + assesments.DeleteBucketClaim()). Assess("Bucket is deleted", - cosi.BucketExists(false)). + assesments.BucketExists(false)). Feature(), ) } diff --git a/test/e2e/envfuncs/cosi.go b/test/e2e/envfuncs/cosi.go index cd77b3fe..5d27766e 100644 --- a/test/e2e/envfuncs/cosi.go +++ b/test/e2e/envfuncs/cosi.go @@ -2,11 +2,39 @@ package envfuncs import ( "context" + "fmt" + + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/e2e-framework/pkg/envconf" "sigs.k8s.io/e2e-framework/pkg/types" ) +type ( + TypeCtxKey string +) + +const ( + CustomResourceDefinitionListTypeCtxKey = TypeCtxKey("apiextensions.CustomResourceDefinitionList") +) + +func RegisterResources(objects ...runtime.Object) types.EnvFunc { + return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + for _, obj := range objects { + switch typedObj := obj.(type) { + case *apiextensions.CustomResourceDefinitionList: + ctx = context.WithValue(ctx, CustomResourceDefinitionListTypeCtxKey, typedObj) + + default: + panic(fmt.Sprintf("unsupported type: %T (Kind: %s)", typedObj, obj.GetObjectKind())) + } + } + + return ctx, nil + } +} + // InstallCRDs installs the necessary CRDs unless skipping is specified. func InstallCRDs(skip bool) types.EnvFunc { if skip { @@ -14,6 +42,14 @@ func InstallCRDs(skip bool) types.EnvFunc { } return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + crds := ctx.Value(CustomResourceDefinitionListTypeCtxKey).(*apiextensions.CustomResourceDefinitionList) + + for _, crd := range crds.Items { + if err := cfg.Client().Resources().Create(ctx, &crd); err != nil { + return ctx, err + } + } + return ctx, nil } } @@ -25,6 +61,14 @@ func UninstallCRDs(skip bool) types.EnvFunc { } return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { + crds := ctx.Value(CustomResourceDefinitionListTypeCtxKey).(*apiextensions.CustomResourceDefinitionList) + + for _, crd := range crds.Items { + if err := cfg.Client().Resources().Delete(ctx, &crd); err != nil { + return ctx, err + } + } + return ctx, nil } } diff --git a/test/e2e/envfuncs/helpers/helpers.go b/test/e2e/envfuncs/helpers/helpers.go index bc625672..21fdcacb 100644 --- a/test/e2e/envfuncs/helpers/helpers.go +++ b/test/e2e/envfuncs/helpers/helpers.go @@ -1,8 +1,14 @@ package helpers import ( + "bytes" + "io" + "os" "strings" "testing" + + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + yaml "sigs.k8s.io/yaml/goyaml.v2" ) type ( @@ -19,3 +25,23 @@ func GetNamespaceKey(t *testing.T) NamespaceCtxKey { // When pass t.Name() from inside a `testenv.BeforeEachTest` function, the name is just TestName return NamespaceCtxKey(t.Name()) } + +func Load(path string) (*apiextensions.CustomResourceDefinition, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + buf := new(bytes.Buffer) + + io.Copy(buf, f) + + crd := &apiextensions.CustomResourceDefinition{} + + if err := yaml.NewDecoder(buf).Decode(crd); err != nil { + return nil, err + } + + return crd, nil +} diff --git a/test/e2e/envfuncs/kind.go b/test/e2e/envfuncs/kind.go index 2ad89053..3d4f32c8 100644 --- a/test/e2e/envfuncs/kind.go +++ b/test/e2e/envfuncs/kind.go @@ -16,6 +16,8 @@ type ( ClusterCtxKey string ) +const clusterKey = ClusterCtxKey("cluster") + // CreateCluster creates a new Kubernetes cluster unless skipping is specified. func CreateCluster(skip bool) types.EnvFunc { if skip { @@ -25,6 +27,7 @@ func CreateCluster(skip bool) types.EnvFunc { return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { name := envconf.RandomName("cosi-e2e-cluster", 32) cluster := kind.NewCluster(name) + kubeconfig, err := cluster.Create(ctx) if err != nil { return ctx, err @@ -40,7 +43,7 @@ func CreateCluster(skip bool) types.EnvFunc { cfg.WithKubeconfigFile(kubeconfig) // propagate cluster value - return context.WithValue(ctx, ClusterCtxKey("cluster"), cluster), nil + return context.WithValue(ctx, clusterKey, cluster), nil } } @@ -74,7 +77,7 @@ func DeleteCluster(skip bool) types.EnvFunc { } return func(ctx context.Context, _ *envconf.Config) (context.Context, error) { - cluster := ctx.Value(ClusterCtxKey("cluster")).(*kind.Cluster) + cluster := ctx.Value(clusterKey).(*kind.Cluster) if cluster == nil { return ctx, fmt.Errorf("error getting kind cluster from context") } diff --git a/test/e2e/envfuncs/ns.go b/test/e2e/envfuncs/ns.go index a399ff4e..e676cc83 100644 --- a/test/e2e/envfuncs/ns.go +++ b/test/e2e/envfuncs/ns.go @@ -7,7 +7,7 @@ import ( "e2e/envfuncs/helpers" - v1 "k8s.io/api/core/v1" + core "k8s.io/api/core/v1" "sigs.k8s.io/e2e-framework/pkg/envconf" "sigs.k8s.io/e2e-framework/pkg/types" ) @@ -20,7 +20,7 @@ func CreateNSForTest() types.TestEnvFunc { ctx = context.WithValue(ctx, helpers.GetNamespaceKey(t), ns) t.Logf("Creating NS %v for test %v", ns, t.Name()) - nsObj := v1.Namespace{} + nsObj := core.Namespace{} nsObj.Name = ns return ctx, cfg.Client().Resources().Create(ctx, &nsObj) } @@ -32,7 +32,7 @@ func DeleteNSForTest() types.TestEnvFunc { ns := fmt.Sprint(ctx.Value(helpers.GetNamespaceKey(t))) t.Logf("Deleting NS %v for test %v", ns, t.Name()) - nsObj := v1.Namespace{} + nsObj := core.Namespace{} nsObj.Name = ns return ctx, cfg.Client().Resources().Delete(ctx, &nsObj) } diff --git a/test/e2e/go.mod b/test/e2e/go.mod index c6304936..4317334d 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -9,6 +9,7 @@ require ( k8s.io/client-go v0.30.1 sigs.k8s.io/container-object-storage-interface-api v0.1.0 sigs.k8s.io/e2e-framework v0.4.0 + sigs.k8s.io/yaml v1.4.0 ) require ( @@ -63,5 +64,4 @@ require ( sigs.k8s.io/controller-runtime v0.18.2 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect )