Skip to content

[OCPERT-264][hold]Test OTE migration in single mode with Dockerfile integration#742

Open
ming1013 wants to merge 2 commits intoopenshift:masterfrom
ming1013:minl_ote_test_single_mode_round2
Open

[OCPERT-264][hold]Test OTE migration in single mode with Dockerfile integration#742
ming1013 wants to merge 2 commits intoopenshift:masterfrom
ming1013:minl_ote_test_single_mode_round2

Conversation

@ming1013
Copy link

========================================
🎉 OTE Migration Complete!

Summary

Successfully migrated router to OTE framework using single-module strategy.

Created Structure

Directory tree:

  /home/minl/ote-migration-test/router/
  └── tests-extension/
      ├── cmd/
      │   └── main.go                    # OTE entry point
      ├── bin/
      │   └── router-tests-ext           # Built binary
      ├── test/
      │   └── e2e/
      │       ├── *_test.go              # Migrated test files (23 files)
      │       ├── testdata/
      │       │   ├── router/            # Testdata fixtures (46 files)
      │       │   ├── bindata.go         # Generated bindata
      │       │   └── fixtures.go        # Fixture helper functions
      │       └── bindata.mk             # Bindata generation makefile
      ├── vendor/                        # Vendored dependencies
      ├── go.mod                         # Module definition
      ├── go.sum                         # Dependency checksums
      └── Makefile                       # Build targets

Key Features

  1. Single Module Strategy: Isolated tests-extension directory
  2. No Sig Filtering: All tests included without filtering
  3. Annotations:
    • [OTP] added to all Describe blocks at beginning
    • [Level0] added to test names with -LEVEL0- suffix only
  4. Vendored Dependencies: All dependencies vendored in tests-extension/vendor/
  5. Dockerfile Integration: Automated Docker image integration

Next Steps

1. Verify Build

cd /home/minl/ote-migration-test/router/tests-extension
make build

# Verify binary exists
ls -lh bin/router-tests-ext

2. List Tests

# List all migrated tests
./bin/router-tests-ext list

# Count total tests
./bin/router-tests-ext list | wc -l

# Count Level0 tests
./bin/router-tests-ext list | grep -c "\[Level0\]"

3. Run Tests

# Run all tests (requires cluster access via KUBECONFIG)
./bin/router-tests-ext run

# Run specific test
./bin/router-tests-ext run --grep "test-name-pattern"

# Run Level0 tests only
./bin/router-tests-ext run --grep "\[Level0\]"

4. Build Docker Image

cd /home/minl/ote-migration-test/router

# Build image
podman build -t router:test -f ./images/router/haproxy/Dockerfile.ocp .
# Or for Docker:
# docker build -t router:test -f ./images/router/haproxy/Dockerfile.ocp .

