Skip to content

Commit 8b5f0ff

Browse files
committed
feat: implement long-term stability benchmark test
1 parent ab0a67d commit 8b5f0ff

File tree

7 files changed

+418
-11
lines changed

7 files changed

+418
-11
lines changed

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ e2e-test:
9292
@kind get kubeconfig --name $(KIND_NAME) > $$KUBECONFIG
9393
DASHBOARD_VERSION=$(DASHBOARD_VERSION) go test ./test/e2e/ -test.timeout=$(TEST_TIMEOUT) -v -ginkgo.v -ginkgo.focus="$(TEST_FOCUS)"
9494

95+
.PHONY: kind-lts-test
96+
kind-lts-test: kind-up build-image kind-load-images lts-test
97+
98+
# lts-test is long-term-stability test
99+
.PHONY: lts-test
100+
lts-test:
101+
@kind get kubeconfig --name $(KIND_NAME) > $$KUBECONFIG
102+
DASHBOARD_VERSION=$(DASHBOARD_VERSION) go test ./test/long_term_stability/ -test.timeout=$(TEST_TIMEOUT) -v -ginkgo.v -ginkgo.focus="$(TEST_FOCUS)"
103+
95104
.PHONY: conformance-test
96105
conformance-test:
97106
DASHBOARD_VERSION=$(DASHBOARD_VERSION) go test -v ./test/conformance -tags=conformance

test/e2e/gatewayapi/httproute.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
var _ = Describe("Test HTTPRoute", func() {
1717
s := scaffold.NewDefaultScaffold()
1818

19-
var defautlGatewayClass = `
19+
var defaultGatewayClass = `
2020
apiVersion: gateway.networking.k8s.io/v1
2121
kind: GatewayClass
2222
metadata:
@@ -25,7 +25,7 @@ spec:
2525
controllerName: %s
2626
`
2727

28-
var defautlGateway = `
28+
var defaultGateway = `
2929
apiVersion: gateway.networking.k8s.io/v1
3030
kind: Gateway
3131
metadata:
@@ -37,7 +37,7 @@ spec:
3737
protocol: HTTP
3838
port: 80
3939
`
40-
var defautlGatewayHTTPS = `
40+
var defaultGatewayHTTPS = `
4141
apiVersion: gateway.networking.k8s.io/v1
4242
kind: Gateway
4343
metadata:
@@ -78,7 +78,7 @@ spec:
7878
var beforeEachHTTP = func() {
7979
By("create GatewayClass")
8080
gatewayClassName := fmt.Sprintf("api7-%d", time.Now().Unix())
81-
err := s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defautlGatewayClass, gatewayClassName, s.GetControllerName()), "")
81+
err := s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defaultGatewayClass, gatewayClassName, s.GetControllerName()), "")
8282
Expect(err).NotTo(HaveOccurred(), "creating GatewayClass")
8383
time.Sleep(5 * time.Second)
8484

@@ -89,7 +89,7 @@ spec:
8989
Expect(gcyaml).To(ContainSubstring("message: the gatewayclass has been accepted by the api7-ingress-controller"), "checking GatewayClass condition message")
9090

9191
By("create Gateway")
92-
err = s.CreateResourceFromString(fmt.Sprintf(defautlGateway, gatewayClassName))
92+
err = s.CreateResourceFromString(fmt.Sprintf(defaultGateway, gatewayClassName))
9393
Expect(err).NotTo(HaveOccurred(), "creating Gateway")
9494
time.Sleep(5 * time.Second)
9595

@@ -105,7 +105,7 @@ spec:
105105
createSecret(s, secretName)
106106
By("create GatewayClass")
107107
gatewayClassName := fmt.Sprintf("api7-%d", time.Now().Unix())
108-
err := s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defautlGatewayClass, gatewayClassName, s.GetControllerName()), "")
108+
err := s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defaultGatewayClass, gatewayClassName, s.GetControllerName()), "")
109109
Expect(err).NotTo(HaveOccurred(), "creating GatewayClass")
110110
time.Sleep(5 * time.Second)
111111

@@ -116,7 +116,7 @@ spec:
116116
Expect(gcyaml).To(ContainSubstring("message: the gatewayclass has been accepted by the api7-ingress-controller"), "checking GatewayClass condition message")
117117

