Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌱 Configure ENVTEST Binaries for IDE Debugging #1454

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:[email protected])
Expand Down
18 changes: 12 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
m1kola marked this conversation as resolved.
Show resolved Hide resolved
camilamacedo86 marked this conversation as resolved.
Show resolved Hide resolved
endif
Expand Down Expand Up @@ -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)) && \
camilamacedo86 marked this conversation as resolved.
Show resolved Hide resolved
KUBEBUILDER_ASSETS="$(shell $(SETUP_ENVTEST) use -p path $(ENVTEST_VERSION) $(SETUP_ENVTEST_BIN_DIR_OVERRIDE))" \
Copy link
Contributor Author

@camilamacedo86 camilamacedo86 Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @m1kola,

Just to clarify, the binaries installed by envtest-setup are no longer placed in the HOME/global directory. See the default behavior here:
https://github.com/kubernetes-sigs/controller-runtime/blob/e3347b5405cdd0da5bff527af3d406117938ba6b/tools/setup-envtest/README.md?plain=1#L57-L70

This change ensures binaries are placed locally, avoiding the conflicts and clutter caused by global installations.

To clarify, I’m referring to the binaries required for ENVTEST (not those managed by BINGO).

Screenshot 2024-11-19 at 01 22 59

Hope this clears things up! 😊

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@perdasilva ^ That might clarify better.

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
Expand Down
31 changes: 29 additions & 2 deletions internal/controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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() != "" {
camilamacedo86 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor Author

@camilamacedo86 camilamacedo86 Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @perdasilva,

You added this comment, and I deviated—sorry about that!

Would it be cleaner if we just used an environment variable like ENV_TEST_BINARY_DIR or something like that?
Then, it doesn't really matter where the binaries are placed.

We already have an environment variable (KUBEBUILDER_ASSETS) that can be used, but it’s not very convenient and involves a steep learning curve:

  • Know that ENVTEST requires binaries.
  • Then, by default, ENVTEST is expected to find the bins in local/....
  • We can override this using the KUBEBUILDER_ASSETS environment variable.
  • Note that those bins never was in the $GOPATH/bin because they are placed via the setup-invest in the global place (see here) which indeed cause issues for those which have restrictive envs.
  • Then, there’s the added challenge of knowing how to configure the tests properly in an IDE.

This PR simplifies the process so everything “just works” without extra setup or deep knowledge.
Making tests and debugging smoother and hassle-free.

I’ve also updated the code comments to clarify things further.
Hope this clears everything up! 😊

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both are done 👍
Thank you 🥇

testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir()
}

var err error
config, err = testEnv.Start()
utilruntime.Must(err)
Expand All @@ -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 ""
}
Loading