Skip to content
179 changes: 179 additions & 0 deletions test/extended/authentication/component_proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package authentication

import (
"context"
"slices"
"time"

g "github.com/onsi/ginkgo/v2"
o "github.com/onsi/gomega"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

operatorv1 "github.com/openshift/api/operator/v1"

exutil "github.com/openshift/origin/test/extended/util"
operator "github.com/openshift/origin/test/extended/util/operator"
)

var _ = g.Describe("[sig-auth][Suite:openshift/conformance/serial][OCPFeatureGate:AuthenticationComponentProxy][Serial]", func() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify package registration and the component-proxy suite definition.
set -euo pipefail

rg -n 'test/extended/authentication' test/extended/include.go || echo "authentication package NOT registered in include.go"

fd -t f 'standard_suites.go' --exec rg -n -C4 'component-proxy|AuthenticationComponentProxy' {} \;

rg -n --type=go 'OCPFeatureGate:AuthenticationComponentProxy'

Repository: openshift/origin

Length of output: 218


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== include.go registration =="
rg -n -C2 'test/extended/authentication' test/extended/include.go

echo "== suite definitions =="
fd -t f 'standard_suites.go' . --exec rg -n -C8 -i 'component.proxy|authenticationcomponentproxy|openshift/auth' {} \; || true

echo "== component-proxy references =="
rg -n -C4 --type=go -i 'component.proxy|AuthenticationComponentProxy|Disruptive|Suite:openshift/conformance/serial' test/extended || true

echo "== authentication test package files =="
fd -t f . test/extended/authentication | sort

Repository: openshift/origin

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== authentication suite entries =="
fd -t f 'standard_suites.go' . --exec rg -n -C6 'Name: "openshift/auth|openshift/auth/' {} \; || true

echo "== exact component-proxy suite references =="
rg -n -i --glob '*.go' --glob '*.md' 'openshift/auth/component-proxy|component-proxy' . | head -80 || true

echo "== component-proxy implementation =="
cat -n test/extended/authentication/component_proxy.go | sed -n '1,180p'

echo "== cluster mutation calls in component-proxy =="
rg -n -C3 'Authentication|OAuth|oauth|Patch|Update|Delete|Create' test/extended/authentication/component_proxy.go

Repository: openshift/origin

Length of output: 11130


Register and classify the component-proxy suite. test/extended/authentication is already registered in test/extended/include.go. Add the openshift/auth/component-proxy entry to standard_suites.go, update the Describe label, and add [Disruptive] because the tests mutate cluster authentication and OAuth configuration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/extended/authentication/component_proxy.go` at line 18, Register the
component-proxy suite in standard_suites.go under
openshift/auth/component-proxy, then update the Describe declaration in the
suite to use the matching suite label and include [Disruptive]. Preserve the
existing authentication and conformance classification labels.

Source: Coding guidelines

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I missed this beforehand, but we probably should also add the tag [Jira:"Authentication"] so that any regression bugs get assigned to a component we are responsible for.

oc := exutil.NewCLIWithoutNamespace("component-proxy")

var (
ctx context.Context
httpProxyURL string
httpsProxyURL string
caCertPEM []byte
proxyNamespace string
kcSetup *keycloakProxySetup
cleanups []removalFunc
)

g.BeforeEach(func() {
ctx = context.Background()
cleanups = nil

g.By("Saving auth state for restore after test")
authRestore, err := saveAndRestoreAuthState(ctx, oc)
cleanups = append(cleanups, authRestore)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Deploying Squid forward proxy")
var proxyCleanup removalFunc
httpProxyURL, httpsProxyURL, caCertPEM, proxyNamespace, proxyCleanup, err = deploySquidProxy(ctx, oc)
cleanups = append(cleanups, proxyCleanup)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Deploying Keycloak (without registering IdP yet)")
var kcCleanups []removalFunc
kcSetup, kcCleanups, err = deployKeycloakForProxy(ctx, oc)
cleanups = append(cleanups, kcCleanups...)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Waiting for operators to be stable before test")
err = operator.WaitForOperatorsToSettle(ctx, oc.AdminConfigClient(), 10)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Waiting for OAuth server deployment to be stable before test")
err = verifyOAuthServerDeploymentProxyConfig(ctx, oc, "", "", "", false)
o.Expect(err).NotTo(o.HaveOccurred())

g.GinkgoWriter.Printf("Squid proxy URL: http=%s https=%s\n", httpProxyURL, httpsProxyURL)
g.GinkgoWriter.Printf("Keycloak issuer URL: %s\n", kcSetup.issuerURL)
g.GinkgoWriter.Printf("Keycloak namespace: %s\n", kcSetup.namespace)
})

g.AfterEach(func() {
// We are appending cleanups, but we actually want to do LIFO.
slices.Reverse(cleanups)
_ = removeResources(ctx, cleanups...)
Comment on lines +67 to +69

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is LIFO what we actually want here? Wouldn't we want to reset the authentication configuration state before removing the squid proxy from the cluster?


g.By("Waiting for operators to be stable after test")
err := operator.WaitForOperatorsToSettle(ctx, oc.AdminConfigClient(), 10)
o.Expect(err).NotTo(o.HaveOccurred())
})

g.It("operator should validate OIDC IdP through component proxy", func() {
testOIDCIdPThroughComponentProxy(ctx, oc, kcSetup, httpProxyURL, nil, proxyNamespace)
})
g.It("operator should validate OIDC IdP through component proxy with trustedCA", func() {
testOIDCIdPThroughComponentProxy(ctx, oc, kcSetup, httpsProxyURL, caCertPEM, proxyNamespace)
})
g.It("operator should fall back to original configuration on spec.proxy removal", func() {
testFallbackOnProxyRemoval(ctx, oc, kcSetup, httpProxyURL, proxyNamespace)
})
})

func testOIDCIdPThroughComponentProxy(ctx context.Context, oc *exutil.CLI, kcSetup *keycloakProxySetup, proxyURL string, trustedCACertPEM []byte, proxyNamespace string) {
withTrustedCA := len(trustedCACertPEM) > 0

var trustedCAConfigMapName string
if withTrustedCA {
g.By("Creating trustedCA ConfigMap in openshift-config")
cmName, cmCleanup, err := createTrustedCAConfigMap(ctx, oc, trustedCACertPEM)
g.DeferCleanup(cmCleanup)
o.Expect(err).NotTo(o.HaveOccurred())
trustedCAConfigMapName = cmName
}

proxyTrafficStart := time.Now()

g.By("Setting component-scoped proxy")
proxyConfig := operatorv1.AuthenticationProxyConfig{
HTTPSProxy: proxyURL,
}
if withTrustedCA {
proxyConfig.TrustedCA = operatorv1.AuthenticationConfigMapReference{Name: trustedCAConfigMapName}
}
err := updateAuthenticationProxy(ctx, oc, proxyConfig)
o.Expect(err).NotTo(o.HaveOccurred())

if withTrustedCA {
g.By("Waiting for trustedCA ConfigMap to be synced before registering IdP")
err = verifyTrustedCAConfigMapSynced(ctx, oc)
o.Expect(err).NotTo(o.HaveOccurred())
}

g.By("Registering Keycloak as OIDC IdP (operator discovers it through the proxy)")
idpCleanups, err := addKeycloakOIDCIdPForProxy(ctx, oc, kcSetup)
g.DeferCleanup(func() {
_ = removeResources(ctx, idpCleanups...)
})
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Waiting for operator to pick up IdP changes and stabilize")
err = waitForOperatorToPickUpChanges(ctx, oc, "authentication")
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Verifying OAuth server deployment has proxy env vars and trustedCA volume/mount")
err = verifyOAuthServerDeploymentProxyConfig(ctx, oc, "", proxyURL, ".cluster.local,.svc,127.0.0.1,localhost", withTrustedCA)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Looking up operator pod IP")
operatorPods, err := oc.AdminKubeClient().CoreV1().Pods("openshift-authentication-operator").List(ctx, metav1.ListOptions{
LabelSelector: "app=authentication-operator",
})
o.Expect(err).NotTo(o.HaveOccurred())
o.Expect(operatorPods.Items).NotTo(o.BeEmpty())
operatorIP := operatorPods.Items[0].Status.PodIP
o.Expect(operatorIP).NotTo(o.BeEmpty())

g.By("Verifying operator traffic went through the Squid proxy")
err = waitForProxyTrafficFrom(ctx, oc, proxyNamespace, operatorIP, proxyTrafficStart, 5*time.Minute)
o.Expect(err).NotTo(o.HaveOccurred())
}

func testFallbackOnProxyRemoval(ctx context.Context, oc *exutil.CLI, kcSetup *keycloakProxySetup, httpProxyURL string, proxyNamespace string) {
g.By("Setting component-scoped proxy")
err := updateAuthenticationProxy(ctx, oc, operatorv1.AuthenticationProxyConfig{
HTTPSProxy: httpProxyURL,
})
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Registering Keycloak as OIDC IdP")
idpCleanups, err := addKeycloakOIDCIdPForProxy(ctx, oc, kcSetup)
g.DeferCleanup(func() {
_ = removeResources(ctx, idpCleanups...)
})
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Waiting for operator to pick up IdP changes and stabilize")
err = waitForOperatorToPickUpChanges(ctx, oc, "authentication")
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Removing spec.proxy from Authentication CR")
err = updateAuthenticationProxy(ctx, oc, operatorv1.AuthenticationProxyConfig{})
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Deleting Squid to prove the operator no longer routes through it")
err = deleteNamespaceSync(ctx, oc, proxyNamespace, 5*time.Minute)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Waiting for operator to pick up proxy removal and stabilize")
err = waitForOperatorToPickUpChanges(ctx, oc, "authentication")
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Verifying proxy env vars are no longer set on OAuth server deployment")
err = verifyOAuthServerDeploymentProxyConfig(ctx, oc, "", "", "", false)
o.Expect(err).NotTo(o.HaveOccurred())
}
Loading