From fa8b88fefe1d54674ed9072669ae5cacb3f4bb17 Mon Sep 17 00:00:00 2001 From: Camila Macedo <7708031+camilamacedo86@users.noreply.github.com> Date: Thu, 29 Aug 2024 08:26:56 +0100 Subject: [PATCH] :sparkles: (go/v4) e2e tests: improve e2e tests and make test-e2e target (#4106) e2e tests: improve e2e tests and make test-e2e target - Ensure that kind is installed and running before run the tests. Otherwise it will fail since we need to load the Manager(Operator) image - Add logic to skip the installation of CertManager and/or Prometheus via envvars. - Ensure that the promethues and certmanager are installed in the suite test instead of beafore each test - Ensure that the image is build and load in the suite instead instead for each test - Add more comments to clarify the purpose of the tests - Add TODO(user) to clarify that is expected action for the users to suplement and/or customize their e2e tests according to their needs --- .../testdata/project/Makefile | 18 ++++- .../project/test/e2e/e2e_suite_test.go | 60 +++++++++++++- .../testdata/project/test/e2e/e2e_test.go | 78 +++++++++---------- .../getting-started/testdata/project/Makefile | 18 ++++- .../project/test/e2e/e2e_suite_test.go | 60 +++++++++++++- .../testdata/project/test/e2e/e2e_test.go | 78 +++++++++---------- .../scaffolds/internal/templates/makefile.go | 18 ++++- .../internal/templates/test/e2e/suite.go | 60 +++++++++++++- .../internal/templates/test/e2e/test.go | 78 +++++++++---------- .../Makefile | 18 ++++- .../test/e2e/e2e_suite_test.go | 60 +++++++++++++- .../test/e2e/e2e_test.go | 78 +++++++++---------- testdata/project-v4-multigroup/Makefile | 18 ++++- .../test/e2e/e2e_suite_test.go | 60 +++++++++++++- .../test/e2e/e2e_test.go | 78 +++++++++---------- .../project-v4-with-deploy-image/Makefile | 18 ++++- .../test/e2e/e2e_suite_test.go | 60 +++++++++++++- .../test/e2e/e2e_test.go | 78 +++++++++---------- testdata/project-v4-with-grafana/Makefile | 18 ++++- .../test/e2e/e2e_suite_test.go | 60 +++++++++++++- .../test/e2e/e2e_test.go | 78 +++++++++---------- testdata/project-v4/Makefile | 18 ++++- .../project-v4/test/e2e/e2e_suite_test.go | 60 +++++++++++++- testdata/project-v4/test/e2e/e2e_test.go | 78 +++++++++---------- 24 files changed, 864 insertions(+), 384 deletions(-) diff --git a/docs/book/src/cronjob-tutorial/testdata/project/Makefile b/docs/book/src/cronjob-tutorial/testdata/project/Makefile index 83a48ff4e57..5e42f77b5f5 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/Makefile +++ b/docs/book/src/cronjob-tutorial/testdata/project/Makefile @@ -67,9 +67,21 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -# 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: +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: ## Run the e2e tests. Expected an isolated environment using Kind. + @command -v kind >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @kind get clusters | grep -q 'kind' || { \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ + } go test ./test/e2e/ -v -ginkgo.v .PHONY: lint diff --git a/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go index 17905eb80cc..f74f3965316 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go @@ -18,15 +18,71 @@ package e2e import ( "fmt" + "os" + "os/exec" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + "tutorial.kubebuilder.io/project/test/utils" +) + +var ( + // Optional Environment Variables: + // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if Prometheus or CertManager is already installed, avoiding + // re-installation and conflicts. + skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/project:v0.0.1" ) -// Run e2e tests using the Ginkgo runner. +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager and Prometheus. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) - _, _ = fmt.Fprintf(GinkgoWriter, "Starting project suite\n") + _, _ = fmt.Fprintf(GinkgoWriter, "Starting project integration test suite\n") RunSpecs(t, "e2e suite") } + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // Setup Prometheus and CertManager before the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") + Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } +}) + +var _ = AfterSuite(func() { + // Teardown Prometheus and CertManager after the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") + utils.UninstallPrometheusOperator() + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_test.go b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_test.go index 1193e5f1532..2812ef5b973 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_test.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_test.go @@ -29,63 +29,52 @@ import ( const namespace = "project-system" +// Define a set of end-to-end (e2e) tests to validate the behavior of the controller. var _ = Describe("controller", Ordered, func() { + // Before running the tests, set up the environment by creating the namespace, + // installing CRDs, and deploying the controller. BeforeAll(func() { - By("installing prometheus operator") - Expect(utils.InstallPrometheusOperator()).To(Succeed()) - - By("installing the cert-manager") - Expect(utils.InstallCertManager()).To(Succeed()) - By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, _ = utils.Run(cmd) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") }) + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. AfterAll(func() { - By("uninstalling the Prometheus manager bundle") - utils.UninstallPrometheusOperator() + By("undeploying the controller-manager") + cmd := exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) - By("uninstalling the cert-manager bundle") - utils.UninstallCertManager() + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) By("removing manager namespace") - cmd := exec.Command("kubectl", "delete", "ns", namespace) + cmd = exec.Command("kubectl", "delete", "ns", namespace) _, _ = utils.Run(cmd) }) + // The Context block contains the actual tests that validate the operator's behavior. Context("Operator", func() { It("should run successfully", func() { var controllerPodName string - var err error - - // projectimage stores the name of the image used in the example - var projectimage = "example.com/project:v0.0.1" - - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectimage) - 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", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) By("validating that the controller-manager pod is running as expected") verifyControllerUp := func() error { - // Get pod name - - cmd = exec.Command("kubectl", "get", + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", "pods", "-l", "control-plane=controller-manager", "-o", "go-template={{ range .items }}"+ "{{ if not .metadata.deletionTimestamp }}"+ @@ -95,28 +84,31 @@ var _ = Describe("controller", Ordered, func() { ) podOutput, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") podNames := utils.GetNonEmptyLines(string(podOutput)) if len(podNames) != 1 { - return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) + return fmt.Errorf("expected 1 controller pod running, but got %d", len(podNames)) } controllerPodName = podNames[0] ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) - // Validate pod status + // Validate the pod's status cmd = exec.Command("kubectl", "get", "pods", controllerPodName, "-o", "jsonpath={.status.phase}", "-n", namespace, ) status, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod status") if string(status) != "Running" { return fmt.Errorf("controller pod in %s status", status) } return nil } + // Repeatedly check if the controller-manager pod is running until it succeeds or times out. EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) - }) + + // TODO(user): Customize the e2e test suite to include + // additional scenarios specific to your project. }) }) diff --git a/docs/book/src/getting-started/testdata/project/Makefile b/docs/book/src/getting-started/testdata/project/Makefile index 07b91331372..5f297a2fe6d 100644 --- a/docs/book/src/getting-started/testdata/project/Makefile +++ b/docs/book/src/getting-started/testdata/project/Makefile @@ -63,9 +63,21 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -# 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: +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: ## Run the e2e tests. Expected an isolated environment using Kind. + @command -v kind >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @kind get clusters | grep -q 'kind' || { \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ + } go test ./test/e2e/ -v -ginkgo.v .PHONY: lint diff --git a/docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go index 17905eb80cc..fc3f37d0234 100644 --- a/docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go +++ b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go @@ -18,15 +18,71 @@ package e2e import ( "fmt" + "os" + "os/exec" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + "example.com/memcached/test/utils" +) + +var ( + // Optional Environment Variables: + // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if Prometheus or CertManager is already installed, avoiding + // re-installation and conflicts. + skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/project:v0.0.1" ) -// Run e2e tests using the Ginkgo runner. +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager and Prometheus. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) - _, _ = fmt.Fprintf(GinkgoWriter, "Starting project suite\n") + _, _ = fmt.Fprintf(GinkgoWriter, "Starting project integration test suite\n") RunSpecs(t, "e2e suite") } + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // Setup Prometheus and CertManager before the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") + Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } +}) + +var _ = AfterSuite(func() { + // Teardown Prometheus and CertManager after the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") + utils.UninstallPrometheusOperator() + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/docs/book/src/getting-started/testdata/project/test/e2e/e2e_test.go b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_test.go index c6b17a62f5e..66f7dcef8fd 100644 --- a/docs/book/src/getting-started/testdata/project/test/e2e/e2e_test.go +++ b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_test.go @@ -29,63 +29,52 @@ import ( const namespace = "project-system" +// Define a set of end-to-end (e2e) tests to validate the behavior of the controller. var _ = Describe("controller", Ordered, func() { + // Before running the tests, set up the environment by creating the namespace, + // installing CRDs, and deploying the controller. BeforeAll(func() { - By("installing prometheus operator") - Expect(utils.InstallPrometheusOperator()).To(Succeed()) - - By("installing the cert-manager") - Expect(utils.InstallCertManager()).To(Succeed()) - By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, _ = utils.Run(cmd) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") }) + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. AfterAll(func() { - By("uninstalling the Prometheus manager bundle") - utils.UninstallPrometheusOperator() + By("undeploying the controller-manager") + cmd := exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) - By("uninstalling the cert-manager bundle") - utils.UninstallCertManager() + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) By("removing manager namespace") - cmd := exec.Command("kubectl", "delete", "ns", namespace) + cmd = exec.Command("kubectl", "delete", "ns", namespace) _, _ = utils.Run(cmd) }) + // The Context block contains the actual tests that validate the operator's behavior. Context("Operator", func() { It("should run successfully", func() { var controllerPodName string - var err error - - // projectimage stores the name of the image used in the example - var projectimage = "example.com/project:v0.0.1" - - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectimage) - 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", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) By("validating that the controller-manager pod is running as expected") verifyControllerUp := func() error { - // Get pod name - - cmd = exec.Command("kubectl", "get", + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", "pods", "-l", "control-plane=controller-manager", "-o", "go-template={{ range .items }}"+ "{{ if not .metadata.deletionTimestamp }}"+ @@ -95,28 +84,31 @@ var _ = Describe("controller", Ordered, func() { ) podOutput, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") podNames := utils.GetNonEmptyLines(string(podOutput)) if len(podNames) != 1 { - return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) + return fmt.Errorf("expected 1 controller pod running, but got %d", len(podNames)) } controllerPodName = podNames[0] ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) - // Validate pod status + // Validate the pod's status cmd = exec.Command("kubectl", "get", "pods", controllerPodName, "-o", "jsonpath={.status.phase}", "-n", namespace, ) status, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod status") if string(status) != "Running" { return fmt.Errorf("controller pod in %s status", status) } return nil } + // Repeatedly check if the controller-manager pod is running until it succeeds or times out. EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) - }) + + // TODO(user): Customize the e2e test suite to include + // additional scenarios specific to your project. }) }) diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go index b1d9e7d11d9..a4b0c59dea7 100644 --- a/pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go @@ -140,9 +140,21 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -# 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: +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: ## Run the e2e tests. Expected an isolated environment using Kind. + @command -v kind >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @kind get clusters | grep -q 'kind' || { \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ + } go test ./test/e2e/ -v -ginkgo.v .PHONY: lint diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/suite.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/suite.go index 53ca8f807c7..d7281a607a0 100644 --- a/pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/suite.go +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/suite.go @@ -45,15 +45,71 @@ package e2e import ( "fmt" "testing" + "os" + "os/exec" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + "{{ .Repo }}/test/utils" +) + +var ( + // Optional Environment Variables: + // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if Prometheus or CertManager is already installed, avoiding + // re-installation and conflicts. + skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/{{ .ProjectName }}:v0.0.1" ) -// Run e2e tests using the Ginkgo runner. +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager and Prometheus. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) - _, _ = fmt.Fprintf(GinkgoWriter, "Starting {{ .ProjectName }} suite\n") + _, _ = fmt.Fprintf(GinkgoWriter, "Starting {{ .ProjectName }} integration test suite\n") RunSpecs(t, "e2e suite") } + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // Setup Prometheus and CertManager before the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") + Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } +}) + +var _ = AfterSuite(func() { + // Teardown Prometheus and CertManager after the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") + utils.UninstallPrometheusOperator() + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) ` diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/test.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/test.go index d6de3ed8306..6288f9d4cfd 100644 --- a/pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/test.go +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/test/e2e/test.go @@ -56,63 +56,52 @@ import ( const namespace = "{{ .ProjectName }}-system" +// Define a set of end-to-end (e2e) tests to validate the behavior of the controller. var _ = Describe("controller", Ordered, func() { + // Before running the tests, set up the environment by creating the namespace, + // installing CRDs, and deploying the controller. BeforeAll(func() { - By("installing prometheus operator") - Expect(utils.InstallPrometheusOperator()).To(Succeed()) - - By("installing the cert-manager") - Expect(utils.InstallCertManager()).To(Succeed()) - By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, _ = utils.Run(cmd) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") }) + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. AfterAll(func() { - By("uninstalling the Prometheus manager bundle") - utils.UninstallPrometheusOperator() + By("undeploying the controller-manager") + cmd := exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) - By("uninstalling the cert-manager bundle") - utils.UninstallCertManager() + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) By("removing manager namespace") - cmd := exec.Command("kubectl", "delete", "ns", namespace) + cmd = exec.Command("kubectl", "delete", "ns", namespace) _, _ = utils.Run(cmd) }) + // The Context block contains the actual tests that validate the operator's behavior. Context("Operator", func() { It("should run successfully", func() { var controllerPodName string - var err error - - // projectimage stores the name of the image used in the example - var projectimage = "example.com/{{ .ProjectName }}:v0.0.1" - - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectimage) - 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", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) By("validating that the controller-manager pod is running as expected") verifyControllerUp := func() error { - // Get pod name - - cmd = exec.Command("kubectl", "get", + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", "pods", "-l", "control-plane=controller-manager", "-o", "go-template={{"{{"}} range .items {{"}}"}}" + "{{"{{"}} if not .metadata.deletionTimestamp {{"}}"}}" + @@ -122,29 +111,32 @@ var _ = Describe("controller", Ordered, func() { ) podOutput, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") podNames := utils.GetNonEmptyLines(string(podOutput)) if len(podNames) != 1 { - return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) + return fmt.Errorf("expected 1 controller pod running, but got %d", len(podNames)) } controllerPodName = podNames[0] ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) - // Validate pod status + // Validate the pod's status cmd = exec.Command("kubectl", "get", "pods", controllerPodName, "-o", "jsonpath={.status.phase}", "-n", namespace, ) status, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod status") if string(status) != "Running" { return fmt.Errorf("controller pod in %s status", status) } return nil } + // Repeatedly check if the controller-manager pod is running until it succeeds or times out. EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) - }) + + // TODO(user): Customize the e2e test suite to include + // additional scenarios specific to your project. }) }) ` diff --git a/testdata/project-v4-multigroup-with-deploy-image/Makefile b/testdata/project-v4-multigroup-with-deploy-image/Makefile index 3b1c9049ddc..be1d2738b96 100644 --- a/testdata/project-v4-multigroup-with-deploy-image/Makefile +++ b/testdata/project-v4-multigroup-with-deploy-image/Makefile @@ -63,9 +63,21 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -# 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: +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: ## Run the e2e tests. Expected an isolated environment using Kind. + @command -v kind >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @kind get clusters | grep -q 'kind' || { \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ + } go test ./test/e2e/ -v -ginkgo.v .PHONY: lint diff --git a/testdata/project-v4-multigroup-with-deploy-image/test/e2e/e2e_suite_test.go b/testdata/project-v4-multigroup-with-deploy-image/test/e2e/e2e_suite_test.go index 03e400a2875..bd22bc701b0 100644 --- a/testdata/project-v4-multigroup-with-deploy-image/test/e2e/e2e_suite_test.go +++ b/testdata/project-v4-multigroup-with-deploy-image/test/e2e/e2e_suite_test.go @@ -18,15 +18,71 @@ package e2e import ( "fmt" + "os" + "os/exec" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup-with-deploy-image/test/utils" +) + +var ( + // Optional Environment Variables: + // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if Prometheus or CertManager is already installed, avoiding + // re-installation and conflicts. + skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/project-v4-multigroup-with-deploy-image:v0.0.1" ) -// Run e2e tests using the Ginkgo runner. +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager and Prometheus. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) - _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-multigroup-with-deploy-image suite\n") + _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-multigroup-with-deploy-image integration test suite\n") RunSpecs(t, "e2e suite") } + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // Setup Prometheus and CertManager before the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") + Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } +}) + +var _ = AfterSuite(func() { + // Teardown Prometheus and CertManager after the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") + utils.UninstallPrometheusOperator() + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/testdata/project-v4-multigroup-with-deploy-image/test/e2e/e2e_test.go b/testdata/project-v4-multigroup-with-deploy-image/test/e2e/e2e_test.go index 619c7651e00..fe3a6e4d75d 100644 --- a/testdata/project-v4-multigroup-with-deploy-image/test/e2e/e2e_test.go +++ b/testdata/project-v4-multigroup-with-deploy-image/test/e2e/e2e_test.go @@ -29,63 +29,52 @@ import ( const namespace = "project-v4-multigroup-with-deploy-image-system" +// Define a set of end-to-end (e2e) tests to validate the behavior of the controller. var _ = Describe("controller", Ordered, func() { + // Before running the tests, set up the environment by creating the namespace, + // installing CRDs, and deploying the controller. BeforeAll(func() { - By("installing prometheus operator") - Expect(utils.InstallPrometheusOperator()).To(Succeed()) - - By("installing the cert-manager") - Expect(utils.InstallCertManager()).To(Succeed()) - By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, _ = utils.Run(cmd) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") }) + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. AfterAll(func() { - By("uninstalling the Prometheus manager bundle") - utils.UninstallPrometheusOperator() + By("undeploying the controller-manager") + cmd := exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) - By("uninstalling the cert-manager bundle") - utils.UninstallCertManager() + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) By("removing manager namespace") - cmd := exec.Command("kubectl", "delete", "ns", namespace) + cmd = exec.Command("kubectl", "delete", "ns", namespace) _, _ = utils.Run(cmd) }) + // The Context block contains the actual tests that validate the operator's behavior. Context("Operator", func() { It("should run successfully", func() { var controllerPodName string - var err error - - // projectimage stores the name of the image used in the example - var projectimage = "example.com/project-v4-multigroup-with-deploy-image:v0.0.1" - - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectimage) - 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", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) By("validating that the controller-manager pod is running as expected") verifyControllerUp := func() error { - // Get pod name - - cmd = exec.Command("kubectl", "get", + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", "pods", "-l", "control-plane=controller-manager", "-o", "go-template={{ range .items }}"+ "{{ if not .metadata.deletionTimestamp }}"+ @@ -95,28 +84,31 @@ var _ = Describe("controller", Ordered, func() { ) podOutput, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") podNames := utils.GetNonEmptyLines(string(podOutput)) if len(podNames) != 1 { - return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) + return fmt.Errorf("expected 1 controller pod running, but got %d", len(podNames)) } controllerPodName = podNames[0] ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) - // Validate pod status + // Validate the pod's status cmd = exec.Command("kubectl", "get", "pods", controllerPodName, "-o", "jsonpath={.status.phase}", "-n", namespace, ) status, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod status") if string(status) != "Running" { return fmt.Errorf("controller pod in %s status", status) } return nil } + // Repeatedly check if the controller-manager pod is running until it succeeds or times out. EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) - }) + + // TODO(user): Customize the e2e test suite to include + // additional scenarios specific to your project. }) }) diff --git a/testdata/project-v4-multigroup/Makefile b/testdata/project-v4-multigroup/Makefile index a715688e40f..be8efe91823 100644 --- a/testdata/project-v4-multigroup/Makefile +++ b/testdata/project-v4-multigroup/Makefile @@ -63,9 +63,21 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -# 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: +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: ## Run the e2e tests. Expected an isolated environment using Kind. + @command -v kind >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @kind get clusters | grep -q 'kind' || { \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ + } go test ./test/e2e/ -v -ginkgo.v .PHONY: lint diff --git a/testdata/project-v4-multigroup/test/e2e/e2e_suite_test.go b/testdata/project-v4-multigroup/test/e2e/e2e_suite_test.go index d73f0477dee..f97a730d667 100644 --- a/testdata/project-v4-multigroup/test/e2e/e2e_suite_test.go +++ b/testdata/project-v4-multigroup/test/e2e/e2e_suite_test.go @@ -18,15 +18,71 @@ package e2e import ( "fmt" + "os" + "os/exec" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/test/utils" +) + +var ( + // Optional Environment Variables: + // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if Prometheus or CertManager is already installed, avoiding + // re-installation and conflicts. + skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/project-v4-multigroup:v0.0.1" ) -// Run e2e tests using the Ginkgo runner. +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager and Prometheus. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) - _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-multigroup suite\n") + _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-multigroup integration test suite\n") RunSpecs(t, "e2e suite") } + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // Setup Prometheus and CertManager before the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") + Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } +}) + +var _ = AfterSuite(func() { + // Teardown Prometheus and CertManager after the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") + utils.UninstallPrometheusOperator() + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/testdata/project-v4-multigroup/test/e2e/e2e_test.go b/testdata/project-v4-multigroup/test/e2e/e2e_test.go index 3f427fde7b4..580c8745b03 100644 --- a/testdata/project-v4-multigroup/test/e2e/e2e_test.go +++ b/testdata/project-v4-multigroup/test/e2e/e2e_test.go @@ -29,63 +29,52 @@ import ( const namespace = "project-v4-multigroup-system" +// Define a set of end-to-end (e2e) tests to validate the behavior of the controller. var _ = Describe("controller", Ordered, func() { + // Before running the tests, set up the environment by creating the namespace, + // installing CRDs, and deploying the controller. BeforeAll(func() { - By("installing prometheus operator") - Expect(utils.InstallPrometheusOperator()).To(Succeed()) - - By("installing the cert-manager") - Expect(utils.InstallCertManager()).To(Succeed()) - By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, _ = utils.Run(cmd) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") }) + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. AfterAll(func() { - By("uninstalling the Prometheus manager bundle") - utils.UninstallPrometheusOperator() + By("undeploying the controller-manager") + cmd := exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) - By("uninstalling the cert-manager bundle") - utils.UninstallCertManager() + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) By("removing manager namespace") - cmd := exec.Command("kubectl", "delete", "ns", namespace) + cmd = exec.Command("kubectl", "delete", "ns", namespace) _, _ = utils.Run(cmd) }) + // The Context block contains the actual tests that validate the operator's behavior. Context("Operator", func() { It("should run successfully", func() { var controllerPodName string - var err error - - // projectimage stores the name of the image used in the example - var projectimage = "example.com/project-v4-multigroup:v0.0.1" - - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectimage) - 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", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) By("validating that the controller-manager pod is running as expected") verifyControllerUp := func() error { - // Get pod name - - cmd = exec.Command("kubectl", "get", + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", "pods", "-l", "control-plane=controller-manager", "-o", "go-template={{ range .items }}"+ "{{ if not .metadata.deletionTimestamp }}"+ @@ -95,28 +84,31 @@ var _ = Describe("controller", Ordered, func() { ) podOutput, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") podNames := utils.GetNonEmptyLines(string(podOutput)) if len(podNames) != 1 { - return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) + return fmt.Errorf("expected 1 controller pod running, but got %d", len(podNames)) } controllerPodName = podNames[0] ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) - // Validate pod status + // Validate the pod's status cmd = exec.Command("kubectl", "get", "pods", controllerPodName, "-o", "jsonpath={.status.phase}", "-n", namespace, ) status, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod status") if string(status) != "Running" { return fmt.Errorf("controller pod in %s status", status) } return nil } + // Repeatedly check if the controller-manager pod is running until it succeeds or times out. EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) - }) + + // TODO(user): Customize the e2e test suite to include + // additional scenarios specific to your project. }) }) diff --git a/testdata/project-v4-with-deploy-image/Makefile b/testdata/project-v4-with-deploy-image/Makefile index 44a0883d740..395c6dcac7b 100644 --- a/testdata/project-v4-with-deploy-image/Makefile +++ b/testdata/project-v4-with-deploy-image/Makefile @@ -63,9 +63,21 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -# 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: +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: ## Run the e2e tests. Expected an isolated environment using Kind. + @command -v kind >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @kind get clusters | grep -q 'kind' || { \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ + } go test ./test/e2e/ -v -ginkgo.v .PHONY: lint diff --git a/testdata/project-v4-with-deploy-image/test/e2e/e2e_suite_test.go b/testdata/project-v4-with-deploy-image/test/e2e/e2e_suite_test.go index 1aadacf387d..63d8dfa3997 100644 --- a/testdata/project-v4-with-deploy-image/test/e2e/e2e_suite_test.go +++ b/testdata/project-v4-with-deploy-image/test/e2e/e2e_suite_test.go @@ -18,15 +18,71 @@ package e2e import ( "fmt" + "os" + "os/exec" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + "sigs.k8s.io/kubebuilder/testdata/project-v4-with-deploy-image/test/utils" +) + +var ( + // Optional Environment Variables: + // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if Prometheus or CertManager is already installed, avoiding + // re-installation and conflicts. + skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/project-v4-with-deploy-image:v0.0.1" ) -// Run e2e tests using the Ginkgo runner. +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager and Prometheus. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) - _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-with-deploy-image suite\n") + _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-with-deploy-image integration test suite\n") RunSpecs(t, "e2e suite") } + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // Setup Prometheus and CertManager before the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") + Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } +}) + +var _ = AfterSuite(func() { + // Teardown Prometheus and CertManager after the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") + utils.UninstallPrometheusOperator() + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/testdata/project-v4-with-deploy-image/test/e2e/e2e_test.go b/testdata/project-v4-with-deploy-image/test/e2e/e2e_test.go index 8e53b230045..144c8c76d1d 100644 --- a/testdata/project-v4-with-deploy-image/test/e2e/e2e_test.go +++ b/testdata/project-v4-with-deploy-image/test/e2e/e2e_test.go @@ -29,63 +29,52 @@ import ( const namespace = "project-v4-with-deploy-image-system" +// Define a set of end-to-end (e2e) tests to validate the behavior of the controller. var _ = Describe("controller", Ordered, func() { + // Before running the tests, set up the environment by creating the namespace, + // installing CRDs, and deploying the controller. BeforeAll(func() { - By("installing prometheus operator") - Expect(utils.InstallPrometheusOperator()).To(Succeed()) - - By("installing the cert-manager") - Expect(utils.InstallCertManager()).To(Succeed()) - By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, _ = utils.Run(cmd) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") }) + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. AfterAll(func() { - By("uninstalling the Prometheus manager bundle") - utils.UninstallPrometheusOperator() + By("undeploying the controller-manager") + cmd := exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) - By("uninstalling the cert-manager bundle") - utils.UninstallCertManager() + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) By("removing manager namespace") - cmd := exec.Command("kubectl", "delete", "ns", namespace) + cmd = exec.Command("kubectl", "delete", "ns", namespace) _, _ = utils.Run(cmd) }) + // The Context block contains the actual tests that validate the operator's behavior. Context("Operator", func() { It("should run successfully", func() { var controllerPodName string - var err error - - // projectimage stores the name of the image used in the example - var projectimage = "example.com/project-v4-with-deploy-image:v0.0.1" - - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectimage) - 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", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) By("validating that the controller-manager pod is running as expected") verifyControllerUp := func() error { - // Get pod name - - cmd = exec.Command("kubectl", "get", + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", "pods", "-l", "control-plane=controller-manager", "-o", "go-template={{ range .items }}"+ "{{ if not .metadata.deletionTimestamp }}"+ @@ -95,28 +84,31 @@ var _ = Describe("controller", Ordered, func() { ) podOutput, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") podNames := utils.GetNonEmptyLines(string(podOutput)) if len(podNames) != 1 { - return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) + return fmt.Errorf("expected 1 controller pod running, but got %d", len(podNames)) } controllerPodName = podNames[0] ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) - // Validate pod status + // Validate the pod's status cmd = exec.Command("kubectl", "get", "pods", controllerPodName, "-o", "jsonpath={.status.phase}", "-n", namespace, ) status, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod status") if string(status) != "Running" { return fmt.Errorf("controller pod in %s status", status) } return nil } + // Repeatedly check if the controller-manager pod is running until it succeeds or times out. EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) - }) + + // TODO(user): Customize the e2e test suite to include + // additional scenarios specific to your project. }) }) diff --git a/testdata/project-v4-with-grafana/Makefile b/testdata/project-v4-with-grafana/Makefile index 316ae9c54f1..30d74e0ca24 100644 --- a/testdata/project-v4-with-grafana/Makefile +++ b/testdata/project-v4-with-grafana/Makefile @@ -63,9 +63,21 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -# 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: +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: ## Run the e2e tests. Expected an isolated environment using Kind. + @command -v kind >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @kind get clusters | grep -q 'kind' || { \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ + } go test ./test/e2e/ -v -ginkgo.v .PHONY: lint diff --git a/testdata/project-v4-with-grafana/test/e2e/e2e_suite_test.go b/testdata/project-v4-with-grafana/test/e2e/e2e_suite_test.go index 5da0bfe4dd3..7f5df9e4a7e 100644 --- a/testdata/project-v4-with-grafana/test/e2e/e2e_suite_test.go +++ b/testdata/project-v4-with-grafana/test/e2e/e2e_suite_test.go @@ -18,15 +18,71 @@ package e2e import ( "fmt" + "os" + "os/exec" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + "sigs.k8s.io/kubebuilder/testdata/project-v4-with-grafana/test/utils" +) + +var ( + // Optional Environment Variables: + // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if Prometheus or CertManager is already installed, avoiding + // re-installation and conflicts. + skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/project-v4-with-grafana:v0.0.1" ) -// Run e2e tests using the Ginkgo runner. +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager and Prometheus. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) - _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-with-grafana suite\n") + _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4-with-grafana integration test suite\n") RunSpecs(t, "e2e suite") } + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // Setup Prometheus and CertManager before the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") + Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } +}) + +var _ = AfterSuite(func() { + // Teardown Prometheus and CertManager after the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") + utils.UninstallPrometheusOperator() + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/testdata/project-v4-with-grafana/test/e2e/e2e_test.go b/testdata/project-v4-with-grafana/test/e2e/e2e_test.go index 18448c90e19..a26e626abbe 100644 --- a/testdata/project-v4-with-grafana/test/e2e/e2e_test.go +++ b/testdata/project-v4-with-grafana/test/e2e/e2e_test.go @@ -29,63 +29,52 @@ import ( const namespace = "project-v4-with-grafana-system" +// Define a set of end-to-end (e2e) tests to validate the behavior of the controller. var _ = Describe("controller", Ordered, func() { + // Before running the tests, set up the environment by creating the namespace, + // installing CRDs, and deploying the controller. BeforeAll(func() { - By("installing prometheus operator") - Expect(utils.InstallPrometheusOperator()).To(Succeed()) - - By("installing the cert-manager") - Expect(utils.InstallCertManager()).To(Succeed()) - By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, _ = utils.Run(cmd) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") }) + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. AfterAll(func() { - By("uninstalling the Prometheus manager bundle") - utils.UninstallPrometheusOperator() + By("undeploying the controller-manager") + cmd := exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) - By("uninstalling the cert-manager bundle") - utils.UninstallCertManager() + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) By("removing manager namespace") - cmd := exec.Command("kubectl", "delete", "ns", namespace) + cmd = exec.Command("kubectl", "delete", "ns", namespace) _, _ = utils.Run(cmd) }) + // The Context block contains the actual tests that validate the operator's behavior. Context("Operator", func() { It("should run successfully", func() { var controllerPodName string - var err error - - // projectimage stores the name of the image used in the example - var projectimage = "example.com/project-v4-with-grafana:v0.0.1" - - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectimage) - 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", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) By("validating that the controller-manager pod is running as expected") verifyControllerUp := func() error { - // Get pod name - - cmd = exec.Command("kubectl", "get", + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", "pods", "-l", "control-plane=controller-manager", "-o", "go-template={{ range .items }}"+ "{{ if not .metadata.deletionTimestamp }}"+ @@ -95,28 +84,31 @@ var _ = Describe("controller", Ordered, func() { ) podOutput, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") podNames := utils.GetNonEmptyLines(string(podOutput)) if len(podNames) != 1 { - return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) + return fmt.Errorf("expected 1 controller pod running, but got %d", len(podNames)) } controllerPodName = podNames[0] ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) - // Validate pod status + // Validate the pod's status cmd = exec.Command("kubectl", "get", "pods", controllerPodName, "-o", "jsonpath={.status.phase}", "-n", namespace, ) status, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod status") if string(status) != "Running" { return fmt.Errorf("controller pod in %s status", status) } return nil } + // Repeatedly check if the controller-manager pod is running until it succeeds or times out. EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) - }) + + // TODO(user): Customize the e2e test suite to include + // additional scenarios specific to your project. }) }) diff --git a/testdata/project-v4/Makefile b/testdata/project-v4/Makefile index d72b16b0cdf..8b3a1590e4d 100644 --- a/testdata/project-v4/Makefile +++ b/testdata/project-v4/Makefile @@ -63,9 +63,21 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -# 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: +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: ## Run the e2e tests. Expected an isolated environment using Kind. + @command -v kind >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @kind get clusters | grep -q 'kind' || { \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ + } go test ./test/e2e/ -v -ginkgo.v .PHONY: lint diff --git a/testdata/project-v4/test/e2e/e2e_suite_test.go b/testdata/project-v4/test/e2e/e2e_suite_test.go index 20c21692867..4e3fa205918 100644 --- a/testdata/project-v4/test/e2e/e2e_suite_test.go +++ b/testdata/project-v4/test/e2e/e2e_suite_test.go @@ -18,15 +18,71 @@ package e2e import ( "fmt" + "os" + "os/exec" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + "sigs.k8s.io/kubebuilder/testdata/project-v4/test/utils" +) + +var ( + // Optional Environment Variables: + // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if Prometheus or CertManager is already installed, avoiding + // re-installation and conflicts. + skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/project-v4:v0.0.1" ) -// Run e2e tests using the Ginkgo runner. +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager and Prometheus. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) - _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4 suite\n") + _, _ = fmt.Fprintf(GinkgoWriter, "Starting project-v4 integration test suite\n") RunSpecs(t, "e2e suite") } + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // Setup Prometheus and CertManager before the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") + Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } +}) + +var _ = AfterSuite(func() { + // Teardown Prometheus and CertManager after the suite if not skipped + if !skipPrometheusInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") + utils.UninstallPrometheusOperator() + } + if !skipCertManagerInstall { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/testdata/project-v4/test/e2e/e2e_test.go b/testdata/project-v4/test/e2e/e2e_test.go index 5dfdb827a21..690c711c470 100644 --- a/testdata/project-v4/test/e2e/e2e_test.go +++ b/testdata/project-v4/test/e2e/e2e_test.go @@ -29,63 +29,52 @@ import ( const namespace = "project-v4-system" +// Define a set of end-to-end (e2e) tests to validate the behavior of the controller. var _ = Describe("controller", Ordered, func() { + // Before running the tests, set up the environment by creating the namespace, + // installing CRDs, and deploying the controller. BeforeAll(func() { - By("installing prometheus operator") - Expect(utils.InstallPrometheusOperator()).To(Succeed()) - - By("installing the cert-manager") - Expect(utils.InstallCertManager()).To(Succeed()) - By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, _ = utils.Run(cmd) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") }) + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. AfterAll(func() { - By("uninstalling the Prometheus manager bundle") - utils.UninstallPrometheusOperator() + By("undeploying the controller-manager") + cmd := exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) - By("uninstalling the cert-manager bundle") - utils.UninstallCertManager() + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) By("removing manager namespace") - cmd := exec.Command("kubectl", "delete", "ns", namespace) + cmd = exec.Command("kubectl", "delete", "ns", namespace) _, _ = utils.Run(cmd) }) + // The Context block contains the actual tests that validate the operator's behavior. Context("Operator", func() { It("should run successfully", func() { var controllerPodName string - var err error - - // projectimage stores the name of the image used in the example - var projectimage = "example.com/project-v4:v0.0.1" - - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - - By("loading the the manager(Operator) image on Kind") - err = utils.LoadImageToKindClusterWithName(projectimage) - 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", projectimage)) - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) By("validating that the controller-manager pod is running as expected") verifyControllerUp := func() error { - // Get pod name - - cmd = exec.Command("kubectl", "get", + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", "pods", "-l", "control-plane=controller-manager", "-o", "go-template={{ range .items }}"+ "{{ if not .metadata.deletionTimestamp }}"+ @@ -95,28 +84,31 @@ var _ = Describe("controller", Ordered, func() { ) podOutput, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") podNames := utils.GetNonEmptyLines(string(podOutput)) if len(podNames) != 1 { - return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) + return fmt.Errorf("expected 1 controller pod running, but got %d", len(podNames)) } controllerPodName = podNames[0] ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) - // Validate pod status + // Validate the pod's status cmd = exec.Command("kubectl", "get", "pods", controllerPodName, "-o", "jsonpath={.status.phase}", "-n", namespace, ) status, err := utils.Run(cmd) - ExpectWithOffset(2, err).NotTo(HaveOccurred()) + ExpectWithOffset(2, err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod status") if string(status) != "Running" { return fmt.Errorf("controller pod in %s status", status) } return nil } + // Repeatedly check if the controller-manager pod is running until it succeeds or times out. EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) - }) + + // TODO(user): Customize the e2e test suite to include + // additional scenarios specific to your project. }) })