You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by zh...@apache.org on 2022/11/01 06:28:48 UTC

[apisix-ingress-controller] branch master updated: feat: support ingress and backend service in different namespace (#1377)

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

zhangjintao 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 6879c81f feat: support ingress and backend service in different namespace (#1377)
6879c81f is described below

commit 6879c81fa5768db13e566d86b85f1c1fe9cf4073
Author: 林靖 <56...@qq.com>
AuthorDate: Tue Nov 1 14:28:42 2022 +0800

    feat: support ingress and backend service in different namespace (#1377)
---
 pkg/providers/ingress/translation/annotations.go   |   3 +
 .../servicenamespace/servicenamespace.go           |  29 +
 .../ingress/translation/annotations/types.go       |   3 +
 .../ingress/translation/annotations_test.go        |   9 +
 pkg/providers/ingress/translation/translator.go    |  24 +-
 test/e2e/suite-annotations/svc_namespace.go        | 628 +++++++++++++++++++++
 6 files changed, 690 insertions(+), 6 deletions(-)

diff --git a/pkg/providers/ingress/translation/annotations.go b/pkg/providers/ingress/translation/annotations.go
index 6bedd91e..008aab8e 100644
--- a/pkg/providers/ingress/translation/annotations.go
+++ b/pkg/providers/ingress/translation/annotations.go
@@ -23,6 +23,7 @@ import (
 	"github.com/apache/apisix-ingress-controller/pkg/providers/ingress/translation/annotations/pluginconfig"
 	"github.com/apache/apisix-ingress-controller/pkg/providers/ingress/translation/annotations/plugins"
 	"github.com/apache/apisix-ingress-controller/pkg/providers/ingress/translation/annotations/regex"
+	"github.com/apache/apisix-ingress-controller/pkg/providers/ingress/translation/annotations/servicenamespace"
 	"github.com/apache/apisix-ingress-controller/pkg/providers/ingress/translation/annotations/websocket"
 	apisix "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
@@ -33,6 +34,7 @@ type Ingress struct {
 	UseRegex         bool
 	EnableWebSocket  bool
 	PluginConfigName string
+	ServiceNamespace string
 }
 
 var (
@@ -41,6 +43,7 @@ var (
 		"UseRegex":         regex.NewParser(),
 		"EnableWebSocket":  websocket.NewParser(),
 		"PluginConfigName": pluginconfig.NewParser(),
+		"ServiceNamespace": servicenamespace.NewParser(),
 	}
 )
 
diff --git a/pkg/providers/ingress/translation/annotations/servicenamespace/servicenamespace.go b/pkg/providers/ingress/translation/annotations/servicenamespace/servicenamespace.go
new file mode 100644
index 00000000..e7790705
--- /dev/null
+++ b/pkg/providers/ingress/translation/annotations/servicenamespace/servicenamespace.go
@@ -0,0 +1,29 @@
+// 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 servicenamespace
+
+import (
+	"github.com/apache/apisix-ingress-controller/pkg/providers/ingress/translation/annotations"
+)
+
+type servicenamespace struct{}
+
+func NewParser() annotations.IngressAnnotationsParser {
+	return &servicenamespace{}
+}
+
+func (w *servicenamespace) Parse(e annotations.Extractor) (interface{}, error) {
+	return e.GetStringAnnotation(annotations.AnnotationsSvcNamespace), nil
+}
diff --git a/pkg/providers/ingress/translation/annotations/types.go b/pkg/providers/ingress/translation/annotations/types.go
index 66ea36e4..f23cc6e7 100644
--- a/pkg/providers/ingress/translation/annotations/types.go
+++ b/pkg/providers/ingress/translation/annotations/types.go
@@ -65,6 +65,9 @@ const (
 	// key-auth plugin and basic-auth plugin
 	// auth-type: keyAuth | basicAuth
 	AnnotationsAuthType = AnnotationsPrefix + "auth-type"
+
+	// support backend service cross namespace
+	AnnotationsSvcNamespace = AnnotationsPrefix + "svc-namespace"
 )
 
 // Handler abstracts the behavior so that the apisix-ingress-controller knows
diff --git a/pkg/providers/ingress/translation/annotations_test.go b/pkg/providers/ingress/translation/annotations_test.go
index 4bf11e1d..a4b80741 100644
--- a/pkg/providers/ingress/translation/annotations_test.go
+++ b/pkg/providers/ingress/translation/annotations_test.go
@@ -72,3 +72,12 @@ func TestAnnotationsUseRegex(t *testing.T) {
 	ingress := (&translator{}).TranslateAnnotations(anno)
 	assert.Equal(t, true, ingress.UseRegex)
 }
+
+func TestAnnotationsServiceNamespace(t *testing.T) {
+	anno := map[string]string{
+		annotations.AnnotationsSvcNamespace: "mynamespace",
+	}
+
+	ingress := (&translator{}).TranslateAnnotations(anno)
+	assert.Equal(t, "mynamespace", ingress.ServiceNamespace)
+}
diff --git a/pkg/providers/ingress/translation/translator.go b/pkg/providers/ingress/translation/translator.go
index 6f709796..c5441af4 100644
--- a/pkg/providers/ingress/translation/translator.go
+++ b/pkg/providers/ingress/translation/translator.go
@@ -139,6 +139,10 @@ func (t *translator) translateIngressV1(ing *networkingv1.Ingress, skipVerify bo
 		}
 		ctx.AddSSL(ssl)
 	}
+	ns := ing.Namespace
+	if ingress.ServiceNamespace != "" {
+		ns = ingress.ServiceNamespace
+	}
 	for _, rule := range ing.Spec.Rules {
 		for _, pathRule := range rule.HTTP.Paths {
 			var (
@@ -147,9 +151,9 @@ func (t *translator) translateIngressV1(ing *networkingv1.Ingress, skipVerify bo
 			)
 			if pathRule.Backend.Service != nil {
 				if skipVerify {
-					ups = t.translateDefaultUpstreamFromIngressV1(ing.Namespace, pathRule.Backend.Service)
+					ups = t.translateDefaultUpstreamFromIngressV1(ns, pathRule.Backend.Service)
 				} else {
-					ups, err = t.translateUpstreamFromIngressV1(ing.Namespace, pathRule.Backend.Service)
+					ups, err = t.translateUpstreamFromIngressV1(ns, pathRule.Backend.Service)
 					if err != nil {
 						log.Errorw("failed to translate ingress backend to upstream",
 							zap.Error(err),
@@ -237,6 +241,10 @@ func (t *translator) translateIngressV1beta1(ing *networkingv1beta1.Ingress, ski
 		}
 		ctx.AddSSL(ssl)
 	}
+	ns := ing.Namespace
+	if ingress.ServiceNamespace != "" {
+		ns = ingress.ServiceNamespace
+	}
 	for _, rule := range ing.Spec.Rules {
 		for _, pathRule := range rule.HTTP.Paths {
 			var (
@@ -245,9 +253,9 @@ func (t *translator) translateIngressV1beta1(ing *networkingv1beta1.Ingress, ski
 			)
 			if pathRule.Backend.ServiceName != "" {
 				if skipVerify {
-					ups = t.translateDefaultUpstreamFromIngressV1beta1(ing.Namespace, pathRule.Backend.ServiceName, pathRule.Backend.ServicePort)
+					ups = t.translateDefaultUpstreamFromIngressV1beta1(ns, pathRule.Backend.ServiceName, pathRule.Backend.ServicePort)
 				} else {
-					ups, err = t.translateUpstreamFromIngressV1beta1(ing.Namespace, pathRule.Backend.ServiceName, pathRule.Backend.ServicePort)
+					ups, err = t.translateUpstreamFromIngressV1beta1(ns, pathRule.Backend.ServiceName, pathRule.Backend.ServicePort)
 					if err != nil {
 						log.Errorw("failed to translate ingress backend to upstream",
 							zap.Error(err),
@@ -389,6 +397,10 @@ func (t *translator) translateIngressExtensionsV1beta1(ing *extensionsv1beta1.In
 		}
 		ctx.AddSSL(ssl)
 	}
+	ns := ing.Namespace
+	if ingress.ServiceNamespace != "" {
+		ns = ingress.ServiceNamespace
+	}
 	for _, rule := range ing.Spec.Rules {
 		for _, pathRule := range rule.HTTP.Paths {
 			var (
@@ -398,9 +410,9 @@ func (t *translator) translateIngressExtensionsV1beta1(ing *extensionsv1beta1.In
 			if pathRule.Backend.ServiceName != "" {
 				// Structure here is same to ingress.extensions/v1beta1, so just use this method.
 				if skipVerify {
-					ups = t.translateDefaultUpstreamFromIngressV1beta1(ing.Namespace, pathRule.Backend.ServiceName, pathRule.Backend.ServicePort)
+					ups = t.translateDefaultUpstreamFromIngressV1beta1(ns, pathRule.Backend.ServiceName, pathRule.Backend.ServicePort)
 				} else {
-					ups, err = t.translateUpstreamFromIngressV1beta1(ing.Namespace, pathRule.Backend.ServiceName, pathRule.Backend.ServicePort)
+					ups, err = t.translateUpstreamFromIngressV1beta1(ns, pathRule.Backend.ServiceName, pathRule.Backend.ServicePort)
 					if err != nil {
 						log.Errorw("failed to translate ingress backend to upstream",
 							zap.Error(err),
diff --git a/test/e2e/suite-annotations/svc_namespace.go b/test/e2e/suite-annotations/svc_namespace.go
new file mode 100644
index 00000000..f2797ed6
--- /dev/null
+++ b/test/e2e/suite-annotations/svc_namespace.go
@@ -0,0 +1,628 @@
+// 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 annotations
+
+import (
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	ginkgo "github.com/onsi/ginkgo/v2"
+	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2beta3", func() {
+
+	s := scaffold.NewDefaultV2beta3Scaffold()
+
+	ginkgo.It("same namespace in ingress networking/v1", func() {
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+  name: ingress-v1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          service:
+            name: %s
+            port:
+              number: %d
+`, backendSvc, backendPort[0])
+		err := s.CreateResourceFromString(ing)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2beta3", func() {
+	s := scaffold.NewDefaultV2beta3Scaffold()
+	createNamespace := func(namespace string, watch string) {
+		k8s.CreateNamespaceWithMetadata(ginkgo.GinkgoT(),
+			&k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()},
+			metav1.ObjectMeta{Name: namespace, Labels: map[string]string{
+				"apisix.ingress.watch": watch,
+			}})
+	}
+
+	deleteNamespace := func(namespace string) {
+		_ = k8s.DeleteNamespaceE(ginkgo.GinkgoT(), &k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()}, namespace)
+	}
+
+	ginkgo.It("different namespace in ingress networking/v1", func() {
+		newNs := fmt.Sprintf("second-svc-namespace-%d", time.Now().Nanosecond())
+		oldNs := s.Namespace()
+		createNamespace(newNs, oldNs)
+		defer deleteNamespace(newNs)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/svc-namespace: %s
+  name: ingress-v1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          service:
+            name: %s
+            port:
+              number: %d
+`, oldNs, backendSvc, backendPort[0])
+		err := s.CreateResourceFromStringWithNamespace(ing, newNs)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2beta3", func() {
+
+	s := scaffold.NewDefaultV2beta3Scaffold()
+	ginkgo.It("same namespace in ingress networking/v1beta1", func() {
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/svc-namespace: ""
+  name: ingress-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, backendSvc, backendPort[0])
+
+		err := s.CreateResourceFromString(ing)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2beta3", func() {
+	s := scaffold.NewDefaultV2beta3Scaffold()
+	createNamespace := func(namespace string, watch string) {
+		k8s.CreateNamespaceWithMetadata(ginkgo.GinkgoT(),
+			&k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()},
+			metav1.ObjectMeta{Name: namespace, Labels: map[string]string{
+				"apisix.ingress.watch": watch,
+			}})
+	}
+
+	deleteNamespace := func(namespace string) {
+		_ = k8s.DeleteNamespaceE(ginkgo.GinkgoT(), &k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()}, namespace)
+	}
+
+	ginkgo.It("different namespace in ingress networking/v1beta1", func() {
+
+		newNs := fmt.Sprintf("second-svc-namespace-%d", time.Now().Nanosecond())
+		oldNs := s.Namespace()
+		createNamespace(newNs, oldNs)
+		defer deleteNamespace(newNs)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/svc-namespace: %s
+  name: ingress-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, oldNs, backendSvc, backendPort[0])
+
+		err := s.CreateResourceFromStringWithNamespace(ing, newNs)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2beta3", func() {
+	s := scaffold.NewDefaultV2beta3Scaffold()
+
+	ginkgo.It("same namespace in ingress extensions/v1beta1", func() {
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix	
+  name: ingress-extensions-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, backendSvc, backendPort[0])
+		err := s.CreateResourceFromString(ing)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2beta3", func() {
+	s := scaffold.NewDefaultV2beta3Scaffold()
+
+	createNamespace := func(namespace string, watch string) {
+		k8s.CreateNamespaceWithMetadata(ginkgo.GinkgoT(),
+			&k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()},
+			metav1.ObjectMeta{Name: namespace, Labels: map[string]string{
+				"apisix.ingress.watch": watch,
+			}})
+	}
+
+	deleteNamespace := func(namespace string) {
+		_ = k8s.DeleteNamespaceE(ginkgo.GinkgoT(), &k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()}, namespace)
+	}
+
+	ginkgo.It("different namespace in ingress extensions/v1beta1", func() {
+
+		newNs := fmt.Sprintf("second-svc-namespace-%d", time.Now().Nanosecond())
+		oldNs := s.Namespace()
+		createNamespace(newNs, oldNs)
+		defer deleteNamespace(newNs)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/svc-namespace: %s
+  name: ingress-extensions-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, oldNs, backendSvc, backendPort[0])
+		err := s.CreateResourceFromStringWithNamespace(ing, newNs)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2", func() {
+
+	s := scaffold.NewDefaultV2Scaffold()
+
+	ginkgo.It("same namespace in ingress networking/v1", func() {
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+  name: ingress-v1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          service:
+            name: %s
+            port:
+              number: %d
+`, backendSvc, backendPort[0])
+		err := s.CreateResourceFromString(ing)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2", func() {
+	s := scaffold.NewDefaultV2Scaffold()
+	createNamespace := func(namespace string, watch string) {
+		k8s.CreateNamespaceWithMetadata(ginkgo.GinkgoT(),
+			&k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()},
+			metav1.ObjectMeta{Name: namespace, Labels: map[string]string{
+				"apisix.ingress.watch": watch,
+			}})
+	}
+
+	deleteNamespace := func(namespace string) {
+		_ = k8s.DeleteNamespaceE(ginkgo.GinkgoT(), &k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()}, namespace)
+	}
+
+	ginkgo.It("different namespace in ingress networking/v1", func() {
+		newNs := fmt.Sprintf("second-svc-namespace-%d", time.Now().Nanosecond())
+		oldNs := s.Namespace()
+		createNamespace(newNs, oldNs)
+		defer deleteNamespace(newNs)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/svc-namespace: %s
+  name: ingress-v1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          service:
+            name: %s
+            port:
+              number: %d
+`, oldNs, backendSvc, backendPort[0])
+		err := s.CreateResourceFromStringWithNamespace(ing, newNs)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2", func() {
+
+	s := scaffold.NewDefaultV2Scaffold()
+	ginkgo.It("same namespace in ingress networking/v1beta1", func() {
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/svc-namespace: ""
+  name: ingress-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, backendSvc, backendPort[0])
+
+		err := s.CreateResourceFromString(ing)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2", func() {
+	s := scaffold.NewDefaultV2Scaffold()
+	createNamespace := func(namespace string, watch string) {
+		k8s.CreateNamespaceWithMetadata(ginkgo.GinkgoT(),
+			&k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()},
+			metav1.ObjectMeta{Name: namespace, Labels: map[string]string{
+				"apisix.ingress.watch": watch,
+			}})
+	}
+
+	deleteNamespace := func(namespace string) {
+		_ = k8s.DeleteNamespaceE(ginkgo.GinkgoT(), &k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()}, namespace)
+	}
+
+	ginkgo.It("different namespace in ingress networking/v1beta1", func() {
+
+		newNs := fmt.Sprintf("second-svc-namespace-%d", time.Now().Nanosecond())
+		oldNs := s.Namespace()
+		createNamespace(newNs, oldNs)
+		defer deleteNamespace(newNs)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: networking.k8s.io/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/svc-namespace: %s
+  name: ingress-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, oldNs, backendSvc, backendPort[0])
+
+		err := s.CreateResourceFromStringWithNamespace(ing, newNs)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2", func() {
+	s := scaffold.NewDefaultV2Scaffold()
+
+	ginkgo.It("same namespace in ingress extensions/v1beta1", func() {
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix	
+  name: ingress-extensions-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, backendSvc, backendPort[0])
+		err := s.CreateResourceFromString(ing)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})
+
+var _ = ginkgo.Describe("suite-annotations: svc-namespace annotations v2", func() {
+	s := scaffold.NewDefaultV2Scaffold()
+
+	createNamespace := func(namespace string, watch string) {
+		k8s.CreateNamespaceWithMetadata(ginkgo.GinkgoT(),
+			&k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()},
+			metav1.ObjectMeta{Name: namespace, Labels: map[string]string{
+				"apisix.ingress.watch": watch,
+			}})
+	}
+
+	deleteNamespace := func(namespace string) {
+		_ = k8s.DeleteNamespaceE(ginkgo.GinkgoT(), &k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()}, namespace)
+	}
+
+	ginkgo.It("different namespace in ingress extensions/v1beta1", func() {
+
+		newNs := fmt.Sprintf("second-svc-namespace-%d", time.Now().Nanosecond())
+		oldNs := s.Namespace()
+		createNamespace(newNs, oldNs)
+		defer deleteNamespace(newNs)
+
+		backendSvc, backendPort := s.DefaultHTTPBackend()
+		ing := fmt.Sprintf(`
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  annotations:
+    kubernetes.io/ingress.class: apisix
+    k8s.apisix.apache.org/svc-namespace: %s
+  name: ingress-extensions-v1beta1
+spec:
+  rules:
+  - host: httpbin.org
+    http:
+      paths:
+      - path: /*
+        pathType: Prefix
+        backend:
+          serviceName: %s
+          servicePort: %d
+`, oldNs, backendSvc, backendPort[0])
+		err := s.CreateResourceFromStringWithNamespace(ing, newNs)
+		if err != nil {
+			assert.Fail(ginkgo.GinkgoT(), err.Error(), "creating ingress")
+		}
+		time.Sleep(5 * time.Second)
+
+		_ = s.NewAPISIXClient().
+			POST("/anything").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+	})
+})