diff --git a/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md b/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md
index 7bbee8b69..e1593c1d5 100644
--- a/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md
+++ b/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md
@@ -318,6 +318,10 @@ The following annotations are organized by category for easier navigation.
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
| `nginx.ingress.kubernetes.io/use-regex` | |
| `nginx.ingress.kubernetes.io/rewrite-target` | |
+| `nginx.ingress.kubernetes.io/permanent-redirect` | Defaults to a 301 Moved Permanently status code. |
+| `nginx.ingress.kubernetes.io/permanent-redirect-code` | Only valid 3XX HTTP Status Codes are accepted. |
+| `nginx.ingress.kubernetes.io/temporal-redirect` | Takes precedence over the `permanent-redirect` annotation. Defaults to a 302 Found status code. |
+| `nginx.ingress.kubernetes.io/temporal-redirect-code` | Only valid 3XX HTTP Status Codes are accepted. |
### IP Whitelist
@@ -393,9 +397,6 @@ The following annotations are organized by category for easier navigation.
| `nginx.ingress.kubernetes.io/global-rate-limit-window` | |
| `nginx.ingress.kubernetes.io/global-rate-limit-key` | |
| `nginx.ingress.kubernetes.io/global-rate-limit-ignored-cidrs` | |
-| `nginx.ingress.kubernetes.io/permanent-redirect` | |
-| `nginx.ingress.kubernetes.io/permanent-redirect-code` | |
-| `nginx.ingress.kubernetes.io/temporal-redirect` | |
| `nginx.ingress.kubernetes.io/preserve-trailing-slash` | Traefik preserves trailing slash by default. |
| `nginx.ingress.kubernetes.io/proxy-cookie-domain` | |
| `nginx.ingress.kubernetes.io/proxy-cookie-path` | |
diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go
index 5b2157d9b..f2681247e 100644
--- a/pkg/config/dynamic/middlewares.go
+++ b/pkg/config/dynamic/middlewares.go
@@ -645,6 +645,9 @@ type RedirectRegex struct {
Replacement string `json:"replacement,omitempty" toml:"replacement,omitempty" yaml:"replacement,omitempty"`
// Permanent defines whether the redirection is permanent (308).
Permanent bool `json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty" export:"true"`
+
+ // StatusCode is for supporting the NGINX annotations related to redirect.
+ StatusCode *int `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
}
// +k8s:deepcopy-gen=true
@@ -663,7 +666,7 @@ type RedirectScheme struct {
// ForcePermanentRedirect is an internal field (not exposed in configuration).
// When set to true, this forces the use of permanent redirects 308, regardless of the request method.
// Used by the provider ingress-ngin.
- ForcePermanentRedirect bool `json:"-" toml:"-" yaml:"-" label:"-"`
+ ForcePermanentRedirect bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
}
// +k8s:deepcopy-gen=true
diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go
index d78443c48..48065822e 100644
--- a/pkg/config/dynamic/zz_generated.deepcopy.go
+++ b/pkg/config/dynamic/zz_generated.deepcopy.go
@@ -918,7 +918,7 @@ func (in *Middleware) DeepCopyInto(out *Middleware) {
if in.RedirectRegex != nil {
in, out := &in.RedirectRegex, &out.RedirectRegex
*out = new(RedirectRegex)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
if in.RedirectScheme != nil {
in, out := &in.RedirectScheme, &out.RedirectScheme
@@ -1186,6 +1186,11 @@ func (in *RateLimit) DeepCopy() *RateLimit {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RedirectRegex) DeepCopyInto(out *RedirectRegex) {
*out = *in
+ if in.StatusCode != nil {
+ in, out := &in.StatusCode, &out.StatusCode
+ *out = new(int)
+ **out = **in
+ }
return
}
diff --git a/pkg/middlewares/redirect/redirect.go b/pkg/middlewares/redirect/redirect.go
index 503df4419..b7ecdad47 100644
--- a/pkg/middlewares/redirect/redirect.go
+++ b/pkg/middlewares/redirect/redirect.go
@@ -18,32 +18,32 @@ const typeName = "Redirect"
var uriRegexp = regexp.MustCompile(`^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$`)
type redirect struct {
- next http.Handler
- regex *regexp.Regexp
- replacement string
- permanent bool
- forcePermanentRedirect bool
- errHandler utils.ErrorHandler
- name string
- rawURL func(*http.Request) string
+ next http.Handler
+ regex *regexp.Regexp
+ replacement string
+ permanent bool
+ statusCode *int
+ errHandler utils.ErrorHandler
+ name string
+ rawURL func(*http.Request) string
}
// New creates a Redirect middleware.
-func newRedirect(next http.Handler, regex, replacement string, permanent bool, forcePermanentRedirect bool, rawURL func(*http.Request) string, name string) (http.Handler, error) {
+func newRedirect(next http.Handler, regex, replacement string, permanent bool, statusCode *int, rawURL func(*http.Request) string, name string) (http.Handler, error) {
re, err := regexp.Compile(regex)
if err != nil {
return nil, err
}
return &redirect{
- regex: re,
- replacement: replacement,
- permanent: permanent,
- forcePermanentRedirect: forcePermanentRedirect,
- errHandler: utils.DefaultHandler,
- next: next,
- name: name,
- rawURL: rawURL,
+ regex: re,
+ replacement: replacement,
+ permanent: permanent,
+ statusCode: statusCode,
+ errHandler: utils.DefaultHandler,
+ next: next,
+ name: name,
+ rawURL: rawURL,
}, nil
}
@@ -71,7 +71,7 @@ func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
if newURL != oldURL {
- handler := &moveHandler{location: parsedURL, permanent: r.permanent, forcePermanentRedirect: r.forcePermanentRedirect}
+ handler := &moveHandler{location: parsedURL, permanent: r.permanent, statusCode: r.statusCode}
handler.ServeHTTP(rw, req)
return
}
@@ -84,9 +84,9 @@ func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
type moveHandler struct {
- location *url.URL
- permanent bool
- forcePermanentRedirect bool
+ location *url.URL
+ permanent bool
+ statusCode *int
}
func (m *moveHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
@@ -104,8 +104,8 @@ func (m *moveHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
}
- if m.forcePermanentRedirect {
- status = http.StatusPermanentRedirect
+ if m.statusCode != nil {
+ status = *m.statusCode
}
rw.WriteHeader(status)
diff --git a/pkg/middlewares/redirect/redirect_regex.go b/pkg/middlewares/redirect/redirect_regex.go
index 5846d5c0d..f82b52121 100644
--- a/pkg/middlewares/redirect/redirect_regex.go
+++ b/pkg/middlewares/redirect/redirect_regex.go
@@ -17,7 +17,7 @@ func NewRedirectRegex(ctx context.Context, next http.Handler, conf dynamic.Redir
logger.Debug().Msg("Creating middleware")
logger.Debug().Msgf("Setting up redirection from %s to %s", conf.Regex, conf.Replacement)
- return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, false, rawURL, name)
+ return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, conf.StatusCode, rawURL, name)
}
func rawURL(req *http.Request) string {
diff --git a/pkg/middlewares/redirect/redirect_scheme.go b/pkg/middlewares/redirect/redirect_scheme.go
index b1b1e3c3c..0fd213799 100644
--- a/pkg/middlewares/redirect/redirect_scheme.go
+++ b/pkg/middlewares/redirect/redirect_scheme.go
@@ -40,7 +40,13 @@ func NewRedirectScheme(ctx context.Context, next http.Handler, conf dynamic.Redi
rs := &redirectScheme{name: name}
- handler, err := newRedirect(next, uriPattern, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, conf.ForcePermanentRedirect, rs.clientRequestURL, name)
+ var permanentRedirectCode *int
+ if conf.ForcePermanentRedirect {
+ status := http.StatusPermanentRedirect
+ permanentRedirectCode = &status
+ }
+
+ handler, err := newRedirect(next, uriPattern, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, permanentRedirectCode, rs.clientRequestURL, name)
if err != nil {
return nil, err
}
diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go
index d577fef82..e354565fe 100644
--- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go
@@ -871,7 +871,7 @@ func (in *MiddlewareSpec) DeepCopyInto(out *MiddlewareSpec) {
if in.RedirectRegex != nil {
in, out := &in.RedirectRegex, &out.RedirectRegex
*out = new(dynamic.RedirectRegex)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
if in.RedirectScheme != nil {
in, out := &in.RedirectScheme, &out.RedirectScheme
diff --git a/pkg/provider/kubernetes/ingress-nginx/annotations.go b/pkg/provider/kubernetes/ingress-nginx/annotations.go
index e37ee659c..c44c632dc 100644
--- a/pkg/provider/kubernetes/ingress-nginx/annotations.go
+++ b/pkg/provider/kubernetes/ingress-nginx/annotations.go
@@ -26,6 +26,11 @@ type ingressConfig struct {
UseRegex *bool `annotation:"nginx.ingress.kubernetes.io/use-regex"`
RewriteTarget *string `annotation:"nginx.ingress.kubernetes.io/rewrite-target"`
+ PermanentRedirect *string `annotation:"nginx.ingress.kubernetes.io/permanent-redirect"`
+ PermanentRedirectCode *int `annotation:"nginx.ingress.kubernetes.io/permanent-redirect-code"`
+ TemporalRedirect *string `annotation:"nginx.ingress.kubernetes.io/temporal-redirect"`
+ TemporalRedirectCode *int `annotation:"nginx.ingress.kubernetes.io/temporal-redirect-code"`
+
Affinity *string `annotation:"nginx.ingress.kubernetes.io/affinity"`
SessionCookieName *string `annotation:"nginx.ingress.kubernetes.io/session-cookie-name"`
SessionCookieSecure *bool `annotation:"nginx.ingress.kubernetes.io/session-cookie-secure"`
diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/14-ingress-with-permanent-redirect-code-correct-code.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/14-ingress-with-permanent-redirect-code-correct-code.yml
new file mode 100644
index 000000000..8ec11e5dd
--- /dev/null
+++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/14-ingress-with-permanent-redirect-code-correct-code.yml
@@ -0,0 +1,23 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: ingress-with-permanent-redirect
+ namespace: default
+ annotations:
+ nginx.ingress.kubernetes.io/permanent-redirect: "https://www.google.com"
+ nginx.ingress.kubernetes.io/permanent-redirect-code: "300"
+
+spec:
+ ingressClassName: nginx
+ rules:
+ - host: permanent-redirect.localhost
+ http:
+ paths:
+ - path: /
+ pathType: Exact
+ backend:
+ service:
+ name: whoami
+ port:
+ number: 80
diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/14-ingress-with-permanent-redirect-code-wrong-code.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/14-ingress-with-permanent-redirect-code-wrong-code.yml
new file mode 100644
index 000000000..df918b473
--- /dev/null
+++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/14-ingress-with-permanent-redirect-code-wrong-code.yml
@@ -0,0 +1,23 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: ingress-with-permanent-redirect
+ namespace: default
+ annotations:
+ nginx.ingress.kubernetes.io/permanent-redirect: "https://www.google.com"
+ nginx.ingress.kubernetes.io/permanent-redirect-code: "500"
+
+spec:
+ ingressClassName: nginx
+ rules:
+ - host: permanent-redirect.localhost
+ http:
+ paths:
+ - path: /
+ pathType: Exact
+ backend:
+ service:
+ name: whoami
+ port:
+ number: 80
diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/14-ingress-with-permanent-redirect.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/14-ingress-with-permanent-redirect.yml
new file mode 100644
index 000000000..5adaea639
--- /dev/null
+++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/14-ingress-with-permanent-redirect.yml
@@ -0,0 +1,22 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: ingress-with-permanent-redirect
+ namespace: default
+ annotations:
+ nginx.ingress.kubernetes.io/permanent-redirect: "https://www.google.com"
+
+spec:
+ ingressClassName: nginx
+ rules:
+ - host: permanent-redirect.localhost
+ http:
+ paths:
+ - path: /
+ pathType: Exact
+ backend:
+ service:
+ name: whoami
+ port:
+ number: 80
diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/15-ingress-with-temporal-redirect.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/15-ingress-with-temporal-redirect.yml
new file mode 100644
index 000000000..b21203050
--- /dev/null
+++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/15-ingress-with-temporal-redirect.yml
@@ -0,0 +1,22 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: ingress-with-temporal-redirect
+ namespace: default
+ annotations:
+ nginx.ingress.kubernetes.io/temporal-redirect: "https://www.google.com"
+
+spec:
+ ingressClassName: nginx
+ rules:
+ - host: temporal-redirect.localhost
+ http:
+ paths:
+ - path: /
+ pathType: Exact
+ backend:
+ service:
+ name: whoami
+ port:
+ number: 80
diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/16-ingress-with-temporal-and-permanent-redirect.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/16-ingress-with-temporal-and-permanent-redirect.yml
new file mode 100644
index 000000000..c51b77243
--- /dev/null
+++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/16-ingress-with-temporal-and-permanent-redirect.yml
@@ -0,0 +1,23 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: ingress-with-redirect
+ namespace: default
+ annotations:
+ nginx.ingress.kubernetes.io/permanent-redirect: "https://www.traefik.io"
+ nginx.ingress.kubernetes.io/temporal-redirect: "https://www.google.com"
+
+spec:
+ ingressClassName: nginx
+ rules:
+ - host: redirect.localhost
+ http:
+ paths:
+ - path: /
+ pathType: Exact
+ backend:
+ service:
+ name: whoami
+ port:
+ number: 80
diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/17-ingress-with-temporal-redirect-code-correct-code.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/17-ingress-with-temporal-redirect-code-correct-code.yml
new file mode 100644
index 000000000..f947040ed
--- /dev/null
+++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/17-ingress-with-temporal-redirect-code-correct-code.yml
@@ -0,0 +1,23 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: ingress-with-temporal-redirect
+ namespace: default
+ annotations:
+ nginx.ingress.kubernetes.io/temporal-redirect: "https://www.google.com"
+ nginx.ingress.kubernetes.io/temporal-redirect-code: "308"
+
+spec:
+ ingressClassName: nginx
+ rules:
+ - host: temporal-redirect.localhost
+ http:
+ paths:
+ - path: /
+ pathType: Exact
+ backend:
+ service:
+ name: whoami
+ port:
+ number: 80
diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/17-ingress-with-temporal-redirect-code-wrong-code.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/17-ingress-with-temporal-redirect-code-wrong-code.yml
new file mode 100644
index 000000000..49f1cde47
--- /dev/null
+++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/17-ingress-with-temporal-redirect-code-wrong-code.yml
@@ -0,0 +1,23 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: ingress-with-temporal-redirect
+ namespace: default
+ annotations:
+ nginx.ingress.kubernetes.io/temporal-redirect: "https://www.google.com"
+ nginx.ingress.kubernetes.io/temporal-redirect-code: "429"
+
+spec:
+ ingressClassName: nginx
+ rules:
+ - host: temporal-redirect.localhost
+ http:
+ paths:
+ - path: /
+ pathType: Exact
+ backend:
+ service:
+ name: whoami
+ port:
+ number: 80
diff --git a/pkg/provider/kubernetes/ingress-nginx/kubernetes.go b/pkg/provider/kubernetes/ingress-nginx/kubernetes.go
index 2da2bef13..3c8834069 100644
--- a/pkg/provider/kubernetes/ingress-nginx/kubernetes.go
+++ b/pkg/provider/kubernetes/ingress-nginx/kubernetes.go
@@ -7,6 +7,7 @@ import (
"maps"
"math"
"net"
+ "net/http"
"os"
"regexp"
"slices"
@@ -810,6 +811,8 @@ func (p *Provider) applyMiddlewares(namespace, routerKey, rulePath string, ingre
// TODO: check how to remove this, and create the HTTP router elsewhere.
p.applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf)
+ applyRedirect(routerKey, ingressConfig, rt, conf)
+
applyUpstreamVhost(routerKey, ingressConfig, rt, conf)
if err := p.applyCustomHeaders(routerKey, ingressConfig, rt, conf); err != nil {
@@ -819,6 +822,48 @@ func (p *Provider) applyMiddlewares(namespace, routerKey, rulePath string, ingre
return nil
}
+func applyRedirect(routerName string, ingressConfig ingressConfig, rt *dynamic.Router, conf *dynamic.Configuration) {
+ if ingressConfig.PermanentRedirect == nil && ingressConfig.TemporalRedirect == nil {
+ return
+ }
+
+ var (
+ redirectURL string
+ code int
+ )
+
+ if ingressConfig.PermanentRedirect != nil {
+ redirectURL = *ingressConfig.PermanentRedirect
+ code = ptr.Deref(ingressConfig.PermanentRedirectCode, http.StatusMovedPermanently)
+
+ // NGINX only accepts valid redirect codes and defaults to 301.
+ if code < 300 || code > 308 {
+ code = http.StatusMovedPermanently
+ }
+ }
+
+ // TemporalRedirect takes precedence over the PermanentRedirect.
+ if ingressConfig.TemporalRedirect != nil {
+ redirectURL = *ingressConfig.TemporalRedirect
+ code = ptr.Deref(ingressConfig.TemporalRedirectCode, http.StatusFound)
+
+ // NGINX only accepts valid redirect codes and defaults to 302.
+ if code < 300 || code > 308 {
+ code = http.StatusFound
+ }
+ }
+
+ redirectMiddlewareName := routerName + "-redirect"
+ conf.HTTP.Middlewares[redirectMiddlewareName] = &dynamic.Middleware{
+ RedirectRegex: &dynamic.RedirectRegex{
+ Regex: ".*",
+ Replacement: redirectURL,
+ StatusCode: &code,
+ },
+ }
+ rt.Middlewares = append(rt.Middlewares, redirectMiddlewareName)
+}
+
func (p *Provider) applyCustomHeaders(routerName string, ingressConfig ingressConfig, rt *dynamic.Router, conf *dynamic.Configuration) error {
customHeaders := ptr.Deref(ingressConfig.CustomHeaders, "")
if customHeaders == "" {
diff --git a/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go b/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go
index c48bae016..b419b8db6 100644
--- a/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go
+++ b/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go
@@ -2,6 +2,7 @@ package ingressnginx
import (
"math"
+ "net/http"
"os"
"path/filepath"
"testing"
@@ -1063,6 +1064,384 @@ func TestLoadIngresses(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
+ {
+ desc: "Permanent Redirect",
+ paths: []string{
+ "services.yml",
+ "ingressclasses.yml",
+ "ingresses/14-ingress-with-permanent-redirect.yml",
+ },
+ expected: &dynamic.Configuration{
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Services: map[string]*dynamic.TCPService{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "default-ingress-with-permanent-redirect-rule-0-path-0": {
+ Rule: "Host(`permanent-redirect.localhost`) && Path(`/`)",
+ RuleSyntax: "default",
+ Service: "default-ingress-with-permanent-redirect-whoami-80",
+ Middlewares: []string{"default-ingress-with-permanent-redirect-rule-0-path-0-redirect"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "default-ingress-with-permanent-redirect-rule-0-path-0-redirect": {
+ RedirectRegex: &dynamic.RedirectRegex{
+ Regex: ".*",
+ Replacement: "https://www.google.com",
+ StatusCode: ptr.To(http.StatusMovedPermanently),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "default-ingress-with-permanent-redirect-whoami-80": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ Strategy: "wrr",
+ PassHostHeader: ptr.To(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: dynamic.DefaultFlushInterval,
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
+ {
+ desc: "Permanent Redirect Code - wrong code",
+ paths: []string{
+ "services.yml",
+ "ingressclasses.yml",
+ "ingresses/14-ingress-with-permanent-redirect-code-wrong-code.yml",
+ },
+ expected: &dynamic.Configuration{
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Services: map[string]*dynamic.TCPService{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "default-ingress-with-permanent-redirect-rule-0-path-0": {
+ Rule: "Host(`permanent-redirect.localhost`) && Path(`/`)",
+ RuleSyntax: "default",
+ Service: "default-ingress-with-permanent-redirect-whoami-80",
+ Middlewares: []string{"default-ingress-with-permanent-redirect-rule-0-path-0-redirect"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "default-ingress-with-permanent-redirect-rule-0-path-0-redirect": {
+ RedirectRegex: &dynamic.RedirectRegex{
+ Regex: ".*",
+ Replacement: "https://www.google.com",
+ StatusCode: ptr.To(http.StatusMovedPermanently),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "default-ingress-with-permanent-redirect-whoami-80": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ Strategy: "wrr",
+ PassHostHeader: ptr.To(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: dynamic.DefaultFlushInterval,
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
+ {
+ desc: "Permanent Redirect Code - correct code",
+ paths: []string{
+ "services.yml",
+ "ingressclasses.yml",
+ "ingresses/14-ingress-with-permanent-redirect-code-correct-code.yml",
+ },
+ expected: &dynamic.Configuration{
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Services: map[string]*dynamic.TCPService{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "default-ingress-with-permanent-redirect-rule-0-path-0": {
+ Rule: "Host(`permanent-redirect.localhost`) && Path(`/`)",
+ RuleSyntax: "default",
+ Service: "default-ingress-with-permanent-redirect-whoami-80",
+ Middlewares: []string{"default-ingress-with-permanent-redirect-rule-0-path-0-redirect"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "default-ingress-with-permanent-redirect-rule-0-path-0-redirect": {
+ RedirectRegex: &dynamic.RedirectRegex{
+ Regex: ".*",
+ Replacement: "https://www.google.com",
+ StatusCode: ptr.To(http.StatusMultipleChoices),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "default-ingress-with-permanent-redirect-whoami-80": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ Strategy: "wrr",
+ PassHostHeader: ptr.To(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: dynamic.DefaultFlushInterval,
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
+ {
+ desc: "Temporal Redirect takes precedence over Permanent Redirect",
+ paths: []string{
+ "services.yml",
+ "ingressclasses.yml",
+ "ingresses/16-ingress-with-temporal-and-permanent-redirect.yml",
+ },
+ expected: &dynamic.Configuration{
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Services: map[string]*dynamic.TCPService{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "default-ingress-with-redirect-rule-0-path-0": {
+ Rule: "Host(`redirect.localhost`) && Path(`/`)",
+ RuleSyntax: "default",
+ Service: "default-ingress-with-redirect-whoami-80",
+ Middlewares: []string{"default-ingress-with-redirect-rule-0-path-0-redirect"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "default-ingress-with-redirect-rule-0-path-0-redirect": {
+ RedirectRegex: &dynamic.RedirectRegex{
+ Regex: ".*",
+ Replacement: "https://www.google.com",
+ StatusCode: ptr.To(http.StatusFound),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "default-ingress-with-redirect-whoami-80": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ Strategy: "wrr",
+ PassHostHeader: ptr.To(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: dynamic.DefaultFlushInterval,
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
+ {
+ desc: "Temporal Redirect",
+ paths: []string{
+ "services.yml",
+ "ingressclasses.yml",
+ "ingresses/15-ingress-with-temporal-redirect.yml",
+ },
+ expected: &dynamic.Configuration{
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Services: map[string]*dynamic.TCPService{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "default-ingress-with-temporal-redirect-rule-0-path-0": {
+ Rule: "Host(`temporal-redirect.localhost`) && Path(`/`)",
+ RuleSyntax: "default",
+ Service: "default-ingress-with-temporal-redirect-whoami-80",
+ Middlewares: []string{"default-ingress-with-temporal-redirect-rule-0-path-0-redirect"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "default-ingress-with-temporal-redirect-rule-0-path-0-redirect": {
+ RedirectRegex: &dynamic.RedirectRegex{
+ Regex: ".*",
+ Replacement: "https://www.google.com",
+ StatusCode: ptr.To(http.StatusFound),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "default-ingress-with-temporal-redirect-whoami-80": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ Strategy: "wrr",
+ PassHostHeader: ptr.To(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: dynamic.DefaultFlushInterval,
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
+ {
+ desc: "Temporal Redirect Code - wrong code",
+ paths: []string{
+ "services.yml",
+ "ingressclasses.yml",
+ "ingresses/17-ingress-with-temporal-redirect-code-wrong-code.yml",
+ },
+ expected: &dynamic.Configuration{
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Services: map[string]*dynamic.TCPService{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "default-ingress-with-temporal-redirect-rule-0-path-0": {
+ Rule: "Host(`temporal-redirect.localhost`) && Path(`/`)",
+ RuleSyntax: "default",
+ Service: "default-ingress-with-temporal-redirect-whoami-80",
+ Middlewares: []string{"default-ingress-with-temporal-redirect-rule-0-path-0-redirect"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "default-ingress-with-temporal-redirect-rule-0-path-0-redirect": {
+ RedirectRegex: &dynamic.RedirectRegex{
+ Regex: ".*",
+ Replacement: "https://www.google.com",
+ StatusCode: ptr.To(http.StatusFound),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "default-ingress-with-temporal-redirect-whoami-80": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ Strategy: "wrr",
+ PassHostHeader: ptr.To(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: dynamic.DefaultFlushInterval,
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
+ {
+ desc: "Temporal Redirect Code - correct code",
+ paths: []string{
+ "services.yml",
+ "ingressclasses.yml",
+ "ingresses/17-ingress-with-temporal-redirect-code-correct-code.yml",
+ },
+ expected: &dynamic.Configuration{
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Services: map[string]*dynamic.TCPService{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "default-ingress-with-temporal-redirect-rule-0-path-0": {
+ Rule: "Host(`temporal-redirect.localhost`) && Path(`/`)",
+ RuleSyntax: "default",
+ Service: "default-ingress-with-temporal-redirect-whoami-80",
+ Middlewares: []string{"default-ingress-with-temporal-redirect-rule-0-path-0-redirect"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "default-ingress-with-temporal-redirect-rule-0-path-0-redirect": {
+ RedirectRegex: &dynamic.RedirectRegex{
+ Regex: ".*",
+ Replacement: "https://www.google.com",
+ StatusCode: ptr.To(http.StatusPermanentRedirect),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "default-ingress-with-temporal-redirect-whoami-80": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ Strategy: "wrr",
+ PassHostHeader: ptr.To(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: dynamic.DefaultFlushInterval,
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
}
for _, test := range testCases {