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/12/12 14:25:24 UTC

[apisix-ingress-controller] branch master updated: feat: support secret plugin config (#1486)

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 0009b5d6 feat: support secret plugin config (#1486)
0009b5d6 is described below

commit 0009b5d6951c89d2b67e0a440d4a75952fb3154c
Author: dongjunduo <an...@gmail.com>
AuthorDate: Mon Dec 12 22:25:18 2022 +0800

    feat: support secret plugin config (#1486)
---
 docs/en/latest/concepts/apisix_route.md            | 47 +++++++++++
 pkg/kube/apisix/apis/config/v2/types.go            |  2 +
 .../apisix/translation/apisix_pluginconfig.go      | 15 ++++
 pkg/providers/apisix/translation/apisix_route.go   | 30 +++++++
 samples/deploy/crd/v1/ApisixPluginConfig.yaml      |  2 +
 samples/deploy/crd/v1/ApisixRoute.yaml             |  4 +
 .../suite-plugins-general/secret_ref.go            | 93 ++++++++++++++++++++++
 7 files changed, 193 insertions(+)

diff --git a/docs/en/latest/concepts/apisix_route.md b/docs/en/latest/concepts/apisix_route.md
index c04860b2..79b0946b 100644
--- a/docs/en/latest/concepts/apisix_route.md
+++ b/docs/en/latest/concepts/apisix_route.md
@@ -201,6 +201,53 @@ spec:
           enable: true
 ```
 
+### Config with secretRef
+
+Plugins are supported to be configured from kubernetes secret with `secretRef`.
+
+The priority is `plugins.secretRef > plugins.config`. That is, the duplicated key in `plugins.config` are replaced by `plugins.secretRef`.
+
+Example below configures echo plugin. The final values of `before_body`, `body` and `after_body` are "This is the replaced preface", "my custom body" and "This is the epilogue", respectively.
+
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+  name: echo
+data:
+  # content is "This is the replaced preface"
+  before_body: IlRoaXMgaXMgdGhlIHJlcGxhY2VkIHByZWZhY2Ui
+  # content is "my custom body"
+  body: Im15IGN1c3RvbSBib2R5Ig==
+---
+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
+      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
+          secretRef: echo
+```
+
 ## Websocket proxy
 
 You can route requests to [WebSocket](https://en.wikipedia.org/wiki/WebSocket#:~:text=WebSocket%20is%20a%20computer%20communications,WebSocket%20is%20distinct%20from%20HTTP.) services by setting the `websocket` attribute to `true` as shown below:
diff --git a/pkg/kube/apisix/apis/config/v2/types.go b/pkg/kube/apisix/apis/config/v2/types.go
index da481997..d87a8a56 100644
--- a/pkg/kube/apisix/apis/config/v2/types.go
+++ b/pkg/kube/apisix/apis/config/v2/types.go
@@ -170,6 +170,8 @@ type ApisixRoutePlugin struct {
 	Enable bool `json:"enable" yaml:"enable"`
 	// Plugin configuration.
 	Config ApisixRoutePluginConfig `json:"config" yaml:"config"`
+	// Plugin configuration secretRef.
+	SecretRef string `json:"secretRef" yaml:"secretRef"`
 }
 
 // ApisixRoutePluginConfig is the configuration for
diff --git a/pkg/providers/apisix/translation/apisix_pluginconfig.go b/pkg/providers/apisix/translation/apisix_pluginconfig.go
index 1812e8c2..732d0ba7 100644
--- a/pkg/providers/apisix/translation/apisix_pluginconfig.go
+++ b/pkg/providers/apisix/translation/apisix_pluginconfig.go
@@ -82,6 +82,21 @@ func (t *translator) TranslatePluginConfigV2(config *configv2.ApisixPluginConfig
 						zap.Any("new", plugin.Config),
 					)
 				}
+				if plugin.SecretRef != "" {
+					sec, err := t.SecretLister.Secrets(config.Namespace).Get(plugin.SecretRef)
+					if err != nil {
+						log.Errorw("The config secretRef is invalid",
+							zap.Any("plugin", plugin.Name),
+							zap.String("secretRef", plugin.SecretRef))
+						break
+					}
+					log.Debugw("Add new items, then override items with the same plugin key",
+						zap.Any("plugin", plugin.Name),
+						zap.String("secretRef", plugin.SecretRef))
+					for key, value := range sec.Data {
+						plugin.Config[key] = string(value)
+					}
+				}
 				pluginMap[plugin.Name] = plugin.Config
 			} else {
 				pluginMap[plugin.Name] = make(map[string]interface{})
diff --git a/pkg/providers/apisix/translation/apisix_route.go b/pkg/providers/apisix/translation/apisix_route.go
index 2d1d032b..76f01fdd 100644
--- a/pkg/providers/apisix/translation/apisix_route.go
+++ b/pkg/providers/apisix/translation/apisix_route.go
@@ -251,6 +251,21 @@ func (t *translator) translateHTTPRouteV2(ctx *translation.TranslateContext, ar
 				continue
 			}
 			if plugin.Config != nil {
+				if plugin.SecretRef != "" {
+					sec, err := t.SecretLister.Secrets(ar.Namespace).Get(plugin.SecretRef)
+					if err != nil {
+						log.Errorw("The config secretRef is invalid",
+							zap.Any("plugin", plugin.Name),
+							zap.String("secretRef", plugin.SecretRef))
+						break
+					}
+					log.Debugw("Add new items, then override items with the same plugin key",
+						zap.Any("plugin", plugin.Name),
+						zap.String("secretRef", plugin.SecretRef))
+					for key, value := range sec.Data {
+						plugin.Config[key] = string(value)
+					}
+				}
 				pluginMap[plugin.Name] = plugin.Config
 			} else {
 				pluginMap[plugin.Name] = make(map[string]interface{})
@@ -753,6 +768,21 @@ func (t *translator) translateStreamRouteV2(ctx *translation.TranslateContext, a
 				continue
 			}
 			if plugin.Config != nil {
+				if plugin.SecretRef != "" {
+					sec, err := t.SecretLister.Secrets(ar.Namespace).Get(plugin.SecretRef)
+					if err != nil {
+						log.Errorw("The config secretRef is invalid",
+							zap.Any("plugin", plugin.Name),
+							zap.String("secretRef", plugin.SecretRef))
+						break
+					}
+					log.Debugw("Add new items, then override items with the same plugin key",
+						zap.Any("plugin", plugin.Name),
+						zap.String("secretRef", plugin.SecretRef))
+					for key, value := range sec.Data {
+						plugin.Config[key] = string(value)
+					}
+				}
 				pluginMap[plugin.Name] = plugin.Config
 			} else {
 				pluginMap[plugin.Name] = make(map[string]interface{})
diff --git a/samples/deploy/crd/v1/ApisixPluginConfig.yaml b/samples/deploy/crd/v1/ApisixPluginConfig.yaml
index 4a79a61b..c49b27bf 100644
--- a/samples/deploy/crd/v1/ApisixPluginConfig.yaml
+++ b/samples/deploy/crd/v1/ApisixPluginConfig.yaml
@@ -114,6 +114,8 @@ spec:
                       config:
                         type: object
                         x-kubernetes-preserve-unknown-fields: true # we have to enable it since plugin config
+                      secretRef:
+                        type: string
                   required:
                     - name
                     - enable
diff --git a/samples/deploy/crd/v1/ApisixRoute.yaml b/samples/deploy/crd/v1/ApisixRoute.yaml
index 89dac1de..e241bd5a 100644
--- a/samples/deploy/crd/v1/ApisixRoute.yaml
+++ b/samples/deploy/crd/v1/ApisixRoute.yaml
@@ -504,6 +504,8 @@ spec:
                             config:
                               type: object
                               x-kubernetes-preserve-unknown-fields: true # we have to enable it since plugin config
+                            secretRef:
+                              type: string
                         required:
                           - name
                           - enable
@@ -592,6 +594,8 @@ spec:
                             config:
                               type: object
                               x-kubernetes-preserve-unknown-fields: true # we have to enable it since plugin config
+                            secretRef:
+                              type: string
                         required:
                           - name
                           - enable
diff --git a/test/e2e/suite-plugins/suite-plugins-general/secret_ref.go b/test/e2e/suite-plugins/suite-plugins-general/secret_ref.go
new file mode 100644
index 00000000..7fa16bc2
--- /dev/null
+++ b/test/e2e/suite-plugins/suite-plugins-general/secret_ref.go
@@ -0,0 +1,93 @@
+// 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 plugins
+
+import (
+	"fmt"
+	ginkgo "github.com/onsi/ginkgo/v2"
+	"github.com/stretchr/testify/assert"
+	"net/http"
+
+	"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = ginkgo.Describe("suite-plugins-general: config plugin with secretRef", func() {
+	suites := func(scaffoldFunc func() *scaffold.Scaffold) {
+		s := scaffoldFunc()
+		ginkgo.It("suite-plugins-general: echo plugin config with secretRef", func() {
+			backendSvc, backendPorts := s.DefaultHTTPBackend()
+			secret := `
+apiVersion: v1
+kind: Secret
+metadata:
+  name: echo
+data:
+  # content is "This is the replaced preface"
+  before_body: IlRoaXMgaXMgdGhlIHJlcGxhY2VkIHByZWZhY2Ui
+  # content is "my custom body"
+  body: Im15IGN1c3RvbSBib2R5Ig==
+  
+`
+			assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(secret), "creating echo secret for ApisixRoute")
+			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
+   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
+     secretRef: echo
+       
+`, backendSvc, backendPorts[0])
+
+			assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar))
+
+			err := s.EnsureNumApisixUpstreamsCreated(1)
+			assert.Nil(ginkgo.GinkgoT(), err, "Checking number of upstreams")
+			err = s.EnsureNumApisixRoutesCreated(1)
+			assert.Nil(ginkgo.GinkgoT(), err, "Checking number of routes")
+
+			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.Body().Contains("This is the replaced preface")
+			resp.Body().Contains("This is the epilogue")
+			resp.Body().Contains("my custom body")
+		})
+	}
+	ginkgo.Describe("suite-plugins-general: scaffold v2", func() {
+		suites(scaffold.NewDefaultV2Scaffold)
+	})
+})