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 6f83da5c feat: support redirect and requestHeaderModifier in HTTPRoute 
filter (#1426)
6f83da5c is described below

commit 6f83da5ca55105d48c8d502b56ccf0ab2190f29e
Author: Xin Rong <[email protected]>
AuthorDate: Wed Nov 9 09:22:35 2022 +0800

    feat: support redirect and requestHeaderModifier in HTTPRoute filter (#1426)
---
 .../gateway/translation/gateway_httproute.go       |  72 ++++++++++++
 pkg/types/apisix/v1/plugin_types.go                |  19 ++++
 pkg/types/apisix/v1/zz_generated.deepcopy.go       |   1 +
 test/e2e/scaffold/k8s.go                           |   6 +-
 test/e2e/suite-gateway/gateway_httproute.go        | 125 ++++++++++++++++++++-
 5 files changed, 217 insertions(+), 6 deletions(-)

diff --git a/pkg/providers/gateway/translation/gateway_httproute.go 
b/pkg/providers/gateway/translation/gateway_httproute.go
index 282a6e57..1b9a6da4 100644
--- a/pkg/providers/gateway/translation/gateway_httproute.go
+++ b/pkg/providers/gateway/translation/gateway_httproute.go
@@ -32,6 +32,76 @@ import (
        apisixv1 
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
 
+func (t *translator) generatePluginsFromHTTPRouteFilter(filters 
[]gatewayv1alpha2.HTTPRouteFilter) apisixv1.Plugins {
+       plugins := apisixv1.Plugins{}
+       for _, filter := range filters {
+               switch filter.Type {
+               case gatewayv1alpha2.HTTPRouteFilterRequestHeaderModifier:
+                       t.generatePluginFromHTTPRequestHeaderFilter(plugins, 
filter.RequestHeaderModifier)
+               case gatewayv1alpha2.HTTPRouteFilterRequestRedirect:
+                       t.generatePluginFromHTTPRequestRedirectFilter(plugins, 
filter.RequestRedirect)
+               case gatewayv1alpha2.HTTPRouteFilterRequestMirror:
+                       // to do
+               }
+       }
+       return plugins
+}
+
+func (t *translator) generatePluginFromHTTPRequestHeaderFilter(plugins 
apisixv1.Plugins, reqHeaderModifier *gatewayv1alpha2.HTTPRequestHeaderFilter) {
+       if reqHeaderModifier == nil {
+               return
+       }
+       headers := map[string]any{}
+       // TODO: The current apisix plugin does not conform to the 
specification.
+       for _, header := range reqHeaderModifier.Add {
+               headers[string(header.Name)] = header.Value
+       }
+       for _, header := range reqHeaderModifier.Set {
+               headers[string(header.Name)] = header.Value
+       }
+       for _, header := range reqHeaderModifier.Remove {
+               headers[header] = ""
+       }
+
+       plugins["proxy-rewrite"] = apisixv1.RewriteConfig{
+               Headers: headers,
+       }
+}
+
+func (t *translator) generatePluginFromHTTPRequestRedirectFilter(plugins 
apisixv1.Plugins, reqRedirect *gatewayv1alpha2.HTTPRequestRedirectFilter) {
+       if reqRedirect == nil {
+               return
+       }
+
+       var uri string
+
+       code := 302
+       if reqRedirect.StatusCode != nil {
+               code = *reqRedirect.StatusCode
+       }
+
+       hostname := "$host"
+       if reqRedirect.Hostname != nil {
+               hostname = string(*reqRedirect.Hostname)
+       }
+
+       scheme := "$scheme"
+       if reqRedirect.Scheme != nil {
+               scheme = *reqRedirect.Scheme
+       }
+
+       if reqRedirect.Port != nil {
+               uri = fmt.Sprintf("%s://%s:%d$request_uri", scheme, hostname, 
int(*reqRedirect.Port))
+       } else {
+               uri = fmt.Sprintf("%s://%s$request_uri", scheme, hostname)
+       }
+
+       plugins["redirect"] = apisixv1.RedirectConfig{
+               RetCode: code,
+               URI:     uri,
+       }
+}
+
 func (t *translator) TranslateGatewayHTTPRouteV1Alpha2(httpRoute 
*gatewayv1alpha2.HTTPRoute) (*translation.TranslateContext, error) {
        ctx := translation.DefaultEmptyTranslateContext()
 
@@ -140,6 +210,7 @@ func (t *translator) 
TranslateGatewayHTTPRouteV1Alpha2(httpRoute *gatewayv1alpha
                                },
                        }
                }
+               plugins := t.generatePluginsFromHTTPRouteFilter(rule.Filters)
 
                for j, match := range matches {
                        route, err := t.translateGatewayHTTPRouteMatch(&match)
@@ -150,6 +221,7 @@ func (t *translator) 
TranslateGatewayHTTPRouteV1Alpha2(httpRoute *gatewayv1alpha
                        name := apisixv1.ComposeRouteName(httpRoute.Namespace, 
httpRoute.Name, fmt.Sprintf("%d-%d", i, j))
                        route.ID = id.GenID(name)
                        route.Hosts = hosts
+                       route.Plugins = plugins
 
                        // Bind Upstream
                        if len(ruleUpstreams) == 1 {
diff --git a/pkg/types/apisix/v1/plugin_types.go 
b/pkg/types/apisix/v1/plugin_types.go
index 26a4b8e0..8958d285 100644
--- a/pkg/types/apisix/v1/plugin_types.go
+++ b/pkg/types/apisix/v1/plugin_types.go
@@ -14,6 +14,8 @@
 // limitations under the License.
 package v1
 
+import "encoding/json"
+
 // TrafficSplitConfig is the config of traffic-split plugin.
 // +k8s:deepcopy-gen=true
 type TrafficSplitConfig struct {
@@ -123,6 +125,7 @@ type WolfRBACConsumerConfig struct {
 type RewriteConfig struct {
        RewriteTarget      string   `json:"uri,omitempty"`
        RewriteTargetRegex []string `json:"regex_uri,omitempty"`
+       Headers            Headers  `json:"headers,omitempty"`
 }
 
 // RedirectConfig is the rule config for redirect plugin.
@@ -152,3 +155,19 @@ type BasicAuthConfig struct {
 // +k8s:deepcopy-gen=true
 type KeyAuthConfig struct {
 }
+
+type Headers map[string]any
+
+func (p *Headers) DeepCopyInto(out *Headers) {
+       b, _ := json.Marshal(&p)
+       _ = json.Unmarshal(b, out)
+}
+
+func (p *Headers) DeepCopy() *Headers {
+       if p == nil {
+               return nil
+       }
+       out := new(Headers)
+       p.DeepCopyInto(out)
+       return out
+}
diff --git a/pkg/types/apisix/v1/zz_generated.deepcopy.go 
b/pkg/types/apisix/v1/zz_generated.deepcopy.go
index e4eeadd9..205436eb 100644
--- a/pkg/types/apisix/v1/zz_generated.deepcopy.go
+++ b/pkg/types/apisix/v1/zz_generated.deepcopy.go
@@ -348,6 +348,7 @@ func (in *RewriteConfig) DeepCopyInto(out *RewriteConfig) {
                *out = make([]string, len(*in))
                copy(*out, *in)
        }
+       in.Headers.DeepCopyInto(&out.Headers)
        return
 }
 
diff --git a/test/e2e/scaffold/k8s.go b/test/e2e/scaffold/k8s.go
index 94591eca..68ab5e79 100644
--- a/test/e2e/scaffold/k8s.go
+++ b/test/e2e/scaffold/k8s.go
@@ -27,7 +27,6 @@ import (
        "time"
 
        "github.com/apache/apisix-ingress-controller/pkg/apisix"
-       "github.com/apache/apisix-ingress-controller/pkg/log"
        "github.com/apache/apisix-ingress-controller/pkg/metrics"
        v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
        "github.com/gruntwork-io/terratest/modules/k8s"
@@ -35,7 +34,6 @@ import (
        "github.com/gruntwork-io/terratest/modules/testing"
        ginkgo "github.com/onsi/ginkgo/v2"
        "github.com/stretchr/testify/assert"
-       "go.uber.org/zap"
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/util/wait"
@@ -128,9 +126,7 @@ func (s *Scaffold) CreateResourceFromString(yaml string) 
error {
 
        // if the error raised, it may be a &shell.ErrWithCmdOutput, which is 
useless in debug
        if err != nil {
-               log.Errorw("create resource failed",
-                       zap.Error(err),
-               )
+               err = fmt.Errorf(err.Error())
        }
        return err
 }
diff --git a/test/e2e/suite-gateway/gateway_httproute.go 
b/test/e2e/suite-gateway/gateway_httproute.go
index e702f640..bc809407 100644
--- a/test/e2e/suite-gateway/gateway_httproute.go
+++ b/test/e2e/suite-gateway/gateway_httproute.go
@@ -27,7 +27,7 @@ import (
        "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
 )
 
-var _ = ginkgo.Describe("suite-gateway: HTTP Route", func() {
+var _ = ginkgo.Describe("suite-gateway: HTTPRoute", func() {
        s := scaffold.NewDefaultScaffold()
 
        ginkgo.It("Basic HTTPRoute with 1 Hosts 1 Rule 1 Match 1 BackendRef", 
func() {
@@ -236,3 +236,126 @@ spec:
                        Status(http.StatusNotFound)
        })
 })
+
+var _ = ginkgo.Describe("suite-gateway: HTTPRoute with filter", func() {
+       s := scaffold.NewDefaultScaffold()
+       ginkgo.It("HTTPRoute with RequestHeaderModifier", func() {
+               backendSvc, backendPorts := s.DefaultHTTPBackend()
+               time.Sleep(time.Second * 15)
+               httproute := fmt.Sprintf(`
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: HTTPRoute
+metadata:
+  name: http-route
+spec:
+  hostnames: ["httpbin.org"]
+  rules:
+  - matches:
+    - path:
+        type: PathPrefix
+        value: /headers
+    filters:
+    - type: RequestHeaderModifier
+      requestHeaderModifier:
+        add:
+        - name: X-Api-Version
+          value: v1
+        - name: X-api-key
+          value: api-value
+        set:
+        - name: X-Auth
+          value: filter
+        remove:
+        - Remove-header
+        - Host
+    backendRefs:
+    - name: %s
+      port: %d
+`, backendSvc, backendPorts[0])
+
+               assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromString(httproute), "creating HTTPRoute")
+               time.Sleep(time.Second * 6)
+               assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), 
"Checking number of routes")
+               assert.Nil(ginkgo.GinkgoT(), 
s.EnsureNumApisixUpstreamsCreated(1), "Checking number of upstreams")
+
+               _ = s.NewAPISIXClient().GET("/headers").
+                       WithHeader("Host", "httpbin.org").
+                       WithHeader("Remove-Header", "remove").
+                       WithHeader("X-Auth", "ingress").
+                       Expect().
+                       Status(http.StatusOK).
+                       Body().
+                       Contains(`"X-Api-Version": "v1"`).
+                       Contains(`"X-Api-Key": "api-value"`).
+                       Contains(`"X-Auth": "filter"`).
+                       NotContains(`"Remove-Header"`)
+       })
+
+       ginkgo.It("HTTPRoute with RequestRidrect", func() {
+               backendSvc, backendPorts := s.DefaultHTTPBackend()
+
+               httproute := fmt.Sprintf(`
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: HTTPRoute
+metadata:
+  name: http-route
+spec:
+  hostnames: ["httpbin.org"]
+  rules:
+  - matches:
+    - path:
+        type: PathPrefix
+        value: /headers
+    filters:
+    - type: RequestRedirect
+      requestRedirect:
+        scheme: https
+        port: 9443
+    backendRefs:
+    - name: %s
+      port: %d
+`, backendSvc, backendPorts[0])
+
+               assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromString(httproute), "creating HTTPRoute")
+               time.Sleep(time.Second * 6)
+               assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), 
"Checking number of routes")
+
+               _ = s.NewAPISIXClient().GET("/headers").
+                       WithHeader("Host", "httpbin.org").
+                       Expect().
+                       Status(http.StatusFound).
+                       
Header("Location").Equal("https://httpbin.org:9443/headers";)
+
+               httproute2 := fmt.Sprintf(`
+apiVersion: gateway.networking.k8s.io/v1alpha2
+kind: HTTPRoute
+metadata:
+  name: http-route2
+spec:
+  hostnames: ["httpbin.com"]
+  rules:
+  - matches:
+    - path:
+        type: PathPrefix
+        value: /ip
+    filters:
+    - type: RequestRedirect
+      requestRedirect:
+        hostname: httpbin.org
+        statusCode: 301
+    backendRefs:
+    - name: %s
+      port: %d
+`, backendSvc, backendPorts[0])
+
+               assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromString(httproute2), "creating HTTPRoute")
+               time.Sleep(time.Second * 6)
+               assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(2), 
"Checking number of routes")
+
+               _ = s.NewAPISIXClient().GET("/ip").
+                       WithHeader("Host", "httpbin.com").
+                       Expect().
+                       Status(http.StatusMovedPermanently).
+                       Header("Location").Equal("http://httpbin.org/ip";)
+       })
+})

Reply via email to