feat: enable ginkgo parallel execution for API7EE E2E tests#404
feat: enable ginkgo parallel execution for API7EE E2E tests#404
Conversation
📝 WalkthroughWalkthroughSwitches E2E tests to Ginkgo parallel nodes with synchronized hooks: adds CI ginkgo install/run steps and Make target, implements a one-time suite deploy on node-1, per-node dashboard connection lifecycle with per-process local ports, and replaces AfterSuite with synchronized before/after hooks. ChangesMulti-Node E2E Test Infrastructure
Sequence DiagramsequenceDiagram
participant GinkgoOrch as Ginkgo Orchestrator
participant Node1 as Node-1 (setup)
participant NodeN as Node-N (parallel node)
participant Dashboard as Dashboard Service
GinkgoOrch->>Node1: SynchronizedBeforeSuite()
activate Node1
Node1->>Node1: DeployAPI7EE() - recreate namespace, deploy components
Node1->>Dashboard: Establish temporary tunnel (service ports)
Node1->>Node1: Upload license, configure endpoints
Node1->>Dashboard: Teardown temporary tunnel
deactivate Node1
Node1-->>GinkgoOrch: Ready payload
GinkgoOrch->>NodeN: InitNodeConnections(payload)
activate NodeN
NodeN->>Dashboard: Establish per-node tunnel using dashboardLocalPorts()
NodeN->>NodeN: Log local endpoints for tests
deactivate NodeN
NodeN-->>GinkgoOrch: Ready
GinkgoOrch->>Node1: Run parallel tests
GinkgoOrch->>NodeN: Run parallel tests
GinkgoOrch->>NodeN: SynchronizedAfterSuite()
activate NodeN
NodeN->>Dashboard: CloseNodeConnections()
deactivate NodeN
GinkgoOrch->>Node1: TeardownInfrastructure()
activate Node1
Node1->>Node1: Suite-level cleanup (no-op)
deactivate Node1
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR speeds up the API7EE E2E CI workflow by switching from go test (serial) to the ginkgo CLI with parallel nodes, while updating suite setup/teardown and dashboard port-forwarding to be safe under parallel execution.
Changes:
- Convert suite hooks to
SynchronizedBeforeSuite/SynchronizedAfterSuiteso the shared API7EE control plane is deployed once per job while each parallel node manages its own dashboard tunnel. - Avoid port-forward collisions by using OS-assigned local ports (
net.Listen(":0")) instead of binding the local port to the Service NodePort. - Add a dedicated
ginkgo-api7ee-e2e-testMakefile target and update the API7EE E2E workflow to install ginkgo and run shards withE2E_NODES=2(except webhook).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
test/e2e/framework/api7_framework.go |
Adds synchronized suite setup helpers and switches dashboard tunnels to auto-assigned local ports for parallel safety. |
test/e2e/e2e_test.go |
Uses synchronized suite hooks to support ginkgo parallel nodes. |
Makefile |
Adds ginkgo-api7ee-e2e-test target for parallel API7EE E2E execution. |
docs/superpowers/specs/2026-05-06-ci-e2e-parallel-design.md |
Documents the parallelization approach and rationale. |
.github/workflows/e2e-test.yml |
Installs ginkgo and runs API7EE E2E shards with conditional parallelism. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/e2e-test.yml:
- Around line 65-66: The workflow installs Ginkgo via the Makefile target
install-ginkgo but the Makefile defines GINKGO_VERSION ?= 2.20.0 while the code
imports github.com/onsi/ginkgo/v2 v2.22.0; update the Makefile variable
GINKGO_VERSION from 2.20.0 to 2.22.0 so the Makefile’s install-ginkgo target
installs Ginkgo CLI v2.22.0 (and verify the CI job that runs make install-ginkgo
uses the updated Makefile).
In `@docs/superpowers/specs/2026-05-06-ci-e2e-parallel-design.md`:
- Around line 77-97: Add blank lines before and after the bullet/numbered lists
that describe the lifecycle functions so the lists are separated from
surrounding paragraphs; specifically insert an empty line immediately before the
list that begins with "DeployAPI7EE() []byte" and another empty line after that
list, and do the same for the later list block around "InitNodeConnections",
"CloseNodeConnections", and "TeardownInfrastructure" so MD032 no longer triggers
for those sections.
- Around line 17-29: Update the two fenced ASCII diagram blocks that start with
"Kind Cluster" (the initial diagram and the expanded ginkgo/node diagram) so
their opening fences include a language tag: change ``` to ```text for both
occurrences (also apply the same change to the second instance mentioned in the
comment around the "Also applies to: 33-46" region); ensure both opening fences
read ```text and the corresponding closing fences remain ``` to satisfy
markdownlint MD040.
- Around line 132-137: The Makefile snippet uses tab characters which triggers
MD010; replace the leading tab on the command lines for the target
ginkgo-api7ee-e2e-test with spaces (preserving indentation) so the lines running
the ginkgo command and the `@-prefixed` invocation use spaces instead of tabs;
ensure the target name ginkgo-api7ee-e2e-test and the command invocation that
references variables $(E2E_NODES) and $(TEST_LABEL) remain unchanged except for
whitespace.
In `@test/e2e/framework/api7_framework.go`:
- Around line 100-108: The call to k8s.DeleteNamespaceE currently ignores its
error; change it to capture the returned error from
k8s.DeleteNamespaceE(GinkgoT(), f.kubectlOpts, _namespace) and immediately
handle failures: if err != nil and !k8serrors.IsNotFound(err) then fail the test
(e.g., call GinkgoT().Fatalf or use Expect/Fail) so API/RBAC delete errors are
reported immediately; keep the existing Eventually block that polls
GetNamespaceE for the NotFound case.
- Around line 239-249: The tunnel startup can fail due to a race between
findFreePort() and ForwardPortE(); wrap the probe-and-bind sequence in a retry
loop so a claimed port doesn’t cause a hard failure. Change the code that calls
findFreePort() and ForwardPortE() (the tunnel startup logic) to: attempt
findFreePort(), call ForwardPortE() for that port, and if ForwardPortE() returns
"address already in use" or other bind errors retry the whole sequence with a
short backoff; limit retries (e.g., 5 attempts) and return a clear error if all
attempts fail. Ensure you reference findFreePort() and ForwardPortE() in the
changes and log each retry attempt.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d762f315-3bcc-46f8-b0a1-f7c548acd326
📒 Files selected for processing (5)
.github/workflows/e2e-test.ymlMakefiledocs/superpowers/specs/2026-05-06-ci-e2e-parallel-design.mdtest/e2e/e2e_test.gotest/e2e/framework/api7_framework.go
conformance test report - apisix-standalone modeapiVersion: gateway.networking.k8s.io/v1
date: "2026-05-06T07:38:34Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
contact: null
organization: APISIX
project: apisix-ingress-controller
url: https://github.com/apache/apisix-ingress-controller.git
version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
result: success
statistics:
Failed: 0
Passed: 12
Skipped: 0
name: GATEWAY-GRPC
summary: Core tests succeeded.
- core:
result: partial
skippedTests:
- HTTPRouteHTTPSListener
statistics:
Failed: 0
Passed: 32
Skipped: 1
extended:
result: partial
skippedTests:
- HTTPRouteRedirectPortAndScheme
statistics:
Failed: 0
Passed: 11
Skipped: 1
supportedFeatures:
- GatewayAddressEmpty
- GatewayPort8080
- HTTPRouteBackendProtocolWebSocket
- HTTPRouteDestinationPortMatching
- HTTPRouteHostRewrite
- HTTPRouteMethodMatching
- HTTPRoutePathRewrite
- HTTPRoutePortRedirect
- HTTPRouteQueryParamMatching
- HTTPRouteRequestMirror
- HTTPRouteResponseHeaderModification
- HTTPRouteSchemeRedirect
unsupportedFeatures:
- GatewayHTTPListenerIsolation
- GatewayInfrastructurePropagation
- GatewayStaticAddresses
- HTTPRouteBackendProtocolH2C
- HTTPRouteBackendRequestHeaderModification
- HTTPRouteBackendTimeout
- HTTPRouteParentRefPort
- HTTPRoutePathRedirect
- HTTPRouteRequestMultipleMirrors
- HTTPRouteRequestPercentageMirror
- HTTPRouteRequestTimeout
name: GATEWAY-HTTP
summary: Core tests partially succeeded with 1 test skips. Extended tests partially
succeeded with 1 test skips.
- core:
result: partial
skippedTests:
- TLSRouteSimpleSameNamespace
statistics:
Failed: 0
Passed: 10
Skipped: 1
name: GATEWAY-TLS
summary: Core tests partially succeeded with 1 test skips. |
conformance test report - apisix modeapiVersion: gateway.networking.k8s.io/v1
date: "2026-05-06T07:38:39Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
contact: null
organization: APISIX
project: apisix-ingress-controller
url: https://github.com/apache/apisix-ingress-controller.git
version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
result: partial
skippedTests:
- TLSRouteSimpleSameNamespace
statistics:
Failed: 0
Passed: 10
Skipped: 1
name: GATEWAY-TLS
summary: Core tests partially succeeded with 1 test skips.
- core:
result: success
statistics:
Failed: 0
Passed: 12
Skipped: 0
name: GATEWAY-GRPC
summary: Core tests succeeded.
- core:
failedTests:
- HTTPRouteInvalidBackendRefUnknownKind
result: failure
skippedTests:
- HTTPRouteHTTPSListener
statistics:
Failed: 1
Passed: 31
Skipped: 1
extended:
result: partial
skippedTests:
- HTTPRouteRedirectPortAndScheme
statistics:
Failed: 0
Passed: 11
Skipped: 1
supportedFeatures:
- GatewayAddressEmpty
- GatewayPort8080
- HTTPRouteBackendProtocolWebSocket
- HTTPRouteDestinationPortMatching
- HTTPRouteHostRewrite
- HTTPRouteMethodMatching
- HTTPRoutePathRewrite
- HTTPRoutePortRedirect
- HTTPRouteQueryParamMatching
- HTTPRouteRequestMirror
- HTTPRouteResponseHeaderModification
- HTTPRouteSchemeRedirect
unsupportedFeatures:
- GatewayHTTPListenerIsolation
- GatewayInfrastructurePropagation
- GatewayStaticAddresses
- HTTPRouteBackendProtocolH2C
- HTTPRouteBackendRequestHeaderModification
- HTTPRouteBackendTimeout
- HTTPRouteParentRefPort
- HTTPRoutePathRedirect
- HTTPRouteRequestMultipleMirrors
- HTTPRouteRequestPercentageMirror
- HTTPRouteRequestTimeout
name: GATEWAY-HTTP
summary: Core tests failed with 1 test failures. Extended tests partially succeeded
with 1 test skips. |
conformance test reportapiVersion: gateway.networking.k8s.io/v1
date: "2026-05-06T08:00:13Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
contact: null
organization: APISIX
project: apisix-ingress-controller
url: https://github.com/apache/apisix-ingress-controller.git
version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
failedTests:
- GRPCExactMethodMatching
- GRPCRouteHeaderMatching
- GRPCRouteListenerHostnameMatching
- GatewayModifyListeners
result: failure
statistics:
Failed: 4
Passed: 8
Skipped: 0
name: GATEWAY-GRPC
summary: Core tests failed with 4 test failures.
- core:
failedTests:
- GatewayModifyListeners
result: failure
skippedTests:
- HTTPRouteHTTPSListener
statistics:
Failed: 1
Passed: 31
Skipped: 1
extended:
failedTests:
- HTTPRouteBackendProtocolWebSocket
result: failure
skippedTests:
- HTTPRouteRedirectPortAndScheme
statistics:
Failed: 1
Passed: 10
Skipped: 1
supportedFeatures:
- GatewayAddressEmpty
- GatewayPort8080
- HTTPRouteBackendProtocolWebSocket
- HTTPRouteDestinationPortMatching
- HTTPRouteHostRewrite
- HTTPRouteMethodMatching
- HTTPRoutePathRewrite
- HTTPRoutePortRedirect
- HTTPRouteQueryParamMatching
- HTTPRouteRequestMirror
- HTTPRouteResponseHeaderModification
- HTTPRouteSchemeRedirect
unsupportedFeatures:
- GatewayHTTPListenerIsolation
- GatewayInfrastructurePropagation
- GatewayStaticAddresses
- HTTPRouteBackendProtocolH2C
- HTTPRouteBackendRequestHeaderModification
- HTTPRouteBackendTimeout
- HTTPRouteParentRefPort
- HTTPRoutePathRedirect
- HTTPRouteRequestMultipleMirrors
- HTTPRouteRequestPercentageMirror
- HTTPRouteRequestTimeout
name: GATEWAY-HTTP
summary: Core tests failed with 1 test failures. Extended tests failed with 1 test
failures.
- core:
failedTests:
- GatewayModifyListeners
- TLSRouteSimpleSameNamespace
result: failure
statistics:
Failed: 2
Passed: 9
Skipped: 0
name: GATEWAY-TLS
summary: Core tests failed with 2 test failures. |
14930e9 to
f182ea3
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/e2e/framework/api7_framework.go (1)
275-279:⚠️ Potential issue | 🟠 Major | ⚡ Quick winClean up the first tunnel when the second forward fails.
If the HTTP forward succeeds and the HTTPS forward fails, this returns with the HTTP port-forward still running. That leaks the background forwarder and can block the next tunnel attempt on the same fixed local port.
Suggested fix
if err := _dashboardHTTPTunnel.ForwardPortE(f.GinkgoT); err != nil { return err } if err := _dashboardHTTPSTunnel.ForwardPortE(f.GinkgoT); err != nil { + _dashboardHTTPTunnel.Close() + _dashboardHTTPSTunnel.Close() + _dashboardHTTPTunnel = nil + _dashboardHTTPSTunnel = nil return err }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/e2e/framework/api7_framework.go` around lines 275 - 279, If _dashboardHTTPSTunnel.ForwardPortE returns an error after _dashboardHTTPTunnel.ForwardPortE succeeded, ensure you clean up the first tunnel before returning: call the teardown method on _dashboardHTTPTunnel (e.g. Close()/Stop()/StopForwarding() whichever exists on that type) with proper nil checks and ignore/aggregate any error from the cleanup, then return the original error from _dashboardHTTPSTunnel.ForwardPortE; this ensures _dashboardHTTPTunnel is not left running when _dashboardHTTPSTunnel fails.
♻️ Duplicate comments (1)
test/e2e/framework/api7_framework.go (1)
99-107:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHandle namespace deletion failures immediately.
Line 99 still discards
DeleteNamespaceEfailures, so RBAC/API errors get reported later as a generic timeout from theEventuallyblock.Suggested fix
- _ = k8s.DeleteNamespaceE(GinkgoT(), f.kubectlOpts, _namespace) + if err := k8s.DeleteNamespaceE(GinkgoT(), f.kubectlOpts, _namespace); err != nil && !k8serrors.IsNotFound(err) { + Expect(err).ShouldNot(HaveOccurred(), "deleting namespace") + }As per coding guidelines, "Every function return value must be checked for errors; errors must be properly handled (not ignored, not silently swallowed)".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/e2e/framework/api7_framework.go` around lines 99 - 107, The call to DeleteNamespaceE currently ignores its error; change this so DeleteNamespaceE(GinkgoT(), f.kubectlOpts, _namespace) returns are checked and any non-nil error is handled immediately (fail the test or call GinkgoT().Fatalf/log the error) instead of being swallowed — this ensures RBAC/API errors surface immediately rather than as a timeout in the subsequent Eventually which calls GetNamespaceE and k8serrors.IsNotFound; update the block around DeleteNamespaceE, referencing the same symbols (DeleteNamespaceE, GinkgoT, f.kubectlOpts, _namespace) to perform error checking and immediate failure handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@test/e2e/framework/api7_framework.go`:
- Around line 275-279: If _dashboardHTTPSTunnel.ForwardPortE returns an error
after _dashboardHTTPTunnel.ForwardPortE succeeded, ensure you clean up the first
tunnel before returning: call the teardown method on _dashboardHTTPTunnel (e.g.
Close()/Stop()/StopForwarding() whichever exists on that type) with proper nil
checks and ignore/aggregate any error from the cleanup, then return the original
error from _dashboardHTTPSTunnel.ForwardPortE; this ensures _dashboardHTTPTunnel
is not left running when _dashboardHTTPSTunnel fails.
---
Duplicate comments:
In `@test/e2e/framework/api7_framework.go`:
- Around line 99-107: The call to DeleteNamespaceE currently ignores its error;
change this so DeleteNamespaceE(GinkgoT(), f.kubectlOpts, _namespace) returns
are checked and any non-nil error is handled immediately (fail the test or call
GinkgoT().Fatalf/log the error) instead of being swallowed — this ensures
RBAC/API errors surface immediately rather than as a timeout in the subsequent
Eventually which calls GetNamespaceE and k8serrors.IsNotFound; update the block
around DeleteNamespaceE, referencing the same symbols (DeleteNamespaceE,
GinkgoT, f.kubectlOpts, _namespace) to perform error checking and immediate
failure handling.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: cdc125c1-3f82-4433-bc33-dad62fdacbe0
📒 Files selected for processing (4)
.github/workflows/e2e-test.ymlMakefiletest/e2e/e2e_test.gotest/e2e/framework/api7_framework.go
🚧 Files skipped from review as they are similar to previous changes (1)
- Makefile
- Convert BeforeSuite/AfterSuite to SynchronizedBeforeSuite/SynchronizedAfterSuite so the API7EE control plane is deployed only once (node 1) while each ginkgo parallel node creates its own dashboard port-forward tunnel - Replace findFreePort/TOCTOU approach with dashboardLocalPorts() using GinkgoParallelProcess() for deterministic, conflict-free local port assignment - Extract initSuiteEnv() to deduplicate env validation across suite setup paths - Add const defaultDashboardVersion to satisfy goconst lint - Add ginkgo-api7ee-e2e-test Makefile target for ginkgo CLI invocation - Update e2e-test.yml to install ginkgo and run with E2E_NODES=2 for non-webhook matrix jobs (webhook remains serial with E2E_NODES=1) - Align GINKGO_VERSION to v2.22.0 matching go.mod Expected: CI time reduced from ~60 min to ~22-25 min on the two heavy shards. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6fbbff1 to
e94556d
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Makefile`:
- Around line 164-166: The install-ginkgo target installs the CLI into
GOPATH/bin instead of the Makefile's $(LOCALBIN), so update the target to export
GOBIN=$(LOCALBIN) when running go install (i.e., run the install command as
GOBIN=$(LOCALBIN) go install
github.com/onsi/ginkgo/v2/ginkgo@v$(GINKGO_VERSION)) so the ginkgo binary is
placed in $(LOCALBIN) and discoverable via the Makefile PATH; also ensure any
required directory creation for $(LOCALBIN) (as other tool targets do) is
preserved if needed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 16f2a69b-2d8b-4e71-a3b9-27f8b967a53c
📒 Files selected for processing (4)
.github/workflows/e2e-test.ymlMakefiletest/e2e/e2e_test.gotest/e2e/framework/api7_framework.go
| .PHONY: install-ginkgo | ||
| install-ginkgo: | ||
| @go install github.com/onsi/ginkgo/v2/ginkgo@v$(GINKGO_VERSION) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo 'Makefile install-ginkgo target:'
sed -n '164,166p' Makefile
echo
printf 'go env GOBIN: %s\n' "$(go env GOBIN)"
printf 'go env GOPATH: %s\n' "$(go env GOPATH)"
gopath_bin="$(go env GOPATH)/bin"
case ":$PATH:" in
*":$gopath_bin:"*)
echo "PATH already contains $gopath_bin"
;;
*)
echo "PATH does NOT contain $gopath_bin"
;;
esacRepository: api7/api7-ingress-controller
Length of output: 301
🏁 Script executed:
#!/bin/bash
# Verify Makefile context
echo "=== Line 37 (GINKGO_VERSION) ==="
sed -n '35,40p' Makefile
echo
echo "=== Lines 157-162 (ginkgo-api7ee-e2e-test target) ==="
sed -n '155,165p' Makefile
echo
echo "=== Lines 379-380 (PATH export) ==="
sed -n '375,385p' Makefile
echo
echo "=== LOCALBIN definition ==="
rg 'LOCALBIN\s*:?=' Makefile | head -5
echo
echo "=== All ginkgo-related targets ==="
rg '\.PHONY:.*ginkgo|^[a-z-]*ginkgo[a-z-]*:' MakefileRepository: api7/api7-ingress-controller
Length of output: 1348
🏁 Script executed:
#!/bin/bash
# Find LOCALBIN definition and go-install-tool function
echo "=== Searching for LOCALBIN definition ==="
rg 'LOCALBIN\s*:?=' Makefile -B 2 -A 2
echo
echo "=== Searching for go-install-tool function ==="
rg -A 10 'define go-install-tool|go-install-tool\s*=' Makefile
echo
echo "=== First 100 lines of Makefile (looking for variable definitions) ==="
head -100 Makefile | cat -n | tail -50Repository: api7/api7-ingress-controller
Length of output: 2808
🏁 Script executed:
#!/bin/bash
# More comprehensive search for LOCALBIN
echo "=== Entire Makefile search for LOCALBIN ==="
rg 'LOCALBIN' Makefile -B 1 -A 1
echo
echo "=== Line count of Makefile ==="
wc -l MakefileRepository: api7/api7-ingress-controller
Length of output: 2048
Install the Ginkgo CLI into $(LOCALBIN) to match the established pattern and ensure it's discoverable.
The install-ginkgo target uses plain go install, but all other tool installs in this Makefile (kustomize, controller-gen, envtest, golangci-lint, adc) set GOBIN=$(LOCALBIN). Without this, the binary installs to GOPATH/bin, which is not in the PATH exported by this Makefile. The ginkgo-api7ee-e2e-test target will fail with ginkgo: command not found since PATH is set to $(LOCALBIN):$(PATH) only.
Suggested fix
.PHONY: install-ginkgo
install-ginkgo:
- `@go` install github.com/onsi/ginkgo/v2/ginkgo@v$(GINKGO_VERSION)
+ `@GOBIN`=$(LOCALBIN) go install github.com/onsi/ginkgo/v2/ginkgo@v$(GINKGO_VERSION)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .PHONY: install-ginkgo | |
| install-ginkgo: | |
| @go install github.com/onsi/ginkgo/v2/ginkgo@v$(GINKGO_VERSION) | |
| .PHONY: install-ginkgo | |
| install-ginkgo: | |
| `@GOBIN`=$(LOCALBIN) go install github.com/onsi/ginkgo/v2/ginkgo@v$(GINKGO_VERSION) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Makefile` around lines 164 - 166, The install-ginkgo target installs the CLI
into GOPATH/bin instead of the Makefile's $(LOCALBIN), so update the target to
export GOBIN=$(LOCALBIN) when running go install (i.e., run the install command
as GOBIN=$(LOCALBIN) go install
github.com/onsi/ginkgo/v2/ginkgo@v$(GINKGO_VERSION)) so the ginkgo binary is
placed in $(LOCALBIN) and discoverable via the Makefile PATH; also ensure any
required directory creation for $(LOCALBIN) (as other tool targets do) is
preserved if needed.
Summary
The API7EE E2E CI pipeline currently takes ~60 minutes. This PR enables ginkgo parallel test execution to bring it down to ~22-25 minutes.
Problem
e2e-test.ymlruns three matrix shards:apisix.apache.org(~89 tests),networking.k8s.io(~81 tests),webhook(~28 tests)go testwhich doesn't support--nodes--nodes=Nusage:BeforeSuiteruns on all N ginkgo nodes simultaneously → multiple helm installs → cluster failurenewDashboardTunnel()binds the NodePort number as local port → parallel processes on same host hitEADDRINUSESolution
1.
SynchronizedBeforeSuite/SynchronizedAfterSuiteConvert ginkgo suite hooks so API7EE control plane deployment runs exactly once (on node 1 only), while each parallel node creates its own independent dashboard tunnel.
2. Auto-assigned local ports
Replace hardcoded NodePort local bind address with
findFreePort()(net.Listen(":0")), eliminating port conflicts between parallel ginkgo nodes.3. Ginkgo CLI + new Makefile target
Add
ginkgo-api7ee-e2e-testtarget that uses theginkgoCLI with--nodes=$(E2E_NODES).4. Workflow update
E2E_NODES=4; webhook shard remains serial (E2E_NODES=1) since it has infrastructure-level side effectsWhy tests are safe to parallelize
Each
It()test creates fully isolated resources viaAPI7Deployer.BeforeEach():No shared mutable state between tests.
Expected Impact
apisix.apache.orgnetworking.k8s.iowebhookFiles Changed
test/e2e/framework/api7_framework.go:findFreePort(),DeployAPI7EE(),InitNodeConnections(),CloseNodeConnections(),TeardownInfrastructure(), fixednewDashboardTunnel()test/e2e/e2e_test.go: Synchronized suite hooksMakefile:ginkgo-api7ee-e2e-testtarget.github/workflows/e2e-test.yml: Install ginkgo + conditionalE2E_NODESDesign doc:
docs/superpowers/specs/2026-05-06-ci-e2e-parallel-design.mdSummary by CodeRabbit
Tests
Chores