This is an automated email from the ASF dual-hosted git repository.

ronething pushed a commit to branch test/load-test
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git

commit 55959246ffa9cd6071cf3b16b64152c41926748c
Author: ashing <axing...@gmail.com>
AuthorDate: Wed Jul 16 19:42:48 2025 +0800

    test: load test
    
    Signed-off-by: ashing <axing...@gmail.com>
---
 Makefile                                  |   4 +
 go.mod                                    |  29 ++--
 go.sum                                    |  57 ++++----
 internal/controller/status/updater.go     |   3 +-
 internal/manager/run.go                   |   1 +
 internal/provider/adc/adc.go              |  28 +++-
 pkg/metrics/metrics.go                    |  89 ++++++++++++
 test/e2e/framework/apisix_consts.go       |   3 +-
 test/e2e/framework/manifests/apisix.yaml  |  18 +++
 test/e2e/framework/manifests/ingress.yaml |  19 ++-
 test/e2e/load-test/e2e_test.go            |  43 ++++++
 test/e2e/load-test/spec_subject.go        | 226 ++++++++++++++++++++++++++++++
 test/e2e/scaffold/apisix_deployer.go      |  75 +++++-----
 test/e2e/scaffold/scaffold.go             |  33 +++++
 14 files changed, 541 insertions(+), 87 deletions(-)

diff --git a/Makefile b/Makefile
index 15b7ecf1..bf23f2bd 100644
--- a/Makefile
+++ b/Makefile
@@ -135,6 +135,10 @@ conformance-test:
 conformance-test-standalone:
        go test -v ./test/conformance/apisix -tags=conformance -timeout 60m
 
+.PHONY: load-test
+load-test:
+       go test -v ./test/e2e/load-test -test.timeout=$(TEST_TIMEOUT) -v 
-ginkgo.v
+
 .PHONY: lint
 lint: sort-import golangci-lint ## Run golangci-lint linter
        $(GOLANGCI_LINT) run
