Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ba91ae8
feat: validate APISIX resources in webhooks
AlinsRan Apr 27, 2026
0b74a9c
fix: use validate API in webhook checks
AlinsRan Apr 27, 2026
10d76b4
fix(e2e): add skip guards and redesign ADC validation tests
AlinsRan Apr 27, 2026
d9ba66c
fix: export kind kubeconfig in v2 e2e
AlinsRan Apr 27, 2026
07b7c4d
fix: recreate kind cluster for v2 e2e
AlinsRan Apr 27, 2026
73f3836
fix: wait for kind apiserver readiness
AlinsRan Apr 27, 2026
23fb4e7
fix: use internal kind kubeconfig in CI
AlinsRan Apr 27, 2026
7663b68
fix: support old kind kubeconfig setup
AlinsRan Apr 27, 2026
8859187
fix: harden e2e environment setup
AlinsRan Apr 27, 2026
742f11b
fix: use mirrored images in self-hosted e2e
AlinsRan Apr 27, 2026
9b1533e
fix: proxy docker hub pulls in self-hosted e2e
AlinsRan Apr 27, 2026
24f141d
fix: build local echo server image for e2e
AlinsRan Apr 27, 2026
ec74a51
fix: proxy remaining docker hub images
AlinsRan Apr 27, 2026
e90710e
fix: defer dashboard readiness checks in e2e
AlinsRan Apr 27, 2026
d401305
fix: wait for postgres readiness in v2 e2e
AlinsRan Apr 27, 2026
6618932
fix: stabilize postgres startup in v2 e2e
AlinsRan Apr 27, 2026
4141447
fix: avoid flaky docker login action downloads
AlinsRan Apr 27, 2026
d1a32ed
fix: scope postgres mirror to v2 CI
AlinsRan Apr 27, 2026
a58fd8c
fix: stabilize webhook CI coverage
AlinsRan Apr 27, 2026
f032228
fix: retry v2 postgres image preload
AlinsRan Apr 27, 2026
4dee995
fix: use legacy postgres image in v2 CI
AlinsRan Apr 27, 2026
eef1a88
test: stabilize standalone apisixconsumer webhook e2e
AlinsRan Apr 27, 2026
9a29eb2
fix: stabilize adc e2e retries
AlinsRan Apr 27, 2026
9efafc9
test: stabilize corrected consumer webhook e2e
AlinsRan Apr 27, 2026
badcb29
ci: fix misspell workflow install
AlinsRan Apr 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/apisix-conformance-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ jobs:
go install sigs.k8s.io/kind@v0.23.0

- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login \
"${{ secrets.DOCKER_REGISTRY }}" \
--username "${{ secrets.DOCKER_USERNAME }}" \
--password-stdin

- name: Build images
env:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/apisix-e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ jobs:
go-version: "1.24"

- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login \
"${{ secrets.DOCKER_REGISTRY }}" \
--username "${{ secrets.DOCKER_USERNAME }}" \
--password-stdin

- name: Setup Node.js
uses: actions/setup-node@v3
Expand Down
20 changes: 10 additions & 10 deletions .github/workflows/conformance-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,18 @@ jobs:
./get_helm.sh

- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login \
"${{ secrets.DOCKER_REGISTRY }}" \
--username "${{ secrets.DOCKER_USERNAME }}" \
--password-stdin

- name: Login to Private Registry
uses: docker/login-action@v3
with:
registry: hkccr.ccs.tencentyun.com
username: ${{ secrets.PRIVATE_DOCKER_USERNAME }}
password: ${{ secrets.PRIVATE_DOCKER_PASSWORD }}
run: |
echo "${{ secrets.PRIVATE_DOCKER_PASSWORD }}" | docker login \
hkccr.ccs.tencentyun.com \
--username "${{ secrets.PRIVATE_DOCKER_USERNAME }}" \
--password-stdin

- name: Build images
env:
Expand Down
51 changes: 40 additions & 11 deletions .github/workflows/e2e-test-k8s.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,32 @@ jobs:
./get_helm.sh

- name: Login to Private Registry
uses: docker/login-action@v3
with:
registry: hkccr.ccs.tencentyun.com
username: ${{ secrets.PRIVATE_DOCKER_USERNAME }}
password: ${{ secrets.PRIVATE_DOCKER_PASSWORD }}
run: |
echo "${{ secrets.PRIVATE_DOCKER_PASSWORD }}" | docker login \
hkccr.ccs.tencentyun.com \
--username "${{ secrets.PRIVATE_DOCKER_USERNAME }}" \
--password-stdin

