diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 732e05172..836958221 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,9 +17,6 @@ updates: patterns: - "k8s.io*" - "sigs.k8s.io*" - # Remove this ignore once #402 is resolved. - ignore: - - dependency-name: "sigs.k8s.io/controller-runtime" - package-ecosystem: "gomod" directory: "/common" @@ -30,9 +27,6 @@ updates: patterns: - "k8s.io*" - "sigs.k8s.io*" - # Remove this ignore once #402 is resolved. - ignore: - - dependency-name: "sigs.k8s.io/controller-runtime" - package-ecosystem: "gomod" directory: "/api" @@ -43,9 +37,6 @@ updates: patterns: - "k8s.io*" - "sigs.k8s.io*" - # Remove this ignore once #402 is resolved. - ignore: - - dependency-name: "sigs.k8s.io/controller-runtime" - package-ecosystem: "gomod" directory: "/hack/third-party/capa" diff --git a/Makefile b/Makefile index 6fa8be54b..d74793887 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,6 @@ REPO_ROOT := $(CURDIR) # Versions for tools that are not managed by devbox. -ENVTEST_VERSION=1.27.x +ENVTEST_VERSION=1.29.x include make/all.mk diff --git a/api/go.mod b/api/go.mod index 71c09a03d..0cc28e6af 100644 --- a/api/go.mod +++ b/api/go.mod @@ -13,7 +13,7 @@ require ( k8s.io/apimachinery v0.29.2 k8s.io/utils v0.0.0-20230726121419-3b25d923346b sigs.k8s.io/cluster-api v1.6.3 - sigs.k8s.io/controller-runtime v0.17.0 + sigs.k8s.io/controller-runtime v0.17.2 ) require ( diff --git a/api/go.sum b/api/go.sum index 2a440a919..6f6f2ffcb 100644 --- a/api/go.sum +++ b/api/go.sum @@ -830,8 +830,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/cluster-api v1.6.3 h1:VOlPNg92PQLlhBVLc5pg+cbAuPvGOOBujeFLk9zgnoo= sigs.k8s.io/cluster-api v1.6.3/go.mod h1:4FzfgPPiYaFq8X9F9j2SvmggH/4OOLEDgVJuWDqKLig= -sigs.k8s.io/controller-runtime v0.17.0 h1:fjJQf8Ukya+VjogLO6/bNX9HE6Y2xpsO5+fyS26ur/s= -sigs.k8s.io/controller-runtime v0.17.0/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= +sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= +sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= diff --git a/common/go.mod b/common/go.mod index 99fa53abf..8cbff8ae6 100644 --- a/common/go.mod +++ b/common/go.mod @@ -8,7 +8,6 @@ go 1.21 replace github.com/d2iq-labs/capi-runtime-extensions/api => ../api require ( - github.com/avast/retry-go/v4 v4.5.1 github.com/d2iq-labs/capi-runtime-extensions/api v0.0.0-00010101000000-000000000000 github.com/evanphx/json-patch/v5 v5.9.0 github.com/go-logr/logr v1.4.1 @@ -19,10 +18,9 @@ require ( k8s.io/api v0.29.2 k8s.io/apiextensions-apiserver v0.29.2 k8s.io/apimachinery v0.29.2 - k8s.io/client-go v0.29.2 k8s.io/utils v0.0.0-20230726121419-3b25d923346b sigs.k8s.io/cluster-api v1.6.3 - sigs.k8s.io/controller-runtime v0.17.1 + sigs.k8s.io/controller-runtime v0.17.2 ) require ( @@ -33,7 +31,6 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect @@ -77,6 +74,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiserver v0.29.2 // indirect + k8s.io/client-go v0.29.2 // indirect k8s.io/cluster-bootstrap v0.29.1 // indirect k8s.io/component-base v0.29.2 // indirect k8s.io/klog/v2 v2.120.1 // indirect diff --git a/common/go.sum b/common/go.sum index 309deb141..836241d67 100644 --- a/common/go.sum +++ b/common/go.sum @@ -2,8 +2,6 @@ github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= -github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= 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= @@ -266,8 +264,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 h1:TgtAeesdhpm2S sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/KrK4fjnV61bE2g3sA7tiETLn8sooImelsCx3Y= sigs.k8s.io/cluster-api v1.6.3 h1:VOlPNg92PQLlhBVLc5pg+cbAuPvGOOBujeFLk9zgnoo= sigs.k8s.io/cluster-api v1.6.3/go.mod h1:4FzfgPPiYaFq8X9F9j2SvmggH/4OOLEDgVJuWDqKLig= -sigs.k8s.io/controller-runtime v0.17.1 h1:V1dQELMGVk46YVXXQUbTFujU7u4DQj6YUj9Rb6cuzz8= -sigs.k8s.io/controller-runtime v0.17.1/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= +sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= +sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= 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= diff --git a/common/pkg/k8s/client/fakessa/client.go b/common/pkg/k8s/client/fakessa/client.go deleted file mode 100644 index c5d0641ae..000000000 --- a/common/pkg/k8s/client/fakessa/client.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2024 D2iQ, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package fakessa - -import ( - "context" - "errors" - "fmt" - - "github.com/avast/retry-go/v4" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/client/interceptor" -) - -func NewClient(_ *rest.Config, _ client.Options) (client.Client, error) { - return fake.NewClientBuilder().WithInterceptorFuncs( - interceptor.Funcs{ - Patch: func( - ctx context.Context, - clnt client.WithWatch, - obj client.Object, - patch client.Patch, - opts ...client.PatchOption, - ) error { - // Apply patches are supposed to upsert, but fake client fails if the object doesn't exist, - // if an apply patch occurs for an object that doesn't yet exist, create it. - if patch.Type() != types.ApplyPatchType { - return clnt.Patch(ctx, obj, patch, opts...) - } - check, ok := obj.DeepCopyObject().(client.Object) - if !ok { - return errors.New("could not check for object in fake client") - } - - return retry.Do( - func() error { - if err := clnt.Get(ctx, client.ObjectKeyFromObject(obj), check); k8serrors.IsNotFound( - err, - ) { - if err := clnt.Create(ctx, check); err != nil { - return fmt.Errorf( - "could not inject object creation for fake: %w", - err, - ) - } - } - return clnt.Patch(ctx, obj, patch, opts...) - }, - retry.Attempts(5), - ) - }, - }, - ).Build(), nil -} diff --git a/go.mod b/go.mod index d425f115e..deb8dab19 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( sigs.k8s.io/cluster-api v1.6.3 sigs.k8s.io/cluster-api-addon-provider-helm v0.1.1-alpha.1 sigs.k8s.io/cluster-api/test v1.6.3 - sigs.k8s.io/controller-runtime v0.17.1 + sigs.k8s.io/controller-runtime v0.17.2 sigs.k8s.io/yaml v1.4.0 ) @@ -47,7 +47,6 @@ require ( github.com/alessio/shellescape v1.4.1 // indirect github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect - github.com/avast/retry-go/v4 v4.5.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect diff --git a/go.sum b/go.sum index 22753d29e..392e2092d 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,6 @@ github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= -github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= 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= @@ -812,8 +810,8 @@ sigs.k8s.io/cluster-api-addon-provider-helm v0.1.1-alpha.1 h1:HfBfswOZk/oUsNfkp5 sigs.k8s.io/cluster-api-addon-provider-helm v0.1.1-alpha.1/go.mod h1:SgeAkpyhAzK5GBPeqfYHbThpMptSXVhDcLyWbM6UTik= sigs.k8s.io/cluster-api/test v1.6.3 h1:ZCboLCTpKWzSbf+f7MpQT7EN8aeH9DNhJC1T9/vAuAM= sigs.k8s.io/cluster-api/test v1.6.3/go.mod h1:AKs25dgW6AnyGaQBoWuXfWnBs+FT7vJmAI/aox64DEI= -sigs.k8s.io/controller-runtime v0.17.1 h1:V1dQELMGVk46YVXXQUbTFujU7u4DQj6YUj9Rb6cuzz8= -sigs.k8s.io/controller-runtime v0.17.1/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= +sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= +sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= 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/kind v0.20.0 h1:f0sc3v9mQbGnjBUaqSFST1dwIuiikKVGgoTwpoP33a8= diff --git a/make/go.mk b/make/go.mk index 0e38dcb7d..683523283 100644 --- a/make/go.mk +++ b/make/go.mk @@ -25,7 +25,6 @@ define go_test -- \ -covermode=atomic \ -coverprofile=coverage.out \ - -race \ -short \ -v \ $(if $(GOTEST_RUN),-run "$(GOTEST_RUN)") \ diff --git a/pkg/handlers/aws/mutation/metapatch_handler_test.go b/pkg/handlers/aws/mutation/metapatch_handler_test.go index 8e753f2a1..8034901b4 100644 --- a/pkg/handlers/aws/mutation/metapatch_handler_test.go +++ b/pkg/handlers/aws/mutation/metapatch_handler_test.go @@ -6,12 +6,10 @@ package mutation import ( "testing" - "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/manager" "github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation" - "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/k8s/client/fakessa" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/ami" amitests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/ami/tests" calicotests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/cni/calico/tests" @@ -57,12 +55,7 @@ func workerPatchGeneratorFunc() func() mutation.GeneratePatches { func TestGeneratePatches(t *testing.T) { t.Parallel() - mgr, _ := manager.New( - &rest.Config{}, - manager.Options{ - NewClient: fakessa.NewClient, - }, - ) + mgr := testEnv.Manager regiontests.TestGeneratePatches( t, diff --git a/pkg/handlers/aws/mutation/suite_test.go b/pkg/handlers/aws/mutation/suite_test.go new file mode 100644 index 000000000..0170ad48e --- /dev/null +++ b/pkg/handlers/aws/mutation/suite_test.go @@ -0,0 +1,45 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package mutation + +import ( + "fmt" + "testing" + + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/d2iq-labs/capi-runtime-extensions/test/helpers" +) + +var ( + testEnv *helpers.TestEnvironment + ctx = ctrl.SetupSignalHandler() +) + +func TestMain(m *testing.M) { + setup() + defer teardown() + m.Run() +} + +func setup() { + testEnvConfig := helpers.NewTestEnvironmentConfiguration() + var err error + testEnv, err = testEnvConfig.Build() + if err != nil { + panic(err) + } + go func() { + fmt.Println("Starting the manager") + if err := testEnv.StartManager(ctx); err != nil { + panic(fmt.Sprintf("Failed to start the envtest manager: %v", err)) + } + }() +} + +func teardown() { + if err := testEnv.Stop(); err != nil { + panic(fmt.Sprintf("Failed to stop envtest: %v", err)) + } +} diff --git a/pkg/handlers/docker/mutation/metapatch_handler_test.go b/pkg/handlers/docker/mutation/metapatch_handler_test.go index ffafe6c8c..aa07d4a9d 100644 --- a/pkg/handlers/docker/mutation/metapatch_handler_test.go +++ b/pkg/handlers/docker/mutation/metapatch_handler_test.go @@ -6,11 +6,9 @@ package mutation import ( "testing" - "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/manager" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation" - "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/k8s/client/fakessa" dockerclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/docker/clusterconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/docker/mutation/customimage" customimagetests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/docker/mutation/customimage/tests" @@ -47,12 +45,7 @@ func workerPatchGeneratorFunc() func() mutation.GeneratePatches { func TestGeneratePatches(t *testing.T) { t.Parallel() - mgr, _ := manager.New( - &rest.Config{}, - manager.Options{ - NewClient: fakessa.NewClient, - }, - ) + mgr := testEnv.Manager customimagetests.TestControlPlaneGeneratePatches( t, diff --git a/pkg/handlers/docker/mutation/suite_test.go b/pkg/handlers/docker/mutation/suite_test.go new file mode 100644 index 000000000..0170ad48e --- /dev/null +++ b/pkg/handlers/docker/mutation/suite_test.go @@ -0,0 +1,45 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package mutation + +import ( + "fmt" + "testing" + + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/d2iq-labs/capi-runtime-extensions/test/helpers" +) + +var ( + testEnv *helpers.TestEnvironment + ctx = ctrl.SetupSignalHandler() +) + +func TestMain(m *testing.M) { + setup() + defer teardown() + m.Run() +} + +func setup() { + testEnvConfig := helpers.NewTestEnvironmentConfiguration() + var err error + testEnv, err = testEnvConfig.Build() + if err != nil { + panic(err) + } + go func() { + fmt.Println("Starting the manager") + if err := testEnv.StartManager(ctx); err != nil { + panic(fmt.Sprintf("Failed to start the envtest manager: %v", err)) + } + }() +} + +func teardown() { + if err := testEnv.Stop(); err != nil { + panic(fmt.Sprintf("Failed to stop envtest: %v", err)) + } +} diff --git a/test/helpers/envtest.go b/test/helpers/envtest.go new file mode 100644 index 000000000..caaa1186d --- /dev/null +++ b/test/helpers/envtest.go @@ -0,0 +1,196 @@ +// Copyright 2024 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Package helpers provides a set of utilities for testing controllers. +package helpers + +import ( + "context" + "errors" + "fmt" + "os/exec" + "path/filepath" + goruntime "runtime" + "strings" + "sync" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/manager" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" +) + +var ( + root string + rootOnce sync.Once +) + +func rootDir() string { + rootOnce.Do(func() { + // Get the root of the current file to use in CRD paths. + _, filename, _, _ := goruntime.Caller(0) //nolint:dogsled // Just want the filename. + root, _ = filepath.Abs(filepath.Join(filepath.Dir(filename), "..", "..")) + }) + return root +} + +type TestEnvironmentConfiguration struct { + env *envtest.Environment +} + +// TestEnvironment encapsulates a Kubernetes local test environment. +type TestEnvironment struct { + manager.Manager + client.Client + Config *rest.Config + env *envtest.Environment + cancel context.CancelFunc +} + +// Cleanup deletes all the given objects. +func (t *TestEnvironment) Cleanup(ctx context.Context, objs ...client.Object) error { + errs := []error{} + for _, o := range objs { + err := t.Client.Delete(ctx, o) + if apierrors.IsNotFound(err) { + continue + } + errs = append(errs, err) + } + return kerrors.NewAggregate(errs) +} + +// CreateNamespace creates a new namespace with a generated name. +func (t *TestEnvironment) CreateNamespace( + ctx context.Context, + generateName string, +) (*corev1.Namespace, error) { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: fmt.Sprintf("%s-", generateName), + Labels: map[string]string{ + "testenv/original-name": generateName, + }, + }, + } + if err := t.Client.Create(ctx, ns); err != nil { + return nil, err + } + + return ns, nil +} + +// NewTestEnvironmentConfiguration creates a new test environment configuration for running tests. +func NewTestEnvironmentConfiguration() *TestEnvironmentConfiguration { + return &TestEnvironmentConfiguration{ + env: &envtest.Environment{ + ErrorIfCRDPathMissing: true, + CRDDirectoryPaths: getFilePathsToCAPICRDs(), + }, + } +} + +// Build creates a new environment spinning up a local api-server. +// This function should be called only once for each package you're running tests within, +// usually the environment is initialized in a suite_test.go file within a `BeforeSuite` ginkgo block. +func (t *TestEnvironmentConfiguration) Build() (*TestEnvironment, error) { + if _, err := t.env.Start(); err != nil { + return nil, err + } + + options := manager.Options{ + Scheme: scheme.Scheme, + Metrics: metricsserver.Options{ + BindAddress: "0", + }, + } + + mgr, err := ctrl.NewManager(t.env.Config, options) + if err != nil { + klog.Fatalf("Failed to start testenv manager: %v", err) + if stopErr := t.env.Stop(); stopErr != nil { + err = errors.Join(err, stopErr) + } + return nil, err + } + + return &TestEnvironment{ + Manager: mgr, + Client: mgr.GetClient(), + Config: mgr.GetConfig(), + env: t.env, + }, nil +} + +// StartManager starts the test controller against the local API server. +func (t *TestEnvironment) StartManager(ctx context.Context) error { + ctx, cancel := context.WithCancel(ctx) + t.cancel = cancel + return t.Manager.Start(ctx) +} + +// Stop stops the test environment. +func (t *TestEnvironment) Stop() error { + t.cancel() + return t.env.Stop() +} + +func getFilePathsToCAPICRDs() []string { + return []string{ + filepath.Join( + getModulePath(rootDir(), "sigs.k8s.io/cluster-api"), + "config", + "crd", + "bases", + ), + filepath.Join( + getModulePath( + filepath.Join(rootDir(), "hack", "third-party", "capa"), + "sigs.k8s.io/cluster-api-provider-aws/v2"), + "config", "crd", "bases", + ), + filepath.Join( + getModulePath( + filepath.Join(rootDir(), "hack", "third-party", "capd"), + "sigs.k8s.io/cluster-api/test", + ), + "infrastructure", + "docker", + "config", + "crd", + "bases", + ), + filepath.Join( + getModulePath( + filepath.Join(rootDir(), "hack", "third-party", "capx"), + "github.com/nutanix-cloud-native/cluster-api-provider-nutanix", + ), + "config", "crd", "bases", + ), + filepath.Join( + getModulePath( + filepath.Join(rootDir(), "hack", "third-party", "caaph"), + "sigs.k8s.io/cluster-api-addon-provider-helm", + ), + "config", "crd", "bases", + ), + } +} + +func getModulePath(moduleDir, moduleName string) string { + cmd := exec.Command("go", "list", "-m", "-f", "{{ .Dir }}", moduleName) + cmd.Dir = moduleDir + out, err := cmd.CombinedOutput() + if err != nil { + panic(err) + } + return strings.TrimSpace(string(out)) +}