From cf6e08238525235d8b83a2f47d1196fc7e80f983 Mon Sep 17 00:00:00 2001 From: Ashing Zheng Date: Fri, 24 Oct 2025 16:41:55 +0800 Subject: [PATCH] feat: support kubernetes.io/ingress.class annotations (#2615) Signed-off-by: Ashing Zheng (cherry picked from commit cbfa038979158331de65b20aeb489b478b581697) --- internal/controller/utils.go | 3 ++ internal/types/k8s.go | 9 ++++++ test/e2e/ingress/ingress.go | 62 ++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/internal/controller/utils.go b/internal/controller/utils.go index f02e7326..e3bdcd80 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -41,7 +41,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" k8stypes "k8s.io/apimachinery/pkg/types" +<<<<<<< HEAD ctrl "sigs.k8s.io/controller-runtime" +======= +>>>>>>> cbfa0389 (feat: support kubernetes.io/ingress.class annotations (#2615)) "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" diff --git a/internal/types/k8s.go b/internal/types/k8s.go index 42e1f7fb..433538ce 100644 --- a/internal/types/k8s.go +++ b/internal/types/k8s.go @@ -22,9 +22,13 @@ import ( netv1 "k8s.io/api/networking/v1" netv1beta1 "k8s.io/api/networking/v1beta1" "k8s.io/apimachinery/pkg/runtime/schema" +<<<<<<< HEAD kschema "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" +======= + "k8s.io/utils/ptr" +>>>>>>> cbfa0389 (feat: support kubernetes.io/ingress.class annotations (#2615)) gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" @@ -215,6 +219,11 @@ func GvkOf(obj any) schema.GroupVersionKind { } } +<<<<<<< HEAD +======= +// GetEffectiveIngressClassName returns the effective ingress class name. +// It first checks spec.IngressClassName, and falls back to the annotation if spec is empty. +>>>>>>> cbfa0389 (feat: support kubernetes.io/ingress.class annotations (#2615)) func GetEffectiveIngressClassName(ingress *netv1.Ingress) string { if cls := ptr.Deref(ingress.Spec.IngressClassName, ""); cls != "" { return cls diff --git a/test/e2e/ingress/ingress.go b/test/e2e/ingress/ingress.go index 06886716..f928f16a 100644 --- a/test/e2e/ingress/ingress.go +++ b/test/e2e/ingress/ingress.go @@ -1117,4 +1117,66 @@ spec: Should(Equal("enabled")) }) }) + + Context("Ingress with annotation-based IngressClass", func() { + const ingressClassSpec = ` +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: %s +spec: + controller: "%s" + parameters: + apiGroup: "apisix.apache.org" + kind: "GatewayProxy" + name: "apisix-proxy-config" + namespace: %s + scope: "Namespace" +` + const ingressSpec = ` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: apisix-ingress-annotation + annotations: + kubernetes.io/ingress.class: %s +spec: + rules: + - host: annotation.example.com + http: + paths: + - path: /get + pathType: Prefix + backend: + service: + name: httpbin-service-e2e-test + port: + number: 80 +` + + It("Ingress with kubernetes.io/ingress.class annotation", func() { + By("create GatewayProxy") + gatewayProxy := fmt.Sprintf(gatewayProxyYaml, s.Namespace(), s.Deployer.GetAdminEndpoint(), s.AdminKey()) + err := s.CreateResourceFromStringWithNamespace(gatewayProxy, s.Namespace()) + Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") + time.Sleep(5 * time.Second) + + By("create IngressClass") + err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(ingressClassSpec, s.Namespace(), s.GetControllerName(), s.Namespace()), s.Namespace()) + Expect(err).NotTo(HaveOccurred(), "creating IngressClass") + + By("create Ingress with annotation") + err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(ingressSpec, s.Namespace()), s.Namespace()) + Expect(err).NotTo(HaveOccurred(), "creating Ingress with annotation") + + By("verify Ingress with annotation works") + Eventually(func() int { + return s.NewAPISIXClient(). + GET("/get"). + WithHost("annotation.example.com"). + Expect().Raw().StatusCode + }).WithTimeout(20 * time.Second).ProbeEvery(time.Second). + Should(Equal(http.StatusOK)) + }) + }) })