diff --git a/go.mod b/go.mod
index 068e83a9..de4b946c 100644
--- a/go.mod
+++ b/go.mod
@@ -17,9 +17,10 @@ require (
        github.com/onsi/ginkgo/v2 v2.20.0
        github.com/onsi/gomega v1.34.1
        github.com/pkg/errors v0.9.1
+       github.com/prometheus/client_golang v1.19.1
        github.com/samber/lo v1.47.0
        github.com/spf13/cobra v1.8.1
-       github.com/stretchr/testify v1.9.0
+       github.com/stretchr/testify v1.10.0
        go.uber.org/zap v1.27.0
        golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
        gopkg.in/yaml.v3 v3.0.1
@@ -108,10 +109,10 @@ require (
        github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // 
indirect
        github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 
indirect
        github.com/pquerna/otp v1.2.0 // indirect
-       github.com/prometheus/client_golang v1.19.1 // indirect
        github.com/prometheus/client_model v0.6.1 // indirect
        github.com/prometheus/common v0.55.0 // indirect
        github.com/prometheus/procfs v0.15.1 // indirect
+       github.com/rogpeppe/go-internal v1.14.1 // indirect
        github.com/russross/blackfriday/v2 v2.1.0 // indirect
        github.com/sanity-io/litter v1.5.5 // indirect
        github.com/sergi/go-diff v1.3.1 // indirect
@@ -129,31 +130,33 @@ require (
        github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
        github.com/yudai/gojsondiff v1.0.0 // indirect
        github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
+       go.opentelemetry.io/auto/sdk v1.1.0 // indirect
        go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 
// indirect
-       go.opentelemetry.io/otel v1.28.0 // indirect
+       go.opentelemetry.io/otel v1.34.0 // indirect
        go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
        go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 
// indirect
-       go.opentelemetry.io/otel/metric v1.28.0 // indirect
-       go.opentelemetry.io/otel/sdk v1.28.0 // indirect
-       go.opentelemetry.io/otel/trace v1.28.0 // indirect
+       go.opentelemetry.io/otel/metric v1.34.0 // indirect
+       go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+       go.opentelemetry.io/otel/trace v1.34.0 // indirect
        go.opentelemetry.io/proto/otlp v1.3.1 // indirect
        go.uber.org/multierr v1.11.0 // indirect
        golang.org/x/arch v0.6.0 // indirect
        golang.org/x/crypto v0.36.0 // indirect
-       golang.org/x/mod v0.20.0 // indirect
+       golang.org/x/mod v0.21.0 // indirect
        golang.org/x/net v0.38.0 // indirect
-       golang.org/x/oauth2 v0.21.0 // indirect
+       golang.org/x/oauth2 v0.25.0 // indirect
        golang.org/x/sync v0.12.0 // indirect
        golang.org/x/sys v0.31.0 // indirect
        golang.org/x/term v0.30.0 // indirect
        golang.org/x/text v0.23.0 // indirect
        golang.org/x/time v0.5.0 // indirect
-       golang.org/x/tools v0.24.0 // indirect
+       golang.org/x/tools v0.26.0 // indirect
        gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
-       google.golang.org/genproto/googleapis/api 
v0.0.0-20240604185151-ef581f913117 // indirect
-       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20240701130421-f6361c86f094 // indirect
-       google.golang.org/grpc v1.66.2 // indirect
-       google.golang.org/protobuf v1.34.2 // indirect
+       google.golang.org/genproto/googleapis/api 
v0.0.0-20250106144421-5f5ef82da422 // indirect
+       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20250303144028-a0af3efb3deb // indirect
+       google.golang.org/grpc v1.71.0 // indirect
+       google.golang.org/protobuf v1.36.6 // indirect
+       gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
        gopkg.in/fsnotify.v1 v1.4.7 // indirect
        gopkg.in/inf.v0 v0.9.1 // indirect
        gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
diff --git a/go.sum b/go.sum
index 2be83ffa..f4b40c36 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,4 @@
+cloud.google.com/go/compute/metadata v0.6.0 
h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
 github.com/BurntSushi/toml v0.3.1/go.mod 
h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/toml v1.3.2/go.mod 
h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 github.com/Masterminds/goutils v1.1.1 
h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
@@ -255,8 +256,8 @@ github.com/prometheus/procfs v0.15.1 
h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
 github.com/prometheus/procfs v0.15.1/go.mod 
h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/rogpeppe/go-internal v1.6.1/go.mod 
h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.8.0/go.mod 
h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
-github.com/rogpeppe/go-internal v1.12.0 
h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod 
h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.14.1 
h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod 
h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0 
h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -298,8 +299,8 @@ github.com/stretchr/testify v1.7.0/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod 
h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.4/go.mod 
h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 
h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod 
h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
 github.com/ugorji/go v1.2.7/go.mod 
h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
 github.com/ugorji/go/codec v1.2.7/go.mod 
h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
@@ -331,20 +332,24 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod 
h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
 github.com/yuin/goldmark v1.1.27/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.4.13/go.mod 
h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.opentelemetry.io/auto/sdk v1.1.0 
h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod 
h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 
h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod 
h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
-go.opentelemetry.io/otel v1.28.0 
h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
-go.opentelemetry.io/otel v1.28.0/go.mod 
h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
+go.opentelemetry.io/otel v1.34.0 
h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod 
h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 
h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod 
h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 
h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod 
h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
-go.opentelemetry.io/otel/metric v1.28.0 
h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
-go.opentelemetry.io/otel/metric v1.28.0/go.mod 
h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
-go.opentelemetry.io/otel/sdk v1.28.0 
h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
-go.opentelemetry.io/otel/sdk v1.28.0/go.mod 
h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
-go.opentelemetry.io/otel/trace v1.28.0 
h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
-go.opentelemetry.io/otel/trace v1.28.0/go.mod 
h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
+go.opentelemetry.io/otel/metric v1.34.0 
h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod 
h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 
h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod 
h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.34.0 
h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
+go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod 
h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
+go.opentelemetry.io/otel/trace v1.34.0 
h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod 
h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 go.opentelemetry.io/proto/otlp v1.3.1 
h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
 go.opentelemetry.io/proto/otlp v1.3.1/go.mod 
h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
 go.uber.org/atomic v1.7.0/go.mod 
h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -374,8 +379,8 @@ golang.org/x/mod v0.2.0/go.mod 
h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod 
h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
-golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
+golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod 
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod 
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -389,8 +394,8 @@ golang.org/x/net v0.1.0/go.mod 
h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
 golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
-golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
-golang.org/x/oauth2 v0.21.0/go.mod 
h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
+golang.org/x/oauth2 v0.25.0/go.mod 
h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -440,24 +445,24 @@ golang.org/x/tools 
v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod 
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod 
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod 
h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
-golang.org/x/tools v0.24.0/go.mod 
h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
+golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
+golang.org/x/tools v0.26.0/go.mod 
h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gomodules.xyz/jsonpatch/v2 v2.4.0 
h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
 gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod 
h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
-google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 
h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU=
-google.golang.org/genproto/googleapis/api 
v0.0.0-20240604185151-ef581f913117/go.mod 
h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 
h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
-google.golang.org/genproto/googleapis/rpc 
v0.0.0-20240701130421-f6361c86f094/go.mod 
h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
-google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
-google.golang.org/grpc v1.66.2/go.mod 
h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
+google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 
h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
+google.golang.org/genproto/googleapis/api 
v0.0.0-20250106144421-5f5ef82da422/go.mod 
h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb 
h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
+google.golang.org/genproto/googleapis/rpc 
v0.0.0-20250303144028-a0af3efb3deb/go.mod 
h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
+google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
+google.golang.org/grpc v1.71.0/go.mod 
h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod 
h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.28.0/go.mod 
h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.34.2 
h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
-google.golang.org/protobuf v1.34.2/go.mod 
h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+google.golang.org/protobuf v1.36.6 
h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod 
h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/controller/status/updater.go 
b/internal/controller/status/updater.go
index 47ac41ee..00b03e01 100644
--- a/internal/controller/status/updater.go
+++ b/internal/controller/status/updater.go
@@ -28,7 +28,7 @@ import (
        "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-const UpdateChannelBufferSize = 1000
+const UpdateChannelBufferSize = 5000
 
 type Update struct {
        NamespacedName types.NamespacedName
@@ -136,5 +136,6 @@ type UpdateWriter struct {
 
 func (u *UpdateWriter) Update(update Update) {
        u.wg.Wait()
+       // len(u.updateChannel)
        u.updateChannel <- update
 }
diff --git a/internal/manager/run.go b/internal/manager/run.go
index b48746c1..a27b2732 100644
--- a/internal/manager/run.go
+++ b/internal/manager/run.go
@@ -42,6 +42,7 @@ import (
        "github.com/apache/apisix-ingress-controller/internal/controller/config"
        "github.com/apache/apisix-ingress-controller/internal/controller/status"
        "github.com/apache/apisix-ingress-controller/internal/provider/adc"
+       _ "github.com/apache/apisix-ingress-controller/pkg/metrics"
 )
 
 var (
diff --git a/internal/provider/adc/adc.go b/internal/provider/adc/adc.go
index baed0d7f..491444a2 100644
--- a/internal/provider/adc/adc.go
+++ b/internal/provider/adc/adc.go
@@ -42,6 +42,7 @@ import (
        
"github.com/apache/apisix-ingress-controller/internal/provider/adc/translator"
        "github.com/apache/apisix-ingress-controller/internal/types"
        "github.com/apache/apisix-ingress-controller/internal/utils"
+       pkgmetrics "github.com/apache/apisix-ingress-controller/pkg/metrics"
 )
 
 type adcConfig struct {
@@ -311,9 +312,13 @@ func (d *adcClient) Start(ctx context.Context) error {
        for {
                select {
                case <-ticker.C:
+                       start := time.Now()
+                       log.Infof("adcClient start sync, %v", start)
                        if err := d.Sync(ctx); err != nil {
                                log.Error(err)
                        }
+                       duration := time.Since(start)
+                       log.Infof("adcClient sync duration: %v, now: %v", 
duration, time.Now())
                case <-ctx.Done():
                        return nil
                }
@@ -390,14 +395,35 @@ func (d *adcClient) sync(ctx context.Context, task Task) 
error {
 
        var errs types.ADCExecutionErrors
        for _, config := range task.configs {
-               if err := d.executor.Execute(ctx, d.BackendMode, config, args); 
err != nil {
+               // Record sync duration for each config
+               startTime := time.Now()
+               resourceType := strings.Join(task.ResourceTypes, ",")
+               if resourceType == "" {
+                       resourceType = "all"
+               }
+
+               err := d.executor.Execute(ctx, d.BackendMode, config, args)
+               duration := time.Since(startTime).Seconds()
+               log.Infof("synced %s in %f seconds, service list length: %d", 
config.Name, duration, len(task.Resources.Services))
+
+               status := "success"
+               if err != nil {
+                       status = "failure"
                        log.Errorw("failed to execute adc command", 
zap.Error(err), zap.Any("config", config))
+
                        var execErr types.ADCExecutionError
                        if errors.As(err, &execErr) {
                                errs.Errors = append(errs.Errors, execErr)
+                               pkgmetrics.RecordExecutionError(config.Name, 
execErr.Name)
+                       } else {
+                               pkgmetrics.RecordExecutionError(config.Name, 
"unknown")
                        }
                }
+
+               // Record metrics
+               pkgmetrics.RecordSyncDuration(config.Name, resourceType, 
status, duration)
        }
+
        if len(errs.Errors) > 0 {
                return errs
        }
diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go
new file mode 100644
index 00000000..db9decdc
--- /dev/null
+++ b/pkg/metrics/metrics.go
@@ -0,0 +1,89 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package metrics
+
+import (
+       "github.com/prometheus/client_golang/prometheus"
+       "sigs.k8s.io/controller-runtime/pkg/metrics"
+)
+
+var (
+       // ADC sync operation duration histogram
+       ADCSyncDuration = prometheus.NewHistogramVec(
+               prometheus.HistogramOpts{
+                       Name:    "apisix_ingress_adc_sync_duration_seconds",
+                       Help:    "Time spent on ADC sync operations",
+                       Buckets: prometheus.DefBuckets,
+               },
+               []string{"config_name", "resource_type", "status"},
+       )
+
+       // ADC sync operation counter
+       ADCSyncTotal = prometheus.NewCounterVec(
+               prometheus.CounterOpts{
+                       Name: "apisix_ingress_adc_sync_total",
+                       Help: "Total number of ADC sync operations",
+               },
+               []string{"config_name", "resource_type", "status"},
+       )
+
+       // ADC execution errors counter
+       ADCExecutionErrors = prometheus.NewCounterVec(
+               prometheus.CounterOpts{
+                       Name: "apisix_ingress_adc_execution_errors_total",
+                       Help: "Total number of ADC execution errors",
+               },
+               []string{"config_name", "error_type"},
+       )
+
+       // Resource sync gauge
+       ResourceSyncGauge = prometheus.NewGaugeVec(
+               prometheus.GaugeOpts{
+                       Name: "apisix_ingress_resources_synced",
+                       Help: "Number of resources currently synced",
+               },
+               []string{"config_name", "resource_type"},
+       )
+)
+
+// init registers all metrics with the global prometheus registry
+func init() {
+       // Register metrics with controller-runtime's metrics registry
+       metrics.Registry.MustRegister(
+               ADCSyncDuration,
+               ADCSyncTotal,
+               ADCExecutionErrors,
+               ResourceSyncGauge,
+       )
+}
+
+// RecordSyncDuration records the duration of an ADC sync operation
+func RecordSyncDuration(configName, resourceType, status string, duration 
float64) {
+       ADCSyncDuration.WithLabelValues(configName, resourceType, 
status).Observe(duration)
+       ADCSyncTotal.WithLabelValues(configName, resourceType, status).Inc()
+}
+
+// RecordExecutionError records an ADC execution error
+func RecordExecutionError(configName, errorType string) {
+       ADCExecutionErrors.WithLabelValues(configName, errorType).Inc()
+}
+
+// UpdateResourceGauge updates the resource sync gauge
+func UpdateResourceGauge(configName, resourceType string, count float64) {
+       ResourceSyncGauge.WithLabelValues(configName, resourceType).Set(count)
+}
diff --git a/test/e2e/framework/apisix_consts.go 
b/test/e2e/framework/apisix_consts.go
index 42214982..cf145b8c 100644
--- a/test/e2e/framework/apisix_consts.go
+++ b/test/e2e/framework/apisix_consts.go
@@ -27,7 +27,8 @@ import (
 )
 
 var (
-       ProviderType = cmp.Or(os.Getenv("PROVIDER_TYPE"), "apisix")
+       ProviderType       = cmp.Or(os.Getenv("PROVIDER_TYPE"), 
"apisix-standalone")
+       ProviderSyncPeriod = cmp.Or(os.Getenv("PROVIDER_SYNC_PERIOD"), "5s")
 )
 
 var (
diff --git a/test/e2e/framework/manifests/apisix.yaml 
b/test/e2e/framework/manifests/apisix.yaml
index affa4bfb..568bde3d 100644
--- a/test/e2e/framework/manifests/apisix.yaml
+++ b/test/e2e/framework/manifests/apisix.yaml
@@ -90,6 +90,9 @@ spec:
             - name: admin
               containerPort: 9180
               protocol: TCP
+            - name: control
+              containerPort: 9090
+              protocol: TCP
           volumeMounts:
             - name: config-writable
               mountPath: /usr/local/apisix/conf
@@ -123,3 +126,18 @@ spec:
   selector:
     app.kubernetes.io/name: apisix 
   type: {{ .ServiceType | default "NodePort" }}
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: apisix-control-api
+  labels:
+    app.kubernetes.io/name: apisix-control-api
+spec:
+  ports:
+    - port: 9090
+      name: control
+      protocol: TCP
+      targetPort: 9090
+  selector:
+    app.kubernetes.io/name: apisix
diff --git a/test/e2e/framework/manifests/ingress.yaml 
b/test/e2e/framework/manifests/ingress.yaml
index c4bb1014..119214a8 100644
--- a/test/e2e/framework/manifests/ingress.yaml
+++ b/test/e2e/framework/manifests/ingress.yaml
@@ -322,7 +322,7 @@ metadata:
   name: ingress-config
 data:
   config.yaml: |
-    log_level: "debug"
+    log_level: "info"
 
     controller_name: {{ .ControllerName | default 
"apisix.apache.org/apisix-ingress-controller" }}
 
@@ -347,10 +347,10 @@ metadata:
   namespace: {{ .Namespace }}
 spec:
   ports:
-  - name: https
-    port: 8443
+  - name: metrics
+    port: 8080
     protocol: TCP
-    targetPort: 8443
+    targetPort: 8080
   selector:
     control-plane: controller-manager
 ---
@@ -400,19 +400,16 @@ spec:
           initialDelaySeconds: 15
           periodSeconds: 20
         name: manager
+        ports:
+        - name: metrics
+          containerPort: 8080
+          protocol: TCP
         readinessProbe:
           httpGet:
             path: /readyz
             port: 8081
           initialDelaySeconds: 5
           periodSeconds: 10
-        resources:
-          limits:
-            cpu: 500m
-            memory: 128Mi
-          requests:
-            cpu: 10m
-            memory: 64Mi
         securityContext:
           allowPrivilegeEscalation: false
           capabilities:
diff --git a/test/e2e/load-test/e2e_test.go b/test/e2e/load-test/e2e_test.go
new file mode 100644
index 00000000..dcaef50c
--- /dev/null
+++ b/test/e2e/load-test/e2e_test.go
@@ -0,0 +1,43 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package load_test
+
+import (
+       "fmt"
+       "testing"
+
+       . "github.com/onsi/ginkgo/v2"
+       . "github.com/onsi/gomega"
+
+       "github.com/apache/apisix-ingress-controller/test/e2e/framework"
+       "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+// Run long-term-stability tests using Ginkgo runner.
+func TestLongTermStability(t *testing.T) {
+       RegisterFailHandler(Fail)
+       var f = framework.NewFramework()
+       _ = f
+
+       scaffold.NewDeployer = func(s *scaffold.Scaffold) scaffold.Deployer {
+               return scaffold.NewAPISIXDeployer(s)
+       }
+
+       _, _ = fmt.Fprintf(GinkgoWriter, "Starting load-test suite\n")
+       RunSpecs(t, "long-term-stability suite")
+}
diff --git a/test/e2e/load-test/spec_subject.go 
b/test/e2e/load-test/spec_subject.go
new file mode 100644
index 00000000..7595c58a
--- /dev/null
+++ b/test/e2e/load-test/spec_subject.go
@@ -0,0 +1,226 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+// // Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package load
+
+import (
+       "bytes"
+       "context"
+       "fmt"
+       "net/http"
+       "time"
+
+       "github.com/api7/gopkg/pkg/log"
+       . "github.com/onsi/ginkgo/v2"
+       . "github.com/onsi/gomega"
+       "go.uber.org/zap"
+       "k8s.io/apimachinery/pkg/util/wait"
+
+       "github.com/apache/apisix-ingress-controller/test/e2e/framework"
+       "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+const gatewayProxyYaml = `
+apiVersion: apisix.apache.org/v1alpha1
+kind: GatewayProxy
+metadata:
+  name: apisix-proxy-config
+spec:
+  provider:
+    type: ControlPlane
+    controlPlane:
+      service:
+        name: %s
+        port: 9180
+      auth:
+        type: AdminKey
+        adminKey:
+          value: "%s"
+`
+
+const ingressClassYaml = `
+apiVersion: networking.k8s.io/v1
+kind: IngressClass
+metadata:
+  name: apisix
+spec:
+  controller: "apisix.apache.org/apisix-ingress-controller"
+  parameters:
+    apiGroup: "apisix.apache.org"
+    kind: "GatewayProxy"
+    name: "apisix-proxy-config"
+    namespace: %s
+    scope: "Namespace"
+`
+
+var _ = Describe("Load Test", func() {
+       var (
+               s = scaffold.NewScaffold(&scaffold.Options{
+                       ControllerName: 
"apisix.apache.org/apisix-ingress-controller",
+               })
+               controlAPIClient scaffold.ControlAPIClient
+               err              error
+       )
+
+       BeforeEach(func() {
+               By("create GatewayProxy")
+               gatewayProxy := fmt.Sprintf(gatewayProxyYaml, 
framework.ProviderType, s.AdminKey())
+               err = s.CreateResourceFromStringWithNamespace(gatewayProxy, 
s.Namespace())
+               Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
+               time.Sleep(5 * time.Second)
+
+               By("create IngressClass")
+               err = 
s.CreateResourceFromStringWithNamespace(fmt.Sprintf(ingressClassYaml, 
s.Namespace()), "")
+               Expect(err).NotTo(HaveOccurred(), "creating IngressClass")
+               time.Sleep(5 * time.Second)
+
+               By("port-forward to control api service")
+               controlAPIClient, err = s.ControlAPIClient()
+               Expect(err).NotTo(HaveOccurred(), "create control api client")
+       })
+
+       Context("Load Test 2000 ApisixRoute", func() {
+               It("test 2000 ApisixRoute", func() {
+                       const total = 2000
+
+                       const apisixRouteSpec = `
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+  name: %s
+spec:
+  ingressClassName: apisix
+  http:
+  - name: rule0
+    match:
+      paths:
+      - /get
+      exprs:
+      - subject:
+          scope: Header
+          name: X-Route-Name
+        op: Equal
+        value: %s
+    backends:
+    - serviceName: httpbin-service-e2e-test
+      servicePort: 80
+`
+
+                       By(fmt.Sprintf("prepare %d ApisixRoutes", total))
+                       var text = bytes.NewBuffer(nil)
+                       for i := range total {
+                               name := getRouteName(i)
+                               _, err := fmt.Fprintf(text, apisixRouteSpec, 
name, name)
+                               Expect(err).NotTo(HaveOccurred())
+                               text.WriteString("\n---\n")
+                       }
+                       err := s.CreateResourceFromString(text.String())
+                       Expect(err).NotTo(HaveOccurred(), "creating 
ApisixRoutes")
+                       log.Infof("created %d ApisixRoutes", total)
+
+                       var (
+                               results []TestResult
+                               now     = time.Now()
+                       )
+                       log.Infof("start calc time, now: %v", now)
+                       By("Test the time required for applying a large number 
of ApisixRoutes to take effect")
+                       var times int
+                       err = 
wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 
10*time.Minute, true, func(ctx context.Context) (done bool, err error) {
+                               times++
+                               results, _, err := 
controlAPIClient.ListServices()
+                               if err != nil {
+                                       log.Errorw("failed to ListServices", 
zap.Error(err))
+                                       return false, nil
+                               }
+                               if len(results) != total {
+                                       log.Debugw("number of effective 
services", zap.Int("number", len(results)), zap.Int("times", times))
+                                       return false, nil
+                               }
+                               return len(results) == total, nil
+                       })
+                       Expect(err).ShouldNot(HaveOccurred())
+                       costTime := time.Since(now)
+                       log.Infof("end calc time, now: %v, costTime: %v", 
time.Now(), costTime)
+                       results = append(results, TestResult{
+                               CaseName: fmt.Sprintf("Apply %d ApisixRoutes", 
total),
+                               CostTime: costTime,
+                       })
+
+                       By("Test the time required for an ApisixRoute update to 
take effect")
+                       var apisixRouteSpec0 = `
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+  name: %s
+spec:
+  ingressClassName: apisix
+  http:
+  - name: rule0
+    match:
+      paths:
+      - /headers
+      exprs:
+      - subject:
+          scope: Header
+          name: X-Route-Name
+        op: Equal
+        value: %s
+    backends:
+    - serviceName: httpbin-service-e2e-test
+      servicePort: 80
+`
+                       name := getRouteName(10)
+                       err = 
s.CreateResourceFromString(fmt.Sprintf(apisixRouteSpec0, name, name))
+                       Expect(err).NotTo(HaveOccurred())
+                       now = time.Now()
+                       Eventually(func() int {
+                               return 
s.NewAPISIXClient().GET("/headers").WithHeader("X-Route-Name", 
name).Expect().Raw().StatusCode
+                       }).WithTimeout(time.Minute).ProbeEvery(100 * 
time.Millisecond).Should(Equal(http.StatusOK))
+                       results = append(results, TestResult{
+                               CaseName: fmt.Sprintf("Update a single 
ApisixRoute base on %d ApisixRoutes", total),
+                               CostTime: time.Since(now),
+                       })
+
+                       PrintResults(results)
+               })
+       })
+})
+
+func getRouteName(i int) string {
+       return fmt.Sprintf("test-route-%04d", i)
+}
+
+type TestResult struct {
+       CaseName string
+       CostTime time.Duration
+}
+
+func (tr TestResult) String() string {
+       return fmt.Sprintf("%s takes effect for %s", tr.CaseName, tr.CostTime)
+}
+
+func PrintResults(results []TestResult) {
+       fmt.Printf("\n======================TEST RESULT ProviderSyncPeriod 
%s===============================\n", framework.ProviderSyncPeriod)
+       fmt.Printf("%-70s", "Test Case")
+       fmt.Printf("%-70s\n", "Time Required")
+       fmt.Printf("%-70s\n", 
"--------------------------------------------------------------------------------------")
+       for _, result := range results {
+               fmt.Printf("%-70s", result.CaseName)
+               fmt.Printf("%-70s\n", result.CostTime)
+       }
+       
fmt.Println("======================================================================================")
+       fmt.Println()
+}
diff --git a/test/e2e/scaffold/apisix_deployer.go 
b/test/e2e/scaffold/apisix_deployer.go
index b6fe7feb..e0ef2d66 100644
--- a/test/e2e/scaffold/apisix_deployer.go
+++ b/test/e2e/scaffold/apisix_deployer.go
@@ -24,8 +24,7 @@ import (
        "time"
 
        "github.com/gruntwork-io/terratest/modules/k8s"
-       . "github.com/onsi/ginkgo/v2" //nolint:staticcheck
-       . "github.com/onsi/gomega"    //nolint:staticcheck
+       . "github.com/onsi/gomega" //nolint:staticcheck
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/utils/ptr"
@@ -111,36 +110,36 @@ func (s *APISIXDeployer) BeforeEach() {
 }
 
 func (s *APISIXDeployer) AfterEach() {
-       if CurrentSpecReport().Failed() {
-               if os.Getenv("TEST_ENV") == "CI" {
-                       _, _ = fmt.Fprintln(GinkgoWriter, "Dumping namespace 
contents")
-                       _, _ = k8s.RunKubectlAndGetOutputE(GinkgoT(), 
s.kubectlOptions, "get", "deploy,sts,svc,pods,gatewayproxy")
-                       _, _ = k8s.RunKubectlAndGetOutputE(GinkgoT(), 
s.kubectlOptions, "describe", "pods")
-               }
-
-               output := s.GetDeploymentLogs("apisix-ingress-controller")
-               if output != "" {
-                       _, _ = fmt.Fprintln(GinkgoWriter, output)
-               }
-       }
-
-       // Delete all additional gateways
-       for identifier := range s.additionalGateways {
-               err := s.CleanupAdditionalGateway(identifier)
-               Expect(err).NotTo(HaveOccurred(), "cleaning up additional 
gateway")
-       }
-
-       // if the test case is successful, just delete namespace
-       err := k8s.DeleteNamespaceE(s.t, s.kubectlOptions, s.namespace)
-       Expect(err).NotTo(HaveOccurred(), "deleting namespace "+s.namespace)
-
-       for i := len(s.finalizers) - 1; i >= 0; i-- {
-               runWithRecover(s.finalizers[i])
-       }
-
-       // Wait for a while to prevent the worker node being overwhelming
-       // (new cases will be run).
-       time.Sleep(3 * time.Second)
+       return
+       // if CurrentSpecReport().Failed() {
+       //      if os.Getenv("TEST_ENV") == "CI" {
+       //              _, _ = fmt.Fprintln(GinkgoWriter, "Dumping namespace 
contents")
+       //              _, _ = k8s.RunKubectlAndGetOutputE(GinkgoT(), 
s.kubectlOptions, "get", "deploy,sts,svc,pods,gatewayproxy")
+       //              _, _ = k8s.RunKubectlAndGetOutputE(GinkgoT(), 
s.kubectlOptions, "describe", "pods")
+       //      }
+
+       //      output := s.GetDeploymentLogs("apisix-ingress-controller")
+       //      if output != "" {
+       //              _, _ = fmt.Fprintln(GinkgoWriter, output)
+       //      }
+       // }
+       // // Delete all additional gateways
+       // for identifier := range s.additionalGateways {
+       //      err := s.CleanupAdditionalGateway(identifier)
+       //      Expect(err).NotTo(HaveOccurred(), "cleaning up additional 
gateway")
+       // }
+
+       // // if the test case is successful, just delete namespace
+       // err := k8s.DeleteNamespaceE(s.t, s.kubectlOptions, s.namespace)
+       // Expect(err).NotTo(HaveOccurred(), "deleting namespace "+s.namespace)
+
+       // for i := len(s.finalizers) - 1; i >= 0; i-- {
+       //      runWithRecover(s.finalizers[i])
+       // }
+
+       // // Wait for a while to prevent the worker node being overwhelming
+       // // (new cases will be run).
+       // time.Sleep(3 * time.Second)
 }
 
 func (s *APISIXDeployer) DeployDataplane(deployOpts DeployDataplaneOptions) {
@@ -260,7 +259,7 @@ func (s *APISIXDeployer) DeployIngress() {
        s.Framework.DeployIngress(framework.IngressDeployOpts{
                ControllerName:     s.opts.ControllerName,
                ProviderType:       framework.ProviderType,
-               ProviderSyncPeriod: 200 * time.Millisecond,
+               ProviderSyncPeriod: getProviderSyncPeriod(),
                Namespace:          s.namespace,
                Replicas:           1,
        })
@@ -270,12 +269,20 @@ func (s *APISIXDeployer) ScaleIngress(replicas int) {
        s.Framework.DeployIngress(framework.IngressDeployOpts{
                ControllerName:     s.opts.ControllerName,
                ProviderType:       framework.ProviderType,
-               ProviderSyncPeriod: 200 * time.Millisecond,
+               ProviderSyncPeriod: getProviderSyncPeriod(),
                Namespace:          s.namespace,
                Replicas:           replicas,
        })
 }
 
+func getProviderSyncPeriod() time.Duration {
+       providerSyncPeriod, err := 
time.ParseDuration(framework.ProviderSyncPeriod)
+       if err != nil {
+               providerSyncPeriod = 5 * time.Second
+       }
+       return providerSyncPeriod
+}
+
 // getEnvOrDefault returns environment variable value or default
 func getEnvOrDefault(key, defaultValue string) string {
        if value := os.Getenv(key); value != "" {
diff --git a/test/e2e/scaffold/scaffold.go b/test/e2e/scaffold/scaffold.go
index fe533a82..36e02588 100644
--- a/test/e2e/scaffold/scaffold.go
+++ b/test/e2e/scaffold/scaffold.go
@@ -20,6 +20,7 @@ package scaffold
 import (
        "context"
        "crypto/tls"
+       "encoding/json"
        "fmt"
        "net/http"
        "net/url"
@@ -31,6 +32,7 @@ import (
        "github.com/gruntwork-io/terratest/modules/testing"
        . "github.com/onsi/ginkgo/v2" //nolint:staticcheck
        . "github.com/onsi/gomega"    //nolint:staticcheck
+       "github.com/pkg/errors"
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -443,3 +445,34 @@ func NewClient(scheme, host string) *httpexpect.Expect {
                ),
        })
 }
+
+func (s *Scaffold) ControlAPIClient() (ControlAPIClient, error) {
+       tunnel := k8s.NewTunnel(s.kubectlOptions, k8s.ResourceTypeService, 
"apisix-control-api", 9090, 9090)
+       if err := tunnel.ForwardPortE(s.t); err != nil {
+               return nil, err
+       }
+       s.addFinalizers(tunnel.Close)
+
+       return &controlAPI{
+               client: NewClient("http", tunnel.Endpoint()),
+       }, nil
+}
+
+type ControlAPIClient interface {
+       ListServices() ([]any, int64, error)
+}
+
+type controlAPI struct {
+       client *httpexpect.Expect
+}
+
+func (c *controlAPI) ListServices() (result []any, total int64, err error) {
+       resp := c.client.Request(http.MethodGet, "/v1/services").Expect()
+       if resp.Raw().StatusCode != http.StatusOK {
+               return nil, 0, fmt.Errorf("unexpected status code: %v, message: 
%s", resp.Raw().StatusCode, resp.Body().Raw())
+       }
+       if err = json.Unmarshal([]byte(resp.Body().Raw()), &result); err != nil 
{
+               return nil, 0, errors.Wrap(err, "failed to unmarshal response 
body")
+       }
+       return result, int64(len(result)), err
+}


Reply via email to