- name: Launch Kind Cluster
env:
KIND_NODE_IMAGE: kindest/node:v1.18.15
run: |
make kind-down
make kind-up
KIND_NODE_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' apisix-ingress-cluster-control-plane)
echo $KIND_NODE_IP

kubectl config get-clusters
kubectl config set-cluster kind-apisix-ingress-cluster --server=https://$KIND_NODE_IP:6443
kubectl wait --for=condition=Ready nodes --all
mkdir -p "${HOME}/.kube"
kind get kubeconfig --name apisix-ingress-cluster --internal > "${HOME}/.kube/config"
kubectl config use-context kind-apisix-ingress-cluster
for attempt in {1..30}; do
if kubectl cluster-info >/dev/null 2>&1; then
break
fi
if [ "${attempt}" -eq 30 ]; then
echo "kind apiserver did not become reachable in time" >&2
exit 1
fi
sleep 2
done
kubectl wait --for=condition=Ready nodes --all --timeout=120s

- name: Build images
env:
Expand All @@ -95,6 +104,23 @@ jobs:
- name: Loading Docker Image to Kind Cluster
run: |
make kind-load-images
loaded_postgres_image=false
for image in \
swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/bitnamilegacy/postgresql:15.4.0-debian-11-r45 \
docker.aityp.com/bitnamilegacy/postgresql:15.4.0-debian-11-r45 \
bitnamilegacy/postgresql:15.4.0-debian-11-r45 \
docker.m.daocloud.io/bitnamilegacy/postgresql:15.4.0-debian-11-r45; do
if docker pull "${image}"; then
docker tag "${image}" docker.io/bitnamilegacy/postgresql:15.4.0-debian-11-r45
kind load docker-image docker.io/bitnamilegacy/postgresql:15.4.0-debian-11-r45 --name apisix-ingress-cluster
loaded_postgres_image=true
break
fi
done
if [ "${loaded_postgres_image}" != "true" ]; then
echo "failed to preload postgres image for kind" >&2
exit 1
fi

- name: Extract adc binary
if: ${{ env.ADC_VERSION == 'dev' }}
Expand All @@ -116,5 +142,8 @@ jobs:
TEST_LABEL: ${{ matrix.cases_subset }}
INGRESS_VERSION: v1beta1
TEST_ENV: CI
POSTGRESQL_IMAGE_REGISTRY: docker.io
POSTGRESQL_IMAGE_REPOSITORY: bitnamilegacy/postgresql
POSTGRESQL_IMAGE_TAG: 15.4.0-debian-11-r45
run: |
make e2e-test
20 changes: 10 additions & 10 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,18 @@ jobs:
chmod 700 get_helm.sh
./get_helm.sh
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login \
"${{ secrets.DOCKER_REGISTRY }}" \
--username "${{ secrets.DOCKER_USERNAME }}" \
--password-stdin

- name: Login to Private Registry
uses: docker/login-action@v3
with:
registry: hkccr.ccs.tencentyun.com
username: ${{ secrets.PRIVATE_DOCKER_USERNAME }}
password: ${{ secrets.PRIVATE_DOCKER_PASSWORD }}
run: |
echo "${{ secrets.PRIVATE_DOCKER_PASSWORD }}" | docker login \
hkccr.ccs.tencentyun.com \
--username "${{ secrets.PRIVATE_DOCKER_USERNAME }}" \
--password-stdin

- name: Build images
env:
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/spell-checker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ jobs:
steps:
- name: Check out code.
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Install
run: |
wget -O - -q https://git.io/misspell | sh -s -- -b .
go install github.com/client9/misspell/cmd/misspell@v0.3.4
- name: Misspell
run: |
find . -name "*.go" -type f | xargs ./misspell -i mosquitto -error
find docs -type f | xargs ./misspell -error
find . -name "*.go" -type f | xargs "$(go env GOPATH)/bin/misspell" -i mosquitto -error
find docs -type f | xargs "$(go env GOPATH)/bin/misspell" -error
49 changes: 38 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ kind-down:
|| echo "kind cluster does not exist"

.PHONY: kind-load-images
kind-load-images: pull-infra-images kind-load-ingress-image kind-load-adc-image
kind-load-images: pull-infra-images build-e2e-echo-server-image kind-load-ingress-image kind-load-adc-image
@kind load docker-image hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-gateway:dev --name $(KIND_NAME)
@kind load docker-image hkccr.ccs.tencentyun.com/api7-dev/api7-ee-dp-manager:$(DASHBOARD_VERSION) --name $(KIND_NAME)
@kind load docker-image hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-integrated:$(DASHBOARD_VERSION) --name $(KIND_NAME)
Expand Down Expand Up @@ -222,16 +222,39 @@ kind-load-adc-image:
@docker tag ghcr.io/api7/adc:$(ADC_VERSION) ghcr.io/api7/adc:dev
@kind load docker-image ghcr.io/api7/adc:dev --name $(KIND_NAME)

.PHONY: build-e2e-echo-server-image
build-e2e-echo-server-image:
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/e2e-echo-server ./cmd/e2e-echo-server
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

build-e2e-echo-server-image hard-codes GOARCH=amd64. This will break running e2e tests on arm64 hosts/clusters (the scratch image will contain an amd64 binary). Consider using the existing $(GOARCH) Makefile variable (or building/loading a multi-arch image) so the echo-server image matches the target platform.

Suggested change
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/e2e-echo-server ./cmd/e2e-echo-server
@CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -o bin/e2e-echo-server ./cmd/e2e-echo-server

Copilot uses AI. Check for mistakes.
@docker build -f test/e2e/images/echo-server.Dockerfile -t jmalloc/echo-server:latest .
Comment on lines +226 to +228
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

build-e2e-echo-server-image hard-codes GOARCH=amd64. This can break local dev and CI runners that are arm64 (or if KIND uses a non-amd64 node image). Consider deriving GOARCH from go env GOARCH / the kind node architecture, or using a multi-arch build strategy.

Copilot uses AI. Check for mistakes.

.PHONY: pull-infra-images
pull-infra-images:
@docker pull hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-gateway:dev
@docker pull hkccr.ccs.tencentyun.com/api7-dev/api7-ee-dp-manager:$(DASHBOARD_VERSION)
@docker pull hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-integrated:$(DASHBOARD_VERSION)
@docker pull kennethreitz/httpbin:latest
@docker pull jmalloc/echo-server:latest
@docker pull ghcr.io/api7/adc:dev
@docker pull apache/apisix:dev
@docker pull openresty/openresty:1.27.1.2-4-bullseye-fat
@retry_pull() { \
source="$$1"; \
target="$$2"; \
for attempt in 1 2 3; do \
if docker pull "$$source"; then \
if [ "$$source" != "$$target" ]; then \
docker tag "$$source" "$$target"; \
fi; \
return 0; \
fi; \
if [ $$attempt -eq 3 ]; then \
echo "failed to pull $$source after $$attempt attempts" >&2; \
exit 1; \
fi; \
echo "retrying docker pull for $$source (attempt $$((attempt + 1))/3)..." >&2; \
sleep 5; \
done; \
}; \
retry_pull "hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-gateway:dev" "hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-gateway:dev"; \
retry_pull "hkccr.ccs.tencentyun.com/api7-dev/api7-ee-dp-manager:$(DASHBOARD_VERSION)" "hkccr.ccs.tencentyun.com/api7-dev/api7-ee-dp-manager:$(DASHBOARD_VERSION)"; \
retry_pull "hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-integrated:$(DASHBOARD_VERSION)" "hkccr.ccs.tencentyun.com/api7-dev/api7-ee-3-integrated:$(DASHBOARD_VERSION)"; \
dockerhub_proxy="$${DOCKERHUB_PROXY:-docker.m.daocloud.io}"; \
retry_pull "$$dockerhub_proxy/kennethreitz/httpbin:latest" "kennethreitz/httpbin:latest"; \
retry_pull "ghcr.io/api7/adc:dev" "ghcr.io/api7/adc:dev"; \
retry_pull "$$dockerhub_proxy/apache/apisix:dev" "apache/apisix:dev"; \
retry_pull "$$dockerhub_proxy/openresty/openresty:1.27.1.2-4-bullseye-fat" "openresty/openresty:1.27.1.2-4-bullseye-fat"

##@ Build

Expand Down Expand Up @@ -398,8 +421,12 @@ $(ADC_BIN):
ifeq ($(ADC_VERSION),dev)
@echo "ADC_VERSION=dev, skip download"
else
curl -sSfL https://github.com/api7/adc/releases/download/v${ADC_VERSION}/adc_${ADC_VERSION}_${GOOS}_${GOARCH}.tar.gz \
| tar -xz -C $(LOCALBIN)
tmp_archive=$$(mktemp); \
trap 'rm -f "$$tmp_archive"' EXIT; \
curl --retry 5 --retry-delay 2 --retry-connrefused -sSfL \
-o "$$tmp_archive" \
https://github.com/api7/adc/releases/download/v${ADC_VERSION}/adc_${ADC_VERSION}_${GOOS}_${GOARCH}.tar.gz; \
tar -xzf "$$tmp_archive" -C $(LOCALBIN)
endif

gofmt: ## Apply go fmt
Expand Down
44 changes: 44 additions & 0 deletions cmd/e2e-echo-server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
Comment on lines +1 to +3
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

This new Go file is missing the standard ASF license header that other cmd/ entrypoints in this repo include. Add the Apache Software Foundation header comment at the top for license compliance/consistency.

Copilot uses AI. Check for mistakes.
"log"
"net/http"

"github.com/gorilla/websocket"
)
Comment on lines +1 to +8
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

New Go files under cmd/ appear to require the ASF license header (e.g., cmd/main.go). cmd/e2e-echo-server/main.go is missing this header; please add the standard Apache license block at the top for consistency/compliance.

Copilot uses AI. Check for mistakes.

var upgrader = websocket.Upgrader{
CheckOrigin: func(*http.Request) bool {
return true
},
}

Comment on lines +6 to +15
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

websocket.Upgrader is configured with CheckOrigin always returning true, which disables the origin check entirely. Even for an e2e helper, this pattern is risky if the binary is ever reused outside tests; consider restricting origins (e.g., same-host) or making this explicitly configurable/documented as test-only behavior.

Suggested change
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(*http.Request) bool {
return true
},
}
"net/url"
"github.com/gorilla/websocket"
)
func sameHostOrigin(r *http.Request) bool {
origin := r.Header.Get("Origin")
if origin == "" {
return true
}
u, err := url.Parse(origin)
if err != nil {
return false
}
return u.Host == r.Host
}
var upgrader = websocket.Upgrader{
CheckOrigin: sameHostOrigin,
}

Copilot uses AI. Check for mistakes.
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if websocket.IsWebSocketUpgrade(r) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
defer func() {
_ = conn.Close()
}()

for {
messageType, message, err := conn.ReadMessage()
if err != nil {
return
}
if err := conn.WriteMessage(messageType, message); err != nil {
return
}
}
}

w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
})

log.Fatal(http.ListenAndServe(":8080", nil))
}
37 changes: 37 additions & 0 deletions internal/adc/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,43 @@ func (c *Client) DeleteConfig(ctx context.Context, args Task) error {
return err
}

func (c *Client) Validate(ctx context.Context, task Task) error {
if len(task.Configs) == 0 || task.Resources == nil {
return nil
}

fileIOStart := time.Now()
syncFilePath, cleanup, err := prepareSyncFile(task.Resources)
if err != nil {
pkgmetrics.RecordFileIODuration("prepare_sync_file", "failure", time.Since(fileIOStart).Seconds())
return err
}
pkgmetrics.RecordFileIODuration("prepare_sync_file", adctypes.StatusSuccess, time.Since(fileIOStart).Seconds())
defer cleanup()

args := BuildADCExecuteArgs(syncFilePath, task.Labels, task.ResourceTypes)

var errs types.ADCValidationErrors
for _, config := range task.Configs {
if config.BackendType == "" {
config.BackendType = c.defaultMode
}
if err := c.executor.Validate(ctx, config, args); err != nil {
var validationErr types.ADCValidationError
if errors.As(err, &validationErr) {
errs.Errors = append(errs.Errors, validationErr)
continue
}
return err
}
Comment on lines +193 to +205
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

Client.Validate returns immediately on the first non-ADCValidationError from executor.Validate. This can cause a mixed outcome to fail-open: if one GatewayProxy config has an infra error (e.g., backend unavailable) but another config would return explicit validation errors, the infra error will short-circuit and the admission layer will treat it as an infra error and allow the request. Consider aggregating infra errors across configs and only returning them if no validation errors were found for any config (similar to how per-server validation is aggregated in runHTTPValidate).

Copilot uses AI. Check for mistakes.
}

if len(errs.Errors) > 0 {
return errs
}
return nil
}

func (c *Client) Sync(ctx context.Context) (map[string]types.ADCExecutionErrors, error) {
c.syncMu.Lock()
defer c.syncMu.Unlock()
Expand Down
Loading
Loading