diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c8609fd9f..6e7c1607e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,6 +59,32 @@ you can follow the steps below to test your changes: make kind-load kind-deploy ``` +## How to debug controller tests using ENVTEST + +[ENVTEST](https://book.kubebuilder.io/reference/envtest) requires k8s binaries to be downloaded to run the tests. +To download the necessary binaries, follow the steps below: + +```sh +make envtest-k8s-bins +``` + +Note that the binaries are downloaded to the `bin/envtest-binaries` directory. + +```sh +$ tree +. +├── envtest-binaries +│   └── k8s +│   └── 1.31.0-darwin-arm64 +│   ├── etcd +│   ├── kube-apiserver +│   └── kubectl +``` + +Now, you can debug them with your IDE: + +![Screenshot IDE example](https://github.com/user-attachments/assets/3096d524-0686-48ca-911c-5b843093ad1f) + ### Communication Channels - Email: [operator-framework-olm-dev](mailto:operator-framework-olm-dev@googlegroups.com) diff --git a/Makefile b/Makefile index b91ce1ffc..b18aca141 100644 --- a/Makefile +++ b/Makefile @@ -29,10 +29,10 @@ export WAIT_TIMEOUT := 60s # Install default ClusterCatalogs export INSTALL_DEFAULT_CATALOGS := true -# By default setup-envtest will write to $XDG_DATA_HOME, or $HOME/.local/share if that is not defined. +# By default setup-envtest binary will write to $XDG_DATA_HOME, or $HOME/.local/share if that is not defined. # If $HOME is not set, we need to specify a binary directory to prevent an error in setup-envtest. # Useful for some CI/CD environments that set neither $XDG_DATA_HOME nor $HOME. -SETUP_ENVTEST_BIN_DIR_OVERRIDE= +SETUP_ENVTEST_BIN_DIR_OVERRIDE += --bin-dir $(ROOT_DIR)/bin/envtest-binaries ifeq ($(shell [[ $$HOME == "" || $$HOME == "/" ]] && [[ $$XDG_DATA_HOME == "" ]] && echo true ), true) SETUP_ENVTEST_BIN_DIR_OVERRIDE += --bin-dir /tmp/envtest-binaries endif @@ -158,19 +158,25 @@ test-ext-dev-e2e: $(OPERATOR_SDK) $(KUSTOMIZE) $(KIND) #HELP Run extension creat test/extension-developer-e2e/setup.sh $(OPERATOR_SDK) $(CONTAINER_RUNTIME) $(KUSTOMIZE) $(KIND) $(KIND_CLUSTER_NAME) $(E2E_REGISTRY_NAMESPACE) go test -count=1 -v ./test/extension-developer-e2e/... -.PHONY: test-unit ENVTEST_VERSION := $(shell go list -m k8s.io/client-go | cut -d" " -f2 | sed 's/^v0\.\([[:digit:]]\{1,\}\)\.[[:digit:]]\{1,\}$$/1.\1.x/') UNIT_TEST_DIRS := $(shell go list ./... | grep -v /test/) COVERAGE_UNIT_DIR := $(ROOT_DIR)/coverage/unit -test-unit: $(SETUP_ENVTEST) #HELP Run the unit tests + +.PHONY: envtest-k8s-bins #HELP Uses setup-envtest to download and install the binaries required to run ENVTEST-test based locally at the project/bin directory. +envtest-k8s-bins: $(SETUP_ENVTEST) + mkdir -p $(ROOT_DIR)/bin + $(SETUP_ENVTEST) use -p env $(ENVTEST_VERSION) $(SETUP_ENVTEST_BIN_DIR_OVERRIDE) + +.PHONY: test-unit +test-unit: $(SETUP_ENVTEST) envtest-k8s-bins #HELP Run the unit tests rm -rf $(COVERAGE_UNIT_DIR) && mkdir -p $(COVERAGE_UNIT_DIR) - eval $$($(SETUP_ENVTEST) use -p env $(ENVTEST_VERSION) $(SETUP_ENVTEST_BIN_DIR_OVERRIDE)) && \ + KUBEBUILDER_ASSETS="$(shell $(SETUP_ENVTEST) use -p path $(ENVTEST_VERSION) $(SETUP_ENVTEST_BIN_DIR_OVERRIDE))" \ CGO_ENABLED=1 go test \ -tags '$(GO_BUILD_TAGS)' \ -cover -coverprofile ${ROOT_DIR}/coverage/unit.out \ -count=1 -race -short \ $(UNIT_TEST_DIRS) \ - -test.gocoverdir=$(ROOT_DIR)/coverage/unit + -test.gocoverdir=$(COVERAGE_UNIT_DIR) .PHONY: image-registry E2E_REGISTRY_IMAGE=localhost/e2e-test-registry:devel diff --git a/internal/controllers/suite_test.go b/internal/controllers/suite_test.go index 52fd8900a..5803d2089 100644 --- a/internal/controllers/suite_test.go +++ b/internal/controllers/suite_test.go @@ -26,7 +26,7 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime" + apimachineryruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" @@ -64,7 +64,7 @@ func (m *MockUnpacker) Cleanup(_ context.Context, _ *source.BundleSource) error func newClient(t *testing.T) client.Client { // TODO: this is a live client, which behaves differently than a cache client. // We may want to use a caching client instead to get closer to real behavior. - sch := runtime.NewScheme() + sch := apimachineryruntime.NewScheme() require.NoError(t, ocv1.AddToScheme(sch)) cl, err := client.New(config, client.Options{Scheme: sch}) require.NoError(t, err) @@ -162,6 +162,21 @@ func TestMain(m *testing.M) { ErrorIfCRDPathMissing: true, } + // ENVTEST-based tests require specific binaries. By default, these binaries are located + // in paths defined by controller-runtime. However, the `BinaryAssetsDirectory` needs + // to be explicitly set when running tests directly (e.g., debugging tests in an IDE) + // without using the Makefile targets. + // + // This is equivalent to configuring your IDE to export the `KUBEBUILDER_ASSETS` environment + // variable before each test execution. The following function simplifies this process + // by handling the configuration for you. + // + // To ensure the binaries are in the expected path without manual configuration, run: + // `make envtest-k8s-bins` + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + var err error config, err = testEnv.Start() utilruntime.Must(err) @@ -179,3 +194,15 @@ func TestMain(m *testing.M) { utilruntime.Must(testEnv.Stop()) os.Exit(code) } + +// getFirstFoundEnvTestBinaryDir finds and returns the first directory under the given path. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "bin", "envtest-binaries", "k8s") + entries, _ := os.ReadDir(basePath) + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +}