This is an automated email from the ASF dual-hosted git repository.
ronething pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git
The following commit(s) were added to refs/heads/master by this push:
new 501b4e89 test: add e2e test case for webhook (#2585)
501b4e89 is described below
commit 501b4e89f2567a8166660131d36511824cf13375
Author: Ashing Zheng <[email protected]>
AuthorDate: Tue Sep 30 14:24:03 2025 +0800
test: add e2e test case for webhook (#2585)
Signed-off-by: Ashing Zheng <[email protected]>
---
test/e2e/apisix/e2e_test.go | 1 +
test/e2e/framework/manifests/webhook.yaml | 194 +++++++++++++++++++--
test/e2e/webhook/apisixconsumer.go | 88 ++++++++++
test/e2e/webhook/apisixroute.go | 117 +++++++++++++
test/e2e/webhook/apisixtls.go | 111 ++++++++++++
test/e2e/webhook/consumer.go | 93 ++++++++++
.../{gatewayapi/webhook.go => webhook/gateway.go} | 2 +-
test/e2e/webhook/gatewayproxy.go | 129 ++++++++++++++
.../{apisix/e2e_test.go => webhook/grpcroute.go} | 40 ++---
test/e2e/webhook/helpers.go | 116 ++++++++++++
.../{apisix/e2e_test.go => webhook/httproute.go} | 40 ++---
.../e2e/{ingress/webhook.go => webhook/ingress.go} | 2 +-
.../ingressclass.go} | 2 +-
test/e2e/webhook/tcproute.go | 121 +++++++++++++
14 files changed, 1000 insertions(+), 56 deletions(-)
diff --git a/test/e2e/apisix/e2e_test.go b/test/e2e/apisix/e2e_test.go
index 03fe0ca6..fde91636 100644
--- a/test/e2e/apisix/e2e_test.go
+++ b/test/e2e/apisix/e2e_test.go
@@ -30,6 +30,7 @@ import (
_ "github.com/apache/apisix-ingress-controller/test/e2e/gatewayapi"
_ "github.com/apache/apisix-ingress-controller/test/e2e/ingress"
"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+ _ "github.com/apache/apisix-ingress-controller/test/e2e/webhook"
)
// TestAPISIXE2E runs e2e tests using the APISIX standalone mode
diff --git a/test/e2e/framework/manifests/webhook.yaml
b/test/e2e/framework/manifests/webhook.yaml
index 432873c9..954ce72d 100644
--- a/test/e2e/framework/manifests/webhook.yaml
+++ b/test/e2e/framework/manifests/webhook.yaml
@@ -20,6 +20,174 @@ kind: ValidatingWebhookConfiguration
metadata:
name: test-webhook-{{ .Namespace }}
webhooks:
+- name: vapisixconsumer-v2.kb.io
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: {{ .Namespace }}
+ path: /validate-apisix-apache-org-v2-apisixconsumer
+ caBundle: {{ .CABundle }}
+ admissionReviewVersions:
+ - v1
+ rules:
+ - operations:
+ - CREATE
+ - UPDATE
+ apiGroups:
+ - apisix.apache.org
+ apiVersions:
+ - v2
+ resources:
+ - apisixconsumers
+ failurePolicy: Fail
+ sideEffects: None
+- name: vapisixroute-v2.kb.io
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: {{ .Namespace }}
+ path: /validate-apisix-apache-org-v2-apisixroute
+ caBundle: {{ .CABundle }}
+ admissionReviewVersions:
+ - v1
+ rules:
+ - operations:
+ - CREATE
+ - UPDATE
+ apiGroups:
+ - apisix.apache.org
+ apiVersions:
+ - v2
+ resources:
+ - apisixroutes
+ failurePolicy: Fail
+ sideEffects: None
+- name: vapisixtls-v2.kb.io
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: {{ .Namespace }}
+ path: /validate-apisix-apache-org-v2-apisixtls
+ caBundle: {{ .CABundle }}
+ admissionReviewVersions:
+ - v1
+ rules:
+ - operations:
+ - CREATE
+ - UPDATE
+ apiGroups:
+ - apisix.apache.org
+ apiVersions:
+ - v2
+ resources:
+ - apisixtlses
+ failurePolicy: Fail
+ sideEffects: None
+- name: vconsumer-v1alpha1.kb.io
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: {{ .Namespace }}
+ path: /validate-apisix-apache-org-v1alpha1-consumer
+ caBundle: {{ .CABundle }}
+ admissionReviewVersions:
+ - v1
+ rules:
+ - operations:
+ - CREATE
+ - UPDATE
+ apiGroups:
+ - apisix.apache.org
+ apiVersions:
+ - v1alpha1
+ resources:
+ - consumers
+ failurePolicy: Fail
+ sideEffects: None
+- name: vgateway-v1.kb.io
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: {{ .Namespace }}
+ path: /validate-gateway-networking-k8s-io-v1-gateway
+ caBundle: {{ .CABundle }}
+ admissionReviewVersions:
+ - v1
+ rules:
+ - operations:
+ - CREATE
+ - UPDATE
+ apiGroups:
+ - gateway.networking.k8s.io
+ apiVersions:
+ - v1
+ resources:
+ - gateways
+ failurePolicy: Fail
+ sideEffects: None
+- name: vgatewayproxy-v1alpha1.kb.io
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: {{ .Namespace }}
+ path: /validate-apisix-apache-org-v1alpha1-gatewayproxy
+ caBundle: {{ .CABundle }}
+ admissionReviewVersions:
+ - v1
+ rules:
+ - operations:
+ - CREATE
+ - UPDATE
+ apiGroups:
+ - apisix.apache.org
+ apiVersions:
+ - v1alpha1
+ resources:
+ - gatewayproxies
+ failurePolicy: Fail
+ sideEffects: None
+- name: vgrpcroute-v1.kb.io
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: {{ .Namespace }}
+ path: /validate-gateway-networking-k8s-io-v1-grpcroute
+ caBundle: {{ .CABundle }}
+ admissionReviewVersions:
+ - v1
+ rules:
+ - operations:
+ - CREATE
+ - UPDATE
+ apiGroups:
+ - gateway.networking.k8s.io
+ apiVersions:
+ - v1
+ resources:
+ - grpcroutes
+ failurePolicy: Fail
+ sideEffects: None
+- name: vhttproute-v1.kb.io
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: {{ .Namespace }}
+ path: /validate-gateway-networking-k8s-io-v1-httproute
+ caBundle: {{ .CABundle }}
+ admissionReviewVersions:
+ - v1
+ rules:
+ - operations:
+ - CREATE
+ - UPDATE
+ apiGroups:
+ - gateway.networking.k8s.io
+ apiVersions:
+ - v1
+ resources:
+ - httproutes
+ failurePolicy: Fail
+ sideEffects: None
- name: vingress-v1.kb.io
clientConfig:
service:
@@ -30,15 +198,15 @@ webhooks:
admissionReviewVersions:
- v1
rules:
- - operations:
- - CREATE
- - UPDATE
- apiGroups:
- - networking.k8s.io
- apiVersions:
- - v1
- resources:
- - ingresses
+ - operations:
+ - CREATE
+ - UPDATE
+ apiGroups:
+ - networking.k8s.io
+ apiVersions:
+ - v1
+ resources:
+ - ingresses
failurePolicy: Fail
sideEffects: None
- name: vingressclass-v1.kb.io
@@ -62,12 +230,12 @@ webhooks:
- ingressclasses
failurePolicy: Fail
sideEffects: None
-- name: vgateway-v1.kb.io
+- name: vtcproute-v1alpha2.kb.io
clientConfig:
service:
name: webhook-service
namespace: {{ .Namespace }}
- path: /validate-gateway-networking-k8s-io-v1-gateway
+ path: /validate-gateway-networking-k8s-io-v1alpha2-tcproute
caBundle: {{ .CABundle }}
admissionReviewVersions:
- v1
@@ -78,8 +246,8 @@ webhooks:
apiGroups:
- gateway.networking.k8s.io
apiVersions:
- - v1
+ - v1alpha2
resources:
- - gateways
+ - tcproutes
failurePolicy: Fail
sideEffects: None
diff --git a/test/e2e/webhook/apisixconsumer.go
b/test/e2e/webhook/apisixconsumer.go
new file mode 100644
index 00000000..7aa1a256
--- /dev/null
+++ b/test/e2e/webhook/apisixconsumer.go
@@ -0,0 +1,88 @@
+// 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 webhook
+
+import (
+ "fmt"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = Describe("Test ApisixConsumer Webhook", Label("webhook"), func() {
+ s := scaffold.NewScaffold(scaffold.Options{
+ Name: "apisixconsumer-webhook-test",
+ EnableWebhook: true,
+ })
+
+ BeforeEach(func() {
+ By("creating GatewayProxy")
+ err := s.CreateResourceFromString(s.GetGatewayProxySpec())
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
+ time.Sleep(5 * time.Second)
+
+ By("creating IngressClass")
+ err =
s.CreateResourceFromStringWithNamespace(s.GetIngressClassYaml(), "")
+ Expect(err).NotTo(HaveOccurred(), "creating IngressClass")
+ time.Sleep(5 * time.Second)
+ })
+
+ It("should warn on missing authentication secrets", func() {
+ missingSecret := "missing-basic-secret"
+ consumerName := "webhook-apisixconsumer"
+ consumerYAML := `
+apiVersion: apisix.apache.org/v2
+kind: ApisixConsumer
+metadata:
+ name: %s
+ namespace: %s
+spec:
+ ingressClassName: %s
+ authParameter:
+ basicAuth:
+ secretRef:
+ name: %s
+`
+
+ output, err :=
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(consumerYAML, consumerName,
s.Namespace(), s.Namespace(), missingSecret))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), missingSecret)))
+
+ By("creating referenced secret")
+ secretYAML := fmt.Sprintf(`
+apiVersion: v1
+kind: Secret
+metadata:
+ name: %s
+stringData:
+ username: demo
+ password: demo
+`, missingSecret)
+ err = s.CreateResourceFromString(secretYAML)
+ Expect(err).NotTo(HaveOccurred(), "creating basic auth secret")
+
+ time.Sleep(2 * time.Second)
+
+ output, err =
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(consumerYAML, consumerName,
s.Namespace(), s.Namespace(), missingSecret))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), missingSecret)))
+ })
+})
diff --git a/test/e2e/webhook/apisixroute.go b/test/e2e/webhook/apisixroute.go
new file mode 100644
index 00000000..51904f43
--- /dev/null
+++ b/test/e2e/webhook/apisixroute.go
@@ -0,0 +1,117 @@
+// 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 webhook
+
+import (
+ "fmt"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = Describe("Test ApisixRoute Webhook", Label("webhook"), func() {
+ s := scaffold.NewScaffold(scaffold.Options{
+ Name: "apisixroute-webhook-test",
+ EnableWebhook: true,
+ })
+
+ BeforeEach(func() {
+ By("creating GatewayProxy")
+ err := s.CreateResourceFromString(s.GetGatewayProxySpec())
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
+ time.Sleep(5 * time.Second)
+
+ By("creating IngressClass")
+ err =
s.CreateResourceFromStringWithNamespace(s.GetIngressClassYaml(), "")
+ Expect(err).NotTo(HaveOccurred(), "creating IngressClass")
+ time.Sleep(5 * time.Second)
+ })
+
+ It("should warn on missing service or secret references", func() {
+ missingService := "missing-backend"
+ missingSecret := "missing-plugin-secret"
+ routeName := "webhook-apisixroute"
+ routeYAML := `
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: %s
+ namespace: %s
+spec:
+ ingressClassName: %s
+ http:
+ - name: rule-webhook
+ match:
+ hosts:
+ - webhook.example.com
+ paths:
+ - /webhook
+ backends:
+ - serviceName: %s
+ servicePort: 80
+ plugins:
+ - name: echo
+ enable: true
+ secretRef: %s
+`
+
+ output, err :=
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(routeYAML, routeName,
s.Namespace(), s.Namespace(), missingService, missingSecret))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning:
Referenced Service '%s/%s' not found", s.Namespace(), missingService)))
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), missingSecret)))
+
+ By("creating referenced Service and Secret")
+ serviceYAML := fmt.Sprintf(`
+apiVersion: v1
+kind: Service
+metadata:
+ name: %s
+spec:
+ selector:
+ app: placeholder
+ ports:
+ - name: http
+ port: 80
+ targetPort: 80
+ type: ClusterIP
+`, missingService)
+ err = s.CreateResourceFromString(serviceYAML)
+ Expect(err).NotTo(HaveOccurred(), "creating backend service
placeholder")
+
+ secretYAML := fmt.Sprintf(`
+apiVersion: v1
+kind: Secret
+metadata:
+ name: %s
+stringData:
+ config: enabled
+`, missingSecret)
+ err = s.CreateResourceFromString(secretYAML)
+ Expect(err).NotTo(HaveOccurred(), "creating plugin secret
placeholder")
+
+ time.Sleep(2 * time.Second)
+
+ output, err =
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(routeYAML, routeName,
s.Namespace(), s.Namespace(), missingService, missingSecret))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Referenced Service '%s/%s' not found", s.Namespace(), missingService)))
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), missingSecret)))
+ })
+})
diff --git a/test/e2e/webhook/apisixtls.go b/test/e2e/webhook/apisixtls.go
new file mode 100644
index 00000000..08defed9
--- /dev/null
+++ b/test/e2e/webhook/apisixtls.go
@@ -0,0 +1,111 @@
+// 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 webhook
+
+import (
+ "fmt"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = Describe("Test ApisixTls Webhook", Label("webhook"), func() {
+ s := scaffold.NewScaffold(scaffold.Options{
+ Name: "apisixtls-webhook-test",
+ EnableWebhook: true,
+ })
+
+ BeforeEach(func() {
+ By("creating GatewayProxy")
+ err := s.CreateResourceFromString(s.GetGatewayProxySpec())
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
+ time.Sleep(5 * time.Second)
+
+ By("creating IngressClass")
+ err =
s.CreateResourceFromStringWithNamespace(s.GetIngressClassYaml(), "")
+ Expect(err).NotTo(HaveOccurred(), "creating IngressClass")
+ time.Sleep(5 * time.Second)
+ })
+
+ It("should warn on missing TLS secrets", func() {
+ serverSecret := "missing-server-tls"
+ clientSecret := "missing-client-ca"
+ tlsName := "webhook-apisixtls"
+ tlsYAML := `
+apiVersion: apisix.apache.org/v2
+kind: ApisixTls
+metadata:
+ name: %s
+ namespace: %s
+spec:
+ ingressClassName: %s
+ hosts:
+ - webhook.example.com
+ secret:
+ name: %s
+ namespace: %s
+ client:
+ caSecret:
+ name: %s
+ namespace: %s
+`
+
+ output, err :=
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(tlsYAML, tlsName,
s.Namespace(), s.Namespace(), serverSecret, s.Namespace(), clientSecret,
s.Namespace()))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), serverSecret)))
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), clientSecret)))
+
+ By("creating referenced TLS secrets")
+ serverSecretYAML := fmt.Sprintf(`
+apiVersion: v1
+kind: Secret
+metadata:
+ name: %s
+ namespace: %s
+type: kubernetes.io/tls
+stringData:
+ tls.crt: dummy-cert
+ tls.key: dummy-key
+`, serverSecret, s.Namespace())
+ err = s.CreateResourceFromString(serverSecretYAML)
+ Expect(err).NotTo(HaveOccurred(), "creating server TLS secret")
+
+ clientSecretYAML := fmt.Sprintf(`
+apiVersion: v1
+kind: Secret
+metadata:
+ name: %s
+ namespace: %s
+type: Opaque
+stringData:
+ ca.crt: dummy-ca
+`, clientSecret, s.Namespace())
+ err = s.CreateResourceFromString(clientSecretYAML)
+ Expect(err).NotTo(HaveOccurred(), "creating client CA secret")
+
+ time.Sleep(2 * time.Second)
+
+ output, err =
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(tlsYAML, tlsName,
s.Namespace(), s.Namespace(), serverSecret, s.Namespace(), clientSecret,
s.Namespace()))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), serverSecret)))
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), clientSecret)))
+ })
+})
diff --git a/test/e2e/webhook/consumer.go b/test/e2e/webhook/consumer.go
new file mode 100644
index 00000000..676adbb8
--- /dev/null
+++ b/test/e2e/webhook/consumer.go
@@ -0,0 +1,93 @@
+// 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 webhook
+
+import (
+ "fmt"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = Describe("Test Consumer Webhook", Label("webhook"), func() {
+ s := scaffold.NewScaffold(scaffold.Options{
+ Name: "consumer-webhook-test",
+ EnableWebhook: true,
+ })
+
+ BeforeEach(func() {
+ By("creating GatewayProxy")
+ err := s.CreateResourceFromString(s.GetGatewayProxySpec())
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
+ time.Sleep(5 * time.Second)
+
+ By("creating GatewayClass")
+ err = s.CreateResourceFromString(s.GetGatewayClassYaml())
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayClass")
+ time.Sleep(2 * time.Second)
+
+ By("creating Gateway")
+ err = s.CreateResourceFromString(s.GetGatewayYaml())
+ Expect(err).NotTo(HaveOccurred(), "creating Gateway")
+ time.Sleep(5 * time.Second)
+ })
+
+ It("should warn on missing secret references", func() {
+ missingSecret := "missing-consumer-secret"
+ consumerName := "webhook-consumer"
+ gatewayName := s.Namespace()
+ consumerYAML := `
+apiVersion: apisix.apache.org/v1alpha1
+kind: Consumer
+metadata:
+ name: %s
+spec:
+ gatewayRef:
+ name: %s
+ credentials:
+ - type: jwt-auth
+ secretRef:
+ name: %s
+`
+
+ output, err :=
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(consumerYAML, consumerName,
gatewayName, missingSecret))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), missingSecret)))
+
+ By("creating referenced secret")
+ secretYAML := fmt.Sprintf(`
+apiVersion: v1
+kind: Secret
+metadata:
+ name: %s
+stringData:
+ token: %s
+`, missingSecret, s.AdminKey())
+ err = s.CreateResourceFromString(secretYAML)
+ Expect(err).NotTo(HaveOccurred(), "creating consumer secret")
+
+ time.Sleep(2 * time.Second)
+
+ output, err =
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(consumerYAML, consumerName,
gatewayName, missingSecret))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), missingSecret)))
+ })
+})
diff --git a/test/e2e/gatewayapi/webhook.go b/test/e2e/webhook/gateway.go
similarity index 99%
rename from test/e2e/gatewayapi/webhook.go
rename to test/e2e/webhook/gateway.go
index 92f65f93..09bfc3ce 100644
--- a/test/e2e/gatewayapi/webhook.go
+++ b/test/e2e/webhook/gateway.go
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-package gatewayapi
+package webhook
import (
"fmt"
diff --git a/test/e2e/webhook/gatewayproxy.go b/test/e2e/webhook/gatewayproxy.go
new file mode 100644
index 00000000..6b1f6189
--- /dev/null
+++ b/test/e2e/webhook/gatewayproxy.go
@@ -0,0 +1,129 @@
+// 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 webhook
+
+import (
+ "fmt"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = Describe("Test GatewayProxy Webhook", Label("webhook"), func() {
+ s := scaffold.NewScaffold(scaffold.Options{
+ Name: "gatewayproxy-webhook-test",
+ EnableWebhook: true,
+ })
+
+ It("should warn on missing service or secret references", func() {
+ missingService := "missing-control-plane"
+ missingSecret := "missing-admin-secret"
+ gpName := "webhook-gateway-proxy"
+ gpWithSecrets := `
+apiVersion: apisix.apache.org/v1alpha1
+kind: GatewayProxy
+metadata:
+ name: %s
+spec:
+ provider:
+ type: ControlPlane
+ controlPlane:
+ service:
+ name: %s
+ port: 9180
+ auth:
+ type: AdminKey
+ adminKey:
+ valueFrom:
+ secretKeyRef:
+ name: %s
+ key: token
+`
+
+ output, err :=
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(gpWithSecrets, gpName,
missingService, missingSecret))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning:
Referenced Service '%s/%s' not found", s.Namespace(), missingService)))
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning:
Referenced Secret '%s/%s' not found", s.Namespace(), missingSecret)))
+
+ By("creating the referenced Service and Secret without the
required key")
+ serviceYAML := fmt.Sprintf(`
+apiVersion: v1
+kind: Service
+metadata:
+ name: %s
+spec:
+ selector:
+ app: placeholder
+ ports:
+ - name: admin
+ port: 9180
+ targetPort: 9180
+ type: ClusterIP
+`, missingService)
+ err = s.CreateResourceFromString(serviceYAML)
+ Expect(err).NotTo(HaveOccurred(), "creating placeholder
service")
+
+ secretWithoutKey := fmt.Sprintf(`
+apiVersion: v1
+kind: Secret
+metadata:
+ name: %s
+stringData:
+ another: value
+`, missingSecret)
+ err = s.CreateResourceFromString(secretWithoutKey)
+ Expect(err).NotTo(HaveOccurred(), "creating placeholder secret
without token key")
+
+ time.Sleep(2 * time.Second)
+
+ By("delete and reapply the GatewayProxy, because gatewayproxy
has no change")
+ err = s.DeleteResource("GatewayProxy", gpName)
+ Expect(err).ShouldNot(HaveOccurred())
+
+ output, err =
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(gpWithSecrets, gpName,
missingService, missingSecret))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Referenced Service '%s/%s' not found", s.Namespace(), missingService)))
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning: Secret
key 'token' not found in Secret '%s/%s'", s.Namespace(), missingSecret)))
+
+ By("updating the Secret to include the expected key")
+ secretWithKey := fmt.Sprintf(`
+apiVersion: v1
+kind: Secret
+metadata:
+ name: %s
+stringData:
+ token: %s
+`, missingSecret, s.AdminKey())
+ err = s.CreateResourceFromString(secretWithKey)
+ Expect(err).NotTo(HaveOccurred(), "adding token key to secret")
+
+ time.Sleep(2 * time.Second)
+
+ By("delete and reapply the GatewayProxy, because gatewayproxy
has no change")
+ err = s.DeleteResource("GatewayProxy", gpName)
+ Expect(err).ShouldNot(HaveOccurred())
+
+ output, err =
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(gpWithSecrets, gpName,
missingService, missingSecret))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Referenced Service '%s/%s' not found", s.Namespace(), missingService)))
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Secret key 'token' not found in Secret '%s/%s'", s.Namespace(), missingSecret)))
+ })
+})
diff --git a/test/e2e/apisix/e2e_test.go b/test/e2e/webhook/grpcroute.go
similarity index 53%
copy from test/e2e/apisix/e2e_test.go
copy to test/e2e/webhook/grpcroute.go
index 03fe0ca6..0954c969 100644
--- a/test/e2e/apisix/e2e_test.go
+++ b/test/e2e/webhook/grpcroute.go
@@ -15,32 +15,32 @@
// specific language governing permissions and limitations
// under the License.
-package apisix
+package webhook
import (
- "fmt"
- "testing"
-
. "github.com/onsi/ginkgo/v2"
- . "github.com/onsi/gomega"
- _ "github.com/apache/apisix-ingress-controller/test/e2e/crds/v1alpha1"
- _ "github.com/apache/apisix-ingress-controller/test/e2e/crds/v2"
- "github.com/apache/apisix-ingress-controller/test/e2e/framework"
- _ "github.com/apache/apisix-ingress-controller/test/e2e/gatewayapi"
- _ "github.com/apache/apisix-ingress-controller/test/e2e/ingress"
"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
)
-// TestAPISIXE2E runs e2e tests using the APISIX standalone mode
-func TestAPISIXE2E(t *testing.T) {
- RegisterFailHandler(Fail)
- // init framework
- _ = framework.NewFramework()
+var _ = Describe("Test GRPCRoute Webhook", Label("webhook"), func() {
+ s := scaffold.NewScaffold(scaffold.Options{
+ Name: "grpcroute-webhook-test",
+ EnableWebhook: true,
+ })
- // init newDeployer function
- scaffold.NewDeployer = scaffold.NewAPISIXDeployer
+ BeforeEach(func() {
+ setupGatewayResources(s)
+ })
- _, _ = fmt.Fprintf(GinkgoWriter, "Starting APISIX standalone e2e
suite\n")
- RunSpecs(t, "apisix standalone e2e suite")
-}
+ It("should warn on missing backend services", func() {
+ verifyMissingBackendWarnings(s, routeWebhookTestCase{
+ routeKind: "GRPCRoute",
+ routeName: "webhook-grpcroute",
+ missingService: "missing-grpc-backend",
+ mirrorService: "missing-grpc-mirror",
+ servicePortName: "grpc",
+ servicePort: 8080,
+ })
+ })
+})
diff --git a/test/e2e/webhook/helpers.go b/test/e2e/webhook/helpers.go
new file mode 100644
index 00000000..696e81df
--- /dev/null
+++ b/test/e2e/webhook/helpers.go
@@ -0,0 +1,116 @@
+// 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 webhook
+
+import (
+ "fmt"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+type routeWebhookTestCase struct {
+ routeKind string
+ routeName string
+ missingService string
+ mirrorService string
+ servicePortName string
+ servicePort int
+}
+
+func setupGatewayResources(s *scaffold.Scaffold) {
+ By("creating GatewayProxy")
+ err := s.CreateResourceFromString(s.GetGatewayProxySpec())
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
+ time.Sleep(5 * time.Second)
+
+ By("creating GatewayClass")
+ err = s.CreateResourceFromString(s.GetGatewayClassYaml())
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayClass")
+ time.Sleep(2 * time.Second)
+
+ By("creating Gateway")
+ err = s.CreateResourceFromString(s.GetGatewayYaml())
+ Expect(err).NotTo(HaveOccurred(), "creating Gateway")
+ time.Sleep(5 * time.Second)
+}
+
+func verifyMissingBackendWarnings(s *scaffold.Scaffold, tc
routeWebhookTestCase) {
+ gatewayName := s.Namespace()
+ routeYAML := fmt.Sprintf(`
+apiVersion: gateway.networking.k8s.io/v1
+kind: %s
+metadata:
+ name: %s
+spec:
+ parentRefs:
+ - name: %s
+ rules:
+ - backendRefs:
+ - name: %s
+ port: %d
+ filters:
+ - type: RequestMirror
+ requestMirror:
+ backendRef:
+ name: %s
+ port: %d
+`, tc.routeKind, tc.routeName, gatewayName, tc.missingService, tc.servicePort,
tc.mirrorService, tc.servicePort)
+
+ missingBackendWarning := fmt.Sprintf("Warning: Referenced Service
'%s/%s' not found", gatewayName, tc.missingService)
+ mirrorBackendWarning := fmt.Sprintf("Warning: Referenced Service
'%s/%s' not found", gatewayName, tc.mirrorService)
+
+ output, err := s.CreateResourceFromStringAndGetOutput(routeYAML)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).To(ContainSubstring(missingBackendWarning))
+ Expect(output).To(ContainSubstring(mirrorBackendWarning))
+
+ By(fmt.Sprintf("creating referenced backend services for %s",
tc.routeKind))
+ serviceYAML := `
+apiVersion: v1
+kind: Service
+metadata:
+ name: %s
+spec:
+ selector:
+ app: placeholder
+ ports:
+ - name: %s
+ port: %d
+ targetPort: %d
+ type: ClusterIP
+`
+
+ backendService := fmt.Sprintf(serviceYAML, tc.missingService,
tc.servicePortName, tc.servicePort, tc.servicePort)
+ err = s.CreateResourceFromString(backendService)
+ Expect(err).NotTo(HaveOccurred(), "creating primary backend service")
+
+ mirrorService := fmt.Sprintf(serviceYAML, tc.mirrorService,
tc.servicePortName, tc.servicePort, tc.servicePort)
+ err = s.CreateResourceFromString(mirrorService)
+ Expect(err).NotTo(HaveOccurred(), "creating mirror backend service")
+
+ time.Sleep(2 * time.Second)
+
+ output, err = s.CreateResourceFromStringAndGetOutput(routeYAML)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).NotTo(ContainSubstring(missingBackendWarning))
+ Expect(output).NotTo(ContainSubstring(mirrorBackendWarning))
+}
diff --git a/test/e2e/apisix/e2e_test.go b/test/e2e/webhook/httproute.go
similarity index 53%
copy from test/e2e/apisix/e2e_test.go
copy to test/e2e/webhook/httproute.go
index 03fe0ca6..240d7686 100644
--- a/test/e2e/apisix/e2e_test.go
+++ b/test/e2e/webhook/httproute.go
@@ -15,32 +15,32 @@
// specific language governing permissions and limitations
// under the License.
-package apisix
+package webhook
import (
- "fmt"
- "testing"
-
. "github.com/onsi/ginkgo/v2"
- . "github.com/onsi/gomega"
- _ "github.com/apache/apisix-ingress-controller/test/e2e/crds/v1alpha1"
- _ "github.com/apache/apisix-ingress-controller/test/e2e/crds/v2"
- "github.com/apache/apisix-ingress-controller/test/e2e/framework"
- _ "github.com/apache/apisix-ingress-controller/test/e2e/gatewayapi"
- _ "github.com/apache/apisix-ingress-controller/test/e2e/ingress"
"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
)
-// TestAPISIXE2E runs e2e tests using the APISIX standalone mode
-func TestAPISIXE2E(t *testing.T) {
- RegisterFailHandler(Fail)
- // init framework
- _ = framework.NewFramework()
+var _ = Describe("Test HTTPRoute Webhook", Label("webhook"), func() {
+ s := scaffold.NewScaffold(scaffold.Options{
+ Name: "httproute-webhook-test",
+ EnableWebhook: true,
+ })
- // init newDeployer function
- scaffold.NewDeployer = scaffold.NewAPISIXDeployer
+ BeforeEach(func() {
+ setupGatewayResources(s)
+ })
- _, _ = fmt.Fprintf(GinkgoWriter, "Starting APISIX standalone e2e
suite\n")
- RunSpecs(t, "apisix standalone e2e suite")
-}
+ It("should warn on missing backend services", func() {
+ verifyMissingBackendWarnings(s, routeWebhookTestCase{
+ routeKind: "HTTPRoute",
+ routeName: "webhook-httproute",
+ missingService: "missing-http-backend",
+ mirrorService: "missing-http-mirror",
+ servicePortName: "http",
+ servicePort: 80,
+ })
+ })
+})
diff --git a/test/e2e/ingress/webhook.go b/test/e2e/webhook/ingress.go
similarity index 99%
rename from test/e2e/ingress/webhook.go
rename to test/e2e/webhook/ingress.go
index 310b6629..37608fb2 100644
--- a/test/e2e/ingress/webhook.go
+++ b/test/e2e/webhook/ingress.go
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-package ingress
+package webhook
import (
"fmt"
diff --git a/test/e2e/ingress/ingressclass_webhook.go
b/test/e2e/webhook/ingressclass.go
similarity index 99%
rename from test/e2e/ingress/ingressclass_webhook.go
rename to test/e2e/webhook/ingressclass.go
index 9c1f3174..8192a51b 100644
--- a/test/e2e/ingress/ingressclass_webhook.go
+++ b/test/e2e/webhook/ingressclass.go
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-package ingress
+package webhook
import (
"fmt"
diff --git a/test/e2e/webhook/tcproute.go b/test/e2e/webhook/tcproute.go
new file mode 100644
index 00000000..de226a33
--- /dev/null
+++ b/test/e2e/webhook/tcproute.go
@@ -0,0 +1,121 @@
+// 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 webhook
+
+import (
+ "fmt"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = Describe("Test TCPRoute Webhook", Label("webhook"), func() {
+ s := scaffold.NewScaffold(scaffold.Options{
+ Name: "tcproute-webhook-test",
+ EnableWebhook: true,
+ })
+
+ const tcpGateway = `
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ name: %s
+spec:
+ gatewayClassName: %s
+ listeners:
+ - name: tcp
+ protocol: TCP
+ port: 9000
+ allowedRoutes:
+ kinds:
+ - kind: TCPRoute
+ infrastructure:
+ parametersRef:
+ group: apisix.apache.org
+ kind: GatewayProxy
+ name: apisix-proxy-config
+`
+
+ BeforeEach(func() {
+ By("creating GatewayProxy")
+ err := s.CreateResourceFromString(s.GetGatewayProxySpec())
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
+ time.Sleep(5 * time.Second)
+
+ By("creating GatewayClass")
+ err = s.CreateResourceFromString(s.GetGatewayClassYaml())
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayClass")
+ time.Sleep(2 * time.Second)
+
+ By("creating Gateway with TCP listener")
+ err = s.CreateResourceFromString(fmt.Sprintf(tcpGateway,
s.Namespace(), s.Namespace()))
+ Expect(err).NotTo(HaveOccurred(), "creating TCP-capable
Gateway")
+ time.Sleep(5 * time.Second)
+ })
+
+ It("should warn on missing backend services", func() {
+ missingService := "missing-tcp-backend"
+ routeName := "webhook-tcproute"
+ gatewayName := s.Namespace()
+ routeYAML := `
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: TCPRoute
+metadata:
+ name: %s
+spec:
+ parentRefs:
+ - name: %s
+ sectionName: tcp
+ rules:
+ - backendRefs:
+ - name: %s
+ port: 80
+`
+
+ output, err :=
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(routeYAML, routeName,
gatewayName, missingService))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).To(ContainSubstring(fmt.Sprintf("Warning:
Referenced Service '%s/%s' not found", s.Namespace(), missingService)))
+
+ By("creating referenced backend service")
+ backendService := fmt.Sprintf(`
+apiVersion: v1
+kind: Service
+metadata:
+ name: %s
+spec:
+ selector:
+ app: placeholder
+ ports:
+ - name: tcp
+ port: 80
+ targetPort: 80
+ type: ClusterIP
+`, missingService)
+ err = s.CreateResourceFromString(backendService)
+ Expect(err).NotTo(HaveOccurred(), "creating tcp backend
service")
+
+ time.Sleep(2 * time.Second)
+
+ output, err =
s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(routeYAML, routeName,
gatewayName, missingService))
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning:
Referenced Service '%s/%s' not found", s.Namespace(), missingService)))
+ })
+})