diff --git a/.github/workflows/apisix-e2e-test.yml b/.github/workflows/apisix-e2e-test.yml index 85ce4e42..f5a59178 100644 --- a/.github/workflows/apisix-e2e-test.yml +++ b/.github/workflows/apisix-e2e-test.yml @@ -42,6 +42,7 @@ jobs: cases_subset: - apisix.apache.org - networking.k8s.io + - webhook fail-fast: false runs-on: ubuntu-latest steps: @@ -115,4 +116,8 @@ jobs: TEST_LABEL: ${{ matrix.cases_subset }} TEST_ENV: CI run: | - make ginkgo-e2e-test + if [[ "${{ matrix.cases_subset }}" == "webhook" ]]; then + E2E_NODES=1 make ginkgo-e2e-test + else + make ginkgo-e2e-test + fi diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 95a90be4..3b6615d7 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -35,6 +35,7 @@ jobs: cases_subset: - apisix.apache.org - networking.k8s.io + - webhook fail-fast: false runs-on: ubuntu-latest steps: diff --git a/PROJECT b/PROJECT index a9b4a155..cb21ee36 100644 --- a/PROJECT +++ b/PROJECT @@ -78,4 +78,21 @@ resources: webhooks: validation: true webhookVersion: v1 +- core: true + domain: k8s.io + group: networking + kind: IngressClass + path: k8s.io/api/networking/v1 + version: v1 + webhooks: + validation: true + webhookVersion: v1 +- external: true + group: gateway.networking.k8s.io + kind: Gateway + path: sigs.k8s.io/gateway-api/apis/v1 + version: v1 + webhooks: + validation: true + webhookVersion: v1 version: "3" diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 8632054a..efcc2fca 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -4,6 +4,26 @@ kind: ValidatingWebhookConfiguration metadata: name: validating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-gateway-networking-k8s-io-v1-gateway + failurePolicy: Fail + name: vgateway-v1.kb.io + rules: + - apiGroups: + - gateway.networking.k8s.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - gateways + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -24,3 +44,23 @@ webhooks: resources: - ingresses sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-networking-k8s-io-v1-ingressclass + failurePolicy: Fail + name: vingressclass-v1.kb.io + rules: + - apiGroups: + - networking.k8s.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - ingressclasses + sideEffects: None diff --git a/internal/manager/webhooks.go b/internal/manager/webhooks.go index 0a67f07a..07b6f381 100644 --- a/internal/manager/webhooks.go +++ b/internal/manager/webhooks.go @@ -29,5 +29,11 @@ func setupWebhooks(_ context.Context, mgr manager.Manager) error { if err := webhookv1.SetupIngressWebhookWithManager(mgr); err != nil { return err } + if err := webhookv1.SetupIngressClassWebhookWithManager(mgr); err != nil { + return err + } + if err := webhookv1.SetupGatewayWebhookWithManager(mgr); err != nil { + return err + } return nil } diff --git a/internal/provider/common/adcdebugserver.go b/internal/provider/common/adcdebugserver.go index b1ca9c5b..357d4e9a 100644 --- a/internal/provider/common/adcdebugserver.go +++ b/internal/provider/common/adcdebugserver.go @@ -262,7 +262,7 @@ func (asrv *ADCDebugProvider) showResourceDetail(w http.ResponseWriter, r *http. return } - var resource interface{} + var resource any switch resourceType { case adctypes.TypeService: for _, svc := range resources.Services { diff --git a/internal/webhook/v1/gateway_webhook.go b/internal/webhook/v1/gateway_webhook.go new file mode 100644 index 00000000..e2c11ff4 --- /dev/null +++ b/internal/webhook/v1/gateway_webhook.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" + "fmt" + + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + 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" + gatewaynetworkingk8siov1 "sigs.k8s.io/gateway-api/apis/v1" + + v1alpha1 "github.com/apache/apisix-ingress-controller/api/v1alpha1" + "github.com/apache/apisix-ingress-controller/internal/controller/config" + internaltypes "github.com/apache/apisix-ingress-controller/internal/types" +) + +// nolint:unused +// log is for logging in this package. +var gatewaylog = logf.Log.WithName("gateway-resource") + +// SetupGatewayWebhookWithManager registers the webhook for Gateway in the manager. +func SetupGatewayWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&gatewaynetworkingk8siov1.Gateway{}). + WithValidator(&GatewayCustomValidator{Client: mgr.GetClient()}). + Complete() +} + +// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here. +// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook. +// +kubebuilder:webhook:path=/validate-gateway-networking-k8s-io-v1-gateway,mutating=false,failurePolicy=fail,sideEffects=None,groups=gateway.networking.k8s.io,resources=gateways,verbs=create;update,versions=v1,name=vgateway-v1.kb.io,admissionReviewVersions=v1 + +// GatewayCustomValidator struct is responsible for validating the Gateway resource +// when it is created, updated, or deleted. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as this struct is used only for temporary operations and does not need to be deeply copied. +type GatewayCustomValidator struct { + Client client.Client +} + +var _ webhook.CustomValidator = &GatewayCustomValidator{} + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type Gateway. +func (v *GatewayCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + gateway, ok := obj.(*gatewaynetworkingk8siov1.Gateway) + if !ok { + return nil, fmt.Errorf("expected a Gateway object but got %T", obj) + } + gatewaylog.Info("Validation for Gateway upon creation", "name", gateway.GetName()) + + warnings := v.warnIfMissingGatewayProxyForGateway(ctx, gateway) + + return warnings, nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type Gateway. +func (v *GatewayCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + gateway, ok := newObj.(*gatewaynetworkingk8siov1.Gateway) + if !ok { + return nil, fmt.Errorf("expected a Gateway object for the newObj but got %T", newObj) + } + gatewaylog.Info("Validation for Gateway upon update", "name", gateway.GetName()) + + warnings := v.warnIfMissingGatewayProxyForGateway(ctx, gateway) + + return warnings, nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type Gateway. +func (v *GatewayCustomValidator) ValidateDelete(_ context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil +} + +func (v *GatewayCustomValidator) warnIfMissingGatewayProxyForGateway(ctx context.Context, gateway *gatewaynetworkingk8siov1.Gateway) admission.Warnings { + var warnings admission.Warnings + + // get gateway class + gatewayClass := &gatewaynetworkingk8siov1.GatewayClass{} + if err := v.Client.Get(ctx, client.ObjectKey{Name: string(gateway.Spec.GatewayClassName)}, gatewayClass); err != nil { + gatewaylog.Error(err, "failed to get gateway class", "gateway", gateway.GetName(), "gatewayclass", gateway.Spec.GatewayClassName) + return nil + } + // match controller + if string(gatewayClass.Spec.ControllerName) != config.ControllerConfig.ControllerName { + return nil + } + + infra := gateway.Spec.Infrastructure + if infra == nil || infra.ParametersRef == nil { + return nil + } + ref := infra.ParametersRef + if string(ref.Group) != v1alpha1.GroupVersion.Group || string(ref.Kind) != internaltypes.KindGatewayProxy { + return nil + } + + ns := gateway.GetNamespace() + name := ref.Name + + var gp v1alpha1.GatewayProxy + if err := v.Client.Get(ctx, client.ObjectKey{Namespace: ns, Name: name}, &gp); err != nil { + if k8serrors.IsNotFound(err) { + msg := fmt.Sprintf("Referenced GatewayProxy '%s/%s' not found.", ns, name) + warnings = append(warnings, msg) + gatewaylog.Info("Gateway references missing GatewayProxy", "gateway", gateway.GetName(), "namespace", ns, "gatewayproxy", name) + } else { + gatewaylog.Error(err, "failed to resolve GatewayProxy for Gateway", "gateway", gateway.GetName(), "namespace", ns, "gatewayproxy", name) + } + } + return warnings +} diff --git a/internal/webhook/v1/ingressclass_webhook.go b/internal/webhook/v1/ingressclass_webhook.go new file mode 100644 index 00000000..06ae18e9 --- /dev/null +++ b/internal/webhook/v1/ingressclass_webhook.go @@ -0,0 +1,126 @@ +// 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" + + networkingv1 "k8s.io/api/networking/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + 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" + + v1alpha1 "github.com/apache/apisix-ingress-controller/api/v1alpha1" + "github.com/apache/apisix-ingress-controller/internal/controller/config" + internaltypes "github.com/apache/apisix-ingress-controller/internal/types" +) + +// nolint:unused +// log is for logging in this package. +var ingressclasslog = logf.Log.WithName("ingressclass-resource") + +// SetupIngressClassWebhookWithManager registers the webhook for IngressClass in the manager. +func SetupIngressClassWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&networkingv1.IngressClass{}). + WithValidator(&IngressClassCustomValidator{Client: mgr.GetClient()}). + Complete() +} + +// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here. +// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook. +// +kubebuilder:webhook:path=/validate-networking-k8s-io-v1-ingressclass,mutating=false,failurePolicy=fail,sideEffects=None,groups=networking.k8s.io,resources=ingressclasses,verbs=create;update,versions=v1,name=vingressclass-v1.kb.io,admissionReviewVersions=v1 + +// IngressClassCustomValidator struct is responsible for validating the IngressClass resource +// when it is created, updated, or deleted. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as this struct is used only for temporary operations and does not need to be deeply copied. +type IngressClassCustomValidator struct { + Client client.Client +} + +var _ webhook.CustomValidator = &IngressClassCustomValidator{} + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type IngressClass. +func (v *IngressClassCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + ingressclass, ok := obj.(*networkingv1.IngressClass) + if !ok { + return nil, fmt.Errorf("expected a IngressClass object but got %T", obj) + } + ingressclasslog.Info("Validation for IngressClass upon creation", "name", ingressclass.GetName()) + + warnings := v.warnIfMissingGatewayProxyForIngressClass(ctx, ingressclass) + + return warnings, nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type IngressClass. +func (v *IngressClassCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + ingressclass, ok := newObj.(*networkingv1.IngressClass) + if !ok { + return nil, fmt.Errorf("expected a IngressClass object for the newObj but got %T", newObj) + } + ingressclasslog.Info("Validation for IngressClass upon update", "name", ingressclass.GetName()) + + warnings := v.warnIfMissingGatewayProxyForIngressClass(ctx, ingressclass) + + return warnings, nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type IngressClass. +func (v *IngressClassCustomValidator) ValidateDelete(_ context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil +} + +func (v *IngressClassCustomValidator) warnIfMissingGatewayProxyForIngressClass(ctx context.Context, ingressClass *networkingv1.IngressClass) admission.Warnings { + var warnings admission.Warnings + + // match controller + if ingressClass.Spec.Controller != config.ControllerConfig.ControllerName { + return nil + } + + params := ingressClass.Spec.Parameters + if params == nil || params.APIGroup == nil { + return nil + } + if *params.APIGroup != v1alpha1.GroupVersion.Group || params.Kind != internaltypes.KindGatewayProxy { + return nil + } + + ns := ingressClass.GetNamespace() + if params.Namespace != nil && *params.Namespace != "" { + ns = *params.Namespace + } + name := params.Name + + var gp v1alpha1.GatewayProxy + if err := v.Client.Get(ctx, client.ObjectKey{Namespace: ns, Name: name}, &gp); err != nil { + if k8serrors.IsNotFound(err) { + msg := fmt.Sprintf("Referenced GatewayProxy '%s/%s' not found.", ns, name) + warnings = append(warnings, msg) + ingressclasslog.Info("IngressClass references missing GatewayProxy", "ingressclass", ingressClass.GetName(), "namespace", ns, "gatewayproxy", name) + } else { + ingressclasslog.Error(err, "failed to resolve GatewayProxy for IngressClass", "ingressclass", ingressClass.GetName(), "namespace", ns, "gatewayproxy", name) + } + } + return warnings +} diff --git a/test/e2e/framework/manifests/webhook.yaml b/test/e2e/framework/manifests/webhook.yaml index 4d96e454..432873c9 100644 --- a/test/e2e/framework/manifests/webhook.yaml +++ b/test/e2e/framework/manifests/webhook.yaml @@ -27,17 +27,59 @@ webhooks: namespace: {{ .Namespace }} path: /validate-networking-k8s-io-v1-ingress caBundle: {{ .CABundle }} - admissionReviewVersions: + admissionReviewVersions: - v1 rules: - - operations: + - operations: - CREATE - UPDATE - apiGroups: + apiGroups: - networking.k8s.io - apiVersions: + apiVersions: - v1 - resources: + resources: - ingresses failurePolicy: Fail sideEffects: None +- name: vingressclass-v1.kb.io + clientConfig: + service: + name: webhook-service + namespace: {{ .Namespace }} + path: /validate-networking-k8s-io-v1-ingressclass + caBundle: {{ .CABundle }} + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + apiGroups: + - networking.k8s.io + apiVersions: + - v1 + resources: + - ingressclasses + 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 diff --git a/test/e2e/gatewayapi/webhook.go b/test/e2e/gatewayapi/webhook.go new file mode 100644 index 00000000..92f65f93 --- /dev/null +++ b/test/e2e/gatewayapi/webhook.go @@ -0,0 +1,94 @@ +// 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 gatewayapi + +import ( + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" +) + +var _ = Describe("Test Gateway Webhook", Label("webhook"), func() { + s := scaffold.NewScaffold(scaffold.Options{ + Name: "gateway-webhook-test", + EnableWebhook: true, + }) + + It("should warn when referenced GatewayProxy does not exist on create and update", func() { + By("creating GatewayClass with controller name") + err := s.CreateResourceFromString(s.GetGatewayClassYaml()) + Expect(err).ShouldNot(HaveOccurred()) + + time.Sleep(2 * time.Second) + + By("creating Gateway referencing a missing GatewayProxy") + missingName := "missing-proxy" + gwYAML := ` +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: %s +spec: + gatewayClassName: %s + listeners: + - name: http1 + protocol: HTTP + port: 80 + infrastructure: + parametersRef: + group: apisix.apache.org + kind: GatewayProxy + name: %s +` + + output, err := s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(gwYAML, s.Namespace(), s.Namespace(), missingName)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(output).To(ContainSubstring(fmt.Sprintf("Warning: Referenced GatewayProxy '%s/%s' not found.", s.Namespace(), missingName))) + + time.Sleep(2 * time.Second) + + By("updating Gateway to reference another missing GatewayProxy") + missingName2 := "missing-proxy-2" + output, err = s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(gwYAML, s.Namespace(), s.Namespace(), missingName2)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(output).To(ContainSubstring(fmt.Sprintf("Warning: Referenced GatewayProxy '%s/%s' not found.", s.Namespace(), missingName2))) + + By("create GatewayProxy") + err = s.CreateResourceFromString(s.GetGatewayProxySpec()) + Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") + time.Sleep(5 * time.Second) + + By("updating Gateway to reference an existing GatewayProxy") + existingName := "apisix-proxy-config" + output, err = s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(gwYAML, s.Namespace(), s.Namespace(), existingName)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning: Referenced GatewayProxy '%s/%s' not found.", s.Namespace(), existingName))) + + By("delete Gateway") + err = s.DeleteResource("Gateway", s.Namespace()) + Expect(err).ShouldNot(HaveOccurred()) + + By("delete GatewayClass") + err = s.DeleteResource("GatewayClass", s.Namespace()) + Expect(err).ShouldNot(HaveOccurred()) + }) +}) diff --git a/test/e2e/ingress/ingressclass_webhook.go b/test/e2e/ingress/ingressclass_webhook.go new file mode 100644 index 00000000..9c1f3174 --- /dev/null +++ b/test/e2e/ingress/ingressclass_webhook.go @@ -0,0 +1,83 @@ +// 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 ingress + +import ( + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" +) + +var _ = Describe("Test IngressClass Webhook", Label("webhook"), func() { + s := scaffold.NewScaffold(scaffold.Options{ + Name: "ingressclass-webhook-test", + EnableWebhook: true, + }) + + Context("IngressClass Validation", func() { + It("should warn when referenced GatewayProxy does not exist on create and update", func() { + By("creating IngressClass referencing a missing GatewayProxy") + missingName := "missing-proxy" + icYAML := ` +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: apisix-with-missing +spec: + controller: "%s" + parameters: + apiGroup: "apisix.apache.org" + kind: "GatewayProxy" + name: "%s" + namespace: "%s" + scope: "Namespace" +` + + output, err := s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(icYAML, s.GetControllerName(), missingName, s.Namespace())) + Expect(err).ShouldNot(HaveOccurred()) + Expect(output).To(ContainSubstring(fmt.Sprintf("Warning: Referenced GatewayProxy '%s/%s' not found.", s.Namespace(), missingName))) + + time.Sleep(2 * time.Second) + + By("updating IngressClass to reference another missing GatewayProxy") + missingName2 := "missing-proxy-2" + output, err = s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(icYAML, s.GetControllerName(), missingName2, s.Namespace())) + Expect(err).ShouldNot(HaveOccurred()) + Expect(output).To(ContainSubstring(fmt.Sprintf("Warning: Referenced GatewayProxy '%s/%s' not found.", s.Namespace(), missingName2))) + + By("create GatewayProxy") + err = s.CreateResourceFromString(s.GetGatewayProxySpec()) + Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") + time.Sleep(5 * time.Second) + + By("updating IngressClass to reference an existing GatewayProxy") + existingName := "apisix-proxy-config" + output, err = s.CreateResourceFromStringAndGetOutput(fmt.Sprintf(icYAML, s.GetControllerName(), existingName, s.Namespace())) + Expect(err).ShouldNot(HaveOccurred()) + Expect(output).NotTo(ContainSubstring(fmt.Sprintf("Warning: Referenced GatewayProxy '%s/%s' not found.", s.Namespace(), existingName))) + + By("deleting IngressClass") + err = s.DeleteResource("IngressClass", "apisix-with-missing") + Expect(err).ShouldNot(HaveOccurred()) + }) + }) +}) diff --git a/test/e2e/ingress/webhook.go b/test/e2e/ingress/webhook.go index 2f8c9eff..310b6629 100644 --- a/test/e2e/ingress/webhook.go +++ b/test/e2e/ingress/webhook.go @@ -28,7 +28,7 @@ import ( "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" ) -var _ = Describe("Test Ingress Webhook", Label("networking.k8s.io", "ingress"), func() { +var _ = Describe("Test Ingress Webhook", Label("webhook"), func() { s := scaffold.NewScaffold(scaffold.Options{ Name: "webhook-test", EnableWebhook: true, diff --git a/test/e2e/scaffold/grpc.go b/test/e2e/scaffold/grpc.go index 5c11bad9..4f06add3 100644 --- a/test/e2e/scaffold/grpc.go +++ b/test/e2e/scaffold/grpc.go @@ -21,13 +21,14 @@ import ( "strings" "time" - "github.com/apache/apisix-ingress-controller/test/e2e/framework" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" pb "sigs.k8s.io/gateway-api/conformance/echo-basic/grpcechoserver" + + "github.com/apache/apisix-ingress-controller/test/e2e/framework" ) type RequestMetadata struct {