Skip to content

Commit 5fca214

Browse files
committed
fix: adc server test
Signed-off-by: Ashing Zheng <[email protected]>
1 parent 70d37e3 commit 5fca214

File tree

10 files changed

+340
-14
lines changed

10 files changed

+340
-14
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,3 +492,7 @@ release-src:
492492
mv $(RELEASE_SRC).tgz release/$(RELEASE_SRC).tgz
493493
mv $(RELEASE_SRC).tgz.asc release/$(RELEASE_SRC).tgz.asc
494494
mv $(RELEASE_SRC).tgz.sha512 release/$(RELEASE_SRC).tgz.sha512
495+
496+
.PHONY: load-test
497+
load-test:
498+
go test -v ./test/e2e/load-test -test.timeout=$(TEST_TIMEOUT) -v -ginkgo.v

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/Masterminds/sprig/v3 v3.2.3
99
github.com/api7/gopkg v0.2.1-0.20230601092738-0f3730f9b57a
1010
github.com/gavv/httpexpect/v2 v2.16.0
11+
github.com/go-errors/errors v1.4.2
1112
github.com/go-logr/logr v1.4.2
1213
github.com/go-logr/zapr v1.3.0
1314
github.com/google/go-cmp v0.6.0
@@ -86,7 +87,6 @@ require (
8687
github.com/felixge/httpsnoop v1.0.4 // indirect
8788
github.com/fsnotify/fsnotify v1.7.0 // indirect
8889
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
89-
github.com/go-errors/errors v1.4.2 // indirect
9090
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
9191
github.com/go-logr/stdr v1.2.2 // indirect
9292
github.com/go-openapi/jsonpointer v0.21.0 // indirect

internal/controller/status/updater.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import (
3737
pkgmetrics "github.com/apache/apisix-ingress-controller/pkg/metrics"
3838
)
3939

40-
const UpdateChannelBufferSize = 1000
40+
const UpdateChannelBufferSize = 5000
4141

4242
type Update struct {
4343
NamespacedName k8stypes.NamespacedName

internal/provider/adc/adc.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,13 @@ func (d *adcClient) Start(ctx context.Context) error {
356356
return nil
357357
}
358358
if synced {
359+
start := time.Now()
360+
log.Infof("adcClient start sync, %v", start)
359361
if err := d.Sync(ctx); err != nil {
360362
log.Error(err)
361363
}
364+
duration := time.Since(start)
365+
log.Infof("adcClient sync duration: %v, now: %v", duration, time.Now())
362366
}
363367
}
364368
}

test/e2e/framework/apisix_consts.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ import (
2727
)
2828

2929
var (
30-
ProviderType = cmp.Or(os.Getenv("PROVIDER_TYPE"), "apisix-standalone")
31-
IngressVersion = cmp.Or(os.Getenv("INGRESS_VERSION"), "v1")
30+
ProviderType = cmp.Or(os.Getenv("PROVIDER_TYPE"), "apisix-standalone")
31+
ProviderSyncPeriod = cmp.Or(os.Getenv("PROVIDER_SYNC_PERIOD"), "5s")
32+
IngressVersion = cmp.Or(os.Getenv("INGRESS_VERSION"), "v1")
3233
)
3334

