diff --git a/Makefile b/Makefile index 2fd941ee..1d9c133b 100644 --- a/Makefile +++ b/Makefile @@ -125,7 +125,7 @@ test: manifests generate fmt vet envtest ## Run tests. # Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. .PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. -test-e2e: +test-e2e: operator-sdk go test ./test/e2e/ -v -ginkgo.v -timeout 30m GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint diff --git a/docs/developer_guide.md b/docs/developer_guide.md index a75e54e2..4d3b46f8 100644 --- a/docs/developer_guide.md +++ b/docs/developer_guide.md @@ -67,6 +67,12 @@ To run e2e testing, make sure that you are logged in to a running kubernetes clu make test-e2e ``` +To run e2e testing using OLM (Operator Lifecycle Manager) bundle installation: + +```sh +make test-e2e BUNDLE_IMG="your-registry/falcon-operator-bundle:version" +``` + To run integration tests, run the following command: ```sh diff --git a/test/e2e/e2e_helpers.go b/test/e2e/e2e_helpers.go new file mode 100644 index 00000000..69a440b5 --- /dev/null +++ b/test/e2e/e2e_helpers.go @@ -0,0 +1,47 @@ +package e2e + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" +) + +// getOperatorSDKPath returns the path to operator-sdk executable, following the same logic as the Makefile +// It first checks LOCALBIN (./bin), then falls back to system PATH +func getOperatorSDKPath() (string, error) { + // Get current working directory to construct LOCALBIN path + pwd, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("failed to get current directory: %w", err) + } + + // Check LOCALBIN first (equivalent to $(LOCALBIN)/operator-sdk) + localBinPath := filepath.Join(pwd, "bin", "operator-sdk") + if _, err := os.Stat(localBinPath); err == nil { + return localBinPath, nil + } + + // Fall back to system PATH (equivalent to $(shell which operator-sdk)) + systemPath, err := exec.LookPath("operator-sdk") + if err != nil { + return "", fmt.Errorf("operator-sdk not found in LOCALBIN (%s) or system PATH: %w", localBinPath, err) + } + + return systemPath, nil +} + +// isOpenShift detects if the current cluster is OpenShift by checking for OpenShift-specific resources +func isOpenShift() bool { + // Check for OpenShift-specific API resources that indicate we're on OpenShift + // This is a common pattern used in operator development + cmd := exec.Command("kubectl", "api-resources", "--api-group=config.openshift.io") + output, err := cmd.Output() + if err != nil { + return false + } + + // If we can find OpenShift config resources, we're on OpenShift + return len(output) > 0 && strings.Contains(string(output), "clusterversions") +} diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 102652a2..3e7952c0 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -55,6 +55,34 @@ var _ = Describe("falcon", Ordered, func() { }) AfterAll(func() { + // Check if BUNDLE_IMG was used for installation and clean up accordingly + bundleImg := os.Getenv("BUNDLE_IMG") + if bundleImg != "" { + By("cleaning up OLM bundle installation") + + // Get the proper operator-sdk executable path (following Makefile logic) + operatorSDKPath, err := getOperatorSDKPath() + if err == nil { + cmd := exec.Command(operatorSDKPath, "cleanup", "falcon-operator", "--namespace", namespace) + _, _ = utils.Run(cmd) + + // Uninstall OLM only if not on OpenShift (OpenShift has built-in OLM) + if !isOpenShift() { + By("uninstalling OLM") + cmd = exec.Command(operatorSDKPath, "olm", "uninstall") + _, _ = utils.Run(cmd) + } else { + By("detected OpenShift - skipping OLM uninstall (managed by OpenShift)") + } + } else { + By("operator-sdk not found for cleanup, attempting manual cleanup") + } + } else { + By("cleaning up traditional deployment") + cmd := exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) + } + By("removing manager namespace") cmd := exec.Command("kubectl", "delete", "ns", namespace) _, _ = utils.Run(cmd) @@ -63,8 +91,12 @@ var _ = Describe("falcon", Ordered, func() { By("removing metrics cluster role binding") cmd = exec.Command("kubectl", "delete", "clusterrolebinding", metricsRoleBindingName) _, _ = utils.Run(cmd) - cmd = exec.Command("make", "install") - _, _ = utils.Run(cmd) + + // Only run make install for traditional deployment cleanup + if bundleImg == "" { + cmd = exec.Command("make", "install") + _, _ = utils.Run(cmd) + } }) Context("Falcon Operator", func() { @@ -79,28 +111,73 @@ var _ = Describe("falcon", Ordered, func() { operatorImage = image } - cmd := exec.Command("kind", "get", "clusters") - _, err = utils.Run(cmd) - if err == nil { - By("building the manager (Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", operatorImage)) + var outputMake []byte + var cmd *exec.Cmd + + // Conditional installation: Use OLM bundle if BUNDLE_IMG is set, otherwise use traditional deployment + // This allows testing both installation methods: + // - OLM bundle: Set BUNDLE_IMG= to test operator-sdk run bundle installation + // - Traditional: Leave BUNDLE_IMG unset to use make deploy with custom operator image + bundleImg := os.Getenv("BUNDLE_IMG") + if bundleImg != "" { + By("installing using OLM bundle: " + bundleImg) + + // Get the proper operator-sdk executable path (following Makefile logic) + operatorSDKPath, err := getOperatorSDKPath() + if err != nil { + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "operator-sdk is required when BUNDLE_IMG is set") + } + + // Check if operator-sdk is available and working + cmd = exec.Command(operatorSDKPath, "version") _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) + if err != nil { + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "operator-sdk is required when BUNDLE_IMG is set") + } - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(operatorImage) + // Install OLM if not already present (skip on OpenShift as it has OLM built-in) + if !isOpenShift() { + By("installing OLM") + cmd = exec.Command(operatorSDKPath, "olm", "install") + _, err = utils.Run(cmd) + if err != nil { + By("OLM may already be installed, continuing...") + } + } else { + By("detected OpenShift - skipping OLM installation (already built-in)") + } + + // Run bundle installation + By("deploying operator via OLM bundle") + cmd = exec.Command(operatorSDKPath, "run", "bundle", bundleImg, "--namespace", namespace) + outputMake, err = utils.Run(cmd) ExpectWithOffset(1, err).NotTo(HaveOccurred()) - } + } else { + By("installing using traditional deployment method") - By("installing CRDs") - cmd = exec.Command("make", "install") - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) + cmd = exec.Command("kind", "get", "clusters") + _, err = utils.Run(cmd) + if err == nil { + By("building the manager (Operator) image") + cmd = exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", operatorImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + + By("loading the the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(operatorImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + } + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) - By("deploying the controller-manager") - cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", operatorImage)) - outputMake, err := utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", operatorImage)) + outputMake, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + } By("validating that manager Pod/container(s) are not restricted") ExpectWithOffset(1, outputMake).NotTo(ContainSubstring("Warning: would violate PodSecurity"))