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

gallardot 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 96510b68 feat: add plugin_config_namespace parameter to ApisixRoute 
(#2137)
96510b68 is described below

commit 96510b68462e1a94370ed1d537beb24f2c16e234
Author: Ashish Tiwari <[email protected]>
AuthorDate: Thu Jan 18 11:43:19 2024 +0530

    feat: add plugin_config_namespace parameter to ApisixRoute (#2137)
    
    * feat: add plugin_config_namespace parameter to ApisixRoute
    
    Add plugin_config_namespace parameter to ApisixRoute resource to allow 
cross namespace discovery.
    
    * fix indentation
    
    Signed-off-by: Ashish Tiwari <[email protected]>
    
    * remove route.yaml
    
    Signed-off-by: Ashish Tiwari <[email protected]>
    
    * fix e2e test
    
    Signed-off-by: Ashish Tiwari <[email protected]>
    
    * update gomod gosum
    
    * fix e2e test
    
    Signed-off-by: Ashish Tiwari <[email protected]>
    
    * fix e2e test
    
    Signed-off-by: Ashish Tiwari <[email protected]>
    
    * Update pkg/providers/apisix/apisix_route.go
    
    Co-authored-by: Gallardot <[email protected]>
    
    * create namespace
    
    * refactor test
    
    * refactor test
    
    * fix e2e
    
    * fix e2e
    
    * update crd
    
    * Add EOL
    
    Signed-off-by: Ashish Tiwari <[email protected]>
    
    ---------
    
    Signed-off-by: Ashish Tiwari <[email protected]>
    Co-authored-by: Gallardot <[email protected]>
---
 docs/en/latest/references/apisix_route_v2.md       |  1 +
 pkg/kube/apisix/apis/config/v2/types.go            | 10 ++-
 pkg/providers/apisix/apisix_route.go               | 10 ++-
 pkg/providers/apisix/translation/apisix_route.go   | 12 ++-
 .../apisix/translation/apisix_route_test.go        | 40 ++++++++++
 samples/deploy/crd/v1/ApisixRoute.yaml             |  3 +
 .../suite-plugins-other/plugin_config.go           | 90 ++++++++++++++++++++++
 7 files changed, 157 insertions(+), 9 deletions(-)

diff --git a/docs/en/latest/references/apisix_route_v2.md 
b/docs/en/latest/references/apisix_route_v2.md
index 5ea3cd50..717b1954 100644
--- a/docs/en/latest/references/apisix_route_v2.md
+++ b/docs/en/latest/references/apisix_route_v2.md
@@ -57,6 +57,7 @@ The table below describes each of the attributes in the spec. 
The fields `apiVer
 | http[].match.exprs[].set             | array              | Set to compare 
the subject with. Only used when the operator is `In` or `NotIn`. Can use 
either this or `http[].match.exprs[].value`.                                    
                 |
 | http[].websocket                     | boolean            | When set to 
`true` enables websocket proxy.                                                 
                                                                                
              |
 | http[].plugin_config_name            | string             | Existing Plugin 
Config name to use in the Route.                                                
                                                                                
          |
+| http[].plugin_config_namespace            | string             | Namespace 
in which to look for `plugin_config_name` Route.                                
                                                                                
                          |
 | http[].backends                      | object             | List of backend 
services. If there are more than one, a weight based traffic split policy would 
be applied.                                                                     
          |
 | http[].backends[].serviceName        | string             | Name of the 
backend service. The service and the `ApisixRoute` resource should be created 
in the same namespace.                                                          
                |
 | http[].backends[].servicePort        | integer or string  | Port number or 
the name defined in the service object of the backend.                          
                                                                                
           |
diff --git a/pkg/kube/apisix/apis/config/v2/types.go 
b/pkg/kube/apisix/apis/config/v2/types.go
index 1edb8e55..dfec5928 100644
--- a/pkg/kube/apisix/apis/config/v2/types.go
+++ b/pkg/kube/apisix/apis/config/v2/types.go
@@ -72,10 +72,12 @@ type ApisixRouteHTTP struct {
        // Upstreams refer to ApisixUpstream CRD
        Upstreams []ApisixRouteUpstreamReference `json:"upstreams,omitempty" 
yaml:"upstreams,omitempty"`
 
-       Websocket        bool                      `json:"websocket" 
yaml:"websocket"`
-       PluginConfigName string                    
`json:"plugin_config_name,omitempty" yaml:"plugin_config_name,omitempty"`
-       Plugins          []ApisixRoutePlugin       `json:"plugins,omitempty" 
yaml:"plugins,omitempty"`
-       Authentication   ApisixRouteAuthentication 
`json:"authentication,omitempty" yaml:"authentication,omitempty"`
+       Websocket        bool   `json:"websocket" yaml:"websocket"`
+       PluginConfigName string `json:"plugin_config_name,omitempty" 
yaml:"plugin_config_name,omitempty"`
+       //By default, PluginConfigNamespace will be the same as the namespace 
of ApisixRoute
+       PluginConfigNamespace string                    
`json:"plugin_config_namespace,omitempty" 
yaml:"plugin_config_namespace,omitempty"`
+       Plugins               []ApisixRoutePlugin       
`json:"plugins,omitempty" yaml:"plugins,omitempty"`
+       Authentication        ApisixRouteAuthentication 
`json:"authentication,omitempty" yaml:"authentication,omitempty"`
 }
 
 // ApisixRouteHTTPBackend represents an HTTP backend (a Kubernetes Service).
diff --git a/pkg/providers/apisix/apisix_route.go 
b/pkg/providers/apisix/apisix_route.go
index 937f02fa..c06a8a0f 100644
--- a/pkg/providers/apisix/apisix_route.go
+++ b/pkg/providers/apisix/apisix_route.go
@@ -397,16 +397,20 @@ updateStatus:
 func (c *apisixRouteController) checkPluginNameIfNotEmptyV2(ctx 
context.Context, in *v2.ApisixRoute) error {
        for _, v := range in.Spec.HTTP {
                if v.PluginConfigName != "" {
-                       _, err := 
c.APISIX.Cluster(c.Config.APISIX.DefaultClusterName).PluginConfig().Get(ctx, 
apisixv1.ComposePluginConfigName(in.Namespace, v.PluginConfigName))
+                       ns := in.Namespace
+                       if v.PluginConfigNamespace != "" {
+                               ns = v.PluginConfigNamespace
+                       }
+                       _, err := 
c.APISIX.Cluster(c.Config.APISIX.DefaultClusterName).PluginConfig().Get(ctx, 
apisixv1.ComposePluginConfigName(ns, v.PluginConfigName))
                        if err != nil {
                                if err == apisixcache.ErrNotFound {
                                        log.Errorw("checkPluginNameIfNotEmptyV2 
error: plugin_config not found",
-                                               zap.String("name", 
apisixv1.ComposePluginConfigName(in.Namespace, v.PluginConfigName)),
+                                               zap.String("name", 
apisixv1.ComposePluginConfigName(ns, v.PluginConfigName)),
                                                zap.Any("obj", in),
                                                zap.Error(err))
                                } else {
                                        log.Errorw("checkPluginNameIfNotEmptyV2 
PluginConfig get failed",
-                                               zap.String("name", 
apisixv1.ComposePluginConfigName(in.Namespace, v.PluginConfigName)),
+                                               zap.String("name", 
apisixv1.ComposePluginConfigName(ns, v.PluginConfigName)),
                                                zap.Any("obj", in),
                                                zap.Error(err))
                                }
diff --git a/pkg/providers/apisix/translation/apisix_route.go 
b/pkg/providers/apisix/translation/apisix_route.go
index 856709e6..9fa8b2b2 100644
--- a/pkg/providers/apisix/translation/apisix_route.go
+++ b/pkg/providers/apisix/translation/apisix_route.go
@@ -171,7 +171,11 @@ func (t *translator) translateHTTPRouteV2(ctx 
*translation.TranslateContext, ar
                route.FilterFunc = part.Match.FilterFunc
 
                if part.PluginConfigName != "" {
-                       route.PluginConfigId = 
id.GenID(apisixv1.ComposePluginConfigName(ar.Namespace, part.PluginConfigName))
+                       ns := ar.Namespace
+                       if part.PluginConfigNamespace != "" {
+                               ns = part.PluginConfigNamespace
+                       }
+                       route.PluginConfigId = 
id.GenID(apisixv1.ComposePluginConfigName(ns, part.PluginConfigName))
                }
 
                for k, v := range ar.ObjectMeta.Labels {
@@ -465,7 +469,11 @@ func (t *translator) generateHTTPRouteV2DeleteMark(ctx 
*translation.TranslateCon
                route.Name = apisixv1.ComposeRouteName(ar.Namespace, ar.Name, 
part.Name)
                route.ID = id.GenID(route.Name)
                if part.PluginConfigName != "" {
-                       route.PluginConfigId = 
id.GenID(apisixv1.ComposePluginConfigName(ar.Namespace, part.PluginConfigName))
+                       ns := ar.Namespace
+                       if part.PluginConfigNamespace != "" {
+                               ns = part.PluginConfigNamespace
+                       }
+                       route.PluginConfigId = 
id.GenID(apisixv1.ComposePluginConfigName(ns, part.PluginConfigName))
                }
 
                ctx.AddRoute(route)
diff --git a/pkg/providers/apisix/translation/apisix_route_test.go 
b/pkg/providers/apisix/translation/apisix_route_test.go
index 36f12983..64fa166f 100644
--- a/pkg/providers/apisix/translation/apisix_route_test.go
+++ b/pkg/providers/apisix/translation/apisix_route_test.go
@@ -315,6 +315,46 @@ func TestTranslateApisixRouteV2WithEmptyPluginConfigName(t 
*testing.T) {
        assert.Equal(t, "", res.Routes[2].PluginConfigId)
 }
 
+func TestTranslateApisixRouteV2WithPluginConfigNamespace(t *testing.T) {
+       tr, processCh := mockTranslatorV2(t)
+       <-processCh
+       <-processCh
+       pluginConfigNamespace := "test-2"
+       ar := &configv2.ApisixRoute{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "ar",
+                       Namespace: "test",
+               },
+               Spec: configv2.ApisixRouteSpec{
+                       HTTP: []configv2.ApisixRouteHTTP{
+                               {
+                                       Name: "rule1",
+                                       Match: configv2.ApisixRouteHTTPMatch{
+                                               Paths: []string{
+                                                       "/*",
+                                               },
+                                       },
+                                       Backends: 
[]configv2.ApisixRouteHTTPBackend{
+                                               {
+                                                       ServiceName: "svc",
+                                                       ServicePort: 
intstr.IntOrString{
+                                                               IntVal: 80,
+                                                       },
+                                               },
+                                       },
+                                       PluginConfigName:      
"test-PluginConfigName-1",
+                                       PluginConfigNamespace: 
pluginConfigNamespace,
+                               },
+                       },
+               },
+       }
+       res, err := tr.TranslateRouteV2(ar)
+       assert.NoError(t, err)
+       assert.Len(t, res.PluginConfigs, 0)
+       expectedPluginId := 
id.GenID(apisixv1.ComposePluginConfigName(pluginConfigNamespace, 
ar.Spec.HTTP[0].PluginConfigName))
+       assert.Equal(t, expectedPluginId, res.Routes[0].PluginConfigId)
+}
+
 func TestGenerateApisixRouteV2DeleteMark(t *testing.T) {
        tr := &translator{
                &TranslatorOptions{},
diff --git a/samples/deploy/crd/v1/ApisixRoute.yaml 
b/samples/deploy/crd/v1/ApisixRoute.yaml
index 8f3ba25d..4f8ae42c 100644
--- a/samples/deploy/crd/v1/ApisixRoute.yaml
+++ b/samples/deploy/crd/v1/ApisixRoute.yaml
@@ -184,6 +184,9 @@ spec:
                       plugin_config_name:
                         type: string
                         minLength: 1
+                      plugin_config_namespace:
+                        type: string
+                        minLength: 1
                       upstreams:
                         description: Upstreams refer to ApisixUpstream CRD
                         type: array
diff --git a/test/e2e/suite-plugins/suite-plugins-other/plugin_config.go 
b/test/e2e/suite-plugins/suite-plugins-other/plugin_config.go
index 0a91b837..ea4cbe9c 100644
--- a/test/e2e/suite-plugins/suite-plugins-other/plugin_config.go
+++ b/test/e2e/suite-plugins/suite-plugins-other/plugin_config.go
@@ -594,3 +594,93 @@ spec:
                resp.Status(http.StatusOK)
        })
 })
+
+var _ = ginkgo.Describe("suite-plugins-other: ApisixPluginConfig cross 
namespace", func() {
+       s := scaffold.NewScaffold(&scaffold.Options{
+               NamespaceSelectorLabel: map[string][]string{
+                       "apisix.ingress.watch": {"test"},
+               },
+       })
+       ginkgo.It("ApisixPluginConfig cross namespace", func() {
+               testns := `
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: test
+  labels:
+    apisix.ingress.watch: test
+`
+               err := s.CreateResourceFromString(testns)
+               assert.Nil(ginkgo.GinkgoT(), err, "Creating test namespace")
+               backendSvc, backendPorts := s.DefaultHTTPBackend()
+               apc := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixPluginConfig
+metadata:
+ name: echo-and-cors-apc
+ namespace: test
+spec:
+ plugins:
+ - name: echo
+   enable: true
+   config:
+    before_body: "This is the preface"
+    after_body: "This is the epilogue"
+    headers:
+     X-Foo: v1
+     X-Foo2: v2
+ - name: cors
+   enable: true
+`)
+               assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromStringWithNamespace(apc, "test"))
+
+               err = s.EnsureNumApisixPluginConfigCreated(1)
+               assert.Nil(ginkgo.GinkgoT(), err, "Checking number of 
pluginConfigs")
+
+               time.Sleep(time.Second * 3)
+
+               ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+  - name: rule1
+    match:
+      hosts:
+      - httpbin.org
+      paths:
+      - /ip
+    backends:
+    - serviceName: %s
+      servicePort: %d
+      weight: 10
+    plugin_config_name: echo-and-cors-apc
+    plugin_config_namespace: test
+`, backendSvc, backendPorts[0])
+               assert.Nil(ginkgo.GinkgoT(), 
s.CreateVersionedApisixResource(ar))
+
+               err = s.EnsureNumApisixRoutesCreated(1)
+               assert.Nil(ginkgo.GinkgoT(), err, "Checking number of routes")
+
+               time.Sleep(3 * time.Second)
+               pcs, err := s.ListApisixPluginConfig()
+               assert.Nil(ginkgo.GinkgoT(), err, nil, "listing pluginConfigs")
+               assert.Len(ginkgo.GinkgoT(), pcs, 1)
+               assert.Len(ginkgo.GinkgoT(), pcs[0].Plugins, 2)
+
+               resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.org").Expect()
+               resp.Status(http.StatusOK)
+               resp.Header("X-Foo").Equal("v1")
+               resp.Header("X-Foo2").Equal("v2")
+               resp.Header("Access-Control-Allow-Origin").Equal("*")
+               resp.Header("Access-Control-Allow-Methods").Equal("*")
+               resp.Header("Access-Control-Allow-Headers").Equal("*")
+               resp.Header("Access-Control-Expose-Headers").Equal("*")
+               resp.Header("Access-Control-Max-Age").Equal("5")
+               resp.Body().Contains("This is the preface")
+               resp.Body().Contains("origin")
+               resp.Body().Contains("This is the epilogue")
+       })
+})

Reply via email to