Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions test/extended/authentication/component_proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package authentication

import (
"context"
"time"

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

corev1 "k8s.io/api/core/v1"
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][Slow]", func() {
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
Comment on lines +32 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n test/extended/authentication/component_proxy.go | head -80

Repository: openshift/origin

Length of output: 3378


🏁 Script executed:

# Search for the removeResources function to understand error handling
rg -A 10 "func removeResources" test/extended/authentication/component_proxy.go

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Check the full cleanup lifecycle in the file
rg -B 5 -A 5 "cleanups" test/extended/authentication/component_proxy.go

Repository: openshift/origin

Length of output: 1641


🏁 Script executed:

# Search for removeResources in the entire test/extended directory
rg -l "func removeResources" test/extended/

Repository: openshift/origin

Length of output: 191


🏁 Script executed:

# Also check if removeResources is defined in this file
rg "removeResources" test/extended/authentication/component_proxy.go

Repository: openshift/origin

Length of output: 280


🏁 Script executed:

# Get the full file to understand the structure
wc -l test/extended/authentication/component_proxy.go

Repository: openshift/origin

Length of output: 206


🏁 Script executed:

# Get the removeResources function definition from oidc.go
rg -B 3 -A 15 "func removeResources" test/extended/authentication/oidc.go

Repository: openshift/origin

Length of output: 837


🏁 Script executed:

# Check the removalFunc type definition
rg "type removalFunc" test/extended/authentication/

Repository: openshift/origin

Length of output: 236


🏁 Script executed:

# Check Ginkgo context best practices by looking at other test files
rg "context.Background()" test/extended/ | head -20

Repository: openshift/origin

Length of output: 2525


🏁 Script executed:

# Check if there are other patterns for bounded contexts in test files
rg -A 3 "g.BeforeEach" test/extended/authentication/oidc.go | head -30

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Check how other test files handle context in AfterEach/cleanup
rg -B 2 -A 5 "g.AfterEach" test/extended/authentication/oidc.go

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Look for patterns where context is created with timeout/deadline in test files
rg "context.WithTimeout\|context.WithDeadline" test/extended/ | head -20

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Check the Ginkgo spec context documentation or common usage
rg "SpecContext\|context.TODO" test/extended/ | head -20

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Verify if there are any timeouts applied in removeResources or similar cleanup operations
rg -B 5 -A 5 "AfterEach" test/extended/authentication/component_proxy.go

Repository: openshift/origin

Length of output: 629


🏁 Script executed:

# Check if there's a pattern in how Ginkgo tests are structured
# Look for Ginkgo spec that has context with timeout
rg "g.It\|g.Describe" test/extended/authentication/component_proxy.go -A 3 | head -20

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Check what the spec timeout is for this test
rg "Timeout\|SetDefaultEventuallyTimeout" test/extended/authentication/component_proxy.go

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Let me check if Ginkgo provides context management
rg "context.WithTimeout" test/extended/ -c | head -5

Repository: openshift/origin

Length of output: 387


🏁 Script executed:

# Check the learning context around cleanup patterns
# See if any tests use a special cleanup context
rg -B 3 -A 8 "g.DeferCleanup\|deferred.*cleanup" test/extended/ | head -40

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Check component_proxy_helpers.go for context usage patterns
cat -n test/extended/authentication/component_proxy_helpers.go | head -50

Repository: openshift/origin

Length of output: 1905


🏁 Script executed:

# Look for how other Go tests in the codebase handle context in BeforeEach/AfterEach
rg -B 5 -A 10 "g.BeforeEach.*func\(\)" test/extended/storage/driver_configuration.go | head -40

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Verify the exact error handling at line 63
sed -n '62,65p' test/extended/authentication/component_proxy.go

Repository: openshift/origin

Length of output: 273


🏁 Script executed:

# Check if context.Background() is the standard pattern in Ginkgo test files
rg "ctx = context.Background()" test/extended/ -c

Repository: openshift/origin

Length of output: 370


🏁 Script executed:

# Check the exact error behavior of removeResources - does it always return an error?
sed -n '1,200p' test/extended/authentication/oidc.go | grep -A 20 "func removeResources"

Repository: openshift/origin

Length of output: 154


🏁 Script executed:

# Look for patterns in how removeResources errors are handled elsewhere
rg "removeResources" test/extended/authentication/ -B 2 -A 1

Repository: openshift/origin

Length of output: 1967


🏁 Script executed:

# Verify the guideline interpretation: Check Go security guidelines in coding_guidelines
# Look for the exact context usage guidance
rg "context.Context" test/extended/authentication/component_proxy.go

Repository: openshift/origin

Length of output: 553


Add timeout context for cleanup operations and handle errors.

