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/11/09 01:22:41 UTC
[apisix-ingress-controller] branch master updated: feat: support redirect and requestHeaderModifier in HTTPRoute filter (#1426)
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 <al...@gmail.com>
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")
+ })
+})