blob: 3fcec34e03719489867c69b445520ba7d931317a [file] [log] [blame]
// 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 features
import (
"fmt"
"os/exec"
"strconv"
"strings"
"time"
"github.com/apache/apisix-ingress-controller/pkg/log"
ginkgo "github.com/onsi/ginkgo/v2"
"github.com/stretchr/testify/assert"
"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
)
func getApisixResourceRequestsCount(s *scaffold.Scaffold, res string) int {
pods, err := s.GetIngressPodDetails()
assert.Nil(ginkgo.GinkgoT(), err, "get ingress pod")
assert.True(ginkgo.GinkgoT(), len(pods) >= 1, "get ingress pod")
output, err := s.Exec(pods[0].Name, "ingress-apisix-controller-deployment-e2e-test",
fmt.Sprintf("curl -s localhost:8080/metrics | grep apisix_ingress_controller_apisix_requests | grep 'op=\"write\"' | grep 'resource=\"%v\"'", res),
)
if err != nil {
log.Errorf("failed to get metrics: %v, %v; output: %v", err.Error(), string(err.(*exec.ExitError).Stderr), output)
} else {
log.Infof("output: %v", output)
}
assert.Nil(ginkgo.GinkgoT(), err, "get metrics from controller")
// make sure the output is grep-ed
assert.False(ginkgo.GinkgoT(), strings.Contains(output, "promhttp_metric_handler_requests_total"))
assert.True(ginkgo.GinkgoT(), strings.Contains(output, "apisix_ingress_controller_apisix_requests"))
assert.True(ginkgo.GinkgoT(), strings.Contains(output, fmt.Sprintf("resource=\"%v\"", res)))
arr := strings.Split(output, " ")
if len(arr) == 0 {
ginkgo.Fail("unexpected metrics output: "+output, 1)
return -1
}
i, err := strconv.ParseInt(arr[len(arr)-1], 10, 64)
assert.Nil(ginkgo.GinkgoT(), err, "parse metrics")
return int(i)
}
var _ = ginkgo.Describe("suite-features: sync delay", func() {
suites := func(s *scaffold.Scaffold) {
ginkgo.It("check resource request count", func() {
backendSvc, backendSvcPort := s.DefaultHTTPBackend()
ar := fmt.Sprintf(`
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: httpbin-route
spec:
http:
- name: rule1
match:
paths:
- /ip
backends:
- serviceName: %s
servicePort: %d
`, backendSvc, backendSvcPort[0])
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar), "create ApisixRoute")
err := s.EnsureNumApisixRoutesCreated(1)
assert.Nil(ginkgo.GinkgoT(), err, "checking number of routes")
err = s.EnsureNumApisixUpstreamsCreated(1)
assert.Nil(ginkgo.GinkgoT(), err, "checking number of upstreams")
arStream := fmt.Sprintf(`
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: httpbin-tcp-route
spec:
stream:
- name: rule1
protocol: TCP
match:
ingressPort: 9100
backend:
serviceName: %s
servicePort: %d
`, backendSvc, backendSvcPort[0])
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(arStream), "create ApisixRoute (stream)")
hostA := "a.test.com"
secretA := "server-secret-a"
serverCertA, serverKeyA := s.GenerateCert(ginkgo.GinkgoT(), []string{hostA})
err = s.NewSecret(secretA, serverCertA.String(), serverKeyA.String())
assert.Nil(ginkgo.GinkgoT(), err, "create server cert secret 'a' error")
err = s.NewApisixTls("tls-server-a", hostA, secretA)
assert.Nil(ginkgo.GinkgoT(), err, "create ApisixTls 'a' error")
ac := `
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
name: foo
spec:
authParameter:
jwtAuth:
value:
key: foo-key
`
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ac), "create ApisixConsumer")
agr := `
apiVersion: apisix.apache.org/v2
kind: ApisixGlobalRule
metadata:
name: test-agr-1
spec:
plugins:
- name: echo
enable: true
config:
body: "hello, world!!"
`
assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(agr), "create ApisixGlobalRule")
defer func() {
s.DeleteResource("ApisixGlobalRule", "test-agr-1")
}()
apc := fmt.Sprintf(`
apiVersion: apisix.apache.org/v2
kind: ApisixPluginConfig
metadata:
name: test-apc-1
spec:
plugins:
- name: cors
enable: true
`)
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(apc), "create ApisixPluginConfig")
// ensure resources exist, so ResourceSync will be triggered
time.Sleep(6 * time.Second)
routes, err := s.ListApisixRoutes()
assert.Nil(ginkgo.GinkgoT(), err)
assert.True(ginkgo.GinkgoT(), len(routes) > 0)
ups, err := s.ListApisixUpstreams()
assert.Nil(ginkgo.GinkgoT(), err)
assert.True(ginkgo.GinkgoT(), len(ups) > 0)
srs, err := s.ListApisixStreamRoutes()
assert.Nil(ginkgo.GinkgoT(), err)
assert.True(ginkgo.GinkgoT(), len(srs) > 0)
ssls, err := s.ListApisixSsl()
assert.Nil(ginkgo.GinkgoT(), err)
assert.True(ginkgo.GinkgoT(), len(ssls) > 0)
consumers, err := s.ListApisixConsumers()
assert.Nil(ginkgo.GinkgoT(), err)
assert.True(ginkgo.GinkgoT(), len(consumers) > 0)
grs, err := s.ListApisixGlobalRules()
assert.Nil(ginkgo.GinkgoT(), err)
assert.True(ginkgo.GinkgoT(), len(grs) > 0)
pcs, err := s.ListApisixPluginConfig()
assert.Nil(ginkgo.GinkgoT(), err)
assert.True(ginkgo.GinkgoT(), len(pcs) > 0)
// Check request counts
resTypes := []string{"route", "upstream", "ssl", "streamRoute",
"consumer", "globalRule", "pluginConfig",
}
countersBeforeWait := map[string]int{}
for _, resType := range resTypes {
countersBeforeWait[resType] = getApisixResourceRequestsCount(s, resType)
}
sleepMins := 2
log.Infof("before sleep requests count: %v, wait for %v mins ...", countersBeforeWait, sleepMins)
time.Sleep(time.Second * time.Duration(60*sleepMins+10))
countersExpectedIncrement := map[string]int{
"route": sleepMins,
"upstream": sleepMins * 2,
"streamRoute": sleepMins,
// these resources have only 1 instance, so the last triggered sync event
// haven't been processed yet
"ssl": sleepMins,
"consumer": sleepMins,
"globalRule": sleepMins,
"pluginConfig": sleepMins,
}
countersAfterWait := map[string]int{}
for _, resType := range resTypes {
countersAfterWait[resType] = getApisixResourceRequestsCount(s, resType)
expected := countersBeforeWait[resType] + countersExpectedIncrement[resType]
if countersAfterWait[resType] < expected {
log.Errorf("request count: %v expect %v but got %v", resType, expected, countersAfterWait[resType])
}
}
log.Infof("after sleep requests count: %v, wait for %v mins", countersAfterWait, sleepMins)
for _, resType := range resTypes {
expected := countersBeforeWait[resType] + countersExpectedIncrement[resType]
assert.NotEqual(ginkgo.GinkgoT(), countersBeforeWait[resType], countersAfterWait[resType], resType+" request count")
assert.GreaterOrEqual(ginkgo.GinkgoT(), countersAfterWait[resType], expected, resType+" request count")
}
})
ginkgo.It("check multiple resources request count", func() {
agr := `
apiVersion: apisix.apache.org/v2
kind: ApisixGlobalRule
metadata:
name: test-agr-1
spec:
plugins:
- name: echo
enable: true
config:
body: "hello, world!!"
---
apiVersion: apisix.apache.org/v2
kind: ApisixGlobalRule
metadata:
name: test-agr-2
spec:
plugins:
- name: echo
enable: true
config:
body: "hello, world2!!"
---
apiVersion: apisix.apache.org/v2
kind: ApisixGlobalRule
metadata:
name: test-agr-3
spec:
plugins:
- name: echo
enable: true
config:
body: "hello, world3!!"
`
assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(agr), "create ApisixGlobalRule")
defer func() {
s.DeleteResource("ApisixGlobalRule", "test-agr-1")
s.DeleteResource("ApisixGlobalRule", "test-agr-2")
s.DeleteResource("ApisixGlobalRule", "test-agr-3")
}()
// ensure resources exist, so ResourceSync will be triggered
time.Sleep(6 * time.Second)
grs, err := s.ListApisixGlobalRules()
assert.Nil(ginkgo.GinkgoT(), err)
assert.Equal(ginkgo.GinkgoT(), 3, len(grs))
// Check request counts
resTypes := []string{"globalRule"}
countersBeforeWait := map[string]int{}
for _, resType := range resTypes {
countersBeforeWait[resType] = getApisixResourceRequestsCount(s, resType)
}
sleepMins := 2
log.Infof("before sleep requests count: %v, wait for %v mins ...", countersBeforeWait, sleepMins)
time.Sleep(time.Second * time.Duration(60*sleepMins+3))
countersExpectedIncrement := map[string]int{
"globalRule": (sleepMins)*3 - 2,
}
countersAfterWait := map[string]int{}
for _, resType := range resTypes {
countersAfterWait[resType] = getApisixResourceRequestsCount(s, resType)
expected := countersBeforeWait[resType] + countersExpectedIncrement[resType]
if countersAfterWait[resType] <= expected {
log.Errorf("request count: %v expect %v but got %v", resType, expected, countersAfterWait[resType])
}
}
log.Infof("after sleep requests count: %v, wait for %v mins", countersAfterWait, sleepMins)
for _, resType := range resTypes {
expected := countersBeforeWait[resType] + countersExpectedIncrement[resType]
assert.NotEqual(ginkgo.GinkgoT(), countersBeforeWait[resType], countersAfterWait[resType], resType+" request count")
assert.GreaterOrEqual(ginkgo.GinkgoT(), countersAfterWait[resType], expected, resType+" request count")
}
})
}
ginkgo.Describe("scaffold v2", func() {
suites(scaffold.NewV2Scaffold(&scaffold.Options{
ApisixResourceSyncInterval: "60s",
ApisixResourceSyncComparison: "false",
}))
})
})