feat(apisix): add Cloudron package
- Implements Apache APISIX packaging for Cloudron platform. - Includes Dockerfile, CloudronManifest.json, and start.sh. - Configured to use Cloudron's etcd addon. 🤖 Generated with Gemini CLI Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
292
CloudronPackages/APISIX/apisix-source/t/chaos/utils/utils.go
Normal file
292
CloudronPackages/APISIX/apisix-source/t/chaos/utils/utils.go
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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 utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var (
|
||||
token = "edd1c9f034335f136f87ad84b625c8f1"
|
||||
// TODO: refactor the code. We should move the endpoint from the expect to the http call.
|
||||
// So we don't need to remember to pass the correct expect.
|
||||
Host = "http://127.0.0.1:9180"
|
||||
DataPanelHost = "http://127.0.0.1:9080"
|
||||
PrometheusHost = "http://127.0.0.1:9080"
|
||||
setRouteBody = `{
|
||||
"uri": "/get",
|
||||
"plugins": {
|
||||
"prometheus": {}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"httpbin.default.svc.cluster.local:8000": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
}
|
||||
}`
|
||||
ignoreErrorFuncMap = map[string]func(e *httpexpect.Expect) *httpexpect.Response{
|
||||
http.MethodGet: GetRouteIgnoreError,
|
||||
http.MethodPut: SetRouteIgnoreError,
|
||||
}
|
||||
)
|
||||
|
||||
type httpTestCase struct {
|
||||
E *httpexpect.Expect
|
||||
Method string
|
||||
Path string
|
||||
Body string
|
||||
Headers map[string]string
|
||||
IgnoreError bool
|
||||
ExpectStatus int
|
||||
ExpectBody string
|
||||
ExpectStatusRange httpexpect.StatusRange
|
||||
}
|
||||
|
||||
func caseCheck(tc httpTestCase) *httpexpect.Response {
|
||||
e := tc.E
|
||||
var req *httpexpect.Request
|
||||
switch tc.Method {
|
||||
case http.MethodGet:
|
||||
req = e.GET(tc.Path)
|
||||
case http.MethodPut:
|
||||
req = e.PUT(tc.Path)
|
||||
case http.MethodDelete:
|
||||
req = e.DELETE(tc.Path)
|
||||
default:
|
||||
panic("invalid HTTP method")
|
||||
}
|
||||
|
||||
if req == nil {
|
||||
panic("fail to init request")
|
||||
}
|
||||
for key, val := range tc.Headers {
|
||||
req.WithHeader(key, val)
|
||||
}
|
||||
if tc.Body != "" {
|
||||
req.WithText(tc.Body)
|
||||
}
|
||||
|
||||
resp := req.Expect()
|
||||
if tc.IgnoreError {
|
||||
return resp
|
||||
}
|
||||
|
||||
if tc.ExpectStatus != 0 {
|
||||
resp.Status(tc.ExpectStatus)
|
||||
}
|
||||
|
||||
if tc.ExpectStatusRange != 0 {
|
||||
resp.StatusRange(tc.ExpectStatusRange)
|
||||
}
|
||||
|
||||
if tc.ExpectBody != "" {
|
||||
resp.Body().Contains(tc.ExpectBody)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func SetRoute(e *httpexpect.Expect, expectStatusRange httpexpect.StatusRange) *httpexpect.Response {
|
||||
return caseCheck(httpTestCase{
|
||||
E: e,
|
||||
Method: http.MethodPut,
|
||||
Path: "/apisix/admin/routes/1",
|
||||
Headers: map[string]string{"X-API-KEY": token},
|
||||
Body: setRouteBody,
|
||||
ExpectStatusRange: expectStatusRange,
|
||||
})
|
||||
}
|
||||
|
||||
func SetRouteIgnoreError(e *httpexpect.Expect) *httpexpect.Response {
|
||||
return caseCheck(httpTestCase{
|
||||
E: e,
|
||||
Method: http.MethodPut,
|
||||
Path: "/apisix/admin/routes/1",
|
||||
Headers: map[string]string{"X-API-KEY": token},
|
||||
Body: setRouteBody,
|
||||
IgnoreError: true,
|
||||
})
|
||||
}
|
||||
|
||||
func GetRoute(e *httpexpect.Expect, expectStatus int) *httpexpect.Response {
|
||||
return caseCheck(httpTestCase{
|
||||
E: e,
|
||||
Method: http.MethodGet,
|
||||
Path: "/get",
|
||||
ExpectStatus: expectStatus,
|
||||
})
|
||||
}
|
||||
|
||||
func GetRouteIgnoreError(e *httpexpect.Expect) *httpexpect.Response {
|
||||
return caseCheck(httpTestCase{
|
||||
E: e,
|
||||
Method: http.MethodGet,
|
||||
Path: "/get",
|
||||
IgnoreError: true,
|
||||
})
|
||||
}
|
||||
|
||||
func GetRouteList(e *httpexpect.Expect, expectStatus int) *httpexpect.Response {
|
||||
return caseCheck(httpTestCase{
|
||||
E: e,
|
||||
Method: http.MethodGet,
|
||||
Path: "/apisix/admin/routes",
|
||||
Headers: map[string]string{"X-API-KEY": token},
|
||||
ExpectStatus: expectStatus,
|
||||
ExpectBody: "httpbin.default.svc.cluster.local",
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteRoute(e *httpexpect.Expect) *httpexpect.Response {
|
||||
return caseCheck(httpTestCase{
|
||||
E: e,
|
||||
Method: http.MethodDelete,
|
||||
Path: "/apisix/admin/routes/1",
|
||||
Headers: map[string]string{"X-API-KEY": token},
|
||||
})
|
||||
}
|
||||
|
||||
func SetPrometheusMetricsPublicAPI(e *httpexpect.Expect) *httpexpect.Response {
|
||||
return caseCheck(httpTestCase{
|
||||
E: e,
|
||||
Method: http.MethodPut,
|
||||
Path: "/apisix/admin/routes/metrics",
|
||||
Headers: map[string]string{"X-API-KEY": token},
|
||||
Body: `{
|
||||
"uri": "/apisix/prometheus/metrics",
|
||||
"plugins": {
|
||||
"public-api": {}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"httpbin.default.svc.cluster.local:8000": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
}
|
||||
}`,
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrometheusEtcdMetric(e *httpexpect.Expect, expectEtcd int) *httpexpect.Response {
|
||||
return caseCheck(httpTestCase{
|
||||
E: e,
|
||||
Method: http.MethodGet,
|
||||
Path: "/apisix/prometheus/metrics",
|
||||
ExpectBody: fmt.Sprintf("apisix_etcd_reachable %d", expectEtcd),
|
||||
})
|
||||
}
|
||||
|
||||
// get the first line which contains the key
|
||||
func getPrometheusMetric(e *httpexpect.Expect, key string) string {
|
||||
resp := caseCheck(httpTestCase{
|
||||
E: e,
|
||||
Method: http.MethodGet,
|
||||
Path: "/apisix/prometheus/metrics",
|
||||
})
|
||||
resps := strings.Split(resp.Body().Raw(), "\n")
|
||||
var targetLine string
|
||||
for _, line := range resps {
|
||||
if strings.Contains(line, key) {
|
||||
targetLine = line
|
||||
break
|
||||
}
|
||||
}
|
||||
targetSlice := strings.Fields(targetLine)
|
||||
gomega.Ω(len(targetSlice)).Should(gomega.BeNumerically("==", 2))
|
||||
return targetSlice[1]
|
||||
}
|
||||
|
||||
func GetEgressBandwidthPerSecond(e *httpexpect.Expect) (float64, float64) {
|
||||
key := "apisix_bandwidth{type=\"egress\","
|
||||
bandWidthString := getPrometheusMetric(e, key)
|
||||
bandWidthStart, err := strconv.ParseFloat(bandWidthString, 64)
|
||||
gomega.Expect(err).To(gomega.BeNil())
|
||||
// after etcd got killed, it would take longer time to get the metrics
|
||||
// so need to calculate the duration
|
||||
timeStart := time.Now()
|
||||
|
||||
time.Sleep(10 * time.Second)
|
||||
bandWidthString = getPrometheusMetric(e, key)
|
||||
bandWidthEnd, err := strconv.ParseFloat(bandWidthString, 64)
|
||||
gomega.Expect(err).To(gomega.BeNil())
|
||||
duration := time.Since(timeStart)
|
||||
|
||||
return bandWidthEnd - bandWidthStart, duration.Seconds()
|
||||
}
|
||||
|
||||
func GetSilentHttpexpectClient() *httpexpect.Expect {
|
||||
return httpexpect.WithConfig(httpexpect.Config{
|
||||
BaseURL: Host,
|
||||
Reporter: httpexpect.NewAssertReporter(ginkgo.GinkgoT()),
|
||||
Printers: []httpexpect.Printer{
|
||||
newSilentPrinter(ginkgo.GinkgoT()),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func WaitUntilMethodSucceed(e *httpexpect.Expect, method string, interval int) {
|
||||
f, ok := ignoreErrorFuncMap[method]
|
||||
gomega.Expect(ok).To(gomega.BeTrue())
|
||||
resp := f(e)
|
||||
if resp.Raw().StatusCode != http.StatusOK {
|
||||
for i := range [60]int{} {
|
||||
timeWait := fmt.Sprintf("wait for %ds\n", i*interval)
|
||||
fmt.Fprint(ginkgo.GinkgoWriter, timeWait)
|
||||
resp = f(e)
|
||||
if resp.Raw().StatusCode != http.StatusOK {
|
||||
time.Sleep(5 * time.Second)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
gomega.Ω(resp.Raw().StatusCode).Should(gomega.BeNumerically("==", http.StatusOK))
|
||||
}
|
||||
|
||||
func RoughCompare(a float64, b float64) bool {
|
||||
ratio := a / b
|
||||
if ratio < 1.3 && ratio > 0.7 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type silentPrinter struct {
|
||||
logger httpexpect.Logger
|
||||
}
|
||||
|
||||
func newSilentPrinter(logger httpexpect.Logger) silentPrinter {
|
||||
return silentPrinter{logger}
|
||||
}
|
||||
|
||||
// Request implements Printer.Request.
|
||||
func (p silentPrinter) Request(req *http.Request) {
|
||||
}
|
||||
|
||||
// Response implements Printer.Response.
|
||||
func (silentPrinter) Response(*http.Response, time.Duration) {
|
||||
}
|
Reference in New Issue
Block a user