118118
By("create Gateway")
119-
err = s.CreateResourceFromString(fmt.Sprintf(defautlGatewayHTTPS, gatewayClassName))
119+
err = s.CreateResourceFromString(fmt.Sprintf(defaultGatewayHTTPS, gatewayClassName))
120120
Expect(err).NotTo(HaveOccurred(), "creating Gateway")
121121
time.Sleep(5 * time.Second)
122122

test/e2e/scaffold/httpbin.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
"time"
2020

2121
"github.com/gruntwork-io/terratest/modules/k8s"
22-
ginkgo "github.com/onsi/ginkgo/v2"
22+
"github.com/onsi/ginkgo/v2"
2323
corev1 "k8s.io/api/core/v1"
2424
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2525
)
@@ -70,6 +70,10 @@ spec:
7070
- containerPort: 80
7171
name: "http"
7272
protocol: "TCP"
73+
lifecycle:
74+
preStop:
75+
exec:
76+
command: ["/bin/sh", "-c", "sleep 5"]
7377
`
7478
_httpService = `
7579
apiVersion: v1
@@ -112,7 +116,7 @@ func (s *Scaffold) NewHTTPBINWithNamespace(namespace string) (*corev1.Service, e
112116
return s.newHTTPBIN()
113117
}
114118

115-
// ScaleHTTPBIN scales the number of HTTPBIN pods to desired.
119+
// ScaleHTTPBIN scales the number of HTTPBIN pods to desire.
116120
func (s *Scaffold) ScaleHTTPBIN(desired int) error {
117121
httpbinDeployment := fmt.Sprintf(s.FormatRegistry(_httpbinDeploymentTemplate), desired)
118122
if err := s.CreateResourceFromString(httpbinDeployment); err != nil {

test/e2e/scaffold/locust.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package scaffold
2+
3+
import (
4+
"io"
5+
"net/http"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
"github.com/gruntwork-io/terratest/modules/k8s"
11+
. "github.com/onsi/gomega"
12+
"github.com/pkg/errors"
13+
corev1 "k8s.io/api/core/v1"
14+
)
15+
16+
const (
17+
_locustConfigMapTemplate = `
18+
apiVersion: v1
19+
kind: ConfigMap
20+
metadata:
21+
name: locust-config
22+
data:
23+
locustfile.py: |-
24+
from locust import HttpUser, task, between
25+
26+
class HttpbinRequester(HttpUser):
27+
@task
28+
def request_headers(self):
29+
self.client.get("/headers", headers={"Host": "httpbin.example"})
30+
LOCUST_HOST: http://api7ee3-apisix-gateway-mtls:9080
31+
LOCUST_SPAWN_RATE: "50"
32+
LOCUST_USERS: "500"
33+
LOCUST_AUTOSTART: "true"
34+
`
35+
_locustDeploymentTemplate = `
36+
apiVersion: apps/v1
37+
kind: Deployment
38+
metadata:
39+
name: locust
40+
spec:
41+
selector:
42+
matchLabels:
43+
app: locust
44+
template:
45+
metadata:
46+
labels:
47+
app: locust
48+
spec:
49+
containers:
50+
- name: locust
51+
image: locustio/locust
52+
ports:
53+
- containerPort: 8089
54+
env:
55+
- name: LOCUST_HOST
56+
valueFrom:
57+
configMapKeyRef:
58+
name: locust-config
59+
key: LOCUST_HOST
60+
- name: LOCUST_SPAWN_RATE
61+
valueFrom:
62+
configMapKeyRef:
63+
name: locust-config
64+
key: LOCUST_SPAWN_RATE
65+
- name: LOCUST_USERS
66+
valueFrom:
67+
configMapKeyRef:
68+
name: locust-config
69+
key: LOCUST_USERS
70+
- name: LOCUST_AUTOSTART
71+
valueFrom:
72+
configMapKeyRef:
73+
name: locust-config
74+
key: LOCUST_AUTOSTART
75+
volumeMounts:
76+
- mountPath: /home/locust
77+
name: locust-config
78+
volumes:
79+
- name: locust-config
80+
configMap:
81+
name: locust-config
82+
`
83+
_locustServiceTemplate = `
84+
apiVersion: v1
85+
kind: Service
86+
metadata:
87+
name: locust
88+
spec:
89+
selector:
90+
app: locust
91+
ports:
92+
- name: web
93+
port: 8089
94+
targetPort: 8089
95+
protocol: TCP
96+
type: ClusterIP `
97+
)
98+
99+
func (s *Scaffold) DeployLocust() *corev1.Service {
100+
// create ConfigMap, Deployment, Service
101+
for _, yaml_ := range []string{_locustConfigMapTemplate, _locustDeploymentTemplate, _locustServiceTemplate} {
102+
err := s.CreateResourceFromString(yaml_)
103+
Expect(err).NotTo(HaveOccurred(), "create resource: %s", yaml_)
104+
}
105+
106+
service, err := k8s.GetServiceE(s.t, s.kubectlOptions, "locust")
107+
Expect(err).NotTo(HaveOccurred(), "get service: locust")
108+
109+
s.EnsureNumEndpointsReady(s.t, service.Name, 1)
110+
s.locustTunnel = k8s.NewTunnel(s.kubectlOptions, k8s.ResourceTypeService, "locust", 8089, 8089)
111+
s.addFinalizers(s.locustTunnel.Close)
112+
113+
err = s.locustTunnel.ForwardPortE(s.t)
114+
Expect(err).NotTo(HaveOccurred(), "port-forward service: locust")
115+
116+
return service
117+
}
118+
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+
138+
func (s *Scaffold) ResetLocust() error {
139+
if s.locustTunnel == nil {
140+
return errors.New("locust is not deployed")
141+
}
142+
resp, err := http.Get("http://" + s.locustTunnel.Endpoint() + "/stats/reset")
143+
if err != nil {
144+
return errors.Wrap(err, "failed to request reset locust")
145+
}
146+
defer resp.Body.Close()
147+
if resp.StatusCode != http.StatusOK {
148+
return errors.Errorf("request reset locust not OK, status: %s", resp.Status)
149+
}
150+
return nil
151+
}
152+
153+
func (s *Scaffold) DownloadLocustReport(filename string) error {
154+
if s.locustTunnel == nil {
155+
return errors.New("locust is not deployed")
156+
}
157+
if !strings.EqualFold(filepath.Ext(filename), ".html") {
158+
filename += ".html"
159+
}
160+
_ = os.Remove(filename)
161+
resp, err := http.Get("http://" + s.locustTunnel.Endpoint() + "/stats/report?download=1&theme=light")
162+
if err != nil {
163+
return errors.Wrap(err, "failed to request download report")
164+
}
165+
defer resp.Body.Close()
166+
if resp.StatusCode != http.StatusOK {
167+
return errors.Errorf("request download report not OK, status: %s", resp.Status)
168+
}
169+
data, err := io.ReadAll(resp.Body)
170+
if err != nil {
171+
return errors.Wrap(err, "failed to read report")
172+
}
173+
return os.WriteFile(filename, data, 0644)
174+
}

test/e2e/scaffold/scaffold.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ type Options struct {
7171
NamespaceSelectorLabel map[string][]string
7272
DisableNamespaceSelector bool
7373
DisableNamespaceLabel bool
74+
75+
GinkgoBeforeCallback func(...any) bool
76+
GinkgoAfterCallback func(...any) bool
7477
}
7578

7679
type Scaffold struct {
@@ -96,6 +99,7 @@ type Scaffold struct {
9699
apisixTCPTunnel *k8s.Tunnel
97100
apisixTLSOverTCPTunnel *k8s.Tunnel
98101
apisixUDPTunnel *k8s.Tunnel
102+
locustTunnel *k8s.Tunnel
99103
// apisixControlTunnel *k8s.Tunnel
100104

101105
}
@@ -158,8 +162,14 @@ func NewScaffold(o *Options) *Scaffold {
158162
t: GinkgoT(),
159163
}
160164

161-
BeforeEach(s.beforeEach)
162-
AfterEach(s.afterEach)
165+
if o.GinkgoBeforeCallback == nil {
166+
o.GinkgoBeforeCallback = BeforeEach
167+
}
168+
if o.GinkgoAfterCallback == nil {
169+
o.GinkgoAfterCallback = AfterEach
170+
}
171+
o.GinkgoBeforeCallback(s.beforeEach)
172+
o.GinkgoAfterCallback(s.afterEach)
163173

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

0 commit comments

Comments
 (0)