Line 33 assigns context.Background() which has no cancellation or deadline. When cleanup operations in AfterEach (line 63) reuse this context, a stalled API request can hang indefinitely. Line 63 also ignores the error return from removeResources.

In AfterEach, create a time-bounded cleanup context: cleanupCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel(). Use cleanupCtx instead of the root ctx for removeResources. Check the error return or document explicitly why it is safe to discard.

The coding guidelines require context with cancellation/timeout for operations and never to ignore error returns.

🤖 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` around lines 32 - 34, Update
the test setup and cleanup flow around BeforeEach and AfterEach to create a
30-second timeout context with cancellation for cleanup, pass that cleanup
context to removeResources instead of the root ctx, and handle the returned
error explicitly rather than ignoring it.

Sources: Path instructions, Learnings


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())
Comment on lines +36 to +45

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Register cleanup functions only after you assert the error. Both files pass a cleanup function to g.DeferCleanup before checking the error from the helper that returned it. If the helper fails and returns a nil function, g.DeferCleanup receives nil and panics, which replaces the real assertion failure.

  • test/extended/authentication/component_proxy.go#L34-L43: move o.Expect(err).NotTo(o.HaveOccurred()) above g.DeferCleanup(authRestore) and above g.DeferCleanup(proxyCleanup).
  • test/extended/authentication/component_proxy_oauth.go#L88-L91: move o.Expect(err).NotTo(o.HaveOccurred()) above g.DeferCleanup(authRestore).
📍 Affects 2 files
  • test/extended/authentication/component_proxy.go#L34-L43 (this comment)
  • test/extended/authentication/component_proxy_oauth.go#L88-L91
🤖 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` around lines 34 - 43,
Register each cleanup only after its helper error assertion succeeds: in
test/extended/authentication/component_proxy.go lines 34-43, move the assertions
before DeferCleanup(authRestore) and DeferCleanup(proxyCleanup); in
test/extended/authentication/component_proxy_oauth.go lines 88-91, move the
assertion before DeferCleanup(authRestore).


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.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)
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

g.AfterEach(func() {
_ = removeResources(ctx, cleanups...)

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Report cleanup failures.

Line 63 discards the error from removeResources. If state restoration or resource deletion fails, later serial specs can run against leaked authentication configuration. Capture and assert this error.

As per path instructions, Go code must never ignore error returns.

🤖 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` around lines 62 - 67, The
removeResources function call in the AfterEach block discards its error return
value using the blank identifier, which violates Go error handling practices and
could hide cleanup failures. Capture the error return from removeResources
instead of discarding it, then assert that the error did not occur using the
same expectation pattern applied to the WaitForOperatorsToSettle error below it,
ensuring cleanup failures are properly reported before the test completes.

Source: Path instructions

})

g.It("should validate OIDC IdP through component proxy", func() {
testOIDCIdPThroughComponentProxy(ctx, oc, kcSetup, httpProxyURL, nil, proxyNamespace)
})
g.It("should validate OIDC IdP through component proxy with trustedCA", func() {
testOIDCIdPThroughComponentProxy(ctx, oc, kcSetup, httpsProxyURL, caCertPEM, proxyNamespace)
})
g.It("should fall back 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

const trustedCAConfigMapName = "e2e-proxy-ca"
if withTrustedCA {
g.By("Creating trustedCA ConfigMap in openshift-config")
_, err := oc.AdminKubeClient().CoreV1().ConfigMaps("openshift-config").Create(ctx, &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: trustedCAConfigMapName,
Labels: componentProxyTestLabels(),
},
Data: map[string]string{
"ca-bundle.crt": string(trustedCACertPEM),
},
}, metav1.CreateOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
g.DeferCleanup(func(ctx context.Context) error {
return oc.AdminKubeClient().CoreV1().ConfigMaps("openshift-config").Delete(ctx, trustedCAConfigMapName, metav1.DeleteOptions{})
})
}

g.By("Restricting Keycloak route to proxy IP only")
err := restrictKeycloakRouteToProxy(ctx, oc, proxyNamespace, kcSetup.namespace)
o.Expect(err).NotTo(o.HaveOccurred())

g.By("Verifying Keycloak route is not reachable without the proxy")
o.Eventually(func() error {
return kcSetup.client.Authenticate("admin-cli", keycloakAdminUsername, keycloakAdminPassword)
}, 1*time.Minute, 5*time.Second).Should(o.MatchError(o.ContainSubstring("EOF")),
"Keycloak route should be blocked for non-proxy IPs")

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())

}

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 = oc.AdminKubeClient().CoreV1().Namespaces().Delete(ctx, proxyNamespace, metav1.DeleteOptions{})
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