You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ro...@apache.org on 2025/09/26 08:37:15 UTC
(apisix-ingress-controller) 06/06: feat: add apisix route and apisix consumer
This is an automated email from the ASF dual-hosted git repository.
ronething pushed a commit to branch feat/add_resource_verify
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git
commit 22da8f7091950de04f683b87554815611ac4399e
Author: Ashing Zheng <ax...@gmail.com>
AuthorDate: Fri Sep 26 16:36:03 2025 +0800
feat: add apisix route and apisix consumer
Signed-off-by: Ashing Zheng <ax...@gmail.com>
---
config/webhook/manifests.yaml | 40 ++++++
internal/manager/webhooks.go | 6 +
internal/webhook/v1/apisixroute_webhook.go | 139 +++++++++++++++++++++
internal/webhook/v1/apisixroute_webhook_test.go | 158 ++++++++++++++++++++++++
internal/webhook/v1/consumer_webhook.go | 105 ++++++++++++++++
internal/webhook/v1/consumer_webhook_test.go | 130 +++++++++++++++++++
6 files changed, 578 insertions(+)
diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml
index de7ced0e..b5b33201 100644
--- a/config/webhook/manifests.yaml
+++ b/config/webhook/manifests.yaml
@@ -24,6 +24,26 @@ webhooks:
resources:
- apisixconsumers
sideEffects: None
+- admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: system
+ path: /validate-apisix-apache-org-v2-apisixroute
+ failurePolicy: Fail
+ name: vapisixroute-v2.kb.io
+ rules:
+ - apiGroups:
+ - apisix.apache.org
+ apiVersions:
+ - v2
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - apisixroutes
+ sideEffects: None
- admissionReviewVersions:
- v1
clientConfig:
@@ -44,6 +64,26 @@ webhooks:
resources:
- apisixtlses
sideEffects: None
+- admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: system
+ path: /validate-apisix-apache-org-v1alpha1-consumer
+ failurePolicy: Fail
+ name: vconsumer-v1alpha1.kb.io
+ rules:
+ - apiGroups:
+ - apisix.apache.org
+ apiVersions:
+ - v1alpha1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - consumers
+ sideEffects: None
- admissionReviewVersions:
- v1
clientConfig:
diff --git a/internal/manager/webhooks.go b/internal/manager/webhooks.go
index 91242972..6907d762 100644
--- a/internal/manager/webhooks.go
+++ b/internal/manager/webhooks.go
@@ -44,5 +44,11 @@ func setupWebhooks(_ context.Context, mgr manager.Manager) error {
if err := webhookv1.SetupApisixTlsWebhookWithManager(mgr); err != nil {
return err
}
+ if err := webhookv1.SetupApisixRouteWebhookWithManager(mgr); err != nil {
+ return err
+ }
+ if err := webhookv1.SetupConsumerWebhookWithManager(mgr); err != nil {
+ return err
+ }
return nil
}
diff --git a/internal/webhook/v1/apisixroute_webhook.go b/internal/webhook/v1/apisixroute_webhook.go
new file mode 100644
index 00000000..a651f913
--- /dev/null
+++ b/internal/webhook/v1/apisixroute_webhook.go
@@ -0,0 +1,139 @@
+// 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 v1
+
+import (
+ "context"
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/types"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ apisixv2 "github.com/apache/apisix-ingress-controller/api/v2"
+ "github.com/apache/apisix-ingress-controller/internal/webhook/v1/reference"
+)
+
+var apisixRouteLog = logf.Log.WithName("apisixroute-resource")
+
+func SetupApisixRouteWebhookWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewWebhookManagedBy(mgr).
+ For(&apisixv2.ApisixRoute{}).
+ WithValidator(&ApisixRouteCustomValidator{Client: mgr.GetClient()}).
+ Complete()
+}
+
+// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixroute,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixroutes,verbs=create;update,versions=v2,name=vapisixroute-v2.kb.io,admissionReviewVersions=v1
+
+type ApisixRouteCustomValidator struct {
+ Client client.Client
+}
+
+var _ webhook.CustomValidator = &ApisixRouteCustomValidator{}
+
+func (v *ApisixRouteCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
+ route, ok := obj.(*apisixv2.ApisixRoute)
+ if !ok {
+ return nil, fmt.Errorf("expected an ApisixRoute object but got %T", obj)
+ }
+ apisixRouteLog.Info("Validation for ApisixRoute upon creation", "name", route.GetName(), "namespace", route.GetNamespace())
+
+ return v.collectWarnings(ctx, route), nil
+}
+
+func (v *ApisixRouteCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
+ route, ok := newObj.(*apisixv2.ApisixRoute)
+ if !ok {
+ return nil, fmt.Errorf("expected an ApisixRoute object for the newObj but got %T", newObj)
+ }
+ apisixRouteLog.Info("Validation for ApisixRoute upon update", "name", route.GetName(), "namespace", route.GetNamespace())
+
+ return v.collectWarnings(ctx, route), nil
+}
+
+func (*ApisixRouteCustomValidator) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) {
+ return nil, nil
+}
+
+func (v *ApisixRouteCustomValidator) collectWarnings(ctx context.Context, route *apisixv2.ApisixRoute) admission.Warnings {
+ checker := reference.NewChecker(v.Client, apisixRouteLog)
+ namespace := route.GetNamespace()
+
+ serviceVisited := make(map[types.NamespacedName]struct{})
+ secretVisited := make(map[types.NamespacedName]struct{})
+
+ var warnings admission.Warnings
+
+ addServiceWarning := func(nn types.NamespacedName) {
+ if nn.Name == "" || nn.Namespace == "" {
+ return
+ }
+ if _, seen := serviceVisited[nn]; seen {
+ return
+ }
+ serviceVisited[nn] = struct{}{}
+ warnings = append(warnings, checker.Service(ctx, reference.ServiceRef{
+ Object: route,
+ NamespacedName: nn,
+ })...)
+ }
+
+ addSecretWarning := func(nn types.NamespacedName) {
+ if nn.Name == "" || nn.Namespace == "" {
+ return
+ }
+ if _, seen := secretVisited[nn]; seen {
+ return
+ }
+ secretVisited[nn] = struct{}{}
+ warnings = append(warnings, checker.Secret(ctx, reference.SecretRef{
+ Object: route,
+ NamespacedName: nn,
+ })...)
+ }
+
+ for _, rule := range route.Spec.HTTP {
+ for _, backend := range rule.Backends {
+ addServiceWarning(types.NamespacedName{Namespace: namespace, Name: backend.ServiceName})
+ }
+ for _, plugin := range rule.Plugins {
+ if !plugin.Enable {
+ continue
+ }
+ if plugin.SecretRef != "" {
+ addSecretWarning(types.NamespacedName{Namespace: namespace, Name: plugin.SecretRef})
+ }
+ }
+ }
+
+ for _, rule := range route.Spec.Stream {
+ addServiceWarning(types.NamespacedName{Namespace: namespace, Name: rule.Backend.ServiceName})
+ for _, plugin := range rule.Plugins {
+ if !plugin.Enable {
+ continue
+ }
+ if plugin.SecretRef != "" {
+ addSecretWarning(types.NamespacedName{Namespace: namespace, Name: plugin.SecretRef})
+ }
+ }
+ }
+
+ return warnings
+}
diff --git a/internal/webhook/v1/apisixroute_webhook_test.go b/internal/webhook/v1/apisixroute_webhook_test.go
new file mode 100644
index 00000000..3ea88e46
--- /dev/null
+++ b/internal/webhook/v1/apisixroute_webhook_test.go
@@ -0,0 +1,158 @@
+// 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 v1
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ clientgoscheme "k8s.io/client-go/kubernetes/scheme"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+ apisixv2 "github.com/apache/apisix-ingress-controller/api/v2"
+)
+
+func buildApisixRouteValidator(t *testing.T, objects ...runtime.Object) *ApisixRouteCustomValidator {
+ t.Helper()
+
+ scheme := runtime.NewScheme()
+ require.NoError(t, clientgoscheme.AddToScheme(scheme))
+ require.NoError(t, apisixv2.AddToScheme(scheme))
+
+ builder := fake.NewClientBuilder().WithScheme(scheme)
+ if len(objects) > 0 {
+ builder = builder.WithRuntimeObjects(objects...)
+ }
+
+ return &ApisixRouteCustomValidator{Client: builder.Build()}
+}
+
+func TestApisixRouteValidator_MissingHTTPService(t *testing.T) {
+ route := &apisixv2.ApisixRoute{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ },
+ Spec: apisixv2.ApisixRouteSpec{
+ HTTP: []apisixv2.ApisixRouteHTTP{{
+ Name: "rule",
+ Backends: []apisixv2.ApisixRouteHTTPBackend{{
+ ServiceName: "backend",
+ }},
+ }},
+ },
+ }
+
+ validator := buildApisixRouteValidator(t)
+
+ warnings, err := validator.ValidateCreate(context.Background(), route)
+ require.NoError(t, err)
+ require.Len(t, warnings, 1)
+ require.Contains(t, warnings[0], "Referenced Service 'default/backend' not found")
+}
+
+func TestApisixRouteValidator_MissingPluginSecret(t *testing.T) {
+ route := &apisixv2.ApisixRoute{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ },
+ Spec: apisixv2.ApisixRouteSpec{
+ HTTP: []apisixv2.ApisixRouteHTTP{{
+ Name: "rule",
+ Backends: []apisixv2.ApisixRouteHTTPBackend{{
+ ServiceName: "backend",
+ }},
+ Plugins: []apisixv2.ApisixRoutePlugin{{
+ Name: "jwt-auth",
+ Enable: true,
+ SecretRef: "jwt-secret",
+ }},
+ }},
+ },
+ }
+
+ backendSvc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "backend", Namespace: "default"}}
+
+ validator := buildApisixRouteValidator(t, backendSvc)
+
+ warnings, err := validator.ValidateCreate(context.Background(), route)
+ require.NoError(t, err)
+ require.Len(t, warnings, 1)
+ require.Contains(t, warnings[0], "Referenced Secret 'default/jwt-secret' not found")
+}
+
+func TestApisixRouteValidator_MissingStreamService(t *testing.T) {
+ route := &apisixv2.ApisixRoute{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ },
+ Spec: apisixv2.ApisixRouteSpec{
+ Stream: []apisixv2.ApisixRouteStream{{
+ Name: "stream",
+ Protocol: "TCP",
+ Backend: apisixv2.ApisixRouteStreamBackend{
+ ServiceName: "stream-svc",
+ },
+ }},
+ },
+ }
+
+ validator := buildApisixRouteValidator(t)
+
+ warnings, err := validator.ValidateCreate(context.Background(), route)
+ require.NoError(t, err)
+ require.Len(t, warnings, 1)
+ require.Contains(t, warnings[0], "Referenced Service 'default/stream-svc' not found")
+}
+
+func TestApisixRouteValidator_NoWarnings(t *testing.T) {
+ route := &apisixv2.ApisixRoute{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ },
+ Spec: apisixv2.ApisixRouteSpec{
+ HTTP: []apisixv2.ApisixRouteHTTP{{
+ Name: "rule",
+ Backends: []apisixv2.ApisixRouteHTTPBackend{{
+ ServiceName: "backend",
+ }},
+ Plugins: []apisixv2.ApisixRoutePlugin{{
+ Name: "jwt-auth",
+ Enable: true,
+ SecretRef: "jwt-secret",
+ }},
+ }},
+ },
+ }
+
+ objs := []runtime.Object{
+ &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "backend", Namespace: "default"}},
+ &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "jwt-secret", Namespace: "default"}},
+ }
+
+ validator := buildApisixRouteValidator(t, objs...)
+
+ warnings, err := validator.ValidateCreate(context.Background(), route)
+ require.NoError(t, err)
+ require.Empty(t, warnings)
+}
diff --git a/internal/webhook/v1/consumer_webhook.go b/internal/webhook/v1/consumer_webhook.go
new file mode 100644
index 00000000..c640cdfe
--- /dev/null
+++ b/internal/webhook/v1/consumer_webhook.go
@@ -0,0 +1,105 @@
+// 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 v1
+
+import (
+ "context"
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/types"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ apisixv1alpha1 "github.com/apache/apisix-ingress-controller/api/v1alpha1"
+ "github.com/apache/apisix-ingress-controller/internal/webhook/v1/reference"
+)
+
+var consumerLog = logf.Log.WithName("consumer-resource")
+
+func SetupConsumerWebhookWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewWebhookManagedBy(mgr).
+ For(&apisixv1alpha1.Consumer{}).
+ WithValidator(&ConsumerCustomValidator{Client: mgr.GetClient()}).
+ Complete()
+}
+
+// +kubebuilder:webhook:path=/validate-apisix-apache-org-v1alpha1-consumer,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=consumers,verbs=create;update,versions=v1alpha1,name=vconsumer-v1alpha1.kb.io,admissionReviewVersions=v1
+
+type ConsumerCustomValidator struct {
+ Client client.Client
+}
+
+var _ webhook.CustomValidator = &ConsumerCustomValidator{}
+
+func (v *ConsumerCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
+ consumer, ok := obj.(*apisixv1alpha1.Consumer)
+ if !ok {
+ return nil, fmt.Errorf("expected a Consumer object but got %T", obj)
+ }
+ consumerLog.Info("Validation for Consumer upon creation", "name", consumer.GetName(), "namespace", consumer.GetNamespace())
+
+ return v.collectWarnings(ctx, consumer), nil
+}
+
+func (v *ConsumerCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
+ consumer, ok := newObj.(*apisixv1alpha1.Consumer)
+ if !ok {
+ return nil, fmt.Errorf("expected a Consumer object for the newObj but got %T", newObj)
+ }
+ consumerLog.Info("Validation for Consumer upon update", "name", consumer.GetName(), "namespace", consumer.GetNamespace())
+
+ return v.collectWarnings(ctx, consumer), nil
+}
+
+func (*ConsumerCustomValidator) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) {
+ return nil, nil
+}
+
+func (v *ConsumerCustomValidator) collectWarnings(ctx context.Context, consumer *apisixv1alpha1.Consumer) admission.Warnings {
+ checker := reference.NewChecker(v.Client, consumerLog)
+ defaultNamespace := consumer.GetNamespace()
+
+ visited := make(map[types.NamespacedName]struct{})
+ var warnings admission.Warnings
+
+ for _, credential := range consumer.Spec.Credentials {
+ if credential.SecretRef == nil || credential.SecretRef.Name == "" {
+ continue
+ }
+
+ namespace := defaultNamespace
+ if credential.SecretRef.Namespace != nil && *credential.SecretRef.Namespace != "" {
+ namespace = *credential.SecretRef.Namespace
+ }
+
+ nn := types.NamespacedName{Namespace: namespace, Name: credential.SecretRef.Name}
+ if _, ok := visited[nn]; ok {
+ continue
+ }
+ visited[nn] = struct{}{}
+
+ warnings = append(warnings, checker.Secret(ctx, reference.SecretRef{
+ Object: consumer,
+ NamespacedName: nn,
+ })...)
+ }
+
+ return warnings
+}
diff --git a/internal/webhook/v1/consumer_webhook_test.go b/internal/webhook/v1/consumer_webhook_test.go
new file mode 100644
index 00000000..90ea0ae8
--- /dev/null
+++ b/internal/webhook/v1/consumer_webhook_test.go
@@ -0,0 +1,130 @@
+// 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 v1
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ clientgoscheme "k8s.io/client-go/kubernetes/scheme"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+ apisixv1alpha1 "github.com/apache/apisix-ingress-controller/api/v1alpha1"
+)
+
+func buildConsumerValidator(t *testing.T, objects ...runtime.Object) *ConsumerCustomValidator {
+ t.Helper()
+
+ scheme := runtime.NewScheme()
+ require.NoError(t, clientgoscheme.AddToScheme(scheme))
+ require.NoError(t, apisixv1alpha1.AddToScheme(scheme))
+
+ builder := fake.NewClientBuilder().WithScheme(scheme)
+ if len(objects) > 0 {
+ builder = builder.WithRuntimeObjects(objects...)
+ }
+
+ return &ConsumerCustomValidator{Client: builder.Build()}
+}
+
+func TestConsumerValidator_MissingSecretDefaultNamespace(t *testing.T) {
+ consumer := &apisixv1alpha1.Consumer{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ },
+ Spec: apisixv1alpha1.ConsumerSpec{
+ Credentials: []apisixv1alpha1.Credential{{
+ Type: "jwt-auth",
+ SecretRef: &apisixv1alpha1.SecretReference{
+ Name: "jwt-secret",
+ },
+ }},
+ },
+ }
+
+ validator := buildConsumerValidator(t)
+
+ warnings, err := validator.ValidateCreate(context.Background(), consumer)
+ require.NoError(t, err)
+ require.Len(t, warnings, 1)
+ require.Contains(t, warnings[0], "Referenced Secret 'default/jwt-secret' not found")
+}
+
+func TestConsumerValidator_MissingSecretCustomNamespace(t *testing.T) {
+ ns := "auth"
+ consumer := &apisixv1alpha1.Consumer{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ },
+ Spec: apisixv1alpha1.ConsumerSpec{
+ Credentials: []apisixv1alpha1.Credential{{
+ Type: "jwt-auth",
+ SecretRef: &apisixv1alpha1.SecretReference{
+ Name: "jwt-secret",
+ Namespace: &ns,
+ },
+ }},
+ },
+ }
+
+ validator := buildConsumerValidator(t)
+
+ warnings, err := validator.ValidateCreate(context.Background(), consumer)
+ require.NoError(t, err)
+ require.Len(t, warnings, 1)
+ require.Contains(t, warnings[0], "Referenced Secret 'auth/jwt-secret' not found")
+}
+
+func TestConsumerValidator_NoWarnings(t *testing.T) {
+ ns := "auth"
+ consumer := &apisixv1alpha1.Consumer{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ },
+ Spec: apisixv1alpha1.ConsumerSpec{
+ Credentials: []apisixv1alpha1.Credential{{
+ Type: "jwt-auth",
+ SecretRef: &apisixv1alpha1.SecretReference{
+ Name: "jwt-secret",
+ Namespace: &ns,
+ },
+ }, {
+ Type: "key-auth",
+ SecretRef: &apisixv1alpha1.SecretReference{
+ Name: "key-secret",
+ },
+ }},
+ },
+ }
+
+ objs := []runtime.Object{
+ &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "jwt-secret", Namespace: "auth"}},
+ &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "key-secret", Namespace: "default"}},
+ }
+
+ validator := buildConsumerValidator(t, objs...)
+
+ warnings, err := validator.ValidateCreate(context.Background(), consumer)
+ require.NoError(t, err)
+ require.Empty(t, warnings)
+}