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 2026/01/12 08:32:38 UTC

(apisix-ingress-controller) 07/08: feat: support custom gatewayproxy namespace for ingressclass (#2701)

This is an automated email from the ASF dual-hosted git repository.

ronething pushed a commit to branch v2.0.0
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git

commit b01b71b90fb48f19df1f95172ca09c581b1e11b7
Author: Ashing Zheng <ax...@gmail.com>
AuthorDate: Mon Jan 12 14:26:13 2026 +0800

    feat: support custom gatewayproxy namespace for ingressclass (#2701)
    
    Signed-off-by: Ashing Zheng <ax...@gmail.com>
    (cherry picked from commit b52c26c8519389600c968c497533c6a6cf9d485d)
---
 internal/controller/indexer/indexer.go         |  8 +--
 internal/controller/ingressclass_controller.go |  5 +-
 internal/controller/utils.go                   | 10 +--
 internal/utils/k8s.go                          | 12 ++++
 internal/webhook/v1/ingressclass_webhook.go    |  6 +-
 test/e2e/crds/v2/basic.go                      | 91 +++++++++++++++++++++++++-
 6 files changed, 109 insertions(+), 23 deletions(-)

diff --git a/internal/controller/indexer/indexer.go b/internal/controller/indexer/indexer.go
index 91bec714..6f0d66a4 100644
--- a/internal/controller/indexer/indexer.go
+++ b/internal/controller/indexer/indexer.go
@@ -34,6 +34,7 @@ import (
 	apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
 	"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations"
 	internaltypes "github.com/apache/apisix-ingress-controller/internal/types"
+	k8sutils "github.com/apache/apisix-ingress-controller/internal/utils"
 	"github.com/apache/apisix-ingress-controller/pkg/utils"
 )
 
@@ -859,11 +860,8 @@ func IngressClassParametersRefIndexFunc(rawObj client.Object) []string {
 		ingressClass.Spec.Parameters.APIGroup != nil &&
 		*ingressClass.Spec.Parameters.APIGroup == v1alpha1.GroupVersion.Group &&
 		ingressClass.Spec.Parameters.Kind == internaltypes.KindGatewayProxy {
-		ns := ingressClass.GetNamespace()
-		if ingressClass.Spec.Parameters.Namespace != nil {
-			ns = *ingressClass.Spec.Parameters.Namespace
-		}
-		return []string{GenIndexKey(ns, ingressClass.Spec.Parameters.Name)}
+		namespace := k8sutils.GetIngressClassParametersNamespace(*ingressClass)
+		return []string{GenIndexKey(namespace, ingressClass.Spec.Parameters.Name)}
 	}
 	return nil
 }
diff --git a/internal/controller/ingressclass_controller.go b/internal/controller/ingressclass_controller.go
index 5594ee6c..0afbf6f2 100644
--- a/internal/controller/ingressclass_controller.go
+++ b/internal/controller/ingressclass_controller.go
@@ -187,10 +187,7 @@ func (r *IngressClassReconciler) processInfrastructure(tctx *provider.TranslateC
 		return nil
 	}
 
-	namespace := ingressClass.Namespace
-	if ingressClass.Spec.Parameters.Namespace != nil {
-		namespace = *ingressClass.Spec.Parameters.Namespace
-	}
+	namespace := utils.GetIngressClassParametersNamespace(*ingressClass)
 
 	gatewayProxy := new(v1alpha1.GatewayProxy)
 	if err := r.Get(context.Background(), client.ObjectKey{
diff --git a/internal/controller/utils.go b/internal/controller/utils.go
index 892d027c..a00ef916 100644
--- a/internal/controller/utils.go
+++ b/internal/controller/utils.go
@@ -1354,10 +1354,7 @@ func ProcessIngressClassParameters(tctx *provider.TranslateContext, c client.Cli
 	parameters := ingressClass.Spec.Parameters
 	// check if the parameters reference GatewayProxy
 	if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == KindGatewayProxy {
-		ns := object.GetNamespace()
-		if parameters.Namespace != nil {
-			ns = *parameters.Namespace
-		}
+		ns := utils.GetIngressClassParametersNamespace(*ingressClass)
 
 		gatewayProxy := &v1alpha1.GatewayProxy{}
 		if err := c.Get(tctx, client.ObjectKey{
@@ -1553,10 +1550,7 @@ func GetGatewayProxyByIngressClass(ctx context.Context, r client.Client, ingress
 		return nil, nil
 	}
 
-	namespace := ingressClass.Namespace
-	if ingressClass.Spec.Parameters.Namespace != nil {
-		namespace = *ingressClass.Spec.Parameters.Namespace
-	}
+	namespace := utils.GetIngressClassParametersNamespace(*ingressClass)
 
 	gatewayProxy := new(v1alpha1.GatewayProxy)
 	if err := r.Get(ctx, client.ObjectKey{
diff --git a/internal/utils/k8s.go b/internal/utils/k8s.go
index 0bca19d0..0d58f301 100644
--- a/internal/utils/k8s.go
+++ b/internal/utils/k8s.go
@@ -21,6 +21,7 @@ import (
 	"net"
 	"regexp"
 
+	networkingv1 "k8s.io/api/networking/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	k8stypes "k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -99,3 +100,14 @@ func ConditionStatus(status bool) metav1.ConditionStatus {
 	}
 	return metav1.ConditionFalse
 }
+
+func GetIngressClassParametersNamespace(ingressClass networkingv1.IngressClass) string {
+	namespace := "default"
+	if ingressClass.Spec.Parameters.Namespace != nil {
+		namespace = *ingressClass.Spec.Parameters.Namespace
+	}
+	if annotationNamespace, exists := ingressClass.Annotations["apisix.apache.org/parameters-namespace"]; exists && annotationNamespace != "" {
+		namespace = annotationNamespace
+	}
+	return namespace
+}
diff --git a/internal/webhook/v1/ingressclass_webhook.go b/internal/webhook/v1/ingressclass_webhook.go
index 4d77fe8a..67d2c360 100644
--- a/internal/webhook/v1/ingressclass_webhook.go
+++ b/internal/webhook/v1/ingressclass_webhook.go
@@ -31,6 +31,7 @@ import (
 	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"
+	"github.com/apache/apisix-ingress-controller/internal/utils"
 )
 
 // nolint:unused
@@ -106,10 +107,7 @@ func (v *IngressClassCustomValidator) warnIfMissingGatewayProxyForIngressClass(c
 		return nil
 	}
 
-	ns := ingressClass.GetNamespace()
-	if params.Namespace != nil && *params.Namespace != "" {
-		ns = *params.Namespace
-	}
+	ns := utils.GetIngressClassParametersNamespace(*ingressClass)
 	name := params.Name
 
 	var gp v1alpha1.GatewayProxy
diff --git a/test/e2e/crds/v2/basic.go b/test/e2e/crds/v2/basic.go
index 36205d5c..65415340 100644
--- a/test/e2e/crds/v2/basic.go
+++ b/test/e2e/crds/v2/basic.go
@@ -18,16 +18,26 @@
 package v2
 
 import (
+	"fmt"
+	"net/http"
+	"time"
+
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+	"k8s.io/apimachinery/pkg/types"
 
+	apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
+	"github.com/apache/apisix-ingress-controller/test/e2e/framework"
 	"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
 )
 
 var _ = Describe("APISIX Standalone Basic Tests", Label("apisix.apache.org", "v2", "basic"), func() {
-	s := scaffold.NewDefaultScaffold()
+	var (
+		s       = scaffold.NewDefaultScaffold()
+		applier = framework.NewApplier(s.GinkgoT, s.K8sClient, s.CreateResourceFromString)
+	)
 
-	Describe("APISIX HTTP Proxy", func() {
+	Context("APISIX HTTP Proxy", func() {
 		It("should handle basic HTTP requests", func() {
 			httpClient := s.NewAPISIXClient()
 			Expect(httpClient).NotTo(BeNil())
@@ -52,4 +62,81 @@ var _ = Describe("APISIX Standalone Basic Tests", Label("apisix.apache.org", "v2
 		})
 
 	})
+
+	Context("IngressClass Annotations", func() {
+		It("Basic tests", func() {
+			const ingressClassYaml = `
+apiVersion: networking.k8s.io/v1
+kind: IngressClass
+metadata:
+  name: %s
+  annotations:
+    apisix.apache.org/parameters-namespace: %s
+spec:
+  controller: %s
+  parameters:
+    apiGroup: apisix.apache.org
+    kind: GatewayProxy
+    name: apisix-proxy-config
+`
+
+			By("create GatewayProxy")
+
+			err := s.CreateResourceFromString(s.GetGatewayProxySpec())
+			Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
+			time.Sleep(5 * time.Second)
+
+			By("create IngressClass")
+			ingressClass := fmt.Sprintf(ingressClassYaml, s.Namespace(), s.Namespace(), s.GetControllerName())
+			err = s.CreateResourceFromString(ingressClass)
+			Expect(err).NotTo(HaveOccurred(), "creating IngressClass")
+			time.Sleep(5 * time.Second)
+
+			const apisixRouteSpec = `
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+  name: default
+spec:
+  ingressClassName: %s
+  http:
+  - name: rule0
+    match:
+      hosts:
+      - httpbin
+      paths:
+      - %s
+    backends:
+    - serviceName: httpbin-service-e2e-test
+      servicePort: 80
+`
+			request := func(path string) int {
+				return s.NewAPISIXClient().GET(path).WithHost("httpbin").Expect().Raw().StatusCode
+			}
+
+			By("apply ApisixRoute")
+			var apisixRoute apiv2.ApisixRoute
+			applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, &apisixRoute,
+				fmt.Sprintf(apisixRouteSpec, s.Namespace(), "/get"))
+
+			By("verify ApisixRoute works")
+			Eventually(request).WithArguments("/get").WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK))
+
+			By("update ApisixRoute")
+			applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, &apisixRoute,
+				fmt.Sprintf(apisixRouteSpec, s.Namespace(), "/headers"))
+			Eventually(request).WithArguments("/get").WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound))
+			s.RequestAssert(&scaffold.RequestAssert{
+				Method: "GET",
+				Path:   "/headers",
+				Host:   "httpbin",
+				Check:  scaffold.WithExpectedStatus(http.StatusOK),
+			})
+
+			By("delete ApisixRoute")
+			err = s.DeleteResource("ApisixRoute", "default")
+			Expect(err).ShouldNot(HaveOccurred(), "deleting ApisixRoute")
+			Eventually(request).WithArguments("/headers").WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound))
+		})
+	})
 })