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 <[email protected]>
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)
+       })
+})

Reply via email to