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")
+ })
+})