Support regex redirect by frontend
This commit is contained in:
parent
bddad57a7b
commit
7ecd6d20ba
22 changed files with 405 additions and 195 deletions
|
@ -172,7 +172,6 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
[frontends."frontend-{{getServiceBackend $container $serviceName}}"]
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}"]
|
||||||
backend = "backend-{{getServiceBackend $container $serviceName}}"
|
backend = "backend-{{getServiceBackend $container $serviceName}}"
|
||||||
passHostHeader = {{getServicePassHostHeader $container $serviceName}}
|
passHostHeader = {{getServicePassHostHeader $container $serviceName}}
|
||||||
redirect = "{{getServiceRedirect $container $serviceName}}"
|
|
||||||
{{if getWhitelistSourceRange $container}}
|
{{if getWhitelistSourceRange $container}}
|
||||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
|
@ -185,14 +184,21 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
|
||||||
|
{{if hasServiceRedirect $container $serviceName}}
|
||||||
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".redirect]
|
||||||
|
entryPoint = "{{getServiceRedirectEntryPoint $container $serviceName}}"
|
||||||
|
regex = "{{getServiceRedirectRegex $container $serviceName}}"
|
||||||
|
replacement = "{{getServiceRedirectReplacement $container $serviceName}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
||||||
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
[frontends."frontend-{{$frontend}}"]
|
[frontends."frontend-{{$frontend}}"]
|
||||||
backend = "backend-{{getBackend $container}}"
|
backend = "backend-{{getBackend $container}}"
|
||||||
passHostHeader = {{getPassHostHeader $container}}
|
passHostHeader = {{getPassHostHeader $container}}
|
||||||
redirect = "{{getRedirect $container}}"
|
|
||||||
{{if getWhitelistSourceRange $container}}
|
{{if getWhitelistSourceRange $container}}
|
||||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
|
@ -205,6 +211,14 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
basicAuth = [{{range getBasicAuth $container}}
|
basicAuth = [{{range getBasicAuth $container}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{if hasRedirect $container}}
|
||||||
|
[frontends."frontend-{{$frontend}}".redirect]
|
||||||
|
entryPoint = "{{getRedirectEntryPoint $container}}"
|
||||||
|
regex = "{{getRedirectRegex $container}}"
|
||||||
|
replacement = "{{getRedirectReplacement $container}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
[frontends."frontend-{{$frontend}}".headers]
|
[frontends."frontend-{{$frontend}}".headers]
|
||||||
{{if hasSSLRedirectHeaders $container}}
|
{{if hasSSLRedirectHeaders $container}}
|
||||||
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
||||||
|
@ -414,13 +428,20 @@ var _templatesKubernetesTmpl = []byte(`[backends]{{range $backendName, $backend
|
||||||
backend = "{{$frontend.Backend}}"
|
backend = "{{$frontend.Backend}}"
|
||||||
priority = {{$frontend.Priority}}
|
priority = {{$frontend.Priority}}
|
||||||
passHostHeader = {{$frontend.PassHostHeader}}
|
passHostHeader = {{$frontend.PassHostHeader}}
|
||||||
redirect = "{{$frontend.Redirect}}"
|
|
||||||
basicAuth = [{{range $frontend.BasicAuth}}
|
basicAuth = [{{range $frontend.BasicAuth}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
whitelistSourceRange = [{{range $frontend.WhitelistSourceRange}}
|
whitelistSourceRange = [{{range $frontend.WhitelistSourceRange}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{if $frontend.Redirect}}
|
||||||
|
[frontends."{{$frontendName}}".redirect]
|
||||||
|
entryPoint = "{{$frontend.RedirectEntryPoint}}"
|
||||||
|
regex = "{{$frontend.RedirectRegex}}"
|
||||||
|
replacement = "{{$frontend.RedirectReplacement}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
[frontends."{{$frontendName}}".headers]
|
[frontends."{{$frontendName}}".headers]
|
||||||
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
|
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
|
||||||
SSLTemporaryRedirect = {{$frontend.Headers.SSLTemporaryRedirect}}
|
SSLTemporaryRedirect = {{$frontend.Headers.SSLTemporaryRedirect}}
|
||||||
|
@ -752,13 +773,20 @@ var _templatesRancherTmpl = []byte(`{{$backendServers := .Backends}}
|
||||||
backend = "backend-{{getBackend $service}}"
|
backend = "backend-{{getBackend $service}}"
|
||||||
passHostHeader = {{getPassHostHeader $service}}
|
passHostHeader = {{getPassHostHeader $service}}
|
||||||
priority = {{getPriority $service}}
|
priority = {{getPriority $service}}
|
||||||
redirect = "{{getRedirect $service}}"
|
|
||||||
entryPoints = [{{range getEntryPoints $service}}
|
entryPoints = [{{range getEntryPoints $service}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
basicAuth = [{{range getBasicAuth $service}}
|
basicAuth = [{{range getBasicAuth $service}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{if hasRedirect $service}}
|
||||||
|
[frontends."frontend-{{$frontendName}}".redirect]
|
||||||
|
entryPoint = "{{getRedirectEntryPoint $service}}"
|
||||||
|
regex = "{{getRedirectRegex $service}}"
|
||||||
|
replacement = "{{getRedirectReplacement $service}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
[frontends."frontend-{{$frontendName}}".routes."route-frontend-{{$frontendName}}"]
|
[frontends."frontend-{{$frontendName}}".routes."route-frontend-{{$frontendName}}"]
|
||||||
rule = "{{getFrontendRule $service}}"
|
rule = "{{getFrontendRule $service}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -57,7 +57,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
Optional: false,
|
Optional: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Redirect: &configuration.Redirect{
|
Redirect: &types.Redirect{
|
||||||
Replacement: "foo Replacement",
|
Replacement: "foo Replacement",
|
||||||
Regex: "foo Regex",
|
Regex: "foo Regex",
|
||||||
EntryPoint: "foo EntryPoint",
|
EntryPoint: "foo EntryPoint",
|
||||||
|
@ -103,7 +103,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
Optional: false,
|
Optional: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Redirect: &configuration.Redirect{
|
Redirect: &types.Redirect{
|
||||||
Replacement: "fii Replacement",
|
Replacement: "fii Replacement",
|
||||||
Regex: "fii Regex",
|
Regex: "fii Regex",
|
||||||
EntryPoint: "fii EntryPoint",
|
EntryPoint: "fii EntryPoint",
|
||||||
|
|
|
@ -317,9 +317,9 @@ func (ep *EntryPoints) Set(value string) error {
|
||||||
Optional: optional,
|
Optional: optional,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var redirect *Redirect
|
var redirect *types.Redirect
|
||||||
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
|
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
|
||||||
redirect = &Redirect{
|
redirect = &types.Redirect{
|
||||||
EntryPoint: result["redirect_entrypoint"],
|
EntryPoint: result["redirect_entrypoint"],
|
||||||
Regex: result["redirect_regex"],
|
Regex: result["redirect_regex"],
|
||||||
Replacement: result["redirect_replacement"],
|
Replacement: result["redirect_replacement"],
|
||||||
|
@ -422,22 +422,15 @@ func (ep *EntryPoints) Type() string {
|
||||||
type EntryPoint struct {
|
type EntryPoint struct {
|
||||||
Network string
|
Network string
|
||||||
Address string
|
Address string
|
||||||
TLS *tls.TLS `export:"true"`
|
TLS *tls.TLS `export:"true"`
|
||||||
Redirect *Redirect `export:"true"`
|
Redirect *types.Redirect `export:"true"`
|
||||||
Auth *types.Auth `export:"true"`
|
Auth *types.Auth `export:"true"`
|
||||||
WhitelistSourceRange []string
|
WhitelistSourceRange []string
|
||||||
Compress bool `export:"true"`
|
Compress bool `export:"true"`
|
||||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||||
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect configures a redirection of an entry point to another, or to an URL
|
|
||||||
type Redirect struct {
|
|
||||||
EntryPoint string
|
|
||||||
Regex string
|
|
||||||
Replacement string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry contains request retry config
|
// Retry contains request retry config
|
||||||
type Retry struct {
|
type Retry struct {
|
||||||
Attempts int `description:"Number of attempts" export:"true"`
|
Attempts int `description:"Number of attempts" export:"true"`
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/containous/traefik/provider"
|
"github.com/containous/traefik/provider"
|
||||||
"github.com/containous/traefik/provider/file"
|
"github.com/containous/traefik/provider/file"
|
||||||
"github.com/containous/traefik/tls"
|
"github.com/containous/traefik/tls"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -138,7 +139,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
Redirect: &Redirect{
|
Redirect: &types.Redirect{
|
||||||
EntryPoint: "RedirectEntryPoint",
|
EntryPoint: "RedirectEntryPoint",
|
||||||
Regex: "RedirectRegex",
|
Regex: "RedirectRegex",
|
||||||
Replacement: "RedirectReplacement",
|
Replacement: "RedirectReplacement",
|
||||||
|
@ -171,7 +172,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
Redirect: &Redirect{
|
Redirect: &types.Redirect{
|
||||||
EntryPoint: "RedirectEntryPoint",
|
EntryPoint: "RedirectEntryPoint",
|
||||||
Regex: "RedirectRegex",
|
Regex: "RedirectRegex",
|
||||||
Replacement: "RedirectReplacement",
|
Replacement: "RedirectReplacement",
|
||||||
|
|
|
@ -149,29 +149,33 @@ To enable constraints see [backend-specific constraints section](/configuration/
|
||||||
|
|
||||||
Labels can be used on containers to override default behaviour.
|
Labels can be used on containers to override default behaviour.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|-----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. |
|
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. |
|
||||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. |
|
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. |
|
||||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||||
| `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
|
| `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
|
||||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||||
| `traefik.weight=10` | Assign this weight to the container |
|
| `traefik.weight=10` | Assign this weight to the container |
|
||||||
| `traefik.enable=false` | Disable this container in Træfik |
|
| `traefik.enable=false` | Disable this container in Træfik |
|
||||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
||||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints` |
|
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints` |
|
||||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||||
| `traefik.frontend.whitelistSourceRange:RANGE` | List of IP-Ranges which are allowed to access. An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
| `traefik.frontend.whitelistSourceRange:RANGE` | List of IP-Ranges which are allowed to access. An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||||
| `traefik.docker.network` | Set the docker network to use for connections to this container. If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them). For instance when deploying docker `stack` from compose files, the compose defined networks will be prefixed with the `stack` name. |
|
| `traefik.docker.network` | Set the docker network to use for connections to this container. If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them). For instance when deploying docker `stack` from compose files, the compose defined networks will be prefixed with the `stack` name. |
|
||||||
| `traefik.frontend.redirect=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||||
|
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
|
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Security Headers
|
#### Security Headers
|
||||||
|
|
||||||
|
@ -202,18 +206,21 @@ Labels can be used on containers to override default behaviour.
|
||||||
|
|
||||||
Services labels can be used for overriding default behaviour
|
Services labels can be used for overriding default behaviour
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|---------------------------------------------------|--------------------------------------------------------------------------------------------------|
|
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.<service-name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
|
| `traefik.<service-name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
|
||||||
| `traefik.<service-name>.protocol` | Overrides `traefik.protocol`. |
|
| `traefik.<service-name>.protocol` | Overrides `traefik.protocol`. |
|
||||||
| `traefik.<service-name>.weight` | Assign this service weight. Overrides `traefik.weight`. |
|
| `traefik.<service-name>.weight` | Assign this service weight. Overrides `traefik.weight`. |
|
||||||
| `traefik.<service-name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
|
| `traefik.<service-name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
|
||||||
| `traefik.<service-name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
|
| `traefik.<service-name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
|
||||||
| `traefik.<service-name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
|
| `traefik.<service-name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
|
||||||
| `traefik.<service-name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
|
| `traefik.<service-name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
|
||||||
| `traefik.<service-name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
| `traefik.<service-name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
||||||
| `traefik.<service-name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
| `traefik.<service-name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
||||||
| `traefik.<service-name>.frontend.redirect` | Overrides `traefik.frontend.redirect`. |
|
| `traefik.<service-name>.frontend.redirect` | Overrides `traefik.frontend.redirect`. |
|
||||||
|
| `traefik.<service-name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
|
||||||
|
| `traefik.<service-name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
|
||||||
|
| `traefik.<service-name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
||||||
|
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
|
@ -102,13 +102,21 @@ Annotations can be used on containers to override default behaviour for the whol
|
||||||
Override the default frontend rule type. Default: `PathPrefix`.
|
Override the default frontend rule type. Default: `PathPrefix`.
|
||||||
- `traefik.frontend.priority: "3"`
|
- `traefik.frontend.priority: "3"`
|
||||||
Override the default frontend rule priority.
|
Override the default frontend rule priority.
|
||||||
- `traefik.frontend.redirect: https`:
|
- `traefik.frontend.redirect.entryPoint: https`:
|
||||||
Enables Redirect to another entryPoint for that frontend (e.g. HTTPS).
|
Enables Redirect to another entryPoint for that frontend (e.g. HTTPS).
|
||||||
|
- `traefik.frontend.redirect.regex: ^http://localhost/(.*)`:
|
||||||
|
Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.replacement`.
|
||||||
|
- `traefik.frontend.redirect.replacement: http://mydomain/$1`:
|
||||||
|
Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.regex`.
|
||||||
- `traefik.frontend.entryPoints: http,https`
|
- `traefik.frontend.entryPoints: http,https`
|
||||||
Override the default frontend endpoints.
|
Override the default frontend endpoints.
|
||||||
- `traefik.frontend.passTLSCert: true`
|
- `traefik.frontend.passTLSCert: true`
|
||||||
Override the default frontend PassTLSCert value. Default: `false`.
|
Override the default frontend PassTLSCert value. Default: `false`.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Please note that `traefik.frontend.redirect.regex` and `traefik.frontend.redirect.replacement` do not have to be set if `traefik.frontend.redirect.entryPoint` is defined for the redirection (they will not be used in this case).
|
||||||
|
|
||||||
|
|
||||||
Annotations can be used on the Kubernetes service to override default behaviour:
|
Annotations can be used on the Kubernetes service to override default behaviour:
|
||||||
|
|
||||||
- `traefik.backend.loadbalancer.method=drr`
|
- `traefik.backend.loadbalancer.method=drr`
|
||||||
|
|
|
@ -120,19 +120,21 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
|
||||||
Labels can be used on task containers to override default behaviour:
|
Labels can be used on task containers to override default behaviour:
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||||
| `traefik.weight=10` | Assign this weight to the container |
|
| `traefik.weight=10` | Assign this weight to the container |
|
||||||
| `traefik.enable=false` | Disable this container in Træfik |
|
| `traefik.enable=false` | Disable this container in Træfik |
|
||||||
| `traefik.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{containerName}.{domain}`). |
|
| `traefik.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{containerName}.{domain}`). |
|
||||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. |
|
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. |
|
||||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash`. |
|
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash`. |
|
||||||
| `traefik.frontend.redirect=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||||
| `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
| `traefik.frontend.redirect.regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
| `traefik.frontend.redirect.replacement: http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
| `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||||
|
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||||
|
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
|
@ -42,7 +42,7 @@ const (
|
||||||
defaultPassHostHeader = "true"
|
defaultPassHostHeader = "true"
|
||||||
defaultFrontendPriority = "0"
|
defaultFrontendPriority = "0"
|
||||||
defaultCircuitBreakerExpression = "NetworkErrorRatio() > 1"
|
defaultCircuitBreakerExpression = "NetworkErrorRatio() > 1"
|
||||||
defaultFrontendRedirect = ""
|
defaultFrontendRedirectEntryPoint = ""
|
||||||
defaultBackendLoadBalancerMethod = "wrr"
|
defaultBackendLoadBalancerMethod = "wrr"
|
||||||
defaultBackendMaxconnExtractorfunc = "request.host"
|
defaultBackendMaxconnExtractorfunc = "request.host"
|
||||||
)
|
)
|
||||||
|
@ -276,7 +276,6 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con
|
||||||
"getEntryPoints": getFuncSliceStringLabel(types.LabelFrontendEntryPoints),
|
"getEntryPoints": getFuncSliceStringLabel(types.LabelFrontendEntryPoints),
|
||||||
"getBasicAuth": getFuncSliceStringLabel(types.LabelFrontendAuthBasic),
|
"getBasicAuth": getFuncSliceStringLabel(types.LabelFrontendAuthBasic),
|
||||||
"getFrontendRule": p.getFrontendRule,
|
"getFrontendRule": p.getFrontendRule,
|
||||||
"getRedirect": getFuncStringLabel(types.LabelFrontendRedirect, ""),
|
|
||||||
"hasCircuitBreakerLabel": hasLabel(types.LabelBackendCircuitbreakerExpression),
|
"hasCircuitBreakerLabel": hasLabel(types.LabelBackendCircuitbreakerExpression),
|
||||||
"getCircuitBreakerExpression": getFuncStringLabel(types.LabelBackendCircuitbreakerExpression, defaultCircuitBreakerExpression),
|
"getCircuitBreakerExpression": getFuncStringLabel(types.LabelBackendCircuitbreakerExpression, defaultCircuitBreakerExpression),
|
||||||
"hasLoadBalancerLabel": hasLoadBalancerLabel,
|
"hasLoadBalancerLabel": hasLoadBalancerLabel,
|
||||||
|
@ -289,7 +288,6 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con
|
||||||
"getStickinessCookieName": getFuncStringLabel(types.LabelBackendLoadbalancerStickinessCookieName, ""),
|
"getStickinessCookieName": getFuncStringLabel(types.LabelBackendLoadbalancerStickinessCookieName, ""),
|
||||||
"getIsBackendLBSwarm": getIsBackendLBSwarm,
|
"getIsBackendLBSwarm": getIsBackendLBSwarm,
|
||||||
"getServiceBackend": getServiceBackend,
|
"getServiceBackend": getServiceBackend,
|
||||||
"getServiceRedirect": getFuncServiceStringLabel(types.SuffixFrontendRedirect, defaultFrontendRedirect),
|
|
||||||
"getWhitelistSourceRange": getFuncSliceStringLabel(types.LabelTraefikFrontendWhitelistSourceRange),
|
"getWhitelistSourceRange": getFuncSliceStringLabel(types.LabelTraefikFrontendWhitelistSourceRange),
|
||||||
|
|
||||||
"hasRequestHeaders": hasLabel(types.LabelFrontendRequestHeaders),
|
"hasRequestHeaders": hasLabel(types.LabelFrontendRequestHeaders),
|
||||||
|
@ -343,6 +341,15 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con
|
||||||
"getServiceFrontendRule": p.getServiceFrontendRule,
|
"getServiceFrontendRule": p.getServiceFrontendRule,
|
||||||
"getServicePassHostHeader": getFuncServiceStringLabel(types.SuffixFrontendPassHostHeader, defaultPassHostHeader),
|
"getServicePassHostHeader": getFuncServiceStringLabel(types.SuffixFrontendPassHostHeader, defaultPassHostHeader),
|
||||||
"getServicePriority": getFuncServiceStringLabel(types.SuffixFrontendPriority, defaultFrontendPriority),
|
"getServicePriority": getFuncServiceStringLabel(types.SuffixFrontendPriority, defaultFrontendPriority),
|
||||||
|
|
||||||
|
"hasRedirect": hasRedirect,
|
||||||
|
"getRedirectEntryPoint": getFuncStringLabel(types.LabelFrontendRedirectEntryPoint, defaultFrontendRedirectEntryPoint),
|
||||||
|
"getRedirectRegex": getFuncStringLabel(types.LabelFrontendRedirectRegex, ""),
|
||||||
|
"getRedirectReplacement": getFuncStringLabel(types.LabelFrontendRedirectReplacement, ""),
|
||||||
|
"hasServiceRedirect": hasServiceRedirect,
|
||||||
|
"getServiceRedirectEntryPoint": getFuncServiceStringLabel(types.SuffixFrontendRedirectEntryPoint, defaultFrontendRedirectEntryPoint),
|
||||||
|
"getServiceRedirectReplacement": getFuncServiceStringLabel(types.SuffixFrontendRedirectReplacement, ""),
|
||||||
|
"getServiceRedirectRegex": getFuncServiceStringLabel(types.SuffixFrontendRedirectRegex, ""),
|
||||||
}
|
}
|
||||||
// filter containers
|
// filter containers
|
||||||
filteredContainers := fun.Filter(func(container dockerData) bool {
|
filteredContainers := fun.Filter(func(container dockerData) bool {
|
||||||
|
@ -865,3 +872,26 @@ func parseTasks(task swarmtypes.Task, serviceDockerData dockerData, networkMap m
|
||||||
}
|
}
|
||||||
return dockerData
|
return dockerData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO will be rewrite when merge on master
|
||||||
|
func hasServiceRedirect(container dockerData, serviceName string) bool {
|
||||||
|
serviceLabels, ok := extractServicesLabels(container.Labels)[serviceName]
|
||||||
|
if !ok || len(serviceLabels) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
value, ok := serviceLabels[types.SuffixFrontendRedirectEntryPoint]
|
||||||
|
frep := ok && len(value) > 0
|
||||||
|
value, ok = serviceLabels[types.SuffixFrontendRedirectRegex]
|
||||||
|
frrg := ok && len(value) > 0
|
||||||
|
value, ok = serviceLabels[types.SuffixFrontendRedirectReplacement]
|
||||||
|
frrp := ok && len(value) > 0
|
||||||
|
|
||||||
|
return frep || frrg && frrp
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO will be rewrite when merge on master
|
||||||
|
func hasRedirect(container dockerData) bool {
|
||||||
|
return hasLabel(types.LabelFrontendRedirectEntryPoint)(container) ||
|
||||||
|
hasLabel(types.LabelFrontendRedirectReplacement)(container) && hasLabel(types.LabelFrontendRedirectRegex)(container)
|
||||||
|
}
|
||||||
|
|
|
@ -712,7 +712,6 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Redirect: "",
|
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test-docker-localhost-0": {
|
"route-frontend-Host-test-docker-localhost-0": {
|
||||||
Rule: "Host:test.docker.localhost",
|
Rule: "Host:test.docker.localhost",
|
||||||
|
@ -737,10 +736,10 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
containerJSON(
|
containerJSON(
|
||||||
name("test1"),
|
name("test1"),
|
||||||
labels(map[string]string{
|
labels(map[string]string{
|
||||||
types.LabelBackend: "foobar",
|
types.LabelBackend: "foobar",
|
||||||
types.LabelFrontendEntryPoints: "http,https",
|
types.LabelFrontendEntryPoints: "http,https",
|
||||||
types.LabelFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
types.LabelFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
types.LabelFrontendRedirect: "https",
|
types.LabelFrontendRedirectEntryPoint: "https",
|
||||||
}),
|
}),
|
||||||
ports(nat.PortMap{
|
ports(nat.PortMap{
|
||||||
"80/tcp": {},
|
"80/tcp": {},
|
||||||
|
@ -764,7 +763,9 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{"http", "https"},
|
EntryPoints: []string{"http", "https"},
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
||||||
Redirect: "https",
|
Redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test1-docker-localhost-0": {
|
"route-frontend-Host-test1-docker-localhost-0": {
|
||||||
Rule: "Host:test1.docker.localhost",
|
Rule: "Host:test1.docker.localhost",
|
||||||
|
@ -776,7 +777,6 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Redirect: "",
|
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test2-docker-localhost-1": {
|
"route-frontend-Host-test2-docker-localhost-1": {
|
||||||
Rule: "Host:test2.docker.localhost",
|
Rule: "Host:test2.docker.localhost",
|
||||||
|
@ -824,7 +824,6 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{"http", "https"},
|
EntryPoints: []string{"http", "https"},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Redirect: "",
|
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test1-docker-localhost-0": {
|
"route-frontend-Host-test1-docker-localhost-0": {
|
||||||
Rule: "Host:test1.docker.localhost",
|
Rule: "Host:test1.docker.localhost",
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
docker "github.com/docker/docker/api/types"
|
docker "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDockerGetServicePort(t *testing.T) {
|
func TestDockerGetServicePort(t *testing.T) {
|
||||||
|
@ -136,10 +137,10 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
containerJSON(
|
containerJSON(
|
||||||
name("foo"),
|
name("foo"),
|
||||||
labels(map[string]string{
|
labels(map[string]string{
|
||||||
"traefik.service.port": "2503",
|
"traefik.service.port": "2503",
|
||||||
"traefik.service.frontend.entryPoints": "http,https",
|
"traefik.service.frontend.entryPoints": "http,https",
|
||||||
"traefik.service.frontend.auth.basic": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"traefik.service.frontend.auth.basic": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
"traefik.service.frontend.redirect": "https",
|
"traefik.service.frontend.redirect.entryPoint": "https",
|
||||||
}),
|
}),
|
||||||
ports(nat.PortMap{
|
ports(nat.PortMap{
|
||||||
"80/tcp": {},
|
"80/tcp": {},
|
||||||
|
@ -153,7 +154,9 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{"http", "https"},
|
EntryPoints: []string{"http", "https"},
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
||||||
Redirect: "https",
|
Redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"service-service": {
|
"service-service": {
|
||||||
Rule: "Host:foo.docker.localhost",
|
Rule: "Host:foo.docker.localhost",
|
||||||
|
@ -178,16 +181,16 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
containerJSON(
|
containerJSON(
|
||||||
name("test1"),
|
name("test1"),
|
||||||
labels(map[string]string{
|
labels(map[string]string{
|
||||||
"traefik.service.port": "2503",
|
"traefik.service.port": "2503",
|
||||||
"traefik.service.protocol": "https",
|
"traefik.service.protocol": "https",
|
||||||
"traefik.service.weight": "80",
|
"traefik.service.weight": "80",
|
||||||
"traefik.service.frontend.backend": "foobar",
|
"traefik.service.frontend.backend": "foobar",
|
||||||
"traefik.service.frontend.passHostHeader": "false",
|
"traefik.service.frontend.passHostHeader": "false",
|
||||||
"traefik.service.frontend.rule": "Path:/mypath",
|
"traefik.service.frontend.rule": "Path:/mypath",
|
||||||
"traefik.service.frontend.priority": "5000",
|
"traefik.service.frontend.priority": "5000",
|
||||||
"traefik.service.frontend.entryPoints": "http,https,ws",
|
"traefik.service.frontend.entryPoints": "http,https,ws",
|
||||||
"traefik.service.frontend.auth.basic": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"traefik.service.frontend.auth.basic": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
"traefik.service.frontend.redirect": "https",
|
"traefik.service.frontend.redirect.entryPoint": "https",
|
||||||
}),
|
}),
|
||||||
ports(nat.PortMap{
|
ports(nat.PortMap{
|
||||||
"80/tcp": {},
|
"80/tcp": {},
|
||||||
|
@ -214,7 +217,9 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
Priority: 5000,
|
Priority: 5000,
|
||||||
EntryPoints: []string{"http", "https", "ws"},
|
EntryPoints: []string{"http", "https", "ws"},
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
||||||
Redirect: "https",
|
Redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"service-service": {
|
"service-service": {
|
||||||
Rule: "Path:/mypath",
|
Rule: "Path:/mypath",
|
||||||
|
@ -226,7 +231,6 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Redirect: "",
|
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"service-anotherservice": {
|
"service-anotherservice": {
|
||||||
Rule: "Path:/anotherpath",
|
Rule: "Path:/anotherpath",
|
||||||
|
@ -274,9 +278,11 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
|
|
||||||
actualConfig := provider.loadDockerConfig(dockerDataList)
|
actualConfig := provider.loadDockerConfig(dockerDataList)
|
||||||
// Compare backends
|
// Compare backends
|
||||||
|
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||||
if !reflect.DeepEqual(actualConfig.Backends, test.expectedBackends) {
|
if !reflect.DeepEqual(actualConfig.Backends, test.expectedBackends) {
|
||||||
t.Fatalf("expected %#v, got %#v", test.expectedBackends, actualConfig.Backends)
|
t.Fatalf("expected %#v, got %#v", test.expectedBackends, actualConfig.Backends)
|
||||||
}
|
}
|
||||||
|
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
||||||
if !reflect.DeepEqual(actualConfig.Frontends, test.expectedFrontends) {
|
if !reflect.DeepEqual(actualConfig.Frontends, test.expectedFrontends) {
|
||||||
t.Fatalf("expected %#v, got %#v", test.expectedFrontends, actualConfig.Frontends)
|
t.Fatalf("expected %#v, got %#v", test.expectedFrontends, actualConfig.Frontends)
|
||||||
}
|
}
|
||||||
|
|
|
@ -516,7 +516,6 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Redirect: "",
|
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test-docker-localhost-0": {
|
"route-frontend-Host-test-docker-localhost-0": {
|
||||||
Rule: "Host:test.docker.localhost",
|
Rule: "Host:test.docker.localhost",
|
||||||
|
@ -547,11 +546,11 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
|
||||||
swarmService(
|
swarmService(
|
||||||
serviceName("test1"),
|
serviceName("test1"),
|
||||||
serviceLabels(map[string]string{
|
serviceLabels(map[string]string{
|
||||||
types.LabelPort: "80",
|
types.LabelPort: "80",
|
||||||
types.LabelBackend: "foobar",
|
types.LabelBackend: "foobar",
|
||||||
types.LabelFrontendEntryPoints: "http,https",
|
types.LabelFrontendEntryPoints: "http,https",
|
||||||
types.LabelFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
types.LabelFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
types.LabelFrontendRedirect: "https",
|
types.LabelFrontendRedirectEntryPoint: "https",
|
||||||
}),
|
}),
|
||||||
withEndpointSpec(modeVIP),
|
withEndpointSpec(modeVIP),
|
||||||
withEndpoint(virtualIP("1", "127.0.0.1/24")),
|
withEndpoint(virtualIP("1", "127.0.0.1/24")),
|
||||||
|
@ -572,7 +571,9 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{"http", "https"},
|
EntryPoints: []string{"http", "https"},
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
||||||
Redirect: "https",
|
Redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test1-docker-localhost-0": {
|
"route-frontend-Host-test1-docker-localhost-0": {
|
||||||
Rule: "Host:test1.docker.localhost",
|
Rule: "Host:test1.docker.localhost",
|
||||||
|
@ -584,7 +585,6 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Redirect: "",
|
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test2-docker-localhost-1": {
|
"route-frontend-Host-test2-docker-localhost-1": {
|
||||||
Rule: "Host:test2.docker.localhost",
|
Rule: "Host:test2.docker.localhost",
|
||||||
|
|
|
@ -199,8 +199,6 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
||||||
|
|
||||||
whitelistSourceRange := getSliceAnnotation(i, annotationKubernetesWhitelistSourceRange)
|
whitelistSourceRange := getSliceAnnotation(i, annotationKubernetesWhitelistSourceRange)
|
||||||
|
|
||||||
entryPointRedirect, _ := i.Annotations[types.LabelFrontendRedirect]
|
|
||||||
|
|
||||||
if _, exists := templateObjects.Frontends[r.Host+pa.Path]; !exists {
|
if _, exists := templateObjects.Frontends[r.Host+pa.Path]; !exists {
|
||||||
basicAuthCreds, err := handleBasicAuthConfig(i, k8sClient)
|
basicAuthCreds, err := handleBasicAuthConfig(i, k8sClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -241,7 +239,7 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
||||||
Priority: priority,
|
Priority: priority,
|
||||||
BasicAuth: basicAuthCreds,
|
BasicAuth: basicAuthCreds,
|
||||||
WhitelistSourceRange: whitelistSourceRange,
|
WhitelistSourceRange: whitelistSourceRange,
|
||||||
Redirect: entryPointRedirect,
|
Redirect: getFrontendRedirect(i),
|
||||||
EntryPoints: entryPoints,
|
EntryPoints: entryPoints,
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
}
|
}
|
||||||
|
@ -492,3 +490,22 @@ func shouldProcessIngress(ingressClass string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO will be rewrite when merge on master
|
||||||
|
func getFrontendRedirect(i *v1beta1.Ingress) *types.Redirect {
|
||||||
|
frontendRedirectEntryPoint, ok := i.Annotations[types.LabelFrontendRedirectEntryPoint]
|
||||||
|
frep := ok && len(frontendRedirectEntryPoint) > 0
|
||||||
|
frontendRedirectRegex, ok := i.Annotations[types.LabelFrontendRedirectRegex]
|
||||||
|
frrg := ok && len(frontendRedirectRegex) > 0
|
||||||
|
frontendRedirectReplacement, ok := i.Annotations[types.LabelFrontendRedirectReplacement]
|
||||||
|
frrp := ok && len(frontendRedirectReplacement) > 0
|
||||||
|
|
||||||
|
if frep || frrg && frrp {
|
||||||
|
return &types.Redirect{
|
||||||
|
EntryPoint: frontendRedirectEntryPoint,
|
||||||
|
Regex: frontendRedirectRegex,
|
||||||
|
Replacement: frontendRedirectReplacement,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"k8s.io/client-go/pkg/api/v1"
|
"k8s.io/client-go/pkg/api/v1"
|
||||||
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||||
"k8s.io/client-go/pkg/util/intstr"
|
"k8s.io/client-go/pkg/util/intstr"
|
||||||
|
@ -1266,8 +1267,8 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "testing",
|
Namespace: "testing",
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
"kubernetes.io/ingress.class": "traefik",
|
"kubernetes.io/ingress.class": "traefik",
|
||||||
types.LabelFrontendRedirect: "https",
|
types.LabelFrontendRedirectEntryPoint: "https",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: v1beta1.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
|
@ -1452,7 +1453,6 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
Rule: "Host:foo",
|
Rule: "Host:foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Redirect: "",
|
|
||||||
},
|
},
|
||||||
"other/stuff": {
|
"other/stuff": {
|
||||||
Backend: "other/stuff",
|
Backend: "other/stuff",
|
||||||
|
@ -1465,7 +1465,6 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
Rule: "Host:other",
|
Rule: "Host:other",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Redirect: "",
|
|
||||||
},
|
},
|
||||||
"other/": {
|
"other/": {
|
||||||
Backend: "other/",
|
Backend: "other/",
|
||||||
|
@ -1505,7 +1504,6 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
BasicAuth: []string{"myUser:myEncodedPW"},
|
BasicAuth: []string{"myUser:myEncodedPW"},
|
||||||
Redirect: "",
|
|
||||||
},
|
},
|
||||||
"redirect/https": {
|
"redirect/https": {
|
||||||
Backend: "redirect/https",
|
Backend: "redirect/https",
|
||||||
|
@ -1518,7 +1516,9 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
Rule: "Host:redirect",
|
Rule: "Host:redirect",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Redirect: "https",
|
Redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"test/whitelist-source-range": {
|
"test/whitelist-source-range": {
|
||||||
|
@ -1536,7 +1536,6 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
Rule: "Host:test",
|
Rule: "Host:test",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Redirect: "",
|
|
||||||
},
|
},
|
||||||
"rewrite/api": {
|
"rewrite/api": {
|
||||||
Backend: "rewrite/api",
|
Backend: "rewrite/api",
|
||||||
|
@ -1549,7 +1548,6 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
Rule: "Host:rewrite",
|
Rule: "Host:rewrite",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Redirect: "",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -2256,6 +2254,7 @@ func TestBasicAuthInTemplate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
actual = provider.loadConfig(*actual)
|
actual = provider.loadConfig(*actual)
|
||||||
|
require.NotNil(t, actual)
|
||||||
got := actual.Frontends["basic/auth"].BasicAuth
|
got := actual.Frontends["basic/auth"].BasicAuth
|
||||||
if !reflect.DeepEqual(got, []string{"myUser:myEncodedPW"}) {
|
if !reflect.DeepEqual(got, []string{"myUser:myEncodedPW"}) {
|
||||||
t.Fatalf("unexpected credentials: %+v", got)
|
t.Fatalf("unexpected credentials: %+v", got)
|
||||||
|
|
|
@ -76,13 +76,6 @@ func (p *Provider) getBasicAuth(service rancherData) []string {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) getRedirect(service rancherData) string {
|
|
||||||
if redirect, err := getServiceLabel(service, types.LabelFrontendRedirect); err == nil {
|
|
||||||
return redirect
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Provider) getFrontendName(service rancherData) string {
|
func (p *Provider) getFrontendName(service rancherData) string {
|
||||||
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
|
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
|
||||||
return provider.Normalize(p.getFrontendRule(service))
|
return provider.Normalize(p.getFrontendRule(service))
|
||||||
|
@ -246,7 +239,10 @@ func (p *Provider) loadRancherConfig(services []rancherData) *types.Configuratio
|
||||||
"getSticky": p.getSticky,
|
"getSticky": p.getSticky,
|
||||||
"hasStickinessLabel": p.hasStickinessLabel,
|
"hasStickinessLabel": p.hasStickinessLabel,
|
||||||
"getStickinessCookieName": p.getStickinessCookieName,
|
"getStickinessCookieName": p.getStickinessCookieName,
|
||||||
"getRedirect": p.getRedirect,
|
"hasRedirect": hasRedirect,
|
||||||
|
"getRedirectEntryPoint": getRedirectEntryPoint,
|
||||||
|
"getRedirectRegex": getRedirectRegex,
|
||||||
|
"getRedirectReplacement": getRedirectReplacement,
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter services
|
// filter services
|
||||||
|
@ -340,3 +336,42 @@ func isServiceEnabled(service rancherData, exposedByDefault bool) bool {
|
||||||
}
|
}
|
||||||
return exposedByDefault
|
return exposedByDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO will be rewrite when merge on master
|
||||||
|
func hasRedirect(service rancherData) bool {
|
||||||
|
value, err := getServiceLabel(service, types.LabelFrontendRedirectEntryPoint)
|
||||||
|
frep := err == nil && len(value) > 0
|
||||||
|
value, err = getServiceLabel(service, types.LabelFrontendRedirectRegex)
|
||||||
|
frrg := err == nil && len(value) > 0
|
||||||
|
value, err = getServiceLabel(service, types.LabelFrontendRedirectReplacement)
|
||||||
|
frrp := err == nil && len(value) > 0
|
||||||
|
|
||||||
|
return frep || frrg && frrp
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO will be rewrite when merge on master
|
||||||
|
func getRedirectEntryPoint(service rancherData) string {
|
||||||
|
value, err := getServiceLabel(service, types.LabelFrontendRedirectEntryPoint)
|
||||||
|
if err != nil || len(value) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO will be rewrite when merge on master
|
||||||
|
func getRedirectRegex(service rancherData) string {
|
||||||
|
value, err := getServiceLabel(service, types.LabelFrontendRedirectRegex)
|
||||||
|
if err != nil || len(value) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO will be rewrite when merge on master
|
||||||
|
func getRedirectReplacement(service rancherData) string {
|
||||||
|
value, err := getServiceLabel(service, types.LabelFrontendRedirectReplacement)
|
||||||
|
if err != nil || len(value) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProviderServiceFilter(t *testing.T) {
|
func TestProviderServiceFilter(t *testing.T) {
|
||||||
|
@ -529,44 +530,6 @@ func TestProviderGetPassHostHeader(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProviderGetRedirect(t *testing.T) {
|
|
||||||
provider := &Provider{Domain: "rancher.localhost"}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
service rancherData
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "without label",
|
|
||||||
service: rancherData{
|
|
||||||
Name: "test-service",
|
|
||||||
},
|
|
||||||
expected: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "with label",
|
|
||||||
service: rancherData{
|
|
||||||
Name: "test-service",
|
|
||||||
Labels: map[string]string{
|
|
||||||
types.LabelFrontendRedirect: "https",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: "https",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actual := provider.getRedirect(test.service)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProviderGetLabel(t *testing.T) {
|
func TestProviderGetLabel(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -634,9 +597,9 @@ func TestProviderLoadRancherConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
Name: "test/service",
|
Name: "test/service",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
types.LabelPort: "80",
|
types.LabelPort: "80",
|
||||||
types.LabelFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
types.LabelFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
types.LabelFrontendRedirect: "https",
|
types.LabelFrontendRedirectEntryPoint: "https",
|
||||||
},
|
},
|
||||||
Health: "healthy",
|
Health: "healthy",
|
||||||
Containers: []string{"127.0.0.1"},
|
Containers: []string{"127.0.0.1"},
|
||||||
|
@ -649,7 +612,9 @@ func TestProviderLoadRancherConfig(t *testing.T) {
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
||||||
Priority: 0,
|
Priority: 0,
|
||||||
Redirect: "https",
|
Redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test-service-rancher-localhost": {
|
"route-frontend-Host-test-service-rancher-localhost": {
|
||||||
Rule: "Host:test.service.rancher.localhost",
|
Rule: "Host:test.service.rancher.localhost",
|
||||||
|
@ -679,6 +644,7 @@ func TestProviderLoadRancherConfig(t *testing.T) {
|
||||||
|
|
||||||
actualConfig := provider.loadRancherConfig(test.services)
|
actualConfig := provider.loadRancherConfig(test.services)
|
||||||
|
|
||||||
|
require.NotNil(t, actualConfig)
|
||||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||||
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
||||||
})
|
})
|
||||||
|
@ -732,3 +698,70 @@ func TestProviderHasStickinessLabel(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHasRedirect(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
service rancherData
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "without redirect labels",
|
||||||
|
service: rancherData{
|
||||||
|
Name: "test-service",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "with Redirect EntryPoint label",
|
||||||
|
service: rancherData{
|
||||||
|
Name: "test-service",
|
||||||
|
Labels: map[string]string{
|
||||||
|
types.LabelFrontendRedirectEntryPoint: "https",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "with Redirect regex label",
|
||||||
|
service: rancherData{
|
||||||
|
Name: "test-service",
|
||||||
|
Labels: map[string]string{
|
||||||
|
types.LabelFrontendRedirectRegex: `(.+)`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "with Redirect replacement label",
|
||||||
|
service: rancherData{
|
||||||
|
Name: "test-service",
|
||||||
|
Labels: map[string]string{
|
||||||
|
types.LabelFrontendRedirectReplacement: "$1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "with Redirect regex & replacement labels",
|
||||||
|
service: rancherData{
|
||||||
|
Name: "test-service",
|
||||||
|
Labels: map[string]string{
|
||||||
|
types.LabelFrontendRedirectRegex: `(.+)`,
|
||||||
|
types.LabelFrontendRedirectReplacement: "$1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
actual := hasRedirect(test.service)
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -944,7 +944,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
|
||||||
if entryPoint.Redirect != nil {
|
if entryPoint.Redirect != nil {
|
||||||
if redirectHandlers[entryPointName] != nil {
|
if redirectHandlers[entryPointName] != nil {
|
||||||
n.Use(redirectHandlers[entryPointName])
|
n.Use(redirectHandlers[entryPointName])
|
||||||
} else if handler, err := s.buildEntryPointRedirect(entryPointName, entryPoint); err != nil {
|
} else if handler, err := s.buildRedirectHandler(entryPointName, entryPoint.Redirect); err != nil {
|
||||||
log.Errorf("Error loading entrypoint configuration for frontend %s: %v", frontendName, err)
|
log.Errorf("Error loading entrypoint configuration for frontend %s: %v", frontendName, err)
|
||||||
log.Errorf("Skipping frontend %s...", frontendName)
|
log.Errorf("Skipping frontend %s...", frontendName)
|
||||||
continue frontend
|
continue frontend
|
||||||
|
@ -1131,8 +1131,8 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
|
||||||
log.Infof("Configured IP Whitelists: %s", frontend.WhitelistSourceRange)
|
log.Infof("Configured IP Whitelists: %s", frontend.WhitelistSourceRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(frontend.Redirect) > 0 {
|
if frontend.Redirect != nil {
|
||||||
rewrite, err := s.buildRedirectRewrite(entryPointName, frontend.Redirect)
|
rewrite, err := s.buildRedirectHandler(entryPointName, frontend.Redirect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error creating Frontend Redirect: %v", err)
|
log.Errorf("Error creating Frontend Redirect: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1287,23 +1287,23 @@ func (s *Server) wireFrontendBackend(serverRoute *serverRoute, handler http.Hand
|
||||||
serverRoute.route.Handler(handler)
|
serverRoute.route.Handler(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) buildEntryPointRedirect(srcEntryPointName string, entryPoint *configuration.EntryPoint) (*middlewares.Rewrite, error) {
|
func (s *Server) buildRedirectHandler(srcEntryPointName string, redirect *types.Redirect) (*middlewares.Rewrite, error) {
|
||||||
if len(entryPoint.Redirect.EntryPoint) > 0 {
|
// entry point redirect
|
||||||
return s.buildRedirectRewrite(srcEntryPointName, entryPoint.Redirect.EntryPoint)
|
if len(redirect.EntryPoint) > 0 {
|
||||||
|
return s.buildEntryPointRedirect(srcEntryPointName, redirect.EntryPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
regex := entryPoint.Redirect.Regex
|
// regex redirect
|
||||||
replacement := entryPoint.Redirect.Replacement
|
rewrite, err := middlewares.NewRewrite(redirect.Regex, redirect.Replacement, true)
|
||||||
rewrite, err := middlewares.NewRewrite(regex, replacement, true)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", srcEntryPointName, entryPoint.Redirect.EntryPoint, regex, replacement)
|
log.Debugf("Creating entryPoint redirect %s -> %s -> %s", srcEntryPointName, redirect.Regex, redirect.Replacement)
|
||||||
|
|
||||||
return rewrite, nil
|
return rewrite, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) buildRedirectRewrite(srcEntryPointName string, redirectEntryPoint string) (*middlewares.Rewrite, error) {
|
func (s *Server) buildEntryPointRedirect(srcEntryPointName string, redirectEntryPoint string) (*middlewares.Rewrite, error) {
|
||||||
regex, replacement, err := s.buildRedirect(redirectEntryPoint)
|
regex, replacement, err := s.buildRedirect(redirectEntryPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -918,15 +918,20 @@ func TestBuildEntryPointRedirect(t *testing.T) {
|
||||||
srcEntryPointName string
|
srcEntryPointName string
|
||||||
url string
|
url string
|
||||||
entryPoint *configuration.EntryPoint
|
entryPoint *configuration.EntryPoint
|
||||||
|
redirect *types.Redirect
|
||||||
expectedURL string
|
expectedURL string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "redirect regex",
|
desc: "redirect regex",
|
||||||
srcEntryPointName: "http",
|
srcEntryPointName: "http",
|
||||||
url: "http://foo.com",
|
url: "http://foo.com",
|
||||||
|
redirect: &types.Redirect{
|
||||||
|
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
||||||
|
Replacement: "https://$1{{\"bar\"}}$2",
|
||||||
|
},
|
||||||
entryPoint: &configuration.EntryPoint{
|
entryPoint: &configuration.EntryPoint{
|
||||||
Address: ":80",
|
Address: ":80",
|
||||||
Redirect: &configuration.Redirect{
|
Redirect: &types.Redirect{
|
||||||
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
||||||
Replacement: "https://$1{{\"bar\"}}$2",
|
Replacement: "https://$1{{\"bar\"}}$2",
|
||||||
},
|
},
|
||||||
|
@ -937,9 +942,12 @@ func TestBuildEntryPointRedirect(t *testing.T) {
|
||||||
desc: "redirect entry point",
|
desc: "redirect entry point",
|
||||||
srcEntryPointName: "http",
|
srcEntryPointName: "http",
|
||||||
url: "http://foo:80",
|
url: "http://foo:80",
|
||||||
|
redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
},
|
||||||
entryPoint: &configuration.EntryPoint{
|
entryPoint: &configuration.EntryPoint{
|
||||||
Address: ":80",
|
Address: ":80",
|
||||||
Redirect: &configuration.Redirect{
|
Redirect: &types.Redirect{
|
||||||
EntryPoint: "https",
|
EntryPoint: "https",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -949,9 +957,14 @@ func TestBuildEntryPointRedirect(t *testing.T) {
|
||||||
desc: "redirect entry point with regex (ignored)",
|
desc: "redirect entry point with regex (ignored)",
|
||||||
srcEntryPointName: "http",
|
srcEntryPointName: "http",
|
||||||
url: "http://foo.com:80",
|
url: "http://foo.com:80",
|
||||||
|
redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
||||||
|
Replacement: "https://$1{{\"bar\"}}$2",
|
||||||
|
},
|
||||||
entryPoint: &configuration.EntryPoint{
|
entryPoint: &configuration.EntryPoint{
|
||||||
Address: ":80",
|
Address: ":80",
|
||||||
Redirect: &configuration.Redirect{
|
Redirect: &types.Redirect{
|
||||||
EntryPoint: "https",
|
EntryPoint: "https",
|
||||||
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
||||||
Replacement: "https://$1{{\"bar\"}}$2",
|
Replacement: "https://$1{{\"bar\"}}$2",
|
||||||
|
@ -966,7 +979,7 @@ func TestBuildEntryPointRedirect(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
rewrite, err := srv.buildEntryPointRedirect(test.srcEntryPointName, test.entryPoint)
|
rewrite, err := srv.buildRedirectHandler(test.srcEntryPointName, test.redirect)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, test.url, nil)
|
req := testhelpers.MustNewRequest(http.MethodGet, test.url, nil)
|
||||||
|
@ -983,7 +996,7 @@ func TestBuildEntryPointRedirect(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerBuildRedirectRewrite(t *testing.T) {
|
func TestServerBuildEntryPointRedirect(t *testing.T) {
|
||||||
srv := Server{
|
srv := Server{
|
||||||
globalConfiguration: configuration.GlobalConfiguration{
|
globalConfiguration: configuration.GlobalConfiguration{
|
||||||
EntryPoints: configuration.EntryPoints{
|
EntryPoints: configuration.EntryPoints{
|
||||||
|
@ -1022,7 +1035,7 @@ func TestServerBuildRedirectRewrite(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
rewrite, err := srv.buildRedirectRewrite(test.srcEntryPointName, test.redirectEntryPoint)
|
rewrite, err := srv.buildEntryPointRedirect(test.srcEntryPointName, test.redirectEntryPoint)
|
||||||
if test.errorExpected {
|
if test.errorExpected {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -47,7 +47,6 @@
|
||||||
[frontends."frontend-{{getServiceBackend $container $serviceName}}"]
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}"]
|
||||||
backend = "backend-{{getServiceBackend $container $serviceName}}"
|
backend = "backend-{{getServiceBackend $container $serviceName}}"
|
||||||
passHostHeader = {{getServicePassHostHeader $container $serviceName}}
|
passHostHeader = {{getServicePassHostHeader $container $serviceName}}
|
||||||
redirect = "{{getServiceRedirect $container $serviceName}}"
|
|
||||||
{{if getWhitelistSourceRange $container}}
|
{{if getWhitelistSourceRange $container}}
|
||||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
|
@ -60,14 +59,21 @@
|
||||||
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
|
||||||
|
{{if hasServiceRedirect $container $serviceName}}
|
||||||
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".redirect]
|
||||||
|
entryPoint = "{{getServiceRedirectEntryPoint $container $serviceName}}"
|
||||||
|
regex = "{{getServiceRedirectRegex $container $serviceName}}"
|
||||||
|
replacement = "{{getServiceRedirectReplacement $container $serviceName}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
||||||
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
[frontends."frontend-{{$frontend}}"]
|
[frontends."frontend-{{$frontend}}"]
|
||||||
backend = "backend-{{getBackend $container}}"
|
backend = "backend-{{getBackend $container}}"
|
||||||
passHostHeader = {{getPassHostHeader $container}}
|
passHostHeader = {{getPassHostHeader $container}}
|
||||||
redirect = "{{getRedirect $container}}"
|
|
||||||
{{if getWhitelistSourceRange $container}}
|
{{if getWhitelistSourceRange $container}}
|
||||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
|
@ -80,6 +86,14 @@
|
||||||
basicAuth = [{{range getBasicAuth $container}}
|
basicAuth = [{{range getBasicAuth $container}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{if hasRedirect $container}}
|
||||||
|
[frontends."frontend-{{$frontend}}".redirect]
|
||||||
|
entryPoint = "{{getRedirectEntryPoint $container}}"
|
||||||
|
regex = "{{getRedirectRegex $container}}"
|
||||||
|
replacement = "{{getRedirectReplacement $container}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
[frontends."frontend-{{$frontend}}".headers]
|
[frontends."frontend-{{$frontend}}".headers]
|
||||||
{{if hasSSLRedirectHeaders $container}}
|
{{if hasSSLRedirectHeaders $container}}
|
||||||
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
||||||
|
|
|
@ -25,13 +25,20 @@
|
||||||
backend = "{{$frontend.Backend}}"
|
backend = "{{$frontend.Backend}}"
|
||||||
priority = {{$frontend.Priority}}
|
priority = {{$frontend.Priority}}
|
||||||
passHostHeader = {{$frontend.PassHostHeader}}
|
passHostHeader = {{$frontend.PassHostHeader}}
|
||||||
redirect = "{{$frontend.Redirect}}"
|
|
||||||
basicAuth = [{{range $frontend.BasicAuth}}
|
basicAuth = [{{range $frontend.BasicAuth}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
whitelistSourceRange = [{{range $frontend.WhitelistSourceRange}}
|
whitelistSourceRange = [{{range $frontend.WhitelistSourceRange}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{if $frontend.Redirect}}
|
||||||
|
[frontends."{{$frontendName}}".redirect]
|
||||||
|
entryPoint = "{{$frontend.RedirectEntryPoint}}"
|
||||||
|
regex = "{{$frontend.RedirectRegex}}"
|
||||||
|
replacement = "{{$frontend.RedirectReplacement}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
[frontends."{{$frontendName}}".headers]
|
[frontends."{{$frontendName}}".headers]
|
||||||
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
|
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
|
||||||
SSLTemporaryRedirect = {{$frontend.Headers.SSLTemporaryRedirect}}
|
SSLTemporaryRedirect = {{$frontend.Headers.SSLTemporaryRedirect}}
|
||||||
|
|
|
@ -34,13 +34,20 @@
|
||||||
backend = "backend-{{getBackend $service}}"
|
backend = "backend-{{getBackend $service}}"
|
||||||
passHostHeader = {{getPassHostHeader $service}}
|
passHostHeader = {{getPassHostHeader $service}}
|
||||||
priority = {{getPriority $service}}
|
priority = {{getPriority $service}}
|
||||||
redirect = "{{getRedirect $service}}"
|
|
||||||
entryPoints = [{{range getEntryPoints $service}}
|
entryPoints = [{{range getEntryPoints $service}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
basicAuth = [{{range getBasicAuth $service}}
|
basicAuth = [{{range getBasicAuth $service}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{if hasRedirect $service}}
|
||||||
|
[frontends."frontend-{{$frontendName}}".redirect]
|
||||||
|
entryPoint = "{{getRedirectEntryPoint $service}}"
|
||||||
|
regex = "{{getRedirectRegex $service}}"
|
||||||
|
replacement = "{{getRedirectReplacement $service}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
[frontends."frontend-{{$frontendName}}".routes."route-frontend-{{$frontendName}}"]
|
[frontends."frontend-{{$frontendName}}".routes."route-frontend-{{$frontendName}}"]
|
||||||
rule = "{{getFrontendRule $service}}"
|
rule = "{{getFrontendRule $service}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -13,7 +13,9 @@ const (
|
||||||
SuffixFrontendEntryPoints = "frontend.entryPoints"
|
SuffixFrontendEntryPoints = "frontend.entryPoints"
|
||||||
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
|
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
|
||||||
SuffixFrontendPriority = "frontend.priority"
|
SuffixFrontendPriority = "frontend.priority"
|
||||||
SuffixFrontendRedirect = "frontend.redirect"
|
SuffixFrontendRedirectEntryPoint = "frontend.redirect.entryPoint"
|
||||||
|
SuffixFrontendRedirectRegex = "frontend.redirect.regex"
|
||||||
|
SuffixFrontendRedirectReplacement = "frontend.redirect.replacement"
|
||||||
SuffixFrontendRule = "frontend.rule"
|
SuffixFrontendRule = "frontend.rule"
|
||||||
LabelDomain = LabelPrefix + "domain"
|
LabelDomain = LabelPrefix + "domain"
|
||||||
LabelEnable = LabelPrefix + "enable"
|
LabelEnable = LabelPrefix + "enable"
|
||||||
|
@ -29,7 +31,9 @@ const (
|
||||||
LabelFrontendPriority = LabelPrefix + SuffixFrontendPriority
|
LabelFrontendPriority = LabelPrefix + SuffixFrontendPriority
|
||||||
LabelFrontendRule = LabelPrefix + SuffixFrontendRule
|
LabelFrontendRule = LabelPrefix + SuffixFrontendRule
|
||||||
LabelFrontendRuleType = LabelPrefix + "frontend.rule.type"
|
LabelFrontendRuleType = LabelPrefix + "frontend.rule.type"
|
||||||
LabelFrontendRedirect = LabelPrefix + SuffixFrontendRedirect
|
LabelFrontendRedirectEntryPoint = LabelPrefix + SuffixFrontendRedirectEntryPoint
|
||||||
|
LabelFrontendRedirectRegex = LabelPrefix + SuffixFrontendRedirectRegex
|
||||||
|
LabelFrontendRedirectReplacement = LabelPrefix + SuffixFrontendRedirectReplacement
|
||||||
LabelTraefikFrontendValue = LabelPrefix + "frontend.value"
|
LabelTraefikFrontendValue = LabelPrefix + "frontend.value"
|
||||||
LabelTraefikFrontendWhitelistSourceRange = LabelPrefix + "frontend.whitelistSourceRange"
|
LabelTraefikFrontendWhitelistSourceRange = LabelPrefix + "frontend.whitelistSourceRange"
|
||||||
LabelFrontendRequestHeaders = LabelPrefix + "frontend.headers.customRequestHeaders"
|
LabelFrontendRequestHeaders = LabelPrefix + "frontend.headers.customRequestHeaders"
|
||||||
|
|
|
@ -153,7 +153,14 @@ type Frontend struct {
|
||||||
Headers Headers `json:"headers,omitempty"`
|
Headers Headers `json:"headers,omitempty"`
|
||||||
Errors map[string]ErrorPage `json:"errors,omitempty"`
|
Errors map[string]ErrorPage `json:"errors,omitempty"`
|
||||||
RateLimit *RateLimit `json:"ratelimit,omitempty"`
|
RateLimit *RateLimit `json:"ratelimit,omitempty"`
|
||||||
Redirect string `json:"redirect,omitempty"`
|
Redirect *Redirect `json:"redirect,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect configures a redirection of an entry point to another, or to an URL
|
||||||
|
type Redirect struct {
|
||||||
|
EntryPoint string `json:"entryPoint,omitempty"`
|
||||||
|
Regex string `json:"regex,omitempty"`
|
||||||
|
Replacement string `json:"replacement,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadBalancerMethod holds the method of load balancing to use.
|
// LoadBalancerMethod holds the method of load balancing to use.
|
||||||
|
|
Loading…
Reference in a new issue