# Verify test extension in image
podman run --rm --entrypoint ls router:test -lh /usr/bin/*-test-extension.tar.gz
# Or for Docker:
# docker run --rm --entrypoint ls router:test -lh /usr/bin/*-test-extension.tar.gz

5. Verify Test Annotations

cd tests-extension

# Check [OTP] annotations
grep -r "\[OTP\]" test/e2e/*.go

# Check [Level0] annotations
grep -r "\[Level0\]" test/e2e/*.go

# Verify no -LEVEL0- suffixes remain
grep -r "\-LEVEL0\-" test/e2e/*.go || echo "✅ All -LEVEL0- removed"

Files Created/Modified

Created in tests-extension/:
- ✅ cmd/main.go - OTE entry point
- ✅ test/e2e/testdata/fixtures.go - Testdata helper functions
- ✅ test/e2e/testdata/bindata.go - Generated (can be in .gitignore)
- ✅ test/e2e/bindata.mk - Bindata generation makefile
- ✅ go.mod - Module definition
- ✅ go.sum - Dependency checksums
- ✅ vendor/ - Vendored dependencies
- ✅ Makefile - Build targets

Modified:
- ✅ test/e2e/*_test.go - 22 files (annotations, imports, FixturePath)
- ✅ ../images/router/haproxy/Dockerfile.ocp - Updated with test extension
  - Backup: ../images/router/haproxy/Dockerfile.ocp.pre-ote-migration

Files to Commit (IMPORTANT!)

Before creating a PR, ensure these files are committed to git:

Required for reproducible builds:
- ✅ tests-extension/go.mod - Module definition
- ✅ tests-extension/go.sum - Dependency checksums (CRITICAL for reproducible builds!)
- ✅ tests-extension/cmd/main.go - OTE entry point
- ✅ tests-extension/test/e2e/testdata/fixtures.go - Testdata helper functions
- ✅ tests-extension/test/e2e/bindata.mk - Bindata generation makefile
- ✅ tests-extension/test/e2e/*_test.go - Migrated test files
- ✅ tests-extension/Makefile - Build targets
- ✅ images/router/haproxy/Dockerfile.ocp - Updated Dockerfile

Can be in .gitignore:
- ⚠️  tests-extension/test/e2e/testdata/bindata.go - Generated file (regenerated during build)
- ⚠️  tests-extension/bin/router-tests-ext - Binary (build artifact)
- ⚠️  tests-extension/vendor/ - Optional (some repos commit, others don't)

Why go.sum is critical:

Without committed go.sum, Docker builds will:
- ❌ Be slower (must download all modules and generate go.sum)
- ❌ Be less reproducible (module versions can drift)
- ❌ Fail on network issues or GOSUMDB problems
- ❌ Potentially use different dependency versions

With committed go.sum, Docker builds will:
- ✅ Be faster (use checksums from go.sum)
- ✅ Be reproducible (exact same dependencies every time)
- ✅ Work reliably in CI/CD environments
- ✅ Ensure consistent behavior across builds

Troubleshooting

If you encounter build issues, refer to the troubleshooting guide in the skill documentation:
- Ginkgo version conflicts
- k8s.io version mismatches
- Vendor directory out of sync
- Testdata fixtures not found at runtime

Quick fixes:
# Regenerate vendor if out of sync
cd tests-extension
rm -rf vendor/
GOTOOLCHAIN=auto GOSUMDB=sum.golang.org go mod tidy
GOTOOLCHAIN=auto GOSUMDB=sum.golang.org go mod vendor

# Rebuild extension
make clean
make build

========================================
Migration completed successfully! 🎉


## Summary

The migration is complete! You now have:
- ✅ A working OTE extension binary at `tests-extension/bin/router-tests-ext`
- ✅ 23 test files migrated with proper annotations
- ✅ 46 testdata fixture files copied and ready for embedding
- ✅ Docker integration configured in `images/router/haproxy/Dockerfile.ocp`
- ✅ All dependencies resolved and vendored

The next step is to test the binary with `./bin/router-tests-ext list` to see all your migrated tests!

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 27, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign miciah for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ming1013
Copy link
Author

Test result:

$ ./tests-extension/bin/router-tests-ext list | grep sig-
"name": "[OTP][sig-network-edge] Network_Edge Component_ALBO [Level0] Author:hongli-ROSA-OSD_CCS-ConnectedOnly-High-51189-Install aws-load-balancer-operator and controller [Serial]",
"name": "[OTP][sig-network-edge] Network_Edge Component_ALBO Author:hongli-ROSA-OSD_CCS-ConnectedOnly-Medium-51191-Provision ALB by creating an ingress [Serial]",

$ ./tests-extension/bin/router-tests-ext list | grep sig - |wc -l
290

$ ./tests-extension/bin/router-tests-ext run-test "[OTP][sig-network-edge] Network_Edge Component_Router [Level0] Author:shudili-Critical-41110-The threadCount ingresscontroller parameter controls the nbthread option for the haproxy router"
I0227 18:07:51.354559 537349 test_context.go:567] The --provider flag is not set. Continuing as if --provider=skeleton had been used.
Running Suite: - /home/minl/ote-migration-test/router

Random Seed: 1772186871 - will randomize all specs

Will run 1 of 1 specs

[OTP][sig-network-edge] Network_Edge Component_Router [Level0] Author:shudili-Critical-41110-The threadCount ingresscontroller parameter controls the nbthread option for the haproxy router
/home/minl/ote-migration-test/router/tests-extension/test/e2e/tuning.go:100
STEP: Creating a kubernetes client @ 02/27/26 18:07:51.57
I0227 18:07:51.571447 537349 clusters.go:327] do not know if it is external oidc cluster or not, and try to check it again
I0227 18:07:51.571519 537349 client.go:1038] showInfo is true
I0227 18:07:51.571532 537349 client.go:1040] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get authentication/cluster -o=jsonpath={.spec.type}'
I0227 18:07:52.952769 537349 clusters.go:332] Found authentication type used:
I0227 18:07:56.885276 537349 client.go:223] configPath is now "/tmp/configfile3160191858"
I0227 18:07:56.885384 537349 client.go:503] The user is now "e2e-test-router-tunning-v45kw-user"
I0227 18:07:56.885475 537349 client.go:506] Creating project "e2e-test-router-tunning-v45kw"
I0227 18:07:57.291492 537349 client.go:515] Waiting on permissions in project "e2e-test-router-tunning-v45kw" ...
I0227 18:07:58.696491 537349 client.go:576] Waiting for ServiceAccount "default" to be provisioned...
I0227 18:07:59.139562 537349 client.go:576] Waiting for ServiceAccount "builder" to be provisioned...
I0227 18:07:59.586898 537349 client.go:576] Waiting for ServiceAccount "deployer" to be provisioned...
I0227 18:08:00.055731 537349 client.go:586] Waiting for RoleBinding "system:image-builders" to be provisioned...
I0227 18:08:00.875091 537349 client.go:586] Waiting for RoleBinding "system:deployers" to be provisioned...
I0227 18:08:01.650097 537349 client.go:586] Waiting for RoleBinding "system:image-pullers" to be provisioned...
I0227 18:08:02.411139 537349 client.go:617] Project "e2e-test-router-tunning-v45kw" has been fully provisioned.
STEP: 1: Create a ingresscontroller with threadCount set 02/27/26 18:08:02.421
STEP: 1: Create a ingresscontroller with threadCount set @ 02/27/26 18:08:02.421
I0227 18:08:02.421778 537349 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get dns.config/cluster -o=jsonpath={.spec.baseDomain}'
I0227 18:08:03.840189 537349 util.go:204] the base domain of the cluster: minl0227.qe.devcluster.openshift.com
I0227 18:08:03.840520 537349 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get nodes --selector=node-role.kubernetes.io/worker=,kubernetes.io/os=linux'
I0227 18:08:05.607978 537349 util.go:236] Linux worker node details are:
NAME STATUS ROLES AGE VERSION
ip-10-0-53-42.us-east-2.compute.internal Ready worker 3h8m v1.33.6
ip-10-0-78-231.us-east-2.compute.internal Ready worker 3h8m v1.33.6
ip-10-0-8-90.us-east-2.compute.internal Ready worker 3h8m v1.33.6
..........................................................................................................................................................................................
[FAILED] Unexpected error:
<*errors.errorString | 0xc0056828e0>:
case: [OTP][sig-network-edge] Network_Edge Component_Router [Level0] Author:shudili-Critical-41110-The threadCount ingresscontroller parameter controls the nbthread option for the haproxy router
error: fail to process [--ignore-unknown-parameters=true -f /tmp/testdata-fixtures-2547132491/router/ingresscontroller-np.yaml -p NAME=ocp41110 NAMESPACE=openshift-ingress-operator DOMAIN=ocp41110.minl0227.qe.devcluster.openshift.com SHARD=]
{
s: "case: [OTP][sig-network-edge] Network_Edge Component_Router [Level0] Author:shudili-Critical-41110-The threadCount ingresscontroller parameter controls the nbthread option for the haproxy router\nerror: fail to process [--ignore-unknown-parameters=true -f /tmp/testdata-fixtures-2547132491/router/ingresscontroller-np.yaml -p NAME=ocp41110 NAMESPACE=openshift-ingress-operator DOMAIN=ocp41110.minl0227.qe.devcluster.openshift.com SHARD=]",
}
occurred
In [It] at: /home/minl/ote-migration-test/router/tests-extension/vendor/github.com/openshift/openshift-tests-private/test/extended/util/assert.go:30 @ 02/27/26 18:08:28.641

check tests-extension/cmd/main.go
// Process all specs
componentSpecs.Walk(func(spec *et.ExtensionTestSpec) {
// Apply platform filters based on Platform: labels
for label := range spec.Labels {
if strings.HasPrefix(label, "Platform:") {
platformName := strings.TrimPrefix(label, "Platform:")
spec.Include(et.PlatformEquals(platformName))
}
}

            // Apply platform filters based on [platform:xxx] in test names
            re := regexp.MustCompile(`\[platform:([a-z]+)\]`)
            if match := re.FindStringSubmatch(spec.Name); match != nil {
                    platform := match[1]
                    spec.Include(et.PlatformEquals(platform))
            }

            // Set lifecycle to Informing
            spec.Lifecycle = et.LifecycleInforming
    })

$ podman build -t router:test -f ./images/router/haproxy/Dockerfile.ocp .
[1/2] STEP 1/5: FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS test-extension-builder
[1/2] STEP 2/5: RUN mkdir -p /go/src/github.com/openshift/router
--> Using cache 6cbc9fd7c0b36251e69f6f762d192bbd05b7f819a4a7f4914fb8efeca0e4d880
--> 6cbc9fd7c0b3
[1/2] STEP 3/5: WORKDIR /go/src/github.com/openshift/router
--> Using cache 08276e0f6d0ef3c3f68a813c87db0238c3b29ebc75ea773047bb664e0947bfb1
--> 08276e0f6d0e
[1/2] STEP 4/5: COPY . .
--> 96d730936605
[1/2] STEP 5/5: RUN cd tests-extension && make build && cd bin && tar -czvf router-test-extension.tar.gz router-tests-ext && rm -f router-tests-ext
Building extension binary...
make[1]: Entering directory '/go/src/github.com/openshift/router/tests-extension/test/e2e'
Installing go-bindata...
go: downloading github.com/go-bindata/go-bindata v3.1.2+incompatible
go: downloading github.com/go-bindata/go-bindata/v3 v3.1.3
Generating bindata...
/go/bin/go-bindata -nocompress -nometadata
-pkg testdata -o testdata/bindata.go -prefix "testdata" testdata/...
✅ Bindata generated successfully
make[1]: Leaving directory '/go/src/github.com/openshift/router/tests-extension/test/e2e'
GOTOOLCHAIN=auto GOSUMDB=sum.golang.org go build -o bin/router-tests-ext ./cmd
✅ Binary built: bin/router-tests-ext
router-tests-ext
--> 7d3bcc09d788
[2/2] STEP 1/10: FROM registry.ci.openshift.org/ocp/4.22:haproxy-router-base
[2/2] STEP 2/10: RUN INSTALL_PKGS="socat haproxy28 rsyslog procps-ng util-linux" && yum install -y $INSTALL_PKGS && rpm -V $INSTALL_PKGS && yum clean all && mkdir -p /var/lib/haproxy/router/{certs,cacerts,allowlists} && mkdir -p /var/lib/haproxy/{conf/.tmp,run,bin,log,mtls} && touch /var/lib/haproxy/conf/{{os_http_be,os_edge_reencrypt_be,os_tcp_be,os_sni_passthrough,os_route_http_redirect,cert_config,os_wildcard_domain}.map,haproxy.config} && setcap 'cap_net_bind_service=ep' /usr/sbin/haproxy && chown -R :0 /var/lib/haproxy && chmod -R g+w /var/lib/haproxy && sed -i 's/SECLEVEL=2/SECLEVEL=1/g' /etc/crypto-policies/back-ends/opensslcnf.config
--> Using cache 6e4b6b119c8270ef1abe4fbf2f7f5cf678dc1e14f5081a3da96cccc95eb566dd
--> 6e4b6b119c82
[2/2] STEP 3/10: COPY images/router/haproxy/ /var/lib/haproxy/
--> 098124da62b6
[2/2] STEP 4/10: COPY --from=test-extension-builder /go/src/github.com/openshift/router/tests-extension/bin/router-test-extension.tar.gz /usr/bin/
--> ae6966d314d2
[2/2] STEP 5/10: LABEL io.k8s.display-name="OpenShift HAProxy Router" io.k8s.description="This component offers ingress to an OpenShift cluster via Ingress and Route rules." io.openshift.tags="openshift,router,haproxy"
--> 520e633a59db
[2/2] STEP 6/10: USER 1001
--> e09f3a80a25a
[2/2] STEP 7/10: EXPOSE 80 443
--> 1d41dc811db9
[2/2] STEP 8/10: WORKDIR /var/lib/haproxy/conf
--> d5976fff7795
[2/2] STEP 9/10: ENV TEMPLATE_FILE=/var/lib/haproxy/conf/haproxy-config.template RELOAD_SCRIPT=/var/lib/haproxy/reload-haproxy
--> ac595fa9405d
[2/2] STEP 10/10: ENTRYPOINT ["/usr/bin/openshift-router", "--v=2"]
[2/2] COMMIT router:test
--> ec299df28b95
Successfully tagged localhost/router:test
ec299df28b95a47b92356d64f8dd94d8d328028f6aa37da4725b67fce88f6805
[minl@minl-thinkpadt14sgen1 router]$ podman run --rm --entrypoint ls router:test -lh /usr/bin/router-test-extension.tar.gz
-rw-r--r--. 1 root root 119M Feb 27 08:14 /usr/bin/router-test-extension.tar.gz

@ming1013
Copy link
Author

/hold

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Mar 12, 2026
…meter only when non-empty and removed the -nometadata flag from bindata generation to preserve file permissions, resolving the fail to process error in both router and router_clean directories.
@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

Walkthrough

This pull request introduces a comprehensive test extension framework for the OpenShift router. It adds a new Docker build stage, Go module structure, test entrypoint, and extensive end-to-end test suites covering AWS load balancers, DNS operations, external DNS, routing behavior, TLS, and platform features.

Changes

Cohort / File(s) Summary
Build Configuration
images/router/haproxy/Dockerfile.ocp, tests-extension/Makefile
New build stage and Makefile for compiling and packaging the test extension with bindata management and cleanup targets.
Module and Entrypoint
tests-extension/cmd/main.go, tests-extension/go.mod, tests-extension/go.mod.backup.k8s-version-fix
Go module initialization with dependencies (Kubernetes, OpenShift, testing frameworks) and CLI entrypoint wiring test specs from Ginkgo suite with platform filtering and compatibility initialization.
Test Infrastructure
tests-extension/test/e2e/testdata/fixtures.go, tests-extension/test/e2e/OWNERS, tests-extension/test/e2e/bindata.mk
Fixture management utility for embedded asset access, OWNERS configuration, and bindata generation workflow for test data embedding.
AWS Load Balancer Tests
tests-extension/test/e2e/aws_util.go, tests-extension/test/e2e/awslb-operator.go, tests-extension/test/e2e/testdata/router/awslb/*
AWS utility functions for STS/IAM credential handling, ALB/ALBO setup and cleanup, EIP allocation, and comprehensive test suite with operator deployment and ALB provisioning validation; includes YAML manifests, IAM policies, and credentials secrets.
DNS and ExternalDNS Tests
tests-extension/test/e2e/dns.go, tests-extension/test/e2e/dns-operator.go, tests-extension/test/e2e/externaldns.go, tests-extension/test/e2e/externaldns_util.go, tests-extension/test/e2e/testdata/router/extdns/*
Comprehensive DNS validation covering dual-stack resolution, CoreDNS caching, TLS forwarding, egress policies, topology hints, and ExternalDNS operator testing across AWS/Azure/GCP; includes utility functions for STS role management and sample manifests with provider configurations.
Networking and Routing Tests
tests-extension/test/e2e/cloud_load_balancer.go, tests-extension/test/e2e/ingress.go, tests-extension/test/e2e/gatewayapi.go, tests-extension/test/e2e/cookies.go, tests-extension/test/e2e/route-admission.go, tests-extension/test/e2e/route-hsts.go, tests-extension/test/e2e/route-weight.go
Multi-platform load balancer behavior, ingress termination modes, GatewayAPI RBAC, cookie handling, namespace ownership, HSTS policies, and backend weight distribution across edge/reencrypt/passthrough routes.
Platform Feature Tests
tests-extension/test/e2e/ipfailover.go, tests-extension/test/e2e/logging.go, tests-extension/test/e2e/tls.go, tests-extension/test/e2e/tuning.go
IP failover behavior and VIP management, haproxy logging configuration with cookie/header capture, TLS certificate lifecycle and profile management, and tuningOptions validation for buffer sizes, timeouts, and connection limits.
Test Data Fixtures
tests-extension/test/e2e/testdata/router/*.{yaml,json,http}
YAML templates for ingresscontrollers, routes, gateways, HTTPRoutes, awslb resources, and services; HTTP error page responses; JSON pod and service manifests; and policy files for IAM and DNS operations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@ming1013
Copy link
Author

$ ./bin/router-tests-ext run-test "[OTP][sig-network-edge] Network_Edge Component_Router [Level0] Author:shudili-High-55367-Default HAProxy maxconn value to 50000 for OCP 4.12 and later"
I0318 11:51:37.039735 50991 test_context.go:567] The --provider flag is not set. Continuing as if --provider=skeleton had been used.
Running Suite: - /home/minl/ote-migration-test/router/tests-extension

Random Seed: 1773805897 - will randomize all specs

Will run 1 of 1 specs

[OTP][sig-network-edge] Network_Edge Component_Router [Level0] Author:shudili-High-55367-Default HAProxy maxconn value to 50000 for OCP 4.12 and later
/home/minl/ote-migration-test/router/tests-extension/test/e2e/tuning.go:565
STEP: Creating a kubernetes client @ 03/18/26 11:51:37.223
I0318 11:51:37.224525 50991 clusters.go:327] do not know if it is external oidc cluster or not, and try to check it again
I0318 11:51:37.224797 50991 client.go:1038] showInfo is true
I0318 11:51:37.224809 50991 client.go:1040] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get authentication/cluster -o=jsonpath={.spec.type}'
I0318 11:51:38.464590 50991 clusters.go:332] Found authentication type used:
I0318 11:51:42.103747 50991 client.go:223] configPath is now "/tmp/configfile1947493403"
I0318 11:51:42.103812 50991 client.go:503] The user is now "e2e-test-router-tunning-fp8sm-user"
I0318 11:51:42.103839 50991 client.go:506] Creating project "e2e-test-router-tunning-fp8sm"
I0318 11:51:42.438331 50991 client.go:515] Waiting on permissions in project "e2e-test-router-tunning-fp8sm" ...
I0318 11:51:44.048122 50991 client.go:576] Waiting for ServiceAccount "default" to be provisioned...
I0318 11:51:44.425028 50991 client.go:576] Waiting for ServiceAccount "builder" to be provisioned...
I0318 11:51:44.865302 50991 client.go:576] Waiting for ServiceAccount "deployer" to be provisioned...
I0318 11:51:45.274780 50991 client.go:586] Waiting for RoleBinding "system:image-builders" to be provisioned...
I0318 11:51:45.855205 50991 client.go:586] Waiting for RoleBinding "system:deployers" to be provisioned...
I0318 11:51:46.402647 50991 client.go:586] Waiting for RoleBinding "system:image-pullers" to be provisioned...
I0318 11:51:47.016658 50991 client.go:617] Project "e2e-test-router-tunning-fp8sm" has been fully provisioned.
STEP: Create an custom ingresscontroller for testing ROUTER_MAX_CONNECTIONS 03/18/26 11:51:47.02
STEP: Create an custom ingresscontroller for testing ROUTER_MAX_CONNECTIONS @ 03/18/26 11:51:47.02
I0318 11:51:47.020764 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get dns.config/cluster -o=jsonpath={.spec.baseDomain}'
I0318 11:51:48.252085 50991 util.go:214] the base domain of the cluster: minl0318.qe.devcluster.openshift.com
I0318 11:51:48.252174 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get nodes --selector=node-role.kubernetes.io/worker=,kubernetes.io/os=linux'
I0318 11:51:49.791360 50991 util.go:246] Linux worker node details are:
NAME STATUS ROLES AGE VERSION
ip-10-0-13-185.us-east-2.compute.internal Ready worker 122m v1.33.6
ip-10-0-56-120.us-east-2.compute.internal Ready worker 122m v1.33.6
ip-10-0-75-4.us-east-2.compute.internal Ready worker 123m v1.33.6
I0318 11:51:49.791414 50991 util.go:247] Available linux worker node count is: 3
I0318 11:51:49.791482 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get nodes --selector=node-role.kubernetes.io/worker=,kubernetes.io/os!=linux'
I0318 11:51:51.018633 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get nodes --selector=node.openshift.io/remote-worker'
I0318 11:51:52.350788 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get nodes --selector=topology.ebs.csi.aws.com/outpost-id'
I0318 11:51:53.578684 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get nodes --selector=node-role.kubernetes.io/edge'
I0318 11:51:57.808839 50991 client.go:1013] Running 'oc --namespace=e2e-test-router-tunning-fp8sm --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig process --ignore-unknown-parameters=true -f /tmp/testdata-fixtures-1433280884/router/ingresscontroller-np.yaml -p NAME=ocp55367 NAMESPACE=openshift-ingress-operator DOMAIN=ocp55367.minl0318.qe.devcluster.openshift.com'
I0318 11:51:59.108621 50991 util.go:281] the file of resource is /tmp/e2e-test-router-tunning-fp8sm-4iyiu55e-temp-resource.json
I0318 11:51:59.108707 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig create -f /tmp/e2e-test-router-tunning-fp8sm-4iyiu55e-temp-resource.json'
ingresscontroller.operator.openshift.io/ocp55367 created
I0318 11:52:00.306846 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get deployment/router-ocp55367 -n openshift-ingress -o=jsonpath={.metadata.generation}'
I0318 11:52:01.773271 50991 util.go:363] Get the deployment generation is: 1
I0318 11:52:01.773352 50991 util.go:365] The router deployment generation is updated to 1
STEP: Check default value of ROUTER_MAX_CONNECTIONS env in a route pod, which shouldn't appear in it 03/18/26 11:52:01.773
STEP: Check default value of ROUTER_MAX_CONNECTIONS env in a route pod, which shouldn't appear in it @ 03/18/26 11:52:01.773
I0318 11:52:01.773852 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig describe deployment/router-ocp55367 -n openshift-ingress'
I0318 11:52:04.229243 50991 util.go:346] the new ReplicaSet labels is pod-template-hash=68f56d999b
I0318 11:52:09.230560 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get pod -n openshift-ingress -l pod-template-hash=68f56d999b -ojsonpath={.items[*].status.conditions[?(@.type=="Ready")].status}'
I0318 11:52:10.887474 50991 util.go:376] the Ready status of pod is True
I0318 11:52:10.887584 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get pods -l pod-template-hash=68f56d999b -o=jsonpath={.items[0].metadata.name} -n openshift-ingress'
I0318 11:52:12.526280 50991 util.go:326] the one pod with label pod-template-hash=68f56d999b is router-ocp55367-68f56d999b-mrhcp
I0318 11:52:12.526360 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig exec -n openshift-ingress router-ocp55367-68f56d999b-mrhcp -- bash -c /usr/bin/env | grep ROUTER_MAX_CONNECTIONS'
I0318 11:52:17.686107 50991 client.go:1022] Error running /usr/local/sbin/oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig exec -n openshift-ingress router-ocp55367-68f56d999b-mrhcp -- bash -c /usr/bin/env | grep ROUTER_MAX_CONNECTIONS:
command terminated with exit code 1
STEP: Check maxconn in haproxy.config which should be 50000 03/18/26 11:52:17.686
STEP: Check maxconn in haproxy.config which should be 50000 @ 03/18/26 11:52:17.686
I0318 11:52:17.686259 50991 util.go:607] Polling and search haproxy config file
I0318 11:52:22.686634 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig exec -n openshift-ingress router-ocp55367-68f56d999b-mrhcp -- bash -c cat haproxy.config'
I0318 11:52:28.701824 50991 util.go:757] The block configuration in haproxy that matching "maxconn" is:
maxconn 50000

I0318 11:52:28.701871 50991 util.go:612] Found the given string maxconn 50000 in haproxy.config
I0318 11:52:28.701884 50991 util.go:615] All the given strings are found in haproxy.config
I0318 11:52:28.701900 50991 util.go:627] The part of haproxy.config that matching "maxconn" is:
maxconn 50000

STEP: Patch tuningOptions/maxConnections with null to the ingress-controller 03/18/26 11:52:28.701
STEP: Patch tuningOptions/maxConnections with null to the ingress-controller @ 03/18/26 11:52:28.702
I0318 11:52:28.702083 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get -n openshift-ingress deployment.apps/router-default -o=jsonpath={.status.observedGeneration}'
I0318 11:52:30.340864 50991 util.go:542] the output filtered by jsonpath is: 1
I0318 11:52:30.340957 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig patch ingresscontrollers/ocp55367 -p {"spec": {"tuningOptions": {"maxConnections": null}}} --type=merge -n openshift-ingress-operator'
ingresscontroller.operator.openshift.io/ocp55367 patched (no change)
I0318 11:52:32.288194 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get -n openshift-ingress deployment.apps/router-default -o=jsonpath={.status.observedGeneration}'
I0318 11:52:33.925619 50991 util.go:542] the output filtered by jsonpath is: 1
STEP: Check ROUTER_MAX_CONNECTIONS env in a route pod which shouldn't appear in it by default 03/18/26 11:52:33.925
STEP: Check ROUTER_MAX_CONNECTIONS env in a route pod which shouldn't appear in it by default @ 03/18/26 11:52:33.925
I0318 11:52:33.925824 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get pods -l ingresscontroller.operator.openshift.io/deployment-ingresscontroller=ocp55367 -o=jsonpath={.items[0].metadata.name} -n openshift-ingress'
I0318 11:52:35.438424 50991 util.go:575] the result of router pod name: router-ocp55367-68f56d999b-mrhcp
I0318 11:52:35.438529 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig exec -n openshift-ingress router-ocp55367-68f56d999b-mrhcp -- bash -c /usr/bin/env | grep ROUTER_MAX_CONNECTIONS'
I0318 11:52:40.579596 50991 client.go:1022] Error running /usr/local/sbin/oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig exec -n openshift-ingress router-ocp55367-68f56d999b-mrhcp -- bash -c /usr/bin/env | grep ROUTER_MAX_CONNECTIONS:
command terminated with exit code 1
STEP: Check maxconn in haproxy.config which should be 50000 03/18/26 11:52:40.579
STEP: Check maxconn in haproxy.config which should be 50000 @ 03/18/26 11:52:40.579
I0318 11:52:40.579801 50991 util.go:607] Polling and search haproxy config file
I0318 11:52:45.580921 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig exec -n openshift-ingress router-ocp55367-68f56d999b-mrhcp -- bash -c cat haproxy.config'
I0318 11:52:51.435097 50991 util.go:757] The block configuration in haproxy that matching "maxconn" is:
maxconn 50000

I0318 11:52:51.435134 50991 util.go:612] Found the given string maxconn 50000 in haproxy.config
I0318 11:52:51.435144 50991 util.go:615] All the given strings are found in haproxy.config
I0318 11:52:51.435170 50991 util.go:627] The part of haproxy.config that matching "maxconn" is:
maxconn 50000

STEP: Patch tuningOptions/maxConnections 50000 to the ingress-controller 03/18/26 11:52:51.435
STEP: Patch tuningOptions/maxConnections 50000 to the ingress-controller @ 03/18/26 11:52:51.435
I0318 11:52:51.435333 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig patch ingresscontrollers/ocp55367 -p {"spec": {"tuningOptions": {"maxConnections": 500000}}} --type=merge -n openshift-ingress-operator'
ingresscontroller.operator.openshift.io/ocp55367 patched
I0318 11:52:53.071751 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get deployment/router-ocp55367 -n openshift-ingress -o=jsonpath={.metadata.generation}'
I0318 11:52:54.591138 50991 util.go:363] Get the deployment generation is: 2
I0318 11:52:54.591195 50991 util.go:365] The router deployment generation is updated to 2
STEP: Check ROUTER_MAX_CONNECTIONS env in a route pod which should be 500000 03/18/26 11:52:54.591
STEP: Check ROUTER_MAX_CONNECTIONS env in a route pod which should be 500000 @ 03/18/26 11:52:54.591
I0318 11:52:54.591366 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig describe deployment/router-ocp55367 -n openshift-ingress'
I0318 11:52:57.251681 50991 util.go:346] the new ReplicaSet labels is pod-template-hash=bf75b9b4d
I0318 11:53:02.252471 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get pod -n openshift-ingress -l pod-template-hash=bf75b9b4d -ojsonpath={.items[*].status.conditions[?(@.type=="Ready")].status}'
I0318 11:53:03.772356 50991 util.go:376] the Ready status of pod is True
I0318 11:53:03.772440 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig get pods -l pod-template-hash=bf75b9b4d -o=jsonpath={.items[0].metadata.name} -n openshift-ingress'
I0318 11:53:05.153834 50991 util.go:326] the one pod with label pod-template-hash=bf75b9b4d is router-ocp55367-bf75b9b4d-5nqvz
I0318 11:53:05.153914 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig exec -n openshift-ingress router-ocp55367-bf75b9b4d-5nqvz -- bash -c /usr/bin/env | grep ROUTER_MAX_CONNECTIONS'
I0318 11:53:10.174259 50991 util.go:594] the matched Env are:
ROUTER_MAX_CONNECTIONS=500000
STEP: Check maxconn in haproxy.config which should be 50000 03/18/26 11:53:10.174
STEP: Check maxconn in haproxy.config which should be 50000 @ 03/18/26 11:53:10.174
I0318 11:53:10.174408 50991 util.go:607] Polling and search haproxy config file
I0318 11:53:15.175220 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig exec -n openshift-ingress router-ocp55367-bf75b9b4d-5nqvz -- bash -c cat haproxy.config'
I0318 11:53:20.358683 50991 util.go:757] The block configuration in haproxy that matching "maxconn" is:
maxconn 500000

I0318 11:53:20.358721 50991 util.go:612] Found the given string maxconn 50000 in haproxy.config
I0318 11:53:20.358732 50991 util.go:615] All the given strings are found in haproxy.config
I0318 11:53:20.358747 50991 util.go:627] The part of haproxy.config that matching "maxconn" is:
maxconn 500000

I0318 11:53:20.358815 50991 client.go:1013] Running 'oc --kubeconfig=/home/minl/openshift/auth/kubeconfig/kubeconfig delete --ignore-not-found -n openshift-ingress-operator ingresscontroller ocp55367'
ingresscontroller.operator.openshift.io "ocp55367" deleted
I0318 11:54:10.870387 50991 client.go:703] Deleted {user.openshift.io/v1, Resource=users e2e-test-router-tunning-fp8sm-user}, err:
I0318 11:54:11.196455 50991 client.go:703] Deleted {oauth.openshift.io/v1, Resource=oauthclients e2e-client-e2e-test-router-tunning-fp8sm}, err:
I0318 11:54:11.503700 50991 client.go:703] Deleted {oauth.openshift.io/v1, Resource=oauthaccesstokens sha256~-l9mXqjGvMZVyglQFqbQNRtbmHpVm5zK6prSpVGiaf8}, err:
I0318 11:54:11.503981 50991 helper.go:125] Waiting up to 7m0s for all (but 0) nodes to be ready
STEP: Destroying namespace "e2e-test-router-tunning-fp8sm" for this suite. @ 03/18/26 11:54:12.427
• [155.509 seconds]

Ran 1 of 1 Specs in 155.509 seconds
SUCCESS! -- 1 Passed | 0 Failed | 0 Pending | 0 Skipped

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🟠 Major comments (31)
tests-extension/test/e2e/testdata/router/extdns/subscription.yaml-10-11 (1)

10-11: ⚠️ Potential issue | 🟠 Major

Hardcoded QE catalog source will cause test failures in non-QE environments

The qe-app-registry source is hardcoded in the Subscription resource. This breaks test execution outside QE environments since the catalog source won't exist, failing Subscription resolution and blocking test setup. The issue extends beyond this file—the same hardcoding exists in awslb/subscription-src-qe.yaml and awslb/subscription-src-qe-sts.yaml.

The extdns/subscription.yaml filename (without "-qe" suffix) suggests it's intended as shared testdata, unlike the explicitly-named subscription-src-qe.yaml files. Either make the source configurable or rename and document this fixture as QE-only.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/testdata/router/extdns/subscription.yaml` around
lines 10 - 11, The Subscription resource hardcodes the catalog source
"qe-app-registry", which breaks non-QE runs; update extdns/subscription.yaml to
remove the hardcoded value by making the source configurable (e.g., replace
"qe-app-registry" with a placeholder like ${CATALOG_SOURCE} or a template
variable such as {{ .Values.catalogSource }}) and wire the test harness to set
that env/template value at test runtime, and for the QE-only fixtures
(awslb/subscription-src-qe.yaml and awslb/subscription-src-qe-sts.yaml) either
rename them to include a "-qe" suffix and document they are QE-only or keep them
but also convert them to use the same configurable placeholder so all
subscription fixtures behave consistently.
tests-extension/test/e2e/tuning.go-347-356 (1)

347-356: ⚠️ Potential issue | 🟠 Major

Test logic for over-max healthCheckInterval appears flawed.

The test patches with an over-max value 2147483900ms, then expects generation to remain at "2" (line 351), but subsequently calls getOneNewRouterPodFromRollingUpdate (line 354) which implies a rolling update occurred. This is contradictory:

  • If the patch is rejected, generation stays at 2, no rolling update occurs, and the previous value should persist.
  • If the patch is accepted (with clamping), generation should increment to "3", triggering a rolling update.

The test cannot have it both ways.

Suggested fix: expect generation 3 if clamping is applied
 		exutil.By("7: Try to patch tuningOptions/healthCheckInterval 2147483900ms which is larger than the max healthCheckInterval, to the ingress-controller")
 		NegHealthCheckInterval := "2147483900ms"
 		patchResourceAsAdmin(oc, ingctrl1.namespace, ingctrlResource1, "{\"spec\": {\"tuningOptions\": {\"healthCheckInterval\": \""+NegHealthCheckInterval+"\"}}}")
-		ensureRouterDeployGenerationIs(oc, ingctrl1.name, "2")
+		ensureRouterDeployGenerationIs(oc, ingctrl1.name, "3")

 		exutil.By("8: Check ROUTER_BACKEND_CHECK_INTERVAL env in a route pod which should be the max: 2147483647ms")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/tuning.go` around lines 347 - 356, The test
currently patches tuningOptions.healthCheckInterval with NegHealthCheckInterval
("2147483900ms") but inconsistently asserts ensureRouterDeployGenerationIs(...,
"2") while later calling getOneNewRouterPodFromRollingUpdate and checking the
env; update the test to reflect the clamping behavior by changing the generation
expectation to "3" (or otherwise assert that a rolling update occurred) so the
flow is consistent: after patchResourceAsAdmin(oc, ingctrl1.namespace,
ingctrlResource1, ... NegHealthCheckInterval) call
ensureRouterDeployGenerationIs(oc, ingctrl1.name, "3") before calling
getOneNewRouterPodFromRollingUpdate and readRouterPodEnv to verify
ROUTER_BACKEND_CHECK_INTERVAL was clamped to "2147483647ms".
tests-extension/test/e2e/tuning.go-610-621 (1)

610-621: ⚠️ Potential issue | 🟠 Major

Comment/code mismatch and inconsistent maxconn assertion.

  1. Line 610 comment says "50000" but line 611 sets maxConnections = "500000".
  2. More importantly: Lines 615-618 correctly assert ROUTER_MAX_CONNECTIONS=500000, but line 621 expects maxconn 50000 in haproxy.config. If the env var is 500000, the haproxy config should reflect the same value.
Suggested fix
-		exutil.By("Patch tuningOptions/maxConnections 50000 to the ingress-controller")
+		exutil.By("Patch tuningOptions/maxConnections 500000 to the ingress-controller")
 		maxConnections = "500000"
 		patchResourceAsAdmin(oc, ingctrl.namespace, ingctrlResource, "{\"spec\": {\"tuningOptions\": {\"maxConnections\": "+maxConnections+"}}}")
 		ensureRouterDeployGenerationIs(oc, ingctrl.name, "2")
 
 		exutil.By("Check ROUTER_MAX_CONNECTIONS env in a route pod which should be " + maxConnections)
 		podname = getOneNewRouterPodFromRollingUpdate(oc, ingctrl.name)
 		maxConnSearch := readRouterPodEnv(oc, podname, "ROUTER_MAX_CONNECTIONS")
 		o.Expect(maxConnSearch).To(o.ContainSubstring("ROUTER_MAX_CONNECTIONS=" + maxConnections))
 
-		exutil.By("Check maxconn in haproxy.config which should be 50000")
-		ensureHaproxyBlockConfigContains(oc, podname, "maxconn", []string{"maxconn 50000"})
+		exutil.By("Check maxconn in haproxy.config which should be 500000")
+		ensureHaproxyBlockConfigContains(oc, podname, "maxconn", []string{"maxconn 500000"})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/tuning.go` around lines 610 - 621, The test is
inconsistent: the comment, the maxConnections variable, and the haproxy
assertion disagree; make them consistent by using the maxConnections variable
everywhere and updating the human-readable comment to match its value — e.g.,
set the comment in the exutil.By call to include maxConnections (or its current
value) and replace the hard-coded haproxy expectation in
ensureHaproxyBlockConfigContains("maxconn", []string{"maxconn 50000"}) with a
dynamic expectation built from maxConnections (e.g., []string{"maxconn " +
maxConnections}); keep other calls (patchResourceAsAdmin, readRouterPodEnv,
getOneNewRouterPodFromRollingUpdate) as-is.
tests-extension/test/e2e/tls.go-451-452 (1)

451-452: ⚠️ Potential issue | 🟠 Major

Guard the certificate date parsing before indexing it.

If curl's verbose output stops emitting a start date line, this panics instead of failing with context.

🧷 Example fix
 		dateRe := regexp.MustCompile("(start date.*)")
-		certStartDate := dateRe.FindAllString(string(statsOut), -1)
+		certStartDate := dateRe.FindString(statsOut)
+		o.Expect(certStartDate).NotTo(o.BeEmpty(), "expected curl verbose output to include a certificate start date")
-		certStartDate1 := dateRe.FindAllString(string(statsOut1), -1)
-		// Cross check the start date of the ceritificate is not same after reloading
-		o.Expect(certStartDate1[0]).NotTo(o.Equal(certStartDate[0]))
+		certStartDate1 := dateRe.FindString(statsOut1)
+		o.Expect(certStartDate1).NotTo(o.BeEmpty(), "expected curl verbose output to include a certificate start date after reload")
+		// Cross check the start date of the certificate is not same after reloading
+		o.Expect(certStartDate1).NotTo(o.Equal(certStartDate))

Also applies to: 470-472

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/tls.go` around lines 451 - 452, The code currently
assumes dateRe.FindAllString(string(statsOut), -1) returns at least one element
and directly indexes certStartDate[0], which can panic if no "start date" line
exists; update the parsing in the tls.go test to check the result length before
indexing (e.g., verify len(certStartDate) > 0) and fail the test with a clear
error message or skip the assertion if missing; apply the same guard to the
analogous parsing at lines referenced (the second occurrence around 470-472) so
both uses of dateRe, certStartDate, and statsOut are defensive and provide
contextual failure output instead of panicking.
tests-extension/test/e2e/tls.go-515-526 (1)

515-526: ⚠️ Potential issue | 🟠 Major

Wait for the generated route and make the lookup deterministic.

Ingress-to-route reconciliation is async. Both tests list routes once and immediately use routeNames[0]; that can panic on an empty list, and the TLS-cert case also reads annotations/host before the route has necessarily settled.

⏳ Example fix
-		routeNames := getResourceName(oc, ns, "route")
+		var routeNames []string
+		o.Eventually(func() int {
+			routeNames = getResourceName(oc, ns, "route")
+			return len(routeNames)
+		}).Should(o.Equal(1))
-		output := getByJsonPath(oc, ns, "route/"+routeNames[0], "{.metadata.annotations}")
-		o.Expect(output).Should(o.ContainSubstring(`"route.openshift.io/destination-ca-certificate-secret":"service-secret"`))
-		output = getByJsonPath(oc, ns, "route/"+routeNames[0], "{.spec.host}")
-		o.Expect(output).Should(o.ContainSubstring(`service-secure-%s.ocp51980.%s`, ns, baseDomain))
+		waitForOutputContains(oc, ns, "route/"+routeNames[0], "{.metadata.annotations}", `"route.openshift.io/destination-ca-certificate-secret":"service-secret"`)
+		waitForOutputContains(oc, ns, "route/"+routeNames[0], "{.spec.host}", fmt.Sprintf(`service-secure-%s.ocp51980.%s`, ns, baseDomain))

Also applies to: 576-588

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/tls.go` around lines 515 - 526, The test is reading
routeNames[0] immediately after calling getRoutes which can be empty due to
async ingress->route reconciliation; make the lookup deterministic by polling
until a route is present and stable before using routeNames[0]. Specifically,
after calling getRoutes(ns) use a short retry loop that calls
getResourceName(oc, ns, "route") until the returned list length > 0 (and
optionally stable across two successive polls), then assign routeNames := that
result and proceed to call waitForOutputContains and waitForOutputEquals for
that specific route; ensure the same pattern is applied to the TLS-cert case
(the block using getRoutes/getResourceName and later waitForOutput* around
routeNames[0]).
tests-extension/test/e2e/tls.go-225-227 (1)

225-227: ⚠️ Potential issue | 🟠 Major

Wait for route validation before asserting the failure count.

ExtendedValidationFailed is set asynchronously. Reading all routes immediately after the four createRoute calls makes this spec race controller reconciliation and fail intermittently.

⏳ Example fix
-		routeOutput := getRoutes(oc, ns)
-		o.Expect(strings.Count(routeOutput, `ExtendedValidationFailed`) == 4).To(o.BeTrue())
+		o.Eventually(func() int {
+			return strings.Count(getRoutes(oc, ns), "ExtendedValidationFailed")
+		}).Should(o.Equal(4))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/tls.go` around lines 225 - 227, The test reads
routeOutput immediately after the four createRoute calls and races controller
reconciliation; replace the direct assertion using getRoutes(oc, ns) with a
polling/assert-with-timeout that repeatedly calls getRoutes and checks
strings.Count(routeOutput, `ExtendedValidationFailed`) == 4 until it becomes
true or a sensible timeout elapses (use the test suite's wait helper like
wait.PollImmediate or Gomega's Eventually), referencing the existing getRoutes
function, the `ExtendedValidationFailed` marker, and the createRoute sequence so
the spec waits for the controller to finish before asserting.
tests-extension/test/e2e/tls.go-437-440 (1)

437-440: ⚠️ Potential issue | 🟠 Major

Don't convert arbitrary oc get failures into a skip.

|| err != nil hides API/RBAC/transient failures as skips. Skip only when auth is actually absent and assert everything else.

🛑 Example fix
 		output, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("route", "oauth-openshift", "-n", "openshift-authentication").Output()
-		if strings.Contains(output, "namespaces \"openshift-authentication\" not found") || err != nil {
-			g.Skip("This cluster dont have authentication operator, so skipping the test.")
+		if err != nil {
+			if strings.Contains(output, `namespaces "openshift-authentication" not found`) ||
+				strings.Contains(output, `routes.route.openshift.io "oauth-openshift" not found`) {
+				g.Skip("This cluster does not have the authentication operator, so skipping the test.")
+			}
+			o.Expect(err).NotTo(o.HaveOccurred())
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/tls.go` around lines 437 - 440, The current check
converts any error from the oc get command into a test skip by using "|| err !=
nil"; change the logic to only skip when the output explicitly indicates the
authentication namespace is missing (strings.Contains(output, "namespaces
\"openshift-authentication\" not found")), and handle other errors as test
failures: call g.Fatalf (or the test framework's fail/assert helper) with the
err details when oc.WithoutNamespace().AsAdmin().Run(...).Output() returns a
non-nil err so API/RBAC/transient failures are not masked as skips while keeping
the original g.Skip when the namespace is truly absent.
tests-extension/test/e2e/logging.go-44-49 (1)

44-49: ⚠️ Potential issue | 🟠 Major

Make the ingress controller names run-unique.

These specs create fixed names in the shared openshift-ingress-operator namespace (ocp34166, ocp34178, etc.). If a run is interrupted or two jobs hit the same cluster, the next create will fail with AlreadyExists before the deferred cleanup can help. Add a short unique suffix or delete any stale resource first.

Also applies to: 127-132, 189-196, 261-266, 340-345

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/logging.go` around lines 44 - 49, The test creates
fixed ingress controller names (e.g., the ingressControllerDescription instances
with name "ocp34166", "ocp34178", etc.) which can cause AlreadyExists failures
on concurrent or interrupted runs; update the test to generate run-unique names
(for example append a short random/UUID/time-based suffix to
ingressControllerDescription.name when constructing ingctrl and the other
affected instances at lines referenced) or ensure any existing resource is
deleted before create (call the cleanup/delete path for the same name). Locate
usages of ingressControllerDescription and the variables like ingctrl and apply
the unique-suffix strategy (or pre-delete) consistently for the other
occurrences (lines ~127-132, 189-196, 261-266, 340-345).
tests-extension/test/e2e/logging.go-76-78 (1)

76-78: ⚠️ Potential issue | 🟠 Major

Replace IPv4-only pod address lookup with cluster-aware alternative.

Every spec retrieves pod IP via getPodv4Address(...), which only works on IPv4 clusters. This makes the suite fail on IPv6-only clusters and leaves dual-stack runs untested for IPv6 paths. The codebase already has infrastructure for detecting cluster IP stack (ipv6single, dualstack) and retrieving the appropriate address family. Adopt a similar pattern to select the correct pod address based on cluster configuration.

Affected occurrences in logging.go

Lines 76–78, 159–161, 227–229, 293–295, 373–375

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/logging.go` around lines 76 - 78, Replace the
IPv4-only call to getPodv4Address with a cluster-aware lookup: detect the
cluster stack using the existing ipv6single and dualstack flags and call
getPodv6Address when the cluster is IPv6-only (ipv6single) (or choose IPv6 when
dualstack tests require it), otherwise fall back to getPodv4Address; update the
uses in the logging spec (the call that sets podIP used to build toDst and
curlCmd) to use the selected podIP so routehost + ":80:" + podIP remains
correct, and apply the same change for all other occurrences that call
getPodv4Address in this file.
tests-extension/test/e2e/testdata/router/coreDNS-pod.yaml-67-69 (1)

67-69: ⚠️ Potential issue | 🟠 Major

Avoid a public resolver in this fixture.

Hard-coding 8.8.8.8 makes the test depend on internet egress and Google DNS availability, which will fail on disconnected or egress-restricted clusters for reasons unrelated to router behavior.

One safer default
-        forward . 8.8.8.8 {
+        forward . /etc/resolv.conf {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/testdata/router/coreDNS-pod.yaml` around lines 67 -
69, The CoreDNS fixture currently forwards all queries to the public resolver
"8.8.8.8", which makes tests rely on external internet/DNS; replace that
hard-coded public resolver in the "forward . 8.8.8.8" stanza with a
non-public/local resolver used in CI (for example the cluster DNS service name
or IP such as kube-dns.kube-system.svc.cluster.local or 127.0.0.1:53) or remove
the forward stanza and use a local stub resolver for tests; update the "forward"
line in this block so it no longer references 8.8.8.8 to avoid
egress-dependency.
tests-extension/test/e2e/testdata/router/extdns/sts-exdns-perms-policy.json-5-22 (1)

5-22: ⚠️ Potential issue | 🟠 Major

Scope Route53 write access to a specific hosted zone.

The permissions policy allows route53:ChangeResourceRecordSets on all hosted zones in the AWS account (hostedzone/*). This policy is read from the JSON file and attached as-is without modification, meaning a test failure could mutate unrelated DNS zones. This should be scoped to the specific hosted zone under test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/testdata/router/extdns/sts-exdns-perms-policy.json`
around lines 5 - 22, The policy currently grants
route53:ChangeResourceRecordSets to "arn:aws:route53:::hostedzone/*" which
allows changes to all hosted zones; narrow this by replacing that Resource value
with the specific hosted zone ARN
"arn:aws:route53:::hostedzone/<HOSTED_ZONE_ID>" (use your test-hosted-zone id
variable such as TEST_HOSTED_ZONE_ID or inject from the test fixture) so only
the zone under test is writable, and update the test setup/template that reads
sts-exdns-perms-policy.json to substitute the actual HOSTED_ZONE_ID at runtime
(or generate the policy JSON dynamically) so the policy attached during tests is
scoped to that single hosted zone.
tests-extension/test/e2e/testdata/router/coreDNS-pod.yaml-8-27 (1)

8-27: ⚠️ Potential issue | 🟠 Major

Drop privilege escalation from this pod.

Line 22 makes the container harder to admit under restricted SCC/PSA, and Line 9 explicitly opts out of non-root enforcement. That should not be needed just to bind :53 when NET_BIND_SERVICE is already granted.

Minimal hardening
     securityContext:
-      allowPrivilegeEscalation: true
+      allowPrivilegeEscalation: false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/testdata/router/coreDNS-pod.yaml` around lines 8 -
27, The pod spec currently disables non-root enforcement and allows privilege
escalation which is unnecessary for binding :53; update the coredns container
securityContext (container name "coredns") to remove or set runAsNonRoot to true
(replace the explicit false), and set allowPrivilegeEscalation to false while
keeping only the needed capability NET_BIND_SERVICE under capabilities.add and
preserving capabilities.drop: - ALL; make these changes on the securityContext
blocks (pod-level securityContext and containers[].securityContext) to comply
with restricted SCC/PSA.
tests-extension/go.mod-261-261 (1)

261-261: ⚠️ Potential issue | 🟠 Major

Upgrade go.opentelemetry.io/otel/sdk to v1.40.0 or later.

go.opentelemetry.io/otel/sdk v1.37.0 is vulnerable to GHSA-9h8m-3fm2-qjrq, a HIGH-severity arbitrary code execution vulnerability affecting versions 1.21.0–1.39.0 on macOS via PATH hijacking in the ioreg invocation. Patched versions (v1.40.0+) are available and should be used immediately.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/go.mod` at line 261, Dependency go.opentelemetry.io/otel/sdk
is pinned to v1.37.0 which is vulnerable; update the module requirement to
v1.40.0 or later (e.g., v1.40.0) and run the Go module update sequence (go get
go.opentelemetry.io/otel/sdk@v1.40.0 and go mod tidy) so the lockfile and
transitive deps reflect the patched version; search for the module name
"go.opentelemetry.io/otel/sdk" in go.mod to locate the entry to change.
tests-extension/go.mod-213-215 (1)

213-215: ⚠️ Potential issue | 🟠 Major

Upgrade github.com/opencontainers/runc to v1.2.8 or later to address container escape vulnerabilities.

runc v1.2.5 is vulnerable to CVE-2025-31133 and CVE-2025-52565 (both HIGH severity, CVSS ≥7.5), which exploit insufficient verification in maskedPaths handling and /dev/pts bind-mounting. These enable container escape and host DoS. Update to v1.2.8 (or v1.3.3+/v1.4.0-rc.3+) to remediate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/go.mod` around lines 213 - 215, The go.mod lists the indirect
module github.com/opencontainers/runc at v1.2.5 which is vulnerable; update the
version string for the module github.com/opencontainers/runc to v1.2.8 (or a
later patched release such as v1.3.3 or v1.4.0-rc.3+) in go.mod, then run go mod
tidy (and vendor/update dependencies if you vendor) and rebuild/run tests to
ensure the new runc version is picked up and no dependency conflicts occur.
tests-extension/test/e2e/ipfailover.go-91-93 (1)

91-93: ⚠️ Potential issue | 🟠 Major

Mark these disruptive ipfailover cases as serial too.

The surrounding comments say both tests can conflict with other ipfailover scenarios, but unlike the neighboring cases they are missing the [Serial] marker used elsewhere in this file.

♻️ Proposed fix
-	g.It("Author:mjoseph-NonHyperShiftHOST-ConnectedOnly-Medium-41027-pod and service automatically switched over to standby when master fails [Disruptive]", func() {
+	g.It("Author:mjoseph-NonHyperShiftHOST-ConnectedOnly-Medium-41027-pod and service automatically switched over to standby when master fails [Disruptive] [Serial]", func() {
@@
-	g.It("Author:mjoseph-NonHyperShiftHOST-ConnectedOnly-High-41030-preemption strategy for keepalived ipfailover [Disruptive]", func() {
+	g.It("Author:mjoseph-NonHyperShiftHOST-ConnectedOnly-High-41030-preemption strategy for keepalived ipfailover [Disruptive] [Serial]", func() {

Also applies to: 249-251

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/ipfailover.go` around lines 91 - 93, Two disruptive
ipfailover Ginkgo tests in ipfailover.go are missing the [Serial] marker and
should be run serially; update the g.It(...) descriptions to include "[Serial]"
so they match surrounding cases. Locate the g.It(...) calls (e.g., the test
whose description starts with
"Author:mjoseph-NonHyperShiftHOST-ConnectedOnly-Medium-41027-pod and service
automatically switched over to standby when master fails [Disruptive]") and the
other similar ipfailover case further down, and prepend or append the literal
"[Serial]" inside their string descriptions so the tests are marked serial.
tests-extension/test/e2e/route-weight.go-558-559 (1)

558-559: ⚠️ Potential issue | 🟠 Major

Use srvPod2Name in both grep patterns.

Line 558 and Line 559 interpolate srvPod3Name twice and never match srvPod2Name, so this assertion can pass without checking one of the three backends at all.

♻️ Proposed fix
-		selectedSrvNum := fmt.Sprintf("cat haproxy.config | grep -E \"server pod:ingress-canary|server pod:%s|server pod:%s|server pod:%s\"| wc -l", srvPod1Name, srvPod3Name, srvPod3Name)
-		selectedWeight1Num := fmt.Sprintf("cat haproxy.config | grep -E \"server pod:ingress-canary|server pod:%s|server pod:%s|server pod:%s\"| grep \"weight 1\" |wc -l", srvPod1Name, srvPod3Name, srvPod3Name)
+		selectedSrvNum := fmt.Sprintf("cat haproxy.config | grep -E \"server pod:ingress-canary|server pod:%s|server pod:%s|server pod:%s\"| wc -l", srvPod1Name, srvPod2Name, srvPod3Name)
+		selectedWeight1Num := fmt.Sprintf("cat haproxy.config | grep -E \"server pod:ingress-canary|server pod:%s|server pod:%s|server pod:%s\"| grep \"weight 1\" |wc -l", srvPod1Name, srvPod2Name, srvPod3Name)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/route-weight.go` around lines 558 - 559, The grep
patterns used to build selectedSrvNum and selectedWeight1Num mistakenly
interpolate srvPod3Name twice instead of including srvPod2Name, so the check
never verifies the second backend; update both fmt.Sprintf calls (for
selectedSrvNum and selectedWeight1Num) to include srvPod2Name in the middle
pattern (server pod:%s) so the three backends used are srvPod1Name, srvPod2Name,
srvPod3Name and the weight/selection assertions correctly cover all three
servers.
tests-extension/test/e2e/ipfailover.go-36-45 (1)

36-45: ⚠️ Potential issue | 🟠 Major

Fail on setup errors instead of masking them.

These guard clauses drop the errors from node discovery. If either call fails, the spec can skip or continue with empty data instead of surfacing the real cluster/setup problem.

♻️ Proposed fix
-		workerNodeCount, _ := exactNodeDetails(oc)
+		workerNodeCount, err := exactNodeDetails(oc)
+		o.Expect(err).NotTo(o.HaveOccurred())
 		if workerNodeCount < 2 {
 			g.Skip("Skipping as we need two worker nodes")
 		}
 
 		g.By("check the cluster has remote worker profile")
-		remoteWorkerDetails, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("nodes", "-l", "kubernetes.io/hostname").Output()
+		remoteWorkerDetails, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("nodes", "-l", "kubernetes.io/hostname").Output()
+		o.Expect(err).NotTo(o.HaveOccurred())
 		if strings.Contains(remoteWorkerDetails, "remote-worker") {
 			g.Skip("Skip as ipfailover currently doesn't support on remote-worker profile")
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/ipfailover.go` around lines 36 - 45, The setup is
swallowing errors from exactNodeDetails and the oc get nodes call; change both
to capture and check their errors (from exactNodeDetails(...) and
oc.AsAdmin().WithoutNamespace().Run("get").Args(...).Output()), and if err !=
nil fail the spec immediately (e.g. g.Fatalf or t.Fatalf) with the error details
instead of continuing or skipping; only keep the existing g.Skip when the call
succeeds and the output contains "remote-worker". Ensure you reference
exactNodeDetails and the
oc.AsAdmin().WithoutNamespace().Run("get").Args("nodes", "-l",
"kubernetes.io/hostname").Output() call when making the changes.
tests-extension/test/e2e/testdata/fixtures.go-24-30 (1)

24-30: ⚠️ Potential issue | 🟠 Major

Return an isolated fixture copy here.

FixturePath reuses the same extracted path for the whole process. Downstream specs mutate files under that tree—for example tests-extension/test/e2e/cookies.go on Line 52 and Line 123—so later specs can read already-modified fixtures and become order-dependent.

♻️ One way to isolate callers
 func FixturePath(elem ...string) string {
 	relativePath := filepath.Join(elem...)
-	targetPath := filepath.Join(fixtureDir, relativePath)
-
-	if _, err := os.Stat(targetPath); err == nil {
-		return targetPath
-	}
+	copyRoot, err := os.MkdirTemp(fixtureDir, "fixture-copy-")
+	if err != nil {
+		panic(fmt.Sprintf("failed to create fixture copy directory: %v", err))
+	}
+	targetPath := filepath.Join(copyRoot, relativePath)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/testdata/fixtures.go` around lines 24 - 30,
FixturePath currently returns the original extracted fixture tree (targetPath)
which is mutated across tests; change it to return an isolated copy by: after
confirming targetPath exists, create a new temporary directory (os.MkdirTemp),
recursively copy the contents of targetPath into that temp dir (preserving file
contents and permissions), and return the temp dir path instead of targetPath;
update references to fixtureDir/targetPath inside FixturePath and ensure errors
from MkdirTemp and the recursive copy are handled/returned or cause a
test-failing panic as appropriate.
tests-extension/test/e2e/externaldns_util.go-107-112 (1)

107-112: ⚠️ Potential issue | 🟠 Major

Missing bounds check before accessing HostedZones[0] can cause panic.

If no hosted zone matches the domain name, hostedZoneDetails.HostedZones will be empty, and accessing index [0] will panic with an index out of range error.

🐛 Proposed fix to add bounds check
 	hostedZoneDetails, err := route53Client.ListHostedZonesByNameWithContext(
 		context.Background(), &route53.ListHostedZonesByNameInput{
 			DNSName: aws.String(domainName)})
 	o.Expect(err).NotTo(o.HaveOccurred())
+	o.Expect(hostedZoneDetails.HostedZones).NotTo(o.BeEmpty(), "No hosted zone found for domain: "+domainName)
 	e2e.Logf("The zone Id of the host domain '%s' is '%s'", *hostedZoneDetails.HostedZones[0].Name, *hostedZoneDetails.HostedZones[0].Id)
 	return strings.TrimPrefix(*hostedZoneDetails.HostedZones[0].Id, "/hostedzone/")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/externaldns_util.go` around lines 107 - 112, Add a
bounds check after calling route53Client.ListHostedZonesByNameWithContext to
ensure hostedZoneDetails.HostedZones is non-empty before accessing [0]; for
example assert with o.Expect(hostedZoneDetails.HostedZones).NotTo(o.BeEmpty())
or o.Expect(len(hostedZoneDetails.HostedZones)).To(BeNumerically(">", 0)), then
also defensively check that hostedZoneDetails.HostedZones[0].Id and .Name are
non-nil before using them in e2e.Logf and the return statement so the test fails
cleanly instead of panicking.
tests-extension/test/e2e/awslb-operator.go-117-126 (1)

117-126: ⚠️ Potential issue | 🟠 Major

Logic error: String length check instead of subnet count validation.

internalSubnets is a string from the jsonpath output, not a slice. len(internalSubnets) > 1 checks if the string has more than 1 character, not if there are 2+ subnets. A single subnet ID like "subnet-12345678" would exceed this character threshold, defeating the intended subnet count validation. The check should parse the subnet list (likely space-separated or formatted as a JSON array) and count the elements to ensure >= 2 subnets are discovered before attempting ALB provisioning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/awslb-operator.go` around lines 117 - 126,
internalSubnets is a string, so replace the len(internalSubnets) > 1 check with
explicit parsing and element count: parse the returned internalSubnets (from the
oc.AsAdmin().WithoutNamespace().Run("get").Args(...).Output() call) into a
[]string (handle both JSON array output via json.Unmarshal into []string and
plain space/comma-separated output via strings.Fields or strings.Split) and then
check len(parsedSubnets) >= 2 before calling waitForLoadBalancerProvision; if
the parsed slice has fewer than 2 elements, run the
oc.Run("describe").Args("ingress", "ingress-test").Output() branch as currently
implemented.
tests-extension/test/e2e/cloud_load_balancer.go-87-89 (1)

87-89: ⚠️ Potential issue | 🟠 Major

In-place sed modification of shared fixture file.

Same pattern as other test files - using sed -i'' on customTemp permanently modifies the shared fixture file, affecting test isolation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/cloud_load_balancer.go` around lines 87 - 89, The
test currently runs an in-place sed (sedCmd using "sed -i'' -e ..." on
customTemp via exec.Command), which mutates the shared fixture; change it to
edit a temporary copy instead: create a temp file, run sed (or perform the
replace in Go) writing output to that temp file, and use the temp path for the
rest of the test (or atomically rename back if necessary); ensure exec.Command
references the temp path (not customTemp) or replace the exec.Command call with
Go string replacement and os.WriteFile so the original fixture (customTemp) is
never modified.
tests-extension/test/e2e/ingress.go-46-49 (1)

46-49: ⚠️ Potential issue | 🟠 Major

Same in-place sed modification pattern affects multiple tests.

This pattern of using sed -i'' on shared fixture files appears in multiple tests in this file (lines 47, 210, 313, 375, 462). All these instances risk test pollution and should be refactored to use temporary copies or in-memory string replacement.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/ingress.go` around lines 46 - 49, The tests modify
shared fixture files in-place using sed -i'' which causes test pollution; update
the ingress tests (e.g., where routeRandHost is set and createResourceFromFile
is called, and similar occurrences around lines referenced) to avoid in-place
edits by making a temporary copy or performing the replacement in-memory before
passing content to createResourceFromFile (or an equivalent helper), and ensure
cleanup; search for other instances (the other occurrences you noted) and apply
the same change so tests operate on isolated copies instead of editing the
shared fixture files directly.
tests-extension/test/e2e/route-admission.go-47-49 (1)

47-49: ⚠️ Potential issue | 🟠 Major

In-place sed modification of shared fixture file.

Same issue as in ingress.go - using sed -i'' on customTemp permanently modifies the fixture file. This affects test isolation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/route-admission.go` around lines 47 - 49, The test
currently runs an in-place sed command using sedCmd and exec.Command against
customTemp which mutates the shared fixture; instead, create a new temporary
file (e.g., via os.CreateTemp / ioutil.TempFile) or copy customTemp to a temp
path and perform the substitution on that temp file, or perform the string
replacement in Go (read file, strings.Replace, write to temp file), then run the
test assertions against the temp file and ensure cleanup; update usages of
sedCmd/exec.Command to target the temp file variable and do not modify
customTemp directly.
tests-extension/test/e2e/externaldns.go-69-71 (1)

69-71: ⚠️ Potential issue | 🟠 Major

In-place sed modification of shared fixture file.

Same pattern as other test files - using sed -i'' on sampleAWS permanently modifies the shared fixture file, affecting test isolation and parallel execution.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/externaldns.go` around lines 69 - 71, The test is
modifying the shared fixture file via sedCmd and exec.Command("bash", "-c",
sedCmd) which breaks isolation; instead read the contents of sampleAWS into
memory (os.ReadFile), create a per-test temp file (os.CreateTemp /
os.MkdirTemp), perform the basedomain replacement in Go (strings.Replace or
regexp) and write the modified contents to the temp file, then use that temp
file path in the rest of the test (replacing uses of sampleAWS); this preserves
the original fixture and avoids in-place edits by sedCmd/exec.Command.
tests-extension/test/e2e/ingress.go-65-68 (1)

65-68: ⚠️ Potential issue | 🟠 Major

In-place sed modification of shared fixture file may cause test pollution.

Using sed -i'' to modify testIngress (which points to a shared fixture file) permanently alters the file for subsequent test runs. If tests run in parallel or the file isn't restored, later tests will use modified values. Consider using a temporary copy of the fixture file instead.

Proposed approach

Create a temporary copy before modification:

// Create a temp copy of the fixture
tmpFile, err := os.CreateTemp("", "ingress-resource-*.yaml")
o.Expect(err).NotTo(o.HaveOccurred())
defer os.Remove(tmpFile.Name())

content, err := os.ReadFile(testIngress)
o.Expect(err).NotTo(o.HaveOccurred())
// Perform substitution in memory and write to temp file
modifiedContent := strings.ReplaceAll(string(content), "edgehostname", routeEdgeHost)
// ... other replacements
err = os.WriteFile(tmpFile.Name(), []byte(modifiedContent), 0644)
o.Expect(err).NotTo(o.HaveOccurred())
createResourceFromFile(oc, ns, tmpFile.Name())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/ingress.go` around lines 65 - 68, The current
in-place sed call that builds sedCmd and mutates testIngress causes test
pollution; instead create a temporary copy of the fixture, read testIngress into
memory, perform the hostname substitutions for routeEdgeHost, routePassHost,
routeReenHost, routeRandHost (use strings.ReplaceAll or similar), write the
modified content to a temp file created via os.CreateTemp, defer
os.Remove(tmp.Name()), and then call createResourceFromFile(oc, ns, tmp.Name());
remove the exec.Command("bash","-c", sedCmd) call and the direct use of
testIngress so the shared fixture remains unchanged.
tests-extension/test/e2e/dns.go-1172-1174 (1)

1172-1174: ⚠️ Potential issue | 🟠 Major

In-place sed modification of shared fixture file.

Same pattern as other test files - using sed -i'' on egressFirewall permanently modifies the shared fixture file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/dns.go` around lines 1172 - 1174, The test currently
runs sed -i'' directly on the shared fixture file (egressFirewall) which mutates
the repository fixture; instead, create a temporary copy of egressFirewall (use
os.CreateTemp/os.MkdirTemp and copy the file contents), update the code to run
sed on that temp path (replace references to egressFirewall with the temp file
variable used in sedCmd), run exec.Command on the temp file, and ensure the temp
file is removed after the test; reference the sedCmd construction and
egressFirewall variable in dns.go and add temp file creation/cleanup around the
sed execution.
tests-extension/test/e2e/route-admission.go-169-174 (1)

169-174: ⚠️ Potential issue | 🟠 Major

Potential stale pod reference after rolling update.

After patching namespaceOwnership to Strict on line 170, the test reads the env from routerpod (line 173) which was captured before the patch. If the patch triggers a pod restart, routerpod may no longer exist or may be terminating. Should use newRouterpod or re-fetch the pod name.

Proposed fix
 exutil.By("5. Patch the custom ingress controller and set namespaceOwnership to Strict")
 patchResourceAsAdmin(oc, ingctrl.namespace, "ingresscontrollers/"+ingctrl.name, "{\"spec\":{\"routeAdmission\":{\"namespaceOwnership\":\"Strict\"}}}")

 exutil.By("6. Check the ROUTER_DISABLE_NAMESPACE_OWNERSHIP_CHECK env variable, which should be false")
-namespaceOwnershipEnv = readRouterPodEnv(oc, routerpod, "ROUTER_DISABLE_NAMESPACE_OWNERSHIP_CHECK")
+// Re-fetch pod name since Strict is default and may not trigger new rollout
+currentRouterpod := getOneNewRouterPodFromRollingUpdate(oc, ingctrl.name)
+namespaceOwnershipEnv = readRouterPodEnv(oc, currentRouterpod, "ROUTER_DISABLE_NAMESPACE_OWNERSHIP_CHECK")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/route-admission.go` around lines 169 - 174, The test
patches namespaceOwnership via patchResourceAsAdmin which can trigger a router
pod restart; however the code then reads environment from the previously
captured routerpod via readRouterPodEnv, risking a stale/terminated reference.
After calling patchResourceAsAdmin(oc, ingctrl.namespace,
"ingresscontrollers/"+ingctrl.name, ...) re-fetch the current router pod name
(use newRouterpod or call the same logic that populates newRouterpod) and then
call readRouterPodEnv against that fresh pod name (rather than routerpod) to
ensure you're checking the env on the current running pod.
tests-extension/test/e2e/aws_util.go-54-56 (1)

54-56: ⚠️ Potential issue | 🟠 Major

Hardcoded AWS region will cause failures on non-us-west-2 clusters.

Both newStsClient() and newIamClient() hardcode us-west-2 in their AWS SDK configuration. Clusters deployed in other regions will fail because these clients operate in the wrong region. The calling functions (prepareAllForStsCluster and clearUpAlbOnStsCluster) receive the cluster CLI object and can retrieve the actual region using exutil.GetAWSClusterRegion(oc), which is already used elsewhere in the codebase.

Also applies to: lines 80-82

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/aws_util.go` around lines 54 - 56, newStsClient and
newIamClient currently hardcode "us-west-2" in the AWS SDK config; change them
to accept or derive the correct region from the cluster CLI object used by their
callers (prepareAllForStsCluster and clearUpAlbOnStsCluster) by calling
exutil.GetAWSClusterRegion(oc) and pass that region into
config.LoadDefaultConfig (also update the similar hardcoded region usage at
lines 80-82). Ensure the region value is validated/not empty before creating the
config so the STS and IAM clients are created in the cluster's actual region.
tests-extension/test/e2e/route-hsts.go-198-200 (1)

198-200: ⚠️ Potential issue | 🟠 Major

Restore the cluster HSTS policy instead of deleting it.

These patches target the top-level /spec/requiredHSTSPolicies field, and the deferred cleanup deletes that field entirely. If the cluster already has HSTS policies, these specs wipe them out for the duration of the run and then leave them gone, which can leak state into unrelated tests. Snapshot the current value and restore that exact payload in defer.

Also applies to: 210-211, 263-265, 320-323, 332-333, 385-387, 442-444, 530-532, 584-585

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/route-hsts.go` around lines 198 - 200, The deferred
cleanup currently removes the entire /spec/requiredHSTSPolicies which can
clobber existing cluster HSTS settings; change the logic around
patchGlobalResourceAsAdmin and its defer so you first GET (snapshot) the current
value of spec.requiredHSTSPolicies (using the same oc client/context), store
that JSON payload, then in the defer call PATCH/PUT that exact snapshot back
instead of issuing a remove operation; update all locations using
patchGlobalResourceAsAdmin (the test functions around ingctldomain) to capture
and restore the original payload rather than deleting the field.
tests-extension/test/e2e/route-hsts.go-159-159 (1)

159-159: ⚠️ Potential issue | 🟠 Major

Add g.Serial decorator to these test specs.

Tests at lines 159, 224, 281, 346, 403, 463, and 551 use textual [Serial] or [Disruptive] tags in their names, but under Ginkgo v2, these markers have no effect without the corresponding g.Serial or g.Ordered decorator. The OTE entrypoint only processes platform-related tags and does not handle serialization markers.

These specs patch cluster-scoped resources (ingress config and ingresscontrollers) and must run serially to avoid conflicts. Add the g.Serial decorator to each:

- g.It("Author:aiyengar-NonHyperShiftHOST-Critical-43474-The includeSubDomainsPolicy parameter can configure subdomain policy to inherit the HSTS policy of parent domain [Serial]", func() {
+ g.It("Author:aiyengar-NonHyperShiftHOST-Critical-43474-The includeSubDomainsPolicy parameter can configure subdomain policy to inherit the HSTS policy of parent domain [Serial]", g.Serial, func() {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/route-hsts.go` at line 159, The listed Ginkgo test
specs (the g.It calls with titles like
"Author:aiyengar-NonHyperShiftHOST-Critical-43474-The includeSubDomainsPolicy
parameter..." and the other specs at the noted locations) are only marked with
"[Serial]" or "[Disruptive]" in their names but lack the g.Serial decorator;
update each affected spec by adding the g.Serial decorator to the corresponding
g.It invocation (e.g., change g.It(...) to g.It(..., g.Serial) or wrap the spec
with g.Describe/Ginkgo decorator usage that includes g.Serial) so the tests run
serially when patching cluster-scoped resources like
ingressconfig/ingresscontrollers. Ensure you add g.Serial to every spec
referenced (lines 159, 224, 281, 346, 403, 463, 551) and keep the original test
bodies intact.
tests-extension/test/e2e/route-hsts.go-217-219 (1)

217-219: ⚠️ Potential issue | 🟠 Major

Remove the shell quotes from the hsts_header annotation arguments.

The unquoted values at lines 39, 47, 55, and 209 work correctly, but lines 217, 339, 450, 455, 538, and 543 include single quotes that become part of the literal annotation value. Since Args() passes raw argv without shell parsing, the validation receives 'max-age=...' (with literal quotes) instead of max-age=..., preventing proper HSTS policy enforcement checks.

Representative fix
- msg2, err := oc.Run("annotate").WithoutNamespace().Args("route/route-edge", "haproxy.router.openshift.io/hsts_header='max-age=50000'", "--overwrite").Output()
+ msg2, err := oc.Run("annotate").WithoutNamespace().Args("route/route-edge", "haproxy.router.openshift.io/hsts_header=max-age=50000", "--overwrite").Output()

Also applies to: 339, 450, 455, 538, 543

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/route-hsts.go` around lines 217 - 219, The
annotation arguments passed to oc.Run("annotate").WithoutNamespace().Args(...)
include literal single quotes around the hsts_header value (e.g.,
"haproxy.router.openshift.io/hsts_header='max-age=50000'"), which makes the
quotes part of the annotation; remove the single quotes so the Args entry is
haproxy.router.openshift.io/hsts_header=max-age=50000 (and likewise for other
test calls to oc.Run("annotate").Args that set
haproxy.router.openshift.io/hsts_header) so validation receives the raw max-age
value; update the occurrences shown (the annotated Args calls for
route/route-edge and the other similar test cases) accordingly.
🟡 Minor comments (6)
tests-extension/test/e2e/tuning.go-193-197 (1)

193-197: ⚠️ Potential issue | 🟡 Minor

Inconsistent time format assertion on line 196.

The assertion for ROUTER_DEFAULT_SERVER_TIMEOUT is missing the "s" suffix, unlike the other timeout assertions. While ContainSubstring("33") will match "33s", it's inconsistent and less precise than the other assertions.

Proposed fix
 		o.Expect(checkenv).To(o.ContainSubstring(`ROUTER_CLIENT_FIN_TIMEOUT=3s`))
 		o.Expect(checkenv).To(o.ContainSubstring(`ROUTER_DEFAULT_CLIENT_TIMEOUT=33s`))
-		o.Expect(checkenv).To(o.ContainSubstring(`ROUTER_DEFAULT_SERVER_TIMEOUT=33`))
+		o.Expect(checkenv).To(o.ContainSubstring(`ROUTER_DEFAULT_SERVER_TIMEOUT=33s`))
 		o.Expect(checkenv).To(o.ContainSubstring(`ROUTER_DEFAULT_SERVER_FIN_TIMEOUT=3s`))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/tuning.go` around lines 193 - 197, The assertion
checking ROUTER_DEFAULT_SERVER_TIMEOUT is missing the "s" suffix; update the
expectation that uses readRouterPodEnv / checkenv to assert the full unit (e.g.,
"ROUTER_DEFAULT_SERVER_TIMEOUT=33s") instead of "33" so it matches the other
timeout assertions and is precise.
tests-extension/test/e2e/dns-operator.go-192-200 (1)

192-200: ⚠️ Potential issue | 🟡 Minor

Typos in step descriptions.

"updateh" should be "update" on lines 192 and 197.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/dns-operator.go` around lines 192 - 200, The test
step descriptions contain typos: change the strings passed to exutil.By from
"updateh the custom zones policy to RoundRobin, check Corefile and ensure it is
updated " and "updateh the custom zones policy to Sequential, check Corefile and
ensure it is updated" to use "update" instead of "updateh"; update the two
occurrences surrounding the calls to patchGlobalResourceAsAdmin,
pollReadDnsCorefile and o.Expect so the human-readable test output (exutil.By)
reads "update the custom zones policy..." for both RoundRobin and Sequential
cases.
tests-extension/test/e2e/ingress.go-217-219 (1)

217-219: ⚠️ Potential issue | 🟡 Minor

Minor typo in log message.

"skiping" should be "skipping" (missing 'p').

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/ingress.go` around lines 217 - 219, Fix the typo in
the log message inside the conditional that calls checkProxy(oc): update the
e2e.Logf call that currently logs "This is proxy cluster, skiping the curling
part." to use "skipping" (i.e., "This is proxy cluster, skipping the curling
part.") so the message reads correctly; locate this in the block where
checkProxy(oc) returns true and adjust the string passed to e2e.Logf.
tests-extension/test/e2e/dns-operator.go-95-96 (1)

95-96: ⚠️ Potential issue | 🟡 Minor

Extra closing brace in JSONPath may cause parsing issues.

The JSONPath string has an extra } at the end: ...message}} should be ...message}.

Proposed fix
-		outputOpcfg, errOpcfg := oc.AsAdmin().WithoutNamespace().Run("get").Args(
-			"dns.operator", "default", `-o=jsonpath={.status.conditions[?(@.type=="Degraded")].message}}`).Output()
+		outputOpcfg, errOpcfg := oc.AsAdmin().WithoutNamespace().Run("get").Args(
+			"dns.operator", "default", `-o=jsonpath={.status.conditions[?(@.type=="Degraded")].message}`).Output()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/dns-operator.go` around lines 95 - 96, The JSONPath
passed to oc.AsAdmin().WithoutNamespace().Run("get").Args(...) has an extra
closing brace causing parsing errors; update the Args call that sets
`-o=jsonpath=...` (used when building outputOpcfg/errOpcfg) to remove the extra
`}` so the string ends with `...message}` instead of `...message}}`, then run
the command to verify Output()/err handling remains correct.
tests-extension/test/e2e/cloud_load_balancer.go-237-238 (1)

237-238: ⚠️ Potential issue | 🟡 Minor

Incorrect fmt.Sprintf usage - missing argument.

fmt.Sprintf is called with %s placeholder but the error variable is passed as the argument to exutil.AssertWaitPollNoErr, not to Sprintf. The message will contain literal %s.

Proposed fix
-		exutil.AssertWaitPollNoErr(err, fmt.Sprintf("reached max time allowed but the error message doesn't appear", err))
+		exutil.AssertWaitPollNoErr(err, "reached max time allowed but the error message doesn't appear")

Or if the error should be included:

-		exutil.AssertWaitPollNoErr(err, fmt.Sprintf("reached max time allowed but the error message doesn't appear", err))
+		exutil.AssertWaitPollNoErr(err, fmt.Sprintf("reached max time allowed but the error message doesn't appear: %v", err))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/cloud_load_balancer.go` around lines 237 - 238, The
fmt.Sprintf call used inside the exutil.AssertWaitPollNoErr invocation is
missing the error argument, so the format string will contain a literal %s;
update the call that constructs the message (the fmt.Sprintf used before
exutil.AssertWaitPollNoErr) to pass the err variable into fmt.Sprintf (or remove
fmt.Sprintf and pass a single formatted string created with fmt.Sprintf
including err) so the actual error is included in the assertion message; look
for the fmt.Sprintf and exutil.AssertWaitPollNoErr usage around the err variable
and ensure the formatted string receives err as an argument.
tests-extension/test/e2e/aws_util.go-266-280 (1)

266-280: ⚠️ Potential issue | 🟡 Minor

EIP allocation lacks cleanup on partial failure.

If allocateElaticIP fails partway through the loop (e.g., on the 3rd allocation of 4), the previously allocated EIPs (elements 0-2) are returned but may not be properly tracked for cleanup if the caller's defer hasn't captured them yet.

Suggested improvement
 func allocateElaticIP(oc *exutil.CLI, num int) []string {
 	var eipAllocationsList []string
 	clusterinfra.GetAwsCredentialFromCluster(oc)
 	mySession := session.Must(session.NewSession())
 	svc := ec2.New(mySession)
 	for i := 0; i < num; i++ {
 		allocRes, err := svc.AllocateAddress(&ec2.AllocateAddressInput{Domain: aws.String("vpc")})
-		o.Expect(err).NotTo(o.HaveOccurred())
+		if err != nil {
+			// Cleanup already allocated EIPs before failing
+			if len(eipAllocationsList) > 0 {
+				ensureReleaseElaticIP(oc, &eipAllocationsList)
+			}
+			o.Expect(err).NotTo(o.HaveOccurred())
+		}
 		e2e.Logf("The eip allocation details for the %v element is %v", i, allocRes)
 		eipAllocationsList = append(eipAllocationsList, *allocRes.AllocationId)
 	}
 	return eipAllocationsList
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/aws_util.go` around lines 266 - 280, The
allocateElaticIP function can leak earlier allocations if an AllocateAddress
call fails mid-loop; change the error path so that on any allocation error you
immediately release any AllocationIds already appended to eipAllocationsList
before failing the test. In practice, inside allocateElaticIP, after a failed
svc.AllocateAddress call (or before o.Expect fails), iterate eipAllocationsList
and call svc.ReleaseAddress(&ec2.ReleaseAddressInput{AllocationId:
aws.String(id)}) for each id (log but ignore individual release errors), then
fail (keep the existing o.Expect or return an error if you prefer API change).
Reference symbols: allocateElaticIP, eipAllocationsList, svc, allocRes, and use
ec2.ReleaseAddressInput with AllocationId to perform cleanup.

Comment on lines +90 to +98
buildPruningBaseDir := testdata.FixturePath("router", "extdns")
awsStsCredSecret := filepath.Join(buildPruningBaseDir, "aws-sts-creds-secret.yaml")
updateFilebySedCmd(awsStsCredSecret, "external-dns-role-arn", exDnsRoleArn)

_, err := oc.AsAdmin().WithoutNamespace().Run("create").Args("-f", awsStsCredSecret).Output()
o.Expect(err).NotTo(o.HaveOccurred())
// verify the secret creation
output := getByJsonPath(oc, ns, "secret", "{.items[*].metadata.name}")
o.Expect(output).Should(o.ContainSubstring("aws-sts-creds"))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how updateFilebySedCmd is implemented and if there's cleanup logic
ast-grep --pattern 'func updateFilebySedCmd($_, $_, $_) {
  $$$
}'

# Check for other usages of this pattern
rg -n 'updateFilebySedCmd' --type=go

Repository: openshift/router

Length of output: 1394


🏁 Script executed:

sed -n '2207,2230p' tests-extension/test/e2e/util.go

Repository: openshift/router

Length of output: 1508


🏁 Script executed:

rg -A 10 'func updateFilebySedCmd' tests-extension/test/e2e/util.go

Repository: openshift/router

Length of output: 595


🏁 Script executed:

rg -B 3 -A 10 'func FixturePath' --type=go

Repository: openshift/router

Length of output: 2074


🏁 Script executed:

rg -B 5 'fixtureDir' tests-extension/test/e2e/testdata/fixtures.go | head -30

Repository: openshift/router

Length of output: 458


🏁 Script executed:

rg -n 'BeforeEach|AfterEach|Setup|Teardown|DeferCleanup' tests-extension/test/e2e/externaldns_util.go

Repository: openshift/router

Length of output: 42


🏁 Script executed:

sed -n '80,110p' tests-extension/test/e2e/externaldns_util.go

Repository: openshift/router

Length of output: 1516


🏁 Script executed:

rg -n 'CleanupFixtures' tests-extension/test/e2e/

Repository: openshift/router

Length of output: 219


🏁 Script executed:

rg -n 'parallel|Parallel|PARALLEL' tests-extension/test/e2e/ --type=go | head -20

Repository: openshift/router

Length of output: 42


🏁 Script executed:

rg -B 5 'awsStsCredSecret|aws-sts-creds-secret' tests-extension/test/e2e/ --type=go

Repository: openshift/router

Length of output: 3462


🏁 Script executed:

rg -r 'CleanupFixtures\(\)' tests-extension/ --type=go

Repository: openshift/router

Length of output: 3003


🏁 Script executed:

find tests-extension -name "*.go" -type f | xargs grep -l "ginkgo\|testing.M" | head -5

Repository: openshift/router

Length of output: 251


🏁 Script executed:

rg -B 10 'func init\(\)' tests-extension/test/e2e/testdata/fixtures.go

Repository: openshift/router

Length of output: 146


🏁 Script executed:

rg 'CleanupFixtures\(' tests-extension/ --type=go -A 2 -B 2 | grep -v '^--$'

Repository: openshift/router

Length of output: 696


🏁 Script executed:

cat tests-extension/cmd/main.go | head -100

Repository: openshift/router

Length of output: 3143


Fixture files are modified in-place without isolation, causing test interference and race conditions.

updateFilebySedCmd uses sed -i to modify fixture files directly on disk in a shared temporary directory. Since CleanupFixtures() is never invoked between tests, modifications persist across test runs. Multiple tests calling updateFilebySedCmd on the same fixture files will cause race conditions in parallel execution and leave fixtures in altered states when tests fail.

Create a temporary copy of the fixture file before modification, or use template substitution at runtime instead of sed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/externaldns_util.go` around lines 90 - 98, The test
mutates shared fixture files via updateFilebySedCmd (which uses sed -i) causing
inter-test interference; fix by making a temporary copy of the fixture before
modifying it (use testdata.FixturePath("router","extdns") to locate the
original), run updateFilebySedCmd against the temp copy, and pass that temp file
(not the original aws-sts-creds-secret.yaml) to
oc.AsAdmin().WithoutNamespace().Run("create").Args("-f", tempFile).Output();
ensure the temp file is removed in test cleanup (or call CleanupFixtures()) so
fixtures remain immutable for other tests.

Comment on lines +14 to +21
"ports": [
{
"containerPort": 8080,
"securityContext": {
"privileged": "true"
}
}
]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Invalid schema: securityContext placed inside ports array.

The securityContext is incorrectly nested under each port definition. Kubernetes does not support securityContext at the port level—it must be defined at the container level. Additionally, privileged should be a boolean, not a string.

The current structure will be silently ignored, and containers will not run privileged as intended.

🐛 Proposed fix
       "containers": [{
         "name": "httpbin-http",
         "image": "quay.io/openshifttest/httpbin@sha256:cc44fbd857f4148d8aad8359acc03efa719517e01d390b152e4f3830ad871c9f",
+        "securityContext": {
+          "privileged": true
+        },
         "ports": [
           {
-            "containerPort": 8080,
-            "securityContext": {
-              "privileged": "true"
-            }
+            "containerPort": 8080
           }
         ]
       },
       {
         "name": "httpbin-https",
         "image": "quay.io/openshifttest/httpbin@sha256:f57f4e682e05bcdadb103c93ae5ab9be166f79bcbbccaf45d92a2cad18da8d64",
+        "securityContext": {
+          "privileged": true
+        },
         "ports": [
           {
-            "containerPort": 8443,
-            "securityContext": {
-              "privileged": "true"
-            }
+            "containerPort": 8443
           }
         ]
       }]

Also applies to: 26-33

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests-extension/test/e2e/testdata/router/httpbin-pod-withprivilege.json`
around lines 14 - 21, Move the misplaced securityContext out of the port object
into the parent container object and change privileged from a string to a
boolean; specifically, remove the "securityContext" block currently inside the
"ports" array (the object containing "containerPort": 8080 and
"securityContext") and add a "securityContext" entry at the same level as
"ports" inside the container definition, setting "privileged": true (boolean) so
the container-level securityContext is applied correctly.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 18, 2026

@ming1013: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/images 1f13e30 link true /test images
ci/prow/e2e-aws-serial-1of2 1f13e30 link true /test e2e-aws-serial-1of2
ci/prow/e2e-aws-serial-2of2 1f13e30 link true /test e2e-aws-serial-2of2
ci/prow/e2e-agnostic 1f13e30 link true /test e2e-agnostic
ci/prow/verify 1f13e30 link true /test verify
ci/prow/e2e-upgrade 1f13e30 link true /test e2e-upgrade
ci/prow/okd-scos-images 1f13e30 link true /test okd-scos-images

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant