From e26876b7fec2de52415fe7f86ebe9c22c2b3ee8f Mon Sep 17 00:00:00 2001 From: Fabricio Aguiar Date: Tue, 10 Nov 2026 14:01:25 +0000 Subject: [PATCH] refactor(e2e): move catalogd steps and cleanup to its suite Move HA, proxy, and TLS assertions with catalog-specific state and restoration hooks. Consume shared preparation without depending on platform, operator-controller, or object-controller step extractions. Refs: OPRUN-4784 Signed-off-by: Fabricio Aguiar rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED --- test/e2e/catalogd/suite_test.go | 3 + test/e2e/features_test.go | 3 + .../e2e/{ => catalogd}/steps/ha_steps.go | 22 ++--- test/internal/e2e/catalogd/steps/hooks.go | 85 +++++++++++++++++++ .../e2e/{ => catalogd}/steps/proxy_steps.go | 34 ++++---- .../e2e/{ => catalogd}/steps/tls_steps.go | 28 +++--- test/internal/e2e/steps/hooks.go | 42 +-------- test/internal/e2e/steps/steps.go | 17 ---- 8 files changed, 135 insertions(+), 99 deletions(-) rename test/internal/e2e/{ => catalogd}/steps/ha_steps.go (69%) create mode 100644 test/internal/e2e/catalogd/steps/hooks.go rename test/internal/e2e/{ => catalogd}/steps/proxy_steps.go (90%) rename test/internal/e2e/{ => catalogd}/steps/tls_steps.go (94%) diff --git a/test/e2e/catalogd/suite_test.go b/test/e2e/catalogd/suite_test.go index 721ee038df..b0ac4b9694 100644 --- a/test/e2e/catalogd/suite_test.go +++ b/test/e2e/catalogd/suite_test.go @@ -7,6 +7,7 @@ import ( "github.com/cucumber/godog" "github.com/operator-framework/operator-controller/test/internal/e2e" + catalogsteps "github.com/operator-framework/operator-controller/test/internal/e2e/catalogd/steps" "github.com/operator-framework/operator-controller/test/internal/e2e/steps" ) @@ -15,7 +16,9 @@ func TestMain(m *testing.M) { tc.BeforeSuite(steps.BeforeSuite) }, func(sc *godog.ScenarioContext) { steps.RegisterSteps(sc) + catalogsteps.RegisterSteps(sc) steps.RegisterDemoSteps(sc) + catalogsteps.RegisterHooks(sc) steps.RegisterHooks(sc) })) } diff --git a/test/e2e/features_test.go b/test/e2e/features_test.go index 539cd36df0..d52553e64d 100644 --- a/test/e2e/features_test.go +++ b/test/e2e/features_test.go @@ -14,6 +14,7 @@ import ( "github.com/cucumber/godog" "github.com/operator-framework/operator-controller/test/internal/e2e" + catalogsteps "github.com/operator-framework/operator-controller/test/internal/e2e/catalogd/steps" "github.com/operator-framework/operator-controller/test/internal/e2e/steps" ) @@ -41,7 +42,9 @@ func TestMain(m *testing.M) { tc.BeforeSuite(steps.BeforeSuite) }, func(sc *godog.ScenarioContext) { steps.RegisterSteps(sc) + catalogsteps.RegisterSteps(sc) steps.RegisterDemoSteps(sc) + catalogsteps.RegisterHooks(sc) steps.RegisterHooks(sc, steps.RegisterAsciiCastHooks) })) } diff --git a/test/internal/e2e/steps/ha_steps.go b/test/internal/e2e/catalogd/steps/ha_steps.go similarity index 69% rename from test/internal/e2e/steps/ha_steps.go rename to test/internal/e2e/catalogd/steps/ha_steps.go index f39c440972..9f82828137 100644 --- a/test/internal/e2e/steps/ha_steps.go +++ b/test/internal/e2e/catalogd/steps/ha_steps.go @@ -5,15 +5,9 @@ import ( "fmt" "strings" - "k8s.io/component-base/featuregate" + shared "github.com/operator-framework/operator-controller/test/internal/e2e/steps" ) -// catalogdHAFeature gates scenarios that require a multi-node cluster. -// It is set to true in BeforeSuite when the cluster has at least 2 nodes, -// which is the case for the experimental e2e suite (kind-config-2node.yaml) -// but not the standard suite. -const catalogdHAFeature featuregate.Feature = "CatalogdHA" - // CatalogdLeaderPodIsForceDeleted force-deletes the catalogd leader pod to simulate leader loss. // The pod is identified from sc.LeaderPods["catalogd"] (populated by a prior // "catalogd is ready to reconcile resources" step). Force-deletion is equivalent to @@ -24,14 +18,14 @@ const catalogdHAFeature featuregate.Feature = "CatalogdHA" // experimental 2-node cluster are control-plane nodes that run etcd — stopping either // would break etcd quorum and make the API server unreachable for the rest of the test. func CatalogdLeaderPodIsForceDeleted(ctx context.Context) error { - sc := Scenario(ctx) + sc := shared.Scenario(ctx) leaderPod := sc.LeaderPods["catalogd"] if leaderPod == "" { return fmt.Errorf("catalogd leader pod not found in scenario context; run 'catalogd is ready to reconcile resources' first") } - logger.Info("Force-deleting catalogd leader pod", "pod", leaderPod) - if _, err := K8sClient(ctx, "delete", "pod", leaderPod, "-n", NamespaceForComponent("catalogd"), + shared.Log().Info("Force-deleting catalogd leader pod", "pod", leaderPod) + if _, err := shared.K8sClient(ctx, "delete", "pod", leaderPod, "-n", shared.NamespaceForComponent("catalogd"), "--force", "--grace-period=0"); err != nil { return fmt.Errorf("failed to force-delete catalogd leader pod %q: %w", leaderPod, err) } @@ -42,11 +36,11 @@ func CatalogdLeaderPodIsForceDeleted(ctx context.Context) error { // identity changes to a pod other than the deleted leader. It updates // sc.LeaderPods["catalogd"] with the new leader pod name. func NewCatalogdLeaderIsElected(ctx context.Context) error { - sc := Scenario(ctx) + sc := shared.Scenario(ctx) oldLeader := sc.LeaderPods["catalogd"] - WaitFor(ctx, func() bool { - holder, err := K8sClient(ctx, "get", "lease", leaseNames["catalogd"], "-n", NamespaceForComponent("catalogd"), + shared.WaitFor(ctx, func() bool { + holder, err := shared.K8sClient(ctx, "get", "lease", "catalogd-operator-lock", "-n", shared.NamespaceForComponent("catalogd"), "-o", "jsonpath={.spec.holderIdentity}") if err != nil || holder == "" { return false @@ -56,7 +50,7 @@ func NewCatalogdLeaderIsElected(ctx context.Context) error { return false } sc.LeaderPods["catalogd"] = newPod - logger.Info("New catalogd leader elected", "pod", newPod) + shared.Log().Info("New catalogd leader elected", "pod", newPod) return true }) return nil diff --git a/test/internal/e2e/catalogd/steps/hooks.go b/test/internal/e2e/catalogd/steps/hooks.go new file mode 100644 index 0000000000..d05e864e41 --- /dev/null +++ b/test/internal/e2e/catalogd/steps/hooks.go @@ -0,0 +1,85 @@ +package steps + +import ( + "context" + "fmt" + + "github.com/cucumber/godog" + + shared "github.com/operator-framework/operator-controller/test/internal/e2e/steps" +) + +// deploymentRestore records the original state of a deployment so it can be +// rolled back after a test that modifies deployment configuration. +type deploymentRestore struct { + Name string // deployment name + Namespace string + containerName string // container to patch (for env var restores) + patchedArgs bool // true when container args were modified (for TLS profile patches) + originalArgs []string // original container args; may be nil if args were unset + originalEnv []string // original env vars as "NAME=VALUE" (for proxy patches) +} + +type catalogContextKey struct{} +type catalogContext struct { + proxy *recordingProxy + deploymentRestores []deploymentRestore +} + +func catalogScenario(ctx context.Context) *catalogContext { + return ctx.Value(catalogContextKey{}).(*catalogContext) +} + +// RegisterHooks installs catalog-specific state and unconditional deployment restoration. +// Register it before shared hooks so modified deployments are restored before resource cleanup. +func RegisterHooks(sc *godog.ScenarioContext) { + sc.Before(func(ctx context.Context, _ *godog.Scenario) (context.Context, error) { + return context.WithValue(ctx, catalogContextKey{}, &catalogContext{}), nil + }) + sc.After(func(ctx context.Context, _ *godog.Scenario, err error) (context.Context, error) { + sc := catalogScenario(ctx) + // Stop the in-process recording proxy if one was started. + if sc.proxy != nil { + sc.proxy.stop() + } + // Restore any deployments that were modified during the scenario. Runs + // unconditionally (even on failure) to prevent a misconfigured deployment + // from bleeding into subsequent scenarios. Restored in LIFO order so that + // multiple patches to the same deployment unwind to the true original. + for i := len(sc.deploymentRestores) - 1; i >= 0; i-- { + dr := sc.deploymentRestores[i] + if dr.patchedArgs { + if err2 := patchDeploymentArgs(dr.Namespace, dr.Name, dr.originalArgs); err2 != nil { + shared.Log().Info("Error restoring deployment args", "name", dr.Name, "error", err2) + } else if _, err2 := shared.K8sClient(ctx, "rollout", "status", "-n", dr.Namespace, + fmt.Sprintf("deployment/%s", dr.Name), "--timeout=2m"); err2 != nil { + shared.Log().Info("Timeout waiting for deployment rollout after restore", "name", dr.Name) + } + } + if err2 := restoreDeployment(dr); err2 != nil { + shared.Log().Info("Error restoring deployment env", "deployment", dr.Name, "namespace", dr.Namespace, "error", err2) + } + } + + return ctx, err + }) +} + +// RegisterSteps registers catalogd HA, proxy, and TLS assertions. +func RegisterSteps(sc *godog.ScenarioContext) { + sc.Step(`^(?i)the "([^"]+)" deployment is configured with custom TLS minimum version "([^"]+)"$`, ConfigureDeploymentWithCustomTLSVersion) + sc.Step(`^(?i)the "([^"]+)" deployment is configured with custom TLS version "([^"]+)", ciphers "([^"]+)", and curves "([^"]+)"$`, ConfigureDeploymentWithCustomTLSFull) + sc.Step(`^(?i)the "([^"]+)" metrics endpoint accepts a TLS 1\.3 connection$`, MetricsEndpointAcceptsTLS13) + sc.Step(`^(?i)the "([^"]+)" metrics endpoint rejects a TLS 1\.2 connection$`, MetricsEndpointRejectsTLS12) + sc.Step(`^(?i)the "([^"]+)" metrics endpoint negotiates cipher "([^"]+)" over TLS 1\.2$`, MetricsEndpointNegotiatesTLS12Cipher) + sc.Step(`^(?i)the "([^"]+)" metrics endpoint rejects a TLS 1\.2 connection offering only cipher "([^"]+)"$`, MetricsEndpointRejectsTLS12ConnectionWithCipher) + sc.Step(`^(?i)the "([^"]+)" metrics endpoint accepts a TLS 1\.2 connection with cipher "([^"]+)" and curve "([^"]+)"$`, MetricsEndpointAcceptsTLS12ConnectionWithCurve) + sc.Step(`^(?i)the "([^"]+)" metrics endpoint rejects a TLS 1\.2 connection with cipher "([^"]+)" and only curve "([^"]+)"$`, MetricsEndpointRejectsTLS12ConnectionWithCurve) + sc.Step(`^(?i)the "([^"]+)" metrics endpoint accepts a connection using only curve "([^"]+)"$`, MetricsEndpointAcceptsConnectionUsingCurve) + sc.Step(`^(?i)the "([^"]+)" metrics endpoint rejects a connection using only curve "([^"]+)"$`, MetricsEndpointRejectsConnectionUsingOnlyCurve) + sc.Step(`^(?i)the "([^"]+)" component is configured with HTTPS_PROXY "([^"]+)"$`, ConfigureDeploymentWithHTTPSProxy) + sc.Step(`^(?i)the "([^"]+)" component is configured with HTTPS_PROXY pointing to a recording proxy$`, StartRecordingProxyAndConfigureDeployment) + sc.Step(`^(?i)the recording proxy received a CONNECT request for the catalogd service$`, RecordingProxyReceivedCONNECTForCatalogd) + sc.Step(`^(?i)the catalogd leader pod is force-deleted$`, CatalogdLeaderPodIsForceDeleted) + sc.Step(`^(?i)a new catalogd leader is elected$`, NewCatalogdLeaderIsElected) +} diff --git a/test/internal/e2e/steps/proxy_steps.go b/test/internal/e2e/catalogd/steps/proxy_steps.go similarity index 90% rename from test/internal/e2e/steps/proxy_steps.go rename to test/internal/e2e/catalogd/steps/proxy_steps.go index d92fc7eb98..7035797a07 100644 --- a/test/internal/e2e/steps/proxy_steps.go +++ b/test/internal/e2e/catalogd/steps/proxy_steps.go @@ -15,6 +15,8 @@ import ( "time" appsv1 "k8s.io/api/apps/v1" + + shared "github.com/operator-framework/operator-controller/test/internal/e2e/steps" ) // --------------------------------------------------------------------------- @@ -157,7 +159,7 @@ func kindGatewayIP() (string, error) { // the "default" namespace, which is the address client-go uses to reach the // API server from inside a pod (via the KUBERNETES_SERVICE_HOST env var). func kubernetesClusterIP() (string, error) { - ip, err := K8sClient(context.Background(), "get", "service", "kubernetes", "-n", "default", + ip, err := shared.K8sClient(context.Background(), "get", "service", "kubernetes", "-n", "default", "-o", "jsonpath={.spec.clusterIP}") if err != nil { return "", fmt.Errorf("failed to get kubernetes service cluster IP: %w", err) @@ -168,7 +170,7 @@ func kubernetesClusterIP() (string, error) { // getDeploymentContainerEnv returns the environment variables for the named // container in the given deployment, as a slice of "NAME=VALUE" strings. func getDeploymentContainerEnv(deploymentName, namespace, containerName string) ([]string, error) { - raw, err := K8sClient(context.Background(), "get", "deployment", deploymentName, "-n", namespace, "-o", "json") + raw, err := shared.K8sClient(context.Background(), "get", "deployment", deploymentName, "-n", namespace, "-o", "json") if err != nil { return nil, fmt.Errorf("failed to get deployment %s/%s: %w", namespace, deploymentName, err) } @@ -196,7 +198,7 @@ func getDeploymentContainerEnv(deploymentName, namespace, containerName string) // the JSON Patch "add" operation, which creates the env field if absent. func setDeploymentEnvVars(deploymentName, namespace, containerName string, env []string) error { // Fetch the deployment to find the container index. - raw, err := K8sClient(context.Background(), "get", "deployment", deploymentName, "-n", namespace, "-o", "json") + raw, err := shared.K8sClient(context.Background(), "get", "deployment", deploymentName, "-n", namespace, "-o", "json") if err != nil { return fmt.Errorf("failed to get deployment %s/%s: %w", namespace, deploymentName, err) } @@ -243,12 +245,12 @@ func setDeploymentEnvVars(deploymentName, namespace, containerName string, env [ return fmt.Errorf("failed to marshal patch: %w", err) } - if _, err := K8sClient(context.Background(), "patch", "deployment", deploymentName, "-n", namespace, + if _, err := shared.K8sClient(context.Background(), "patch", "deployment", deploymentName, "-n", namespace, "--type=json", fmt.Sprintf("--patch=%s", string(patchBytes))); err != nil { return fmt.Errorf("failed to patch deployment %s/%s: %w", namespace, deploymentName, err) } - if _, err := K8sClient(context.Background(), "rollout", "status", "deployment", deploymentName, "-n", namespace, + if _, err := shared.K8sClient(context.Background(), "rollout", "status", "deployment", deploymentName, "-n", namespace, "--timeout=5m"); err != nil { return fmt.Errorf("rollout of deployment %s/%s did not complete: %w", namespace, deploymentName, err) } @@ -284,12 +286,12 @@ func restoreDeployment(r deploymentRestore) error { // proxyURL and NO_PROXY to the Kubernetes API server cluster IP, then waits // for the rollout. The original env is saved for cleanup. func configureDeploymentProxy(ctx context.Context, component, proxyURL string) error { - sc := Scenario(ctx) + sc := catalogScenario(ctx) var deployName string switch component { case "operator-controller": - deployName = olmDeploymentName + deployName = "operator-controller-controller-manager" case "catalogd": deployName = "catalogd-controller-manager" default: @@ -304,13 +306,13 @@ func configureDeploymentProxy(ctx context.Context, component, proxyURL string) e // namespace fields but have no containerName or originalEnv set. alreadyTracked := false for _, r := range sc.deploymentRestores { - if r.Name == deployName && r.Namespace == OLMNamespace && r.containerName != "" { + if r.Name == deployName && r.Namespace == shared.OLMNamespace && r.containerName != "" { alreadyTracked = true break } } - origEnv, err := getDeploymentContainerEnv(deployName, OLMNamespace, "manager") + origEnv, err := getDeploymentContainerEnv(deployName, shared.OLMNamespace, "manager") if err != nil { return err } @@ -318,7 +320,7 @@ func configureDeploymentProxy(ctx context.Context, component, proxyURL string) e if !alreadyTracked { sc.deploymentRestores = append(sc.deploymentRestores, deploymentRestore{ Name: deployName, - Namespace: OLMNamespace, + Namespace: shared.OLMNamespace, containerName: "manager", originalEnv: origEnv, }) @@ -335,7 +337,7 @@ func configureDeploymentProxy(ctx context.Context, component, proxyURL string) e newEnv := addOrReplaceEnvVar(origEnv, "HTTPS_PROXY", proxyURL) newEnv = addOrReplaceEnvVar(newEnv, "NO_PROXY", k8sIP) - return setDeploymentEnvVars(deployName, OLMNamespace, "manager", newEnv) + return setDeploymentEnvVars(deployName, shared.OLMNamespace, "manager", newEnv) } // --------------------------------------------------------------------------- @@ -353,7 +355,7 @@ func ConfigureDeploymentWithHTTPSProxy(ctx context.Context, component, proxyURL // proxy reachable from the cluster via the container-runtime kind network // gateway, then patches the component deployment to route HTTPS through it. func StartRecordingProxyAndConfigureDeployment(ctx context.Context, component string) error { - sc := Scenario(ctx) + sc := catalogScenario(ctx) p, err := newRecordingProxy() if err != nil { @@ -372,7 +374,7 @@ func StartRecordingProxyAndConfigureDeployment(ctx context.Context, component st } proxyURL := fmt.Sprintf("http://%s:%s", gatewayIP, port) - logger.Info("Recording proxy started", "url", proxyURL) + shared.Log().Info("Recording proxy started", "url", proxyURL) return configureDeploymentProxy(ctx, component, proxyURL) } @@ -386,15 +388,15 @@ func StartRecordingProxyAndConfigureDeployment(ctx context.Context, component st // CONNECT. This is intentional — the step only verifies that operator-controller // respected HTTPS_PROXY and sent the request through the proxy. func RecordingProxyReceivedCONNECTForCatalogd(ctx context.Context) error { - sc := Scenario(ctx) + sc := catalogScenario(ctx) if sc.proxy == nil { return fmt.Errorf("no recording proxy was started in this scenario") } - WaitFor(ctx, func() bool { + shared.WaitFor(ctx, func() bool { for _, h := range sc.proxy.recordedHosts() { if strings.Contains(h, "catalogd") { - logger.Info("Recording proxy confirmed CONNECT for catalogd", "host", h) + shared.Log().Info("Recording proxy confirmed CONNECT for catalogd", "host", h) return true } } diff --git a/test/internal/e2e/steps/tls_steps.go b/test/internal/e2e/catalogd/steps/tls_steps.go similarity index 94% rename from test/internal/e2e/steps/tls_steps.go rename to test/internal/e2e/catalogd/steps/tls_steps.go index d5231507a8..ebf7b0c1f6 100644 --- a/test/internal/e2e/steps/tls_steps.go +++ b/test/internal/e2e/catalogd/steps/tls_steps.go @@ -10,6 +10,8 @@ import ( "time" appsv1 "k8s.io/api/apps/v1" + + shared "github.com/operator-framework/operator-controller/test/internal/e2e/steps" ) // tlsCipherSuiteByName is a name→ID map that covers TLS 1.3 cipher suites @@ -57,16 +59,16 @@ var curveIDByName = map[string]tls.CurveID{ // waits until a basic TLS connection succeeds (confirming the port-forward is ready), // then calls fn with the local address. The port-forward is torn down when fn returns. func withMetricsPortForward(ctx context.Context, component string, fn func(addr string) error) error { - svc, err := GetMetricsService(component) + svc, err := shared.GetMetricsService(component) if err != nil { return err } - port, err := MetricsPort(svc) + port, err := shared.MetricsPort(svc) if err != nil { return err } - addr, cleanup, err := PortForward(ctx, svc.Namespace, fmt.Sprintf("service/%s", svc.Name), port) + addr, cleanup, err := shared.PortForward(ctx, svc.Namespace, fmt.Sprintf("service/%s", svc.Name), port) if err != nil { return err } @@ -76,7 +78,7 @@ func withMetricsPortForward(ctx context.Context, component string, fn func(addr // restrictions) serves as the readiness probe; any successful TLS handshake confirms // the tunnel is up. A short per-attempt timeout prevents the dial from blocking // indefinitely if the local port is open but the upstream handshake stalls. - WaitFor(ctx, func() bool { + shared.WaitFor(ctx, func() bool { dialer := &net.Dialer{Timeout: 3 * time.Second} conn, err := tls.DialWithDialer(dialer, "tcp", addr, &tls.Config{InsecureSkipVerify: true}) //nolint:gosec if err != nil { @@ -157,7 +159,7 @@ func componentDeploymentName(component string) (string, error) { // getDeploymentContainerArgs returns the args list of the container named "manager" // inside the named deployment. func getDeploymentContainerArgs(ns, name string) ([]string, error) { - raw, err := K8sClient(context.Background(), "get", "deployment", name, "-n", ns, "-o", "json") + raw, err := shared.K8sClient(context.Background(), "get", "deployment", name, "-n", ns, "-o", "json") if err != nil { return nil, fmt.Errorf("getting deployment %s/%s: %w", ns, name, err) } @@ -176,7 +178,7 @@ func getDeploymentContainerArgs(ns, name string) ([]string, error) { // getDeploymentContainerIndex returns the index of the container named "manager" // inside the named deployment. func getDeploymentContainerIndex(ns, name string) (int, error) { - raw, err := K8sClient(context.Background(), "get", "deployment", name, "-n", ns, "-o", "json") + raw, err := shared.K8sClient(context.Background(), "get", "deployment", name, "-n", ns, "-o", "json") if err != nil { return -1, fmt.Errorf("getting deployment %s/%s: %w", ns, name, err) } @@ -204,7 +206,7 @@ func patchDeploymentArgs(ns, name string, args []string) error { return err } patch := fmt.Sprintf(`[{"op":"replace","path":"/spec/template/spec/containers/%d/args","value":%s}]`, idx, string(argsJSON)) - _, err = K8sClient(context.Background(), "patch", "deployment", name, "-n", ns, "--type=json", "-p", patch) + _, err = shared.K8sClient(context.Background(), "patch", "deployment", name, "-n", ns, "--type=json", "-p", patch) return err } @@ -241,26 +243,26 @@ func configureDeploymentCustomTLS(ctx context.Context, component, version, ciphe return err } - origArgs, err := getDeploymentContainerArgs(OLMNamespace, deploymentName) + origArgs, err := getDeploymentContainerArgs(shared.OLMNamespace, deploymentName) if err != nil { return err } - sc := Scenario(ctx) + sc := catalogScenario(ctx) sc.deploymentRestores = append(sc.deploymentRestores, deploymentRestore{ Name: deploymentName, - Namespace: OLMNamespace, + Namespace: shared.OLMNamespace, patchedArgs: true, originalArgs: origArgs, }) newArgs := buildCustomTLSArgs(origArgs, version, ciphers, curves) - if err := patchDeploymentArgs(OLMNamespace, deploymentName, newArgs); err != nil { + if err := patchDeploymentArgs(shared.OLMNamespace, deploymentName, newArgs); err != nil { return fmt.Errorf("patching %s with custom TLS args: %w", deploymentName, err) } - WaitFor(ctx, func() bool { - _, err := K8sClient(ctx, "rollout", "status", "-n", OLMNamespace, + shared.WaitFor(ctx, func() bool { + _, err := shared.K8sClient(ctx, "rollout", "status", "-n", shared.OLMNamespace, fmt.Sprintf("deployment/%s", deploymentName), "--timeout=10s") return err == nil }) diff --git a/test/internal/e2e/steps/hooks.go b/test/internal/e2e/steps/hooks.go index 14e88b8c67..6d929f61c6 100644 --- a/test/internal/e2e/steps/hooks.go +++ b/test/internal/e2e/steps/hooks.go @@ -33,17 +33,6 @@ type Resource struct { Namespace string } -// deploymentRestore records the original state of a deployment so it can be -// rolled back after a test that modifies deployment configuration. -type deploymentRestore struct { - Name string // deployment name - Namespace string - containerName string // container to patch (for env var restores) - patchedArgs bool // true when container args were modified (for TLS profile patches) - originalArgs []string // original container args; may be nil if args were unset - originalEnv []string // original env vars as "NAME=VALUE" (for proxy patches) -} - // World holds scenario state shared by component step packages. type World struct { ID string @@ -58,11 +47,9 @@ type World struct { RemovedResources []unstructured.Unstructured MetricsResponse map[string]string LeaderPods map[string]string // component name -> leader pod name - deploymentRestores []deploymentRestore ExtensionObjects []client.Object RevisionSecrets []Resource ResourceUIDs map[string]types.UID - proxy *recordingProxy CatalogAddr string CatalogCleanup func() LastApplyStderr string @@ -102,7 +89,7 @@ var ( features.WebhookProviderOpenshiftServiceCA: false, features.BoxcutterRuntime: false, features.DeploymentConfig: false, - catalogdHAFeature: false, + featuregate.Feature("CatalogdHA"): false, } logger logr.Logger ) @@ -164,7 +151,7 @@ func BeforeSuite() { // step and return early below) still get the gate set correctly. if out, err := K8sClient(context.Background(), "get", "nodes", "--no-headers", "-o", "name"); err == nil && len(strings.Fields(strings.TrimSpace(out))) >= 2 { - FeatureGates[catalogdHAFeature] = true + FeatureGates[featuregate.Feature("CatalogdHA")] = true } olm, catalogdDep, err := detectOLMDeployments() @@ -189,7 +176,7 @@ func BeforeSuite() { // was not found or the replica count is not parseable. if catalogdDep != nil { if replicas := catalogdDep.Spec.Replicas; replicas != nil { - FeatureGates[catalogdHAFeature] = *replicas >= 2 + FeatureGates[featuregate.Feature("CatalogdHA")] = *replicas >= 2 } } @@ -256,34 +243,11 @@ func stderrOutput(err error) string { func ScenarioCleanup(ctx context.Context, _ *godog.Scenario, err error) (context.Context, error) { sc := Scenario(ctx) - // Stop the in-process recording proxy if one was started. - if sc.proxy != nil { - sc.proxy.stop() - } // Stop catalog port-forward if one was started. if sc.CatalogCleanup != nil { sc.CatalogCleanup() } - // Restore any deployments that were modified during the scenario. Runs - // unconditionally (even on failure) to prevent a misconfigured deployment - // from bleeding into subsequent scenarios. Restored in LIFO order so that - // multiple patches to the same deployment unwind to the true original. - for i := len(sc.deploymentRestores) - 1; i >= 0; i-- { - dr := sc.deploymentRestores[i] - if dr.patchedArgs { - if err2 := patchDeploymentArgs(dr.Namespace, dr.Name, dr.originalArgs); err2 != nil { - logger.Info("Error restoring deployment args", "name", dr.Name, "error", err2) - } else if _, err2 := K8sClient(ctx, "rollout", "status", "-n", dr.Namespace, - fmt.Sprintf("deployment/%s", dr.Name), "--timeout=2m"); err2 != nil { - logger.Info("Timeout waiting for deployment rollout after restore", "name", dr.Name) - } - } - if err2 := restoreDeployment(dr); err2 != nil { - logger.Info("Error restoring deployment env", "deployment", dr.Name, "namespace", dr.Namespace, "error", err2) - } - } - if err != nil { return ctx, err } diff --git a/test/internal/e2e/steps/steps.go b/test/internal/e2e/steps/steps.go index 8d09719a76..03738d333d 100644 --- a/test/internal/e2e/steps/steps.go +++ b/test/internal/e2e/steps/steps.go @@ -230,18 +230,8 @@ func RegisterSteps(sc *godog.ScenarioContext) { sc.Step(`^(?i)ClusterExtension "([^"]+)" reports ([[:alnum:]]+) as ([[:alnum:]]+) with Reason ([[:alnum:]]+) and Message includes:$`, NamedClusterExtensionReportsConditionWithMessageFragment) // TLS profile enforcement steps — deployment configuration - sc.Step(`^(?i)the "([^"]+)" deployment is configured with custom TLS minimum version "([^"]+)"$`, ConfigureDeploymentWithCustomTLSVersion) - sc.Step(`^(?i)the "([^"]+)" deployment is configured with custom TLS version "([^"]+)", ciphers "([^"]+)", and curves "([^"]+)"$`, ConfigureDeploymentWithCustomTLSFull) // TLS profile enforcement steps — connection assertions - sc.Step(`^(?i)the "([^"]+)" metrics endpoint accepts a TLS 1\.3 connection$`, MetricsEndpointAcceptsTLS13) - sc.Step(`^(?i)the "([^"]+)" metrics endpoint rejects a TLS 1\.2 connection$`, MetricsEndpointRejectsTLS12) - sc.Step(`^(?i)the "([^"]+)" metrics endpoint negotiates cipher "([^"]+)" over TLS 1\.2$`, MetricsEndpointNegotiatesTLS12Cipher) - sc.Step(`^(?i)the "([^"]+)" metrics endpoint rejects a TLS 1\.2 connection offering only cipher "([^"]+)"$`, MetricsEndpointRejectsTLS12ConnectionWithCipher) - sc.Step(`^(?i)the "([^"]+)" metrics endpoint accepts a TLS 1\.2 connection with cipher "([^"]+)" and curve "([^"]+)"$`, MetricsEndpointAcceptsTLS12ConnectionWithCurve) - sc.Step(`^(?i)the "([^"]+)" metrics endpoint rejects a TLS 1\.2 connection with cipher "([^"]+)" and only curve "([^"]+)"$`, MetricsEndpointRejectsTLS12ConnectionWithCurve) - sc.Step(`^(?i)the "([^"]+)" metrics endpoint accepts a connection using only curve "([^"]+)"$`, MetricsEndpointAcceptsConnectionUsingCurve) - sc.Step(`^(?i)the "([^"]+)" metrics endpoint rejects a connection using only curve "([^"]+)"$`, MetricsEndpointRejectsConnectionUsingOnlyCurve) // Upgrade-specific steps sc.Step(`^(?i)the latest stable OLM release is installed$`, LatestStableOLMReleaseIsInstalled) @@ -251,13 +241,6 @@ func RegisterSteps(sc *godog.ScenarioContext) { sc.Step(`^(?i)ClusterExtension is reconciled$`, ClusterExtensionIsReconciled) sc.Step(`^(?i)catalog "([^"]+)" is reconciled$`, ScenarioCatalogIsReconciled) sc.Step(`^(?i)catalog "([^"]+)" reports ([[:alnum:]]+) as ([[:alnum:]]+) with Reason ([[:alnum:]]+)$`, ScenarioCatalogReportsCondition) - - sc.Step(`^(?i)the "([^"]+)" component is configured with HTTPS_PROXY "([^"]+)"$`, ConfigureDeploymentWithHTTPSProxy) - sc.Step(`^(?i)the "([^"]+)" component is configured with HTTPS_PROXY pointing to a recording proxy$`, StartRecordingProxyAndConfigureDeployment) - sc.Step(`^(?i)the recording proxy received a CONNECT request for the catalogd service$`, RecordingProxyReceivedCONNECTForCatalogd) - - sc.Step(`^(?i)the catalogd leader pod is force-deleted$`, CatalogdLeaderPodIsForceDeleted) - sc.Step(`^(?i)a new catalogd leader is elected$`, NewCatalogdLeaderIsElected) } func init() {