3435
var (

test/e2e/framework/manifests/apisix.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,18 @@ spec:
133133
selector:
134134
app.kubernetes.io/name: apisix
135135
type: {{ .ServiceType | default "NodePort" }}
136+
---
137+
apiVersion: v1
138+
kind: Service
139+
metadata:
140+
name: apisix-control-api
141+
labels:
142+
app.kubernetes.io/name: apisix-control-api
143+
spec:
144+
ports:
145+
- port: 9090
146+
name: control
147+
protocol: TCP
148+
targetPort: 9090
149+
selector:
150+
app.kubernetes.io/name: apisix

test/e2e/load-test/e2e_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package load_test
19+
20+
import (
21+
"fmt"
22+
"testing"
23+
24+
. "github.com/onsi/ginkgo/v2"
25+
. "github.com/onsi/gomega"
26+
27+
"github.com/apache/apisix-ingress-controller/test/e2e/framework"
28+
"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
29+
)
30+
31+
// Run long-term-stability tests using Ginkgo runner.
32+
func TestLongTermStability(t *testing.T) {
33+
RegisterFailHandler(Fail)
34+
var f = framework.NewFramework()
35+
_ = f
36+
37+
scaffold.NewDeployer = func(s *scaffold.Scaffold) scaffold.Deployer {
38+
return scaffold.NewAPISIXDeployer(s)
39+
}
40+
41+
_, _ = fmt.Fprintf(GinkgoWriter, "Starting load-test suite\n")
42+
RunSpecs(t, "long-term-stability suite")
43+
}

test/e2e/load-test/spec_subject.go

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
// // Unless required by applicable law or agreed to in writing,
11+
// software distributed under the License is distributed on an
12+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13+
// KIND, either express or implied. See the License for the
14+
// specific language governing permissions and limitations
15+
// under the License.
16+
17+
package load
18+
19+
import (
20+
"bytes"
21+
"context"
22+
"fmt"
23+
"net/http"
24+
"time"
25+
26+
"github.com/api7/gopkg/pkg/log"
27+
. "github.com/onsi/ginkgo/v2"
28+
. "github.com/onsi/gomega"
29+
"go.uber.org/zap"
30+
"k8s.io/apimachinery/pkg/util/wait"
31+
32+
"github.com/apache/apisix-ingress-controller/test/e2e/framework"
33+
"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
34+
)
35+
36+
const gatewayProxyYaml = `
37+
apiVersion: apisix.apache.org/v1alpha1
38+
kind: GatewayProxy
39+
metadata:
40+
name: apisix-proxy-config
41+
spec:
42+
provider:
43+
type: ControlPlane
44+
controlPlane:
45+
service:
46+
name: %s
47+
port: 9180
48+
auth:
49+
type: AdminKey
50+
adminKey:
51+
value: "%s"
52+
`
53+
54+
const ingressClassYaml = `
55+
apiVersion: networking.k8s.io/v1
56+
kind: IngressClass
57+
metadata:
58+
name: apisix
59+
spec:
60+
controller: "apisix.apache.org/apisix-ingress-controller"
61+
parameters:
62+
apiGroup: "apisix.apache.org"
63+
kind: "GatewayProxy"
64+
name: "apisix-proxy-config"
65+
namespace: %s
66+
scope: "Namespace"
67+
`
68+
69+
var _ = Describe("Load Test", func() {
70+
var (
71+
s = scaffold.NewScaffold(&scaffold.Options{
72+
ControllerName: "apisix.apache.org/apisix-ingress-controller",
73+
})
74+
controlAPIClient scaffold.ControlAPIClient
75+
err error
76+
)
77+
78+
BeforeEach(func() {
79+
By("create GatewayProxy")
80+
gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.ProviderType, s.AdminKey())
81+
err = s.CreateResourceFromStringWithNamespace(gatewayProxy, s.Namespace())
82+
Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
83+
time.Sleep(5 * time.Second)
84+
85+
By("create IngressClass")
86+
err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(ingressClassYaml, s.Namespace()), "")
87+
Expect(err).NotTo(HaveOccurred(), "creating IngressClass")
88+
time.Sleep(5 * time.Second)
89+
90+
By("port-forward to control api service")
91+
controlAPIClient, err = s.ControlAPIClient()
92+
Expect(err).NotTo(HaveOccurred(), "create control api client")
93+
})
94+
95+
Context("Load Test 2000 ApisixRoute", func() {
96+
It("test 2000 ApisixRoute", func() {
97+
const total = 2000
98+
99+
const apisixRouteSpec = `
100+
apiVersion: apisix.apache.org/v2
101+
kind: ApisixRoute
102+
metadata:
103+
name: %s
104+
spec:
105+
ingressClassName: apisix
106+
http:
107+
- name: rule0
108+
match:
109+
paths:
110+
- /get
111+
exprs:
112+
- subject:
113+
scope: Header
114+
name: X-Route-Name
115+
op: Equal
116+
value: %s
117+
backends:
118+
- serviceName: httpbin-service-e2e-test
119+
servicePort: 80
120+
`
121+
122+
By(fmt.Sprintf("prepare %d ApisixRoutes", total))
123+
var text = bytes.NewBuffer(nil)
124+
for i := range total {
125+
name := getRouteName(i)
126+
_, err := fmt.Fprintf(text, apisixRouteSpec, name, name)
127+
Expect(err).NotTo(HaveOccurred())
128+
text.WriteString("\n---\n")
129+
}
130+
err := s.CreateResourceFromString(text.String())
131+
Expect(err).NotTo(HaveOccurred(), "creating ApisixRoutes")
132+
log.Infof("created %d ApisixRoutes", total)
133+
134+
var (
135+
results []TestResult
136+
now = time.Now()
137+
)
138+
log.Infof("start calc time, now: %v", now)
139+
By("Test the time required for applying a large number of ApisixRoutes to take effect")
140+
var times int
141+
err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 10*time.Minute, true, func(ctx context.Context) (done bool, err error) {
142+
times++
143+
results, _, err := controlAPIClient.ListServices()
144+
if err != nil {
145+
log.Errorw("failed to ListServices", zap.Error(err))
146+
return false, nil
147+
}
148+
if len(results) != total {
149+
log.Debugw("number of effective services", zap.Int("number", len(results)), zap.Int("times", times))
150+
return false, nil
151+
}
152+
return len(results) == total, nil
153+
})
154+
Expect(err).ShouldNot(HaveOccurred())
155+
costTime := time.Since(now)
156+
log.Infof("end calc time, now: %v, costTime: %v", time.Now(), costTime)
157+
results = append(results, TestResult{
158+
CaseName: fmt.Sprintf("Apply %d ApisixRoutes", total),
159+
CostTime: costTime,
160+
})
161+
162+
By("Test the time required for an ApisixRoute update to take effect")
163+
var apisixRouteSpec0 = `
164+
apiVersion: apisix.apache.org/v2
165+
kind: ApisixRoute
166+
metadata:
167+
name: %s
168+
spec:
169+
ingressClassName: apisix
170+
http:
171+
- name: rule0
172+
match:
173+
paths:
174+
- /headers
175+
exprs:
176+
- subject:
177+
scope: Header
178+
name: X-Route-Name
179+
op: Equal
180+
value: %s
181+
backends:
182+
- serviceName: httpbin-service-e2e-test
183+
servicePort: 80
184+
`
185+
name := getRouteName(10)
186+
err = s.CreateResourceFromString(fmt.Sprintf(apisixRouteSpec0, name, name))
187+
Expect(err).NotTo(HaveOccurred())
188+
now = time.Now()
189+
Eventually(func() int {
190+
return s.NewAPISIXClient().GET("/headers").WithHeader("X-Route-Name", name).Expect().Raw().StatusCode
191+
}).WithTimeout(time.Minute).ProbeEvery(100 * time.Millisecond).Should(Equal(http.StatusOK))
192+
results = append(results, TestResult{
193+
CaseName: fmt.Sprintf("Update a single ApisixRoute base on %d ApisixRoutes", total),
194+
CostTime: time.Since(now),
195+
})
196+
197+
PrintResults(results)
198+
})
199+
})
200+
})
201+
202+
func getRouteName(i int) string {
203+
return fmt.Sprintf("test-route-%04d", i)
204+
}
205+
206+
type TestResult struct {
207+
CaseName string
208+
CostTime time.Duration
209+
}
210+
211+
func (tr TestResult) String() string {
212+
return fmt.Sprintf("%s takes effect for %s", tr.CaseName, tr.CostTime)
213+
}
214+
215+
func PrintResults(results []TestResult) {
216+
fmt.Printf("\n======================TEST RESULT ProviderSyncPeriod %s===============================\n", framework.ProviderSyncPeriod)
217+
fmt.Printf("%-70s", "Test Case")
218+
fmt.Printf("%-70s\n", "Time Required")
219+
fmt.Printf("%-70s\n", "--------------------------------------------------------------------------------------")
220+
for _, result := range results {
221+
fmt.Printf("%-70s", result.CaseName)
222+
fmt.Printf("%-70s\n", result.CostTime)
223+
}
224+
fmt.Println("======================================================================================")
225+
fmt.Println()
226+
}

