Skip to content

Commit 10ce4bc

Browse files
committed
large scale HTTPRoute
1 parent 8b5f0ff commit 10ce4bc

File tree

4 files changed

+172
-35
lines changed

4 files changed

+172
-35
lines changed

test/e2e/scaffold/k8s.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ func (s *Scaffold) ListPodsByLabels(labels string) ([]corev1.Pod, error) {
110110
})
111111
}
112112

113+
func (s *Scaffold) DeleteResourcesByLabels(resourceType, label string) error {
114+
return k8s.RunKubectlE(s.t, s.kubectlOptions, "delete", resourceType, "-l", label)
115+
}
116+
117+
func (s *Scaffold) GetResourcesByLabelsOutput(resourceType, label string) (string, error) {
118+
return k8s.RunKubectlAndGetOutputE(s.t, s.kubectlOptions, "get", resourceType, "-l", label)
119+
}
120+
113121
// CreateResourceFromStringWithNamespace creates resource from a loaded yaml string
114122
// and sets its namespace to the specified one.
115123
func (s *Scaffold) CreateResourceFromStringWithNamespace(yaml, namespace string) error {

test/e2e/scaffold/locust.go

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,21 @@ data:
2525
2626
class HttpbinRequester(HttpUser):
2727
@task
28-
def request_headers(self):
28+
def headers(self):
2929
self.client.get("/headers", headers={"Host": "httpbin.example"})
30+
31+
@task
32+
def get(self):
33+
self.client.get("/get", headers={"Host": "httpbin.example"})
34+
35+
@task
36+
def post(self):
37+
self.client.post("/post", headers={"Host": "httpbin.example"})
38+
39+
@task
40+
def image(self):
41+
self.client.image("/image", headers={"Host": "httpbin.example"})
42+
3043
LOCUST_HOST: http://api7ee3-apisix-gateway-mtls:9080
3144
LOCUST_SPAWN_RATE: "50"
3245
LOCUST_USERS: "500"
@@ -116,25 +129,6 @@ func (s *Scaffold) DeployLocust() *corev1.Service {
116129
return service
117130
}
118131

119-
// func (s *Scaffold) LocustClient() *httpexpect.Expect {
120-
// u := url.URL{
121-
// Scheme: "http",
122-
// Host: s.locustTunnel.Endpoint(),
123-
// }
124-
// return httpexpect.WithConfig(httpexpect.Config{
125-
// BaseURL: u.String(),
126-
// Client: &http.Client{
127-
// Transport: &http.Transport{},
128-
// CheckRedirect: func(req *http.Request, via []*http.Request) error {
129-
// return http.ErrUseLastResponse
130-
// },
131-
// },
132-
// Reporter: httpexpect.NewAssertReporter(
133-
// httpexpect.NewAssertReporter(s.GinkgoT),
134-
// ),
135-
// })
136-
// }
137-
138132
func (s *Scaffold) ResetLocust() error {
139133
if s.locustTunnel == nil {
140134
return errors.New("locust is not deployed")
@@ -143,7 +137,9 @@ func (s *Scaffold) ResetLocust() error {
143137
if err != nil {
144138
return errors.Wrap(err, "failed to request reset locust")
145139
}
146-
defer resp.Body.Close()
140+
defer func() {
141+
_ = resp.Body.Close()
142+
}()
147143
if resp.StatusCode != http.StatusOK {
148144
return errors.Errorf("request reset locust not OK, status: %s", resp.Status)
149145
}
@@ -162,7 +158,9 @@ func (s *Scaffold) DownloadLocustReport(filename string) error {
162158
if err != nil {
163159
return errors.Wrap(err, "failed to request download report")
164160
}
165-
defer resp.Body.Close()
161+
defer func() {
162+
_ = resp.Body.Close()
163+
}()
166164
if resp.StatusCode != http.StatusOK {
167165
return errors.Errorf("request download report not OK, status: %s", resp.Status)
168166
}

test/e2e/scaffold/scaffold.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131

3232
"github.com/gavv/httpexpect/v2"
3333
"github.com/gruntwork-io/terratest/modules/k8s"
34+
"github.com/gruntwork-io/terratest/modules/logger"
3435
"github.com/gruntwork-io/terratest/modules/testing"
3536
. "github.com/onsi/ginkgo/v2"
3637
. "github.com/onsi/gomega"
@@ -74,6 +75,8 @@ type Options struct {
7475

7576
GinkgoBeforeCallback func(...any) bool
7677
GinkgoAfterCallback func(...any) bool
78+
79+
KubectlLogger *logger.Logger
7780
}
7881

7982
type Scaffold struct {
@@ -365,6 +368,9 @@ func (s *Scaffold) beforeEach() {
365368
ConfigPath: s.opts.Kubeconfig,
366369
Namespace: s.namespace,
367370
}
371+
if s.opts.KubectlLogger != nil {
372+
s.kubectlOptions.Logger = s.opts.KubectlLogger
373+
}
368374
if s.opts.ControllerName == "" {
369375
s.opts.ControllerName = fmt.Sprintf("%s/%d", DefaultControllerName, time.Now().Nanosecond())
370376
}

test/long_term_stability/spec_subjects/spec_subjects.go

Lines changed: 138 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@ package spec_subjects
33
import (
44
"fmt"
55
"net/http"
6+
"strconv"
7+
"strings"
68
"time"
79

810
"github.com/api7/api7-ingress-controller/test/e2e/scaffold"
11+
"github.com/gruntwork-io/terratest/modules/logger"
12+
"github.com/gruntwork-io/terratest/modules/retry"
913
. "github.com/onsi/ginkgo/v2"
1014
. "github.com/onsi/gomega"
15+
"github.com/pkg/errors"
1116
)
1217

1318
var _ = Describe("API7 Ingress Controller Long Term Stability Tests", Ordered, func() {
@@ -16,6 +21,7 @@ var _ = Describe("API7 Ingress Controller Long Term Stability Tests", Ordered, f
1621
ControllerName: "gateway.api7.io/api7-ingress-controller",
1722
GinkgoBeforeCallback: BeforeAll,
1823
GinkgoAfterCallback: AfterAll,
24+
KubectlLogger: logger.Discard, // too many logs in long-term stability test so discard kubectl apply logs
1925
})
2026
it int
2127
)
@@ -55,6 +61,37 @@ spec:
5561
- path:
5662
type: Exact
5763
value: /headers
64+
- path:
65+
type: Exact
66+
value: /get
67+
- path:
68+
type: Exact
69+
value: /post
70+
- path:
71+
type: Exact
72+
value: /image
73+
74+
backendRefs:
75+
- name: httpbin-service-e2e-test
76+
port: 80
77+
`
78+
httpRouteTemplate2 = `
79+
apiVersion: gateway.networking.k8s.io/v1
80+
kind: HTTPRoute
81+
metadata:
82+
name: %s
83+
labels:
84+
template_name: httpRouteTemplate2
85+
spec:
86+
parentRefs:
87+
- name: api7ee
88+
hostnames:
89+
- httpbin.example
90+
rules:
91+
- matches:
92+
- path:
93+
type: Exact
94+
value: %s
5895
backendRefs:
5996
- name: httpbin-service-e2e-test
6097
port: 80
@@ -124,12 +161,12 @@ spec:
124161

125162
Context("Service Discovery", func() {
126163
BeforeEach(func() {
127-
// scale backend pods replicas
164+
By("scale backend pods replicas")
128165
err := s.ScaleHTTPBIN(5)
129166
Expect(err).NotTo(HaveOccurred(), "scaling httpbin")
130167
})
131168

132-
It("service discovery", func() {
169+
It("rolling update", func() {
133170
var total = 5
134171
for i := 0; i < total; i++ {
135172
By(fmt.Sprintf("rolling update deployment/httpbin-deployment-e2e-test [%02d/%02d]", i+1, total))
@@ -139,32 +176,120 @@ spec:
139176
time.Sleep(time.Minute)
140177
}
141178

142-
err := s.DownloadLocustReport(fmt.Sprintf("%02d_service_discovery", it))
179+
err := s.DownloadLocustReport(fmt.Sprintf("%02d_rolling_update", it))
180+
Expect(err).NotTo(HaveOccurred(), "getting locust report")
181+
})
182+
183+
It("scale replicas", func() {
184+
var total = 5
185+
for i := 0; i < total; i++ {
186+
By(fmt.Sprintf("scale replicas for deployment/httpbin-deployment-e2e-test [%02d/%02d]", i+1, total))
187+
err := s.ScaleHTTPBIN(4 + i%2*3) // scale to 4 if "i" is even, scale to 7 if "i" is odd.
188+
Expect(err).NotTo(HaveOccurred(), "scale replicas for deployment/httpbin-deployment-e2e-test")
189+
time.Sleep(time.Minute)
190+
}
191+
192+
err := s.DownloadLocustReport(fmt.Sprintf("%02d_scale_replicas", it))
143193
Expect(err).NotTo(HaveOccurred(), "getting locust report")
144194
})
145195
})
146196

147197
Context("Large-scale HTTPRoute", func() {
148-
It("it 0", func() {
149-
Ω(true).Should(BeTrue())
150-
})
198+
var (
199+
reconcileDurationPerHTTPRoute = 3 * time.Second
200+
probeDuration = 100 * time.Millisecond
201+
202+
resourceTypeHTTPRoute = "HTTPRoute"
203+
label = "template_name=httpRouteTemplate2"
204+
)
205+
206+
for _, total := range []int{500, 2000, 5000} {
207+
var (
208+
reconcileDurationBatchProcess = time.Duration(total) * reconcileDurationPerHTTPRoute
209+
title = strconv.FormatInt(int64(total), 10) + " HTTPRoute"
210+
)
211+
212+
It(title, func() {
213+
defer func() {
214+
By("cleaning up HTTPRoutes")
215+
err := s.DeleteResourcesByLabels(resourceTypeHTTPRoute, label)
216+
Expect(err).NotTo(HaveOccurred(), "delete HTTPRoute by label")
217+
218+
Eventually(func() string {
219+
output, err := s.GetResourcesByLabelsOutput(resourceTypeHTTPRoute, label)
220+
Expect(err).NotTo(HaveOccurred(), "getting HTTPRoute")
221+
return output
222+
}).WithTimeout(reconcileDurationBatchProcess).ProbeEvery(probeDuration).
223+
Should(ContainSubstring("No resources found"))
224+
}()
225+
226+
By("prepare HTTPRoutes")
227+
for i := 0; i < total+100; i++ {
228+
By(fmt.Sprintf("prepare HTTPRoutes [%04d/%04d]", i+1, total))
229+
routeName := "httpbin-" + strconv.FormatInt(int64(i), 10)
230+
pathValue := "/delay/" + strconv.FormatInt(int64(i), 10)
231+
err := s.CreateResourceFromString(fmt.Sprintf(httpRouteTemplate2, routeName, pathValue))
232+
Expect(err).NotTo(HaveOccurred(), "creating HTTPRoute")
233+
234+
message := retry.DoWithRetry(s.GinkgoT, "Wait for HTTPRoute ok", 100, time.Second, func() (string, error) {
235+
yaml_, err := s.GetResourceYaml(resourceTypeHTTPRoute, routeName)
236+
if err != nil {
237+
return "", err
238+
}
239+
if !strings.Contains(yaml_, `status: "True"`) {
240+
return "", errors.New("HTTPRoute status is not True")
241+
}
242+
return "HTTPRoute is now available", nil
243+
},
244+
)
245+
s.Logf(message)
246+
}
247+
248+
By("delete 100 HTTPRoutes")
249+
for i := total; i < total+100; i++ {
250+
By(fmt.Sprintf("prepare 1000 HTTPRoute [%04d/%04d]", i, total+100))
251+
routeName := "httpbin-" + strconv.FormatInt(int64(i), 10)
252+
err := s.DeleteResource(resourceTypeHTTPRoute, routeName)
253+
Expect(err).NotTo(HaveOccurred(), "creating HTTPRoute")
254+
255+
Eventually(func() string {
256+
_, err := s.GetResourceYaml(resourceTypeHTTPRoute, "")
257+
return err.Error()
258+
}).WithTimeout(reconcileDurationPerHTTPRoute).ProbeEvery(probeDuration).
259+
Should(ContainSubstring("not found"))
260+
}
261+
262+
err := s.DownloadLocustReport(fmt.Sprintf("%02d_large_scale_httproute(1000)", it))
263+
Expect(err).NotTo(HaveOccurred(), "getting locust report")
264+
})
265+
}
151266
})
152267

153-
Context("Ingress Controller is crashing", func() {
268+
PContext("Ingress Controller is crashing", func() {
154269
It("it 0", func() {
155270
Ω(true).Should(BeTrue())
156271
})
157272
})
158273

159-
Context("Regression Tests", func() {
160-
It("Under large-scale HTTPRoute, some HTTPRoute resources are abnormal, which cannot affect the synchronization efficient of other resources", func() {})
274+
PContext("Regression Tests", func() {
275+
// Under large-scale HTTPRoute, some HTTPRoute resources are abnormal, which cannot affect the synchronization
276+
// efficient of other resources
277+
It("", func() {})
161278

162-
It("Under large-scale CRDs, some CRDs resources are abnormal, which cannot affect the synchronization efficient of other resources", func() {})
279+
// Under large-scale CRDs, some CRDs resources are abnormal, which cannot affect the synchronization efficient
280+
// of other resources
281+
It("", func() {})
163282

164-
It("When a large number of CRDs are applied concurrently, the processing capacity of IngressController is linear (≤O(n), n is the number of resources applied simultaneously)", func() {})
283+
// When a large number of CRDs are applied concurrently, the processing capacity of IngressController is linear
284+
// (≤O(n), n is the number of resources applied simultaneously)
285+
It("", func() {})
165286

166-
It("Under large-scale CRDs, some CRDs are added/deleted/modified concurrently, and the processing capacity of IngressController has no obvious relationship with the number of existing CRDs", func() {})
287+
// Under large-scale CRDs, some CRDs are added/deleted/modified concurrently, and the processing capacity of
288+
// IngressController has no obvious relationship with the number of existing CRDs
289+
It("", func() {})
167290

168-
It("When IngressController is unexpectedly unavailable, it does not affect the existing configuration of the data plane", func() {})
291+
// When IngressController is unexpectedly unavailable, it does not affect the existing configuration of the
292+
// data plane
293+
It("", func() {})
169294
})
170295
})

0 commit comments

Comments
 (0)