diff --git a/Makefile b/Makefile index 17240b2..b8c9e11 100644 --- a/Makefile +++ b/Makefile @@ -151,8 +151,8 @@ KIND_CLUSTER ?= reloader-e2e CONTAINER_RUNTIME ?= $(shell command -v docker 2>/dev/null || command -v podman 2>/dev/null) # Set SKIP_BUILD=true to skip the image build/load steps and use a pre-built image. SKIP_BUILD ?= false -# Number of parallel Ginkgo workers. Set to 1 to run sequentially. -GINKGO_PROCS ?= 4 +# Number of parallel Ginkgo workers. Defaults to 1 (sequential). Override with GINKGO_PROCS=N. +GINKGO_PROCS ?= 1 .PHONY: e2e-setup e2e-setup: ## One-time setup: create Kind cluster and install dependencies (Argo, CSI, Vault) diff --git a/test/e2e/advanced/advanced_suite_test.go b/test/e2e/advanced/advanced_suite_test.go index 84abcd7..bac2aaa 100644 --- a/test/e2e/advanced/advanced_suite_test.go +++ b/test/e2e/advanced/advanced_suite_test.go @@ -36,6 +36,9 @@ var _ = SynchronizedBeforeSuite( func() []byte { setupEnv, err := utils.SetupTestEnvironment(context.Background(), "reloader-advanced") Expect(err).NotTo(HaveOccurred(), "Failed to setup test environment") + // Ensure the namespace is deleted even if DeployAndWait fails, so + // orphaned namespaces don't accumulate on long-lived clusters. + DeferCleanup(setupEnv.CleanupOnFailure) deployValues := map[string]string{ "reloader.reloadStrategy": "annotations", diff --git a/test/e2e/annotations/annotations_suite_test.go b/test/e2e/annotations/annotations_suite_test.go index 7ce3de6..8c9bc33 100644 --- a/test/e2e/annotations/annotations_suite_test.go +++ b/test/e2e/annotations/annotations_suite_test.go @@ -37,6 +37,9 @@ var _ = SynchronizedBeforeSuite( func() []byte { setupEnv, err := utils.SetupTestEnvironment(context.Background(), "reloader-annotations-test") Expect(err).NotTo(HaveOccurred(), "Failed to setup test environment") + // Ensure the namespace is deleted even if DeployAndWait fails, so + // orphaned namespaces don't accumulate on long-lived clusters. + DeferCleanup(setupEnv.CleanupOnFailure) deployValues := map[string]string{ "reloader.reloadStrategy": "annotations", diff --git a/test/e2e/argo/argo_suite_test.go b/test/e2e/argo/argo_suite_test.go index 6a59935..b34bf37 100644 --- a/test/e2e/argo/argo_suite_test.go +++ b/test/e2e/argo/argo_suite_test.go @@ -34,6 +34,9 @@ var _ = SynchronizedBeforeSuite( func() []byte { setupEnv, err := utils.SetupTestEnvironment(context.Background(), "reloader-argo") Expect(err).NotTo(HaveOccurred(), "Failed to setup test environment") + // Ensure the namespace is deleted even if DeployAndWait fails, so + // orphaned namespaces don't accumulate on long-lived clusters. + DeferCleanup(setupEnv.CleanupOnFailure) if !utils.IsArgoRolloutsInstalled(context.Background(), setupEnv.RolloutsClient) { Skip("Argo Rollouts is not installed. Run ./scripts/e2e-cluster-setup.sh first") diff --git a/test/e2e/core/core_suite_test.go b/test/e2e/core/core_suite_test.go index 82e8582..acf7bf6 100644 --- a/test/e2e/core/core_suite_test.go +++ b/test/e2e/core/core_suite_test.go @@ -37,6 +37,9 @@ var _ = SynchronizedBeforeSuite( func() []byte { setupEnv, err := utils.SetupTestEnvironment(context.Background(), "reloader-core-test") Expect(err).NotTo(HaveOccurred(), "Failed to setup test environment") + // Ensure the namespace is deleted even if DeployAndWait fails, so + // orphaned namespaces don't accumulate on long-lived clusters. + DeferCleanup(setupEnv.CleanupOnFailure) deployValues := map[string]string{ "reloader.reloadStrategy": "annotations", diff --git a/test/e2e/csi/csi_suite_test.go b/test/e2e/csi/csi_suite_test.go index 7ea4a36..f2e809c 100644 --- a/test/e2e/csi/csi_suite_test.go +++ b/test/e2e/csi/csi_suite_test.go @@ -36,6 +36,9 @@ var _ = SynchronizedBeforeSuite( func() []byte { setupEnv, err := utils.SetupTestEnvironment(context.Background(), "reloader-csi-test") Expect(err).NotTo(HaveOccurred(), "Failed to setup test environment") + // Ensure the namespace is deleted even if DeployAndWait fails, so + // orphaned namespaces don't accumulate on long-lived clusters. + DeferCleanup(setupEnv.CleanupOnFailure) if !utils.IsCSIDriverInstalled(context.Background(), setupEnv.CSIClient) { Skip("CSI secrets store driver not installed - skipping CSI suite") diff --git a/test/e2e/flags/auto_reload_all_test.go b/test/e2e/flags/auto_reload_all_test.go index 39ccb49..f4cda1c 100644 --- a/test/e2e/flags/auto_reload_all_test.go +++ b/test/e2e/flags/auto_reload_all_test.go @@ -9,7 +9,7 @@ import ( "github.com/stakater/Reloader/test/e2e/utils" ) -var _ = Describe("Auto Reload All Flag Tests", func() { +var _ = Describe("Auto Reload All Flag Tests", Serial, func() { var ( deploymentName string configMapName string diff --git a/test/e2e/flags/flags_suite_test.go b/test/e2e/flags/flags_suite_test.go index dc922cb..15a8703 100644 --- a/test/e2e/flags/flags_suite_test.go +++ b/test/e2e/flags/flags_suite_test.go @@ -2,6 +2,7 @@ package flags import ( "context" + "encoding/json" "testing" . "github.com/onsi/ginkgo/v2" @@ -23,25 +24,55 @@ func TestFlags(t *testing.T) { RunSpecs(t, "Flag-Based E2E Suite") } -var _ = BeforeSuite(func() { - var err error - ctx = context.Background() +// SynchronizedBeforeSuite ensures only process 1 creates the shared namespace. +// The flags tests each deploy/undeploy Reloader themselves (marked Serial), so +// there is no shared Reloader instance — only the namespace is shared. +var _ = SynchronizedBeforeSuite( + // Process 1 only: create namespace, build clients. + func() []byte { + setupEnv, err := utils.SetupTestEnvironment(context.Background(), "reloader-flags") + Expect(err).NotTo(HaveOccurred(), "Failed to setup test environment") + // Ensure the namespace is cleaned up if setup fails. + DeferCleanup(setupEnv.CleanupOnFailure) - testEnv, err = utils.SetupTestEnvironment(ctx, "reloader-flags") - Expect(err).NotTo(HaveOccurred(), "Failed to setup test environment") + data, err := json.Marshal(utils.SharedEnvData{ + Namespace: setupEnv.Namespace, + ReleaseName: setupEnv.ReleaseName, + }) + Expect(err).NotTo(HaveOccurred()) + return data + }, + // All processes (including #1): connect to the shared namespace. + func(data []byte) { + var shared utils.SharedEnvData + Expect(json.Unmarshal(data, &shared)).To(Succeed()) - kubeClient = testEnv.KubeClient - testNamespace = testEnv.Namespace -}) + var err error + testEnv, err = utils.SetupSharedTestEnvironment(context.Background(), shared.Namespace, shared.ReleaseName) + Expect(err).NotTo(HaveOccurred(), "Failed to setup shared test environment") -var _ = AfterSuite(func() { - if testEnv != nil { - err := testEnv.Cleanup() - Expect(err).NotTo(HaveOccurred(), "Failed to cleanup test environment") - } + kubeClient = testEnv.KubeClient + testNamespace = testEnv.Namespace + ctx = testEnv.Ctx + }, +) - GinkgoWriter.Println("Flags E2E Suite cleanup complete") -}) +var _ = SynchronizedAfterSuite( + // All processes: cancel the per-process context. + func() { + if testEnv != nil { + testEnv.Cancel() + } + }, + // Process 1 only (runs last): delete namespace. + func() { + if testEnv != nil { + err := testEnv.Cleanup() + Expect(err).NotTo(HaveOccurred(), "Failed to cleanup test environment") + } + GinkgoWriter.Println("Flags E2E Suite cleanup complete") + }, +) // deployReloaderWithFlags deploys Reloader with the specified Helm value overrides. // This is a convenience function for tests that need to deploy with specific flags. diff --git a/test/e2e/flags/ignore_resources_test.go b/test/e2e/flags/ignore_resources_test.go index 369cd24..44eb539 100644 --- a/test/e2e/flags/ignore_resources_test.go +++ b/test/e2e/flags/ignore_resources_test.go @@ -9,7 +9,7 @@ import ( "github.com/stakater/Reloader/test/e2e/utils" ) -var _ = Describe("Ignore Resources Flag Tests", func() { +var _ = Describe("Ignore Resources Flag Tests", Serial, func() { var ( deploymentName string configMapName string diff --git a/test/e2e/flags/ignored_workloads_test.go b/test/e2e/flags/ignored_workloads_test.go index 33a8fba..6a392a4 100644 --- a/test/e2e/flags/ignored_workloads_test.go +++ b/test/e2e/flags/ignored_workloads_test.go @@ -9,7 +9,7 @@ import ( "github.com/stakater/Reloader/test/e2e/utils" ) -var _ = Describe("Ignored Workloads Flag Tests", func() { +var _ = Describe("Ignored Workloads Flag Tests", Serial, func() { var ( cronJobName string configMapName string diff --git a/test/e2e/flags/namespace_ignore_test.go b/test/e2e/flags/namespace_ignore_test.go index 5fd2caa..3946790 100644 --- a/test/e2e/flags/namespace_ignore_test.go +++ b/test/e2e/flags/namespace_ignore_test.go @@ -9,7 +9,7 @@ import ( "github.com/stakater/Reloader/test/e2e/utils" ) -var _ = Describe("Namespace Ignore Flag Tests", func() { +var _ = Describe("Namespace Ignore Flag Tests", Serial, func() { var ( deploymentName string configMapName string diff --git a/test/e2e/flags/namespace_selector_test.go b/test/e2e/flags/namespace_selector_test.go index da34927..4ac49cb 100644 --- a/test/e2e/flags/namespace_selector_test.go +++ b/test/e2e/flags/namespace_selector_test.go @@ -9,7 +9,7 @@ import ( "github.com/stakater/Reloader/test/e2e/utils" ) -var _ = Describe("Namespace Selector Flag Tests", func() { +var _ = Describe("Namespace Selector Flag Tests", Serial, func() { var ( deploymentName string configMapName string diff --git a/test/e2e/flags/reload_on_create_test.go b/test/e2e/flags/reload_on_create_test.go index 52a1b08..63fec0b 100644 --- a/test/e2e/flags/reload_on_create_test.go +++ b/test/e2e/flags/reload_on_create_test.go @@ -9,7 +9,7 @@ import ( "github.com/stakater/Reloader/test/e2e/utils" ) -var _ = Describe("Reload On Create Flag Tests", func() { +var _ = Describe("Reload On Create Flag Tests", Serial, func() { var ( deploymentName string configMapName string diff --git a/test/e2e/flags/reload_on_delete_test.go b/test/e2e/flags/reload_on_delete_test.go index f0f3b1e..ed400e3 100644 --- a/test/e2e/flags/reload_on_delete_test.go +++ b/test/e2e/flags/reload_on_delete_test.go @@ -9,7 +9,7 @@ import ( "github.com/stakater/Reloader/test/e2e/utils" ) -var _ = Describe("Reload On Delete Flag Tests", func() { +var _ = Describe("Reload On Delete Flag Tests", Serial, func() { var ( deploymentName string configMapName string diff --git a/test/e2e/flags/resource_selector_test.go b/test/e2e/flags/resource_selector_test.go index 8406310..cc94612 100644 --- a/test/e2e/flags/resource_selector_test.go +++ b/test/e2e/flags/resource_selector_test.go @@ -9,7 +9,7 @@ import ( "github.com/stakater/Reloader/test/e2e/utils" ) -var _ = Describe("Resource Label Selector Flag Tests", func() { +var _ = Describe("Resource Label Selector Flag Tests", Serial, func() { var ( deploymentName string matchingCM string diff --git a/test/e2e/flags/watch_globally_test.go b/test/e2e/flags/watch_globally_test.go index 177daf2..96e3fb2 100644 --- a/test/e2e/flags/watch_globally_test.go +++ b/test/e2e/flags/watch_globally_test.go @@ -9,7 +9,7 @@ import ( "github.com/stakater/Reloader/test/e2e/utils" ) -var _ = Describe("Watch Globally Flag Tests", func() { +var _ = Describe("Watch Globally Flag Tests", Serial, func() { var ( deploymentName string configMapName string diff --git a/test/e2e/utils/helm.go b/test/e2e/utils/helm.go index 28deb5c..320782d 100644 --- a/test/e2e/utils/helm.go +++ b/test/e2e/utils/helm.go @@ -52,7 +52,7 @@ func DeployReloader(opts DeployOptions) error { opts.ReleaseName = DefaultHelmReleaseName } if opts.Timeout == "" { - opts.Timeout = "120s" + opts.Timeout = "180s" } if opts.Image == "" { opts.Image = GetTestImage() diff --git a/test/e2e/utils/testenv.go b/test/e2e/utils/testenv.go index cf95b7d..a8be455 100644 --- a/test/e2e/utils/testenv.go +++ b/test/e2e/utils/testenv.go @@ -177,6 +177,20 @@ func SetupTestEnvironment(ctx context.Context, namespacePrefix string) (*TestEnv return env, nil } +// CleanupOnFailure attempts a best-effort cleanup of the namespace used +// by this environment. It is intended to be deferred in a BeforeSuite +// so that orphaned namespaces don't accumulate on a long-lived cluster +// when the suite setup fails. Errors are logged but not fatal. +func (e *TestEnvironment) CleanupOnFailure() { + if e.Namespace == "" || e.KubeClient == nil { + return + } + cleanupCtx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + defer cancel() + _ = UndeployReloader(e.Namespace, e.ReleaseName) + _ = DeleteNamespace(cleanupCtx, e.KubeClient, e.Namespace) +} + // Cleanup cleans up the test environment resources. // It uses a fresh context so it can run safely even after the suite context // has been cancelled by SynchronizedAfterSuite.