test/e2e/scaffold/apisix_deployer.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -257,28 +257,20 @@ func (s *APISIXDeployer) ScaleDataplane(replicas int) {
257257
}
258258

259259
func (s *APISIXDeployer) DeployIngress() {
260-
syncPeriod := 1 * time.Hour
261-
if s.opts.SyncPeriod != 0 {
262-
syncPeriod = s.opts.SyncPeriod
263-
}
264260
s.Framework.DeployIngress(framework.IngressDeployOpts{
265261
ControllerName: s.opts.ControllerName,
266262
ProviderType: framework.ProviderType,
267-
ProviderSyncPeriod: syncPeriod,
263+
ProviderSyncPeriod: getProviderSyncPeriod(),
268264
Namespace: s.namespace,
269265
Replicas: 1,
270266
})
271267
}
272268

273269
func (s *APISIXDeployer) ScaleIngress(replicas int) {
274-
syncPeriod := 1 * time.Hour
275-
if s.opts.SyncPeriod != 0 {
276-
syncPeriod = s.opts.SyncPeriod
277-
}
278270
s.Framework.DeployIngress(framework.IngressDeployOpts{
279271
ControllerName: s.opts.ControllerName,
280272
ProviderType: framework.ProviderType,
281-
ProviderSyncPeriod: syncPeriod,
273+
ProviderSyncPeriod: getProviderSyncPeriod(),
282274
Namespace: s.namespace,
283275
Replicas: replicas,
284276
})
@@ -429,3 +421,11 @@ func (s *APISIXDeployer) DefaultDataplaneResource() DataplaneResource {
429421
func (s *APISIXDeployer) Name() string {
430422
return "apisix"
431423
}
424+
425+
func getProviderSyncPeriod() time.Duration {
426+
providerSyncPeriod, err := time.ParseDuration(framework.ProviderSyncPeriod)
427+
if err != nil {
428+
providerSyncPeriod = 5 * time.Second
429+
}
430+
return providerSyncPeriod
431+
}

0 commit comments

Comments
 (0)