Merge branch 'v1.5' into master
This commit is contained in:
commit
f6520727a3
44 changed files with 1035 additions and 463 deletions
|
@ -43,7 +43,7 @@ If you want your users to access some of your microservices from the Internet, y
|
||||||
- path `domain.com/web` will point the microservice `web` in your private network
|
- path `domain.com/web` will point the microservice `web` in your private network
|
||||||
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
|
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
|
||||||
|
|
||||||
But a microservices architecture is dynamic... Services are added, removed, killed or upgraded often, eventually several times a day.
|
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day.
|
||||||
|
|
||||||
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
||||||
|
|
||||||
|
|
|
@ -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,6 +184,14 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{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 "." "-"}}"]
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
||||||
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
||||||
{{if hasServiceRequestHeaders $container $serviceName}}
|
{{if hasServiceRequestHeaders $container $serviceName}}
|
||||||
|
@ -204,7 +211,6 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
[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}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
|
@ -217,6 +223,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}}
|
||||||
|
@ -426,13 +440,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}}
|
||||||
|
@ -764,13 +785,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"],
|
||||||
|
@ -423,7 +423,7 @@ 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"`
|
||||||
|
@ -431,13 +431,6 @@ type EntryPoint struct {
|
||||||
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",
|
||||||
|
|
|
@ -356,7 +356,7 @@ A backend is responsible to load-balance the traffic coming from one or more fro
|
||||||
|
|
||||||
Various methods of load-balancing are supported:
|
Various methods of load-balancing are supported:
|
||||||
|
|
||||||
- `wrr`: Weighted Round Robin
|
- `wrr`: Weighted Round Robin.
|
||||||
- `drr`: Dynamic Round Robin: increases weights on servers that perform better than others.
|
- `drr`: Dynamic Round Robin: increases weights on servers that perform better than others.
|
||||||
It also rolls back to original weights if the servers have changed.
|
It also rolls back to original weights if the servers have changed.
|
||||||
|
|
||||||
|
@ -373,16 +373,13 @@ It can be configured using:
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend
|
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend.
|
||||||
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
|
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
|
||||||
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in range [500-600) to [0-600)
|
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in ranges [500-600) and [0-600).
|
||||||
|
|
||||||
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can
|
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can also be applied to each backend.
|
||||||
also be applied to each backend.
|
|
||||||
|
|
||||||
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and
|
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and `maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to evaluate the maximum connections.
|
||||||
`maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to
|
|
||||||
evaluate the maximum connections.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
```toml
|
```toml
|
||||||
|
@ -499,8 +496,8 @@ Here is an example of backends and servers definition:
|
||||||
|
|
||||||
Træfik's configuration has two parts:
|
Træfik's configuration has two parts:
|
||||||
|
|
||||||
- The [static Træfik configuration](/basics#static-trfk-configuration) which is loaded only at the beginning.
|
- The [static Træfik configuration](/basics#static-trfik-configuration) which is loaded only at the beginning.
|
||||||
- The [dynamic Træfik configuration](/basics#dynamic-trfk-configuration) which can be hot-reloaded (no need to restart the process).
|
- The [dynamic Træfik configuration](/basics#dynamic-trfik-configuration) which can be hot-reloaded (no need to restart the process).
|
||||||
|
|
||||||
### Static Træfik configuration
|
### Static Træfik configuration
|
||||||
|
|
||||||
|
@ -585,7 +582,7 @@ traefik [command] [--flag=flag_argument]
|
||||||
List of Træfik available commands with description :
|
List of Træfik available commands with description :
|
||||||
|
|
||||||
- `version` : Print version
|
- `version` : Print version
|
||||||
- `storeconfig` : Store the static Traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it.
|
- `storeconfig` : Store the static Traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-configuration-in-key-value-store) section to get documentation on it.
|
||||||
- `bug`: The easiest way to submit a pre-filled issue.
|
- `bug`: The easiest way to submit a pre-filled issue.
|
||||||
- `healthcheck`: Calls Traefik `/ping` to check health.
|
- `healthcheck`: Calls Traefik `/ping` to check health.
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ Select the provider that matches the DNS domain that will host the challenge TXT
|
||||||
|--------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------|
|
|--------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------|
|
||||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` |
|
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` |
|
||||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` |
|
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` |
|
||||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` |
|
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The Cloudflare `Global API Key` needs to be used and not the `Origin CA Key` |
|
||||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` |
|
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` |
|
||||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` |
|
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` |
|
||||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` |
|
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` |
|
||||||
|
|
|
@ -88,6 +88,12 @@ endpoint = "127.0.0.1:8500"
|
||||||
#
|
#
|
||||||
exposedByDefault = false
|
exposedByDefault = false
|
||||||
|
|
||||||
|
# Default domain used.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
domain = "consul.localhost"
|
||||||
|
|
||||||
# Prefix for Consul catalog tags.
|
# Prefix for Consul catalog tags.
|
||||||
#
|
#
|
||||||
# Optional
|
# Optional
|
||||||
|
|
|
@ -150,7 +150,7 @@ 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. |
|
||||||
|
@ -171,7 +171,11 @@ Labels can be used on containers to override default behaviour.
|
||||||
| `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
|
||||||
|
|
||||||
|
@ -203,7 +207,7 @@ 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`. |
|
||||||
|
@ -214,6 +218,9 @@ Services labels can be used for overriding default behaviour
|
||||||
| `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`. |
|
||||||
|
|
||||||
#### Security Headers
|
#### Security Headers
|
||||||
|
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -121,7 +121,7 @@ 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 |
|
||||||
|
@ -130,7 +130,9 @@ Labels can be used on task containers to override default behaviour:
|
||||||
| `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.frontend.redirect.regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
|
| `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.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
| `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||||
| `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 |
|
||||||
|
|
|
@ -22,7 +22,7 @@ If you want your users to access some of your microservices from the Internet, y
|
||||||
- path `domain.com/web` will point the microservice `web` in your private network
|
- path `domain.com/web` will point the microservice `web` in your private network
|
||||||
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
|
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
|
||||||
|
|
||||||
But a microservices architecture is dynamic... Services are added, removed, killed or upgraded often, eventually several times a day.
|
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day.
|
||||||
|
|
||||||
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ Start it from within the `traefik` folder:
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
In a browser you may open [http://localhost:8080](http://localhost:8080) to access Træfik's dashboard and observe the following magic.
|
In a browser, you may open [http://localhost:8080](http://localhost:8080) to access Træfik's dashboard and observe the following magic.
|
||||||
|
|
||||||
Now, create a folder named `test` and create a `docker-compose.yml` in it with this content:
|
Now, create a folder named `test` and create a `docker-compose.yml` in it with this content:
|
||||||
|
|
||||||
|
|
8
glide.lock
generated
8
glide.lock
generated
|
@ -1,5 +1,5 @@
|
||||||
hash: 8c5908b11f5078edd9ed93e2710ebb3a29b7e02d1259fddd679f8c46540becc9
|
hash: f0d5ef854a4c115306c63c15320b595c29f715950eaf5f18418149886ecda400
|
||||||
updated: 2017-11-30T10:34:41.246378337+01:00
|
updated: 2017-12-15T10:34:41.246378337+01:00
|
||||||
imports:
|
imports:
|
||||||
- name: cloud.google.com/go
|
- name: cloud.google.com/go
|
||||||
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
||||||
|
@ -94,7 +94,7 @@ imports:
|
||||||
- name: github.com/containous/staert
|
- name: github.com/containous/staert
|
||||||
version: af517d5b70db9c4b0505e0144fcc62b054057d2a
|
version: af517d5b70db9c4b0505e0144fcc62b054057d2a
|
||||||
- name: github.com/containous/traefik-extra-service-fabric
|
- name: github.com/containous/traefik-extra-service-fabric
|
||||||
version: 8076098dbfe814cba9e895ecbd896f1896b6b2d5
|
version: c01c1ef60ed612c5e42c1ceae0c6f92e67619cc3
|
||||||
- name: github.com/coreos/bbolt
|
- name: github.com/coreos/bbolt
|
||||||
version: 3c6cbfb299c11444eb2f8c9d48f0d2ce09157423
|
version: 3c6cbfb299c11444eb2f8c9d48f0d2ce09157423
|
||||||
- name: github.com/coreos/etcd
|
- name: github.com/coreos/etcd
|
||||||
|
@ -348,7 +348,7 @@ imports:
|
||||||
subpackages:
|
subpackages:
|
||||||
- lib
|
- lib
|
||||||
- name: github.com/jjcollinge/servicefabric
|
- name: github.com/jjcollinge/servicefabric
|
||||||
version: 93a44e59fc887cda489913c6fc5bda834989f3bd
|
version: 8026935326c842b71dee8e2329c1fda41a7a92f4
|
||||||
- name: github.com/jmespath/go-jmespath
|
- name: github.com/jmespath/go-jmespath
|
||||||
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||||
- name: github.com/jonboulle/clockwork
|
- name: github.com/jonboulle/clockwork
|
||||||
|
|
|
@ -12,7 +12,7 @@ import:
|
||||||
- package: github.com/cenk/backoff
|
- package: github.com/cenk/backoff
|
||||||
- package: github.com/containous/flaeg
|
- package: github.com/containous/flaeg
|
||||||
- package: github.com/containous/traefik-extra-service-fabric
|
- package: github.com/containous/traefik-extra-service-fabric
|
||||||
version: ^v1.0.1
|
version: v1.0.4
|
||||||
- package: github.com/vulcand/oxy
|
- package: github.com/vulcand/oxy
|
||||||
version: 7b6e758ab449705195df638765c4ca472248908a
|
version: 7b6e758ab449705195df638765c4ca472248908a
|
||||||
repo: https://github.com/containous/oxy.git
|
repo: https://github.com/containous/oxy.git
|
||||||
|
|
|
@ -419,6 +419,46 @@ func (s *ConsulCatalogSuite) TestCircuitBreaker(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ConsulCatalogSuite) TestRefreshConfigPortChange(c *check.C) {
|
||||||
|
cmd, display := s.traefikCmd(
|
||||||
|
withConfigFile("fixtures/consul_catalog/simple.toml"),
|
||||||
|
"--consulCatalog",
|
||||||
|
"--consulCatalog.exposedByDefault=false",
|
||||||
|
"--consulCatalog.watch=true",
|
||||||
|
"--consulCatalog.endpoint="+s.consulIP+":8500",
|
||||||
|
"--consulCatalog.domain=consul.localhost")
|
||||||
|
defer display(c)
|
||||||
|
err := cmd.Start()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
nginx := s.composeProject.Container(c, "nginx1")
|
||||||
|
|
||||||
|
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 81, []string{"name=nginx1", "traefik.enable=true"})
|
||||||
|
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
req.Host = "test.consul.localhost"
|
||||||
|
|
||||||
|
err = try.Request(req, 20*time.Second, try.StatusCodeIs(http.StatusBadGateway))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 5*time.Second, try.BodyContains("nginx1"))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1", "traefik.enable=true"})
|
||||||
|
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
|
||||||
|
|
||||||
|
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1"))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = try.Request(req, 20*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestRetryWithConsulServer(c *check.C) {
|
func (s *ConsulCatalogSuite) TestRetryWithConsulServer(c *check.C) {
|
||||||
//Scale consul to 0 to be able to start traefik before and test retry
|
//Scale consul to 0 to be able to start traefik before and test retry
|
||||||
s.composeProject.Scale(c, "consul", 0)
|
s.composeProject.Scale(c, "consul", 0)
|
||||||
|
|
|
@ -12,6 +12,9 @@ type AddPrefix struct {
|
||||||
|
|
||||||
func (s *AddPrefix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *AddPrefix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
r.URL.Path = s.Prefix + r.URL.Path
|
r.URL.Path = s.Prefix + r.URL.Path
|
||||||
|
if r.URL.RawPath != "" {
|
||||||
|
r.URL.RawPath = s.Prefix + r.URL.RawPath
|
||||||
|
}
|
||||||
r.RequestURI = r.URL.RequestURI()
|
r.RequestURI = r.URL.RequestURI()
|
||||||
s.Handler.ServeHTTP(w, r)
|
s.Handler.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,21 +9,56 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAddPrefix(t *testing.T) {
|
func TestAddPrefix(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
prefix string
|
||||||
|
path string
|
||||||
|
expectedPath string
|
||||||
|
expectedRawPath string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "regular path",
|
||||||
|
prefix: "/a",
|
||||||
|
path: "/b",
|
||||||
|
expectedPath: "/a/b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "raw path is supported",
|
||||||
|
prefix: "/a",
|
||||||
|
path: "/b%2Fc",
|
||||||
|
expectedPath: "/a/b/c",
|
||||||
|
expectedRawPath: "/a/b%2Fc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
path := "/bar"
|
for _, test := range tests {
|
||||||
prefix := "/foo"
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
var expectedPath string
|
var actualPath, actualRawPath, requestURI string
|
||||||
handler := &AddPrefix{
|
handler := &AddPrefix{
|
||||||
Prefix: prefix,
|
Prefix: test.prefix,
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
expectedPath = r.URL.Path
|
actualPath = r.URL.Path
|
||||||
|
actualRawPath = r.URL.RawPath
|
||||||
|
requestURI = r.RequestURI
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+path, nil)
|
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+test.path, nil)
|
||||||
|
|
||||||
handler.ServeHTTP(nil, req)
|
handler.ServeHTTP(nil, req)
|
||||||
|
|
||||||
assert.Equal(t, expectedPath, "/foo/bar", "Unexpected path.")
|
assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
|
||||||
|
assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.")
|
||||||
|
|
||||||
|
expectedURI := test.expectedPath
|
||||||
|
if test.expectedRawPath != "" {
|
||||||
|
// go HTTP uses the raw path when existent in the RequestURI
|
||||||
|
expectedURI = test.expectedRawPath
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedURI, requestURI, "Unexpected request URI.")
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ type Service struct {
|
||||||
Name string
|
Name string
|
||||||
Tags []string
|
Tags []string
|
||||||
Nodes []string
|
Nodes []string
|
||||||
|
Ports []int
|
||||||
}
|
}
|
||||||
|
|
||||||
type serviceUpdate struct {
|
type serviceUpdate struct {
|
||||||
|
@ -185,19 +186,25 @@ func (p *CatalogProvider) watchCatalogServices(stopCh <-chan struct{}, watchCh c
|
||||||
errorCh <- err
|
errorCh <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nodesID := getServiceIds(nodes)
|
nodesID := getServiceIds(nodes)
|
||||||
|
ports := getServicePorts(nodes)
|
||||||
|
|
||||||
if service, ok := current[key]; ok {
|
if service, ok := current[key]; ok {
|
||||||
service.Tags = value
|
service.Tags = value
|
||||||
service.Nodes = nodesID
|
service.Nodes = nodesID
|
||||||
|
service.Ports = ports
|
||||||
} else {
|
} else {
|
||||||
service := Service{
|
service := Service{
|
||||||
Name: key,
|
Name: key,
|
||||||
Tags: value,
|
Tags: value,
|
||||||
Nodes: nodesID,
|
Nodes: nodesID,
|
||||||
|
Ports: ports,
|
||||||
}
|
}
|
||||||
current[key] = service
|
current[key] = service
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A critical note is that the return of a blocking request is no guarantee of a change.
|
// A critical note is that the return of a blocking request is no guarantee of a change.
|
||||||
// It is possible that there was an idempotent write that does not affect the result of the query.
|
// It is possible that there was an idempotent write that does not affect the result of the query.
|
||||||
// Thus it is required to do extra check for changes...
|
// Thus it is required to do extra check for changes...
|
||||||
|
@ -304,8 +311,11 @@ func (p *CatalogProvider) getNodes(index map[string][]string) ([]catalogUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasChanged(current map[string]Service, previous map[string]Service) bool {
|
func hasChanged(current map[string]Service, previous map[string]Service) bool {
|
||||||
|
if len(current) != len(previous) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
addedServiceKeys, removedServiceKeys := getChangedServiceKeys(current, previous)
|
addedServiceKeys, removedServiceKeys := getChangedServiceKeys(current, previous)
|
||||||
return len(removedServiceKeys) > 0 || len(addedServiceKeys) > 0 || hasNodeOrTagsChanged(current, previous)
|
return len(removedServiceKeys) > 0 || len(addedServiceKeys) > 0 || hasServiceChanged(current, previous)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChangedServiceKeys(current map[string]Service, previous map[string]Service) ([]string, []string) {
|
func getChangedServiceKeys(current map[string]Service, previous map[string]Service) ([]string, []string) {
|
||||||
|
@ -318,20 +328,24 @@ func getChangedServiceKeys(current map[string]Service, previous map[string]Servi
|
||||||
return fun.Keys(addedKeys).([]string), fun.Keys(removedKeys).([]string)
|
return fun.Keys(addedKeys).([]string), fun.Keys(removedKeys).([]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasNodeOrTagsChanged(current map[string]Service, previous map[string]Service) bool {
|
func hasServiceChanged(current map[string]Service, previous map[string]Service) bool {
|
||||||
var added []string
|
|
||||||
var removed []string
|
|
||||||
for key, value := range current {
|
for key, value := range current {
|
||||||
if prevValue, ok := previous[key]; ok {
|
if prevValue, ok := previous[key]; ok {
|
||||||
addedNodesKeys, removedNodesKeys := getChangedStringKeys(value.Nodes, prevValue.Nodes)
|
addedNodesKeys, removedNodesKeys := getChangedStringKeys(value.Nodes, prevValue.Nodes)
|
||||||
added = append(added, addedNodesKeys...)
|
if len(addedNodesKeys) > 0 || len(removedNodesKeys) > 0 {
|
||||||
removed = append(removed, removedNodesKeys...)
|
return true
|
||||||
|
}
|
||||||
addedTagsKeys, removedTagsKeys := getChangedStringKeys(value.Tags, prevValue.Tags)
|
addedTagsKeys, removedTagsKeys := getChangedStringKeys(value.Tags, prevValue.Tags)
|
||||||
added = append(added, addedTagsKeys...)
|
if len(addedTagsKeys) > 0 || len(removedTagsKeys) > 0 {
|
||||||
removed = append(removed, removedTagsKeys...)
|
return true
|
||||||
|
}
|
||||||
|
addedPortsKeys, removedPortsKeys := getChangedIntKeys(value.Ports, prevValue.Ports)
|
||||||
|
if len(addedPortsKeys) > 0 || len(removedPortsKeys) > 0 {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return len(added) > 0 || len(removed) > 0
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChangedStringKeys(currState []string, prevState []string) ([]string, []string) {
|
func getChangedStringKeys(currState []string, prevState []string) ([]string, []string) {
|
||||||
|
@ -344,14 +358,32 @@ func getChangedStringKeys(currState []string, prevState []string) ([]string, []s
|
||||||
return fun.Keys(addedKeys).([]string), fun.Keys(removedKeys).([]string)
|
return fun.Keys(addedKeys).([]string), fun.Keys(removedKeys).([]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getChangedIntKeys(currState []int, prevState []int) ([]int, []int) {
|
||||||
|
currKeySet := fun.Set(currState).(map[int]bool)
|
||||||
|
prevKeySet := fun.Set(prevState).(map[int]bool)
|
||||||
|
|
||||||
|
addedKeys := fun.Difference(currKeySet, prevKeySet).(map[int]bool)
|
||||||
|
removedKeys := fun.Difference(prevKeySet, currKeySet).(map[int]bool)
|
||||||
|
|
||||||
|
return fun.Keys(addedKeys).([]int), fun.Keys(removedKeys).([]int)
|
||||||
|
}
|
||||||
|
|
||||||
func getServiceIds(services []*api.CatalogService) []string {
|
func getServiceIds(services []*api.CatalogService) []string {
|
||||||
var serviceIds []string
|
var serviceIds []string
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
serviceIds = append(serviceIds, service.ServiceID)
|
serviceIds = append(serviceIds, service.ID)
|
||||||
}
|
}
|
||||||
return serviceIds
|
return serviceIds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getServicePorts(services []*api.CatalogService) []int {
|
||||||
|
var servicePorts []int
|
||||||
|
for _, service := range services {
|
||||||
|
servicePorts = append(servicePorts, service.ServicePort)
|
||||||
|
}
|
||||||
|
return servicePorts
|
||||||
|
}
|
||||||
|
|
||||||
func (p *CatalogProvider) healthyNodes(service string) (catalogUpdate, error) {
|
func (p *CatalogProvider) healthyNodes(service string) (catalogUpdate, error) {
|
||||||
health := p.client.Health()
|
health := p.client.Health()
|
||||||
opts := &api.QueryOptions{}
|
opts := &api.QueryOptions{}
|
||||||
|
@ -364,7 +396,7 @@ func (p *CatalogProvider) healthyNodes(service string) (catalogUpdate, error) {
|
||||||
return p.nodeFilter(service, node)
|
return p.nodeFilter(service, node)
|
||||||
}, data).([]*api.ServiceEntry)
|
}, data).([]*api.ServiceEntry)
|
||||||
|
|
||||||
//Merge tags of nodes matching constraints, in a single slice.
|
// Merge tags of nodes matching constraints, in a single slice.
|
||||||
tags := fun.Foldl(func(node *api.ServiceEntry, set []string) []string {
|
tags := fun.Foldl(func(node *api.ServiceEntry, set []string) []string {
|
||||||
return fun.Keys(fun.Union(
|
return fun.Keys(fun.Union(
|
||||||
fun.Set(set),
|
fun.Set(set),
|
||||||
|
|
|
@ -28,7 +28,7 @@ func (p *CatalogProvider) buildConfiguration(catalog []catalogUpdate) *types.Con
|
||||||
"getTag": getTag,
|
"getTag": getTag,
|
||||||
"hasTag": hasTag,
|
"hasTag": hasTag,
|
||||||
"getEntryPoints": getEntryPoints,
|
"getEntryPoints": getEntryPoints,
|
||||||
"hasMaxconnAttributes": p.hasMaxconnAttributes,
|
"hasMaxconnAttributes": p.hasMaxConnAttributes,
|
||||||
}
|
}
|
||||||
|
|
||||||
var allNodes []*api.ServiceEntry
|
var allNodes []*api.ServiceEntry
|
||||||
|
@ -69,7 +69,7 @@ func (p *CatalogProvider) setupFrontEndTemplate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CatalogProvider) getFrontendRule(service serviceUpdate) string {
|
func (p *CatalogProvider) getFrontendRule(service serviceUpdate) string {
|
||||||
customFrontendRule := p.getAttribute("frontend.rule", service.Attributes, "")
|
customFrontendRule := p.getAttribute(label.SuffixFrontendRule, service.Attributes, "")
|
||||||
if customFrontendRule == "" {
|
if customFrontendRule == "" {
|
||||||
customFrontendRule = p.FrontEndRule
|
customFrontendRule = p.FrontEndRule
|
||||||
}
|
}
|
||||||
|
@ -102,16 +102,16 @@ func (p *CatalogProvider) getFrontendRule(service serviceUpdate) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CatalogProvider) getBasicAuth(tags []string) []string {
|
func (p *CatalogProvider) getBasicAuth(tags []string) []string {
|
||||||
list := p.getAttribute("frontend.auth.basic", tags, "")
|
list := p.getAttribute(label.SuffixFrontendAuthBasic, tags, "")
|
||||||
if list != "" {
|
if list != "" {
|
||||||
return strings.Split(list, ",")
|
return strings.Split(list, ",")
|
||||||
}
|
}
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CatalogProvider) hasMaxconnAttributes(attributes []string) bool {
|
func (p *CatalogProvider) hasMaxConnAttributes(attributes []string) bool {
|
||||||
amount := p.getAttribute("backend.maxconn.amount", attributes, "")
|
amount := p.getAttribute(label.SuffixBackendMaxConnAmount, attributes, "")
|
||||||
extractorfunc := p.getAttribute("backend.maxconn.extractorfunc", attributes, "")
|
extractorfunc := p.getAttribute(label.SuffixBackendMaxConnExtractorFunc, attributes, "")
|
||||||
return amount != "" && extractorfunc != ""
|
return amount != "" && extractorfunc != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1166,7 +1166,7 @@ func TestHasNodeOrTagschanged(t *testing.T) {
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Change detected con tags",
|
desc: "Change detected on tags",
|
||||||
current: map[string]Service{
|
current: map[string]Service{
|
||||||
"foo-service": {
|
"foo-service": {
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -1183,6 +1183,66 @@ func TestHasNodeOrTagschanged(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "Change detected on ports",
|
||||||
|
current: map[string]Service{
|
||||||
|
"foo-service": {
|
||||||
|
Name: "foo",
|
||||||
|
Nodes: []string{"node1"},
|
||||||
|
Tags: []string{"foo=bar"},
|
||||||
|
Ports: []int{80},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
previous: map[string]Service{
|
||||||
|
"foo-service": {
|
||||||
|
Name: "foo",
|
||||||
|
Nodes: []string{"node1"},
|
||||||
|
Tags: []string{"foo"},
|
||||||
|
Ports: []int{81},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Change detected on ports",
|
||||||
|
current: map[string]Service{
|
||||||
|
"foo-service": {
|
||||||
|
Name: "foo",
|
||||||
|
Nodes: []string{"node1"},
|
||||||
|
Tags: []string{"foo=bar"},
|
||||||
|
Ports: []int{80},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
previous: map[string]Service{
|
||||||
|
"foo-service": {
|
||||||
|
Name: "foo",
|
||||||
|
Nodes: []string{"node1"},
|
||||||
|
Tags: []string{"foo"},
|
||||||
|
Ports: []int{81, 82},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "No Change detected",
|
||||||
|
current: map[string]Service{
|
||||||
|
"foo-service": {
|
||||||
|
Name: "foo",
|
||||||
|
Nodes: []string{"node1"},
|
||||||
|
Tags: []string{"foo"},
|
||||||
|
Ports: []int{80},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
previous: map[string]Service{
|
||||||
|
"foo-service": {
|
||||||
|
Name: "foo",
|
||||||
|
Nodes: []string{"node1"},
|
||||||
|
Tags: []string{"foo"},
|
||||||
|
Ports: []int{80},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
@ -1190,7 +1250,7 @@ func TestHasNodeOrTagschanged(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
actual := hasNodeOrTagsChanged(test.current, test.previous)
|
actual := hasServiceChanged(test.current, test.previous)
|
||||||
assert.Equal(t, test.expected, actual)
|
assert.Equal(t, test.expected, actual)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,11 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
||||||
"getEntryPoints": getFuncSliceStringLabel(label.TraefikFrontendEntryPoints),
|
"getEntryPoints": getFuncSliceStringLabel(label.TraefikFrontendEntryPoints),
|
||||||
"getBasicAuth": getFuncSliceStringLabel(label.TraefikFrontendAuthBasic),
|
"getBasicAuth": getFuncSliceStringLabel(label.TraefikFrontendAuthBasic),
|
||||||
"getFrontendRule": p.getFrontendRule,
|
"getFrontendRule": p.getFrontendRule,
|
||||||
"getRedirect": getFuncStringLabel(label.TraefikFrontendRedirect, label.DefaultFrontendRedirect),
|
"hasRedirect": hasRedirect,
|
||||||
|
"getRedirectEntryPoint": getFuncStringLabel(label.TraefikFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint),
|
||||||
|
"getRedirectRegex": getFuncStringLabel(label.TraefikFrontendRedirectRegex, ""),
|
||||||
|
"getRedirectReplacement": getFuncStringLabel(label.TraefikFrontendRedirectReplacement, ""),
|
||||||
|
|
||||||
"hasCircuitBreakerLabel": hasFunc(label.TraefikBackendCircuitBreakerExpression),
|
"hasCircuitBreakerLabel": hasFunc(label.TraefikBackendCircuitBreakerExpression),
|
||||||
"getCircuitBreakerExpression": getFuncStringLabel(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
"getCircuitBreakerExpression": getFuncStringLabel(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
||||||
"hasLoadBalancerLabel": hasLoadBalancerLabel,
|
"hasLoadBalancerLabel": hasLoadBalancerLabel,
|
||||||
|
@ -36,8 +40,6 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
||||||
"hasStickinessLabel": hasFunc(label.TraefikBackendLoadBalancerStickiness),
|
"hasStickinessLabel": hasFunc(label.TraefikBackendLoadBalancerStickiness),
|
||||||
"getStickinessCookieName": getFuncStringLabel(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
"getStickinessCookieName": getFuncStringLabel(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
||||||
"isBackendLBSwarm": isBackendLBSwarm, // FIXME DEAD ?
|
"isBackendLBSwarm": isBackendLBSwarm, // FIXME DEAD ?
|
||||||
"getServiceBackend": getServiceBackend,
|
|
||||||
"getServiceRedirect": getFuncServiceStringLabel(label.SuffixFrontendRedirect, label.DefaultFrontendRedirect),
|
|
||||||
"getWhitelistSourceRange": getFuncSliceStringLabel(label.TraefikFrontendWhitelistSourceRange),
|
"getWhitelistSourceRange": getFuncSliceStringLabel(label.TraefikFrontendWhitelistSourceRange),
|
||||||
|
|
||||||
"hasRequestHeaders": hasFunc(label.TraefikFrontendRequestHeaders),
|
"hasRequestHeaders": hasFunc(label.TraefikFrontendRequestHeaders),
|
||||||
|
@ -82,6 +84,7 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
||||||
"getIsDevelopmentHeaders": getFuncBoolLabel(label.TraefikFrontendIsDevelopment, false),
|
"getIsDevelopmentHeaders": getFuncBoolLabel(label.TraefikFrontendIsDevelopment, false),
|
||||||
|
|
||||||
"hasServices": hasServices,
|
"hasServices": hasServices,
|
||||||
|
"getServiceBackend": getServiceBackend,
|
||||||
"getServiceNames": getServiceNames,
|
"getServiceNames": getServiceNames,
|
||||||
"getServicePort": getServicePort,
|
"getServicePort": getServicePort,
|
||||||
"hasServiceRequestHeaders": hasFuncServiceLabel(label.SuffixFrontendRequestHeaders),
|
"hasServiceRequestHeaders": hasFuncServiceLabel(label.SuffixFrontendRequestHeaders),
|
||||||
|
@ -95,6 +98,10 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
||||||
"getServiceFrontendRule": p.getServiceFrontendRule,
|
"getServiceFrontendRule": p.getServiceFrontendRule,
|
||||||
"getServicePassHostHeader": getFuncServiceStringLabel(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeader),
|
"getServicePassHostHeader": getFuncServiceStringLabel(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||||
"getServicePriority": getFuncServiceStringLabel(label.SuffixFrontendPriority, label.DefaultFrontendPriority),
|
"getServicePriority": getFuncServiceStringLabel(label.SuffixFrontendPriority, label.DefaultFrontendPriority),
|
||||||
|
"hasServiceRedirect": hasServiceRedirect,
|
||||||
|
"getServiceRedirectEntryPoint": getFuncServiceStringLabel(label.SuffixFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint),
|
||||||
|
"getServiceRedirectReplacement": getFuncServiceStringLabel(label.SuffixFrontendRedirectReplacement, ""),
|
||||||
|
"getServiceRedirectRegex": getFuncServiceStringLabel(label.SuffixFrontendRedirectRegex, ""),
|
||||||
}
|
}
|
||||||
// filter containers
|
// filter containers
|
||||||
filteredContainers := fun.Filter(func(container dockerData) bool {
|
filteredContainers := fun.Filter(func(container dockerData) bool {
|
||||||
|
@ -125,11 +132,11 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
||||||
Servers map[string][]dockerData
|
Servers map[string][]dockerData
|
||||||
Domain string
|
Domain string
|
||||||
}{
|
}{
|
||||||
filteredContainers,
|
Containers: filteredContainers,
|
||||||
frontends,
|
Frontends: frontends,
|
||||||
backends,
|
Backends: backends,
|
||||||
servers,
|
Servers: servers,
|
||||||
p.Domain,
|
Domain: p.Domain,
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration, err := p.GetConfiguration("templates/docker.tmpl", DockerFuncMap, templateObjects)
|
configuration, err := p.GetConfiguration("templates/docker.tmpl", DockerFuncMap, templateObjects)
|
||||||
|
|
|
@ -167,6 +167,11 @@ func isBackendLBSwarm(container dockerData) bool {
|
||||||
return label.GetBoolValue(container.Labels, labelBackendLoadBalancerSwarm, false)
|
return label.GetBoolValue(container.Labels, labelBackendLoadBalancerSwarm, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasRedirect(container dockerData) bool {
|
||||||
|
return label.Has(container.Labels, label.TraefikFrontendRedirectEntryPoint) ||
|
||||||
|
label.Has(container.Labels, label.TraefikFrontendRedirectReplacement) && label.Has(container.Labels, label.TraefikFrontendRedirectRegex)
|
||||||
|
}
|
||||||
|
|
||||||
// Label functions
|
// Label functions
|
||||||
|
|
||||||
func getFuncInt64Label(labelName string, defaultValue int64) func(container dockerData) int64 {
|
func getFuncInt64Label(labelName string, defaultValue int64) func(container dockerData) int64 {
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
docker "github.com/docker/docker/api/types"
|
docker "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDockerLoadDockerConfig(t *testing.T) {
|
func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
|
@ -39,7 +41,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",
|
||||||
|
@ -67,7 +68,7 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
label.TraefikBackend: "foobar",
|
label.TraefikBackend: "foobar",
|
||||||
label.TraefikFrontendEntryPoints: "http,https",
|
label.TraefikFrontendEntryPoints: "http,https",
|
||||||
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.TraefikFrontendRedirect: "https",
|
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||||
}),
|
}),
|
||||||
ports(nat.PortMap{
|
ports(nat.PortMap{
|
||||||
"80/tcp": {},
|
"80/tcp": {},
|
||||||
|
@ -91,7 +92,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",
|
||||||
|
@ -103,7 +106,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",
|
||||||
|
@ -151,7 +153,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",
|
||||||
|
@ -197,13 +198,10 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
ExposedByDefault: true,
|
ExposedByDefault: true,
|
||||||
}
|
}
|
||||||
actualConfig := provider.buildConfiguration(dockerDataList)
|
actualConfig := provider.buildConfiguration(dockerDataList)
|
||||||
// Compare backends
|
require.NotNil(t, actualConfig, "actualConfig")
|
||||||
if !reflect.DeepEqual(actualConfig.Backends, test.expectedBackends) {
|
|
||||||
t.Errorf("expected %#v, got %#v", test.expectedBackends, actualConfig.Backends)
|
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||||
}
|
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
||||||
if !reflect.DeepEqual(actualConfig.Frontends, test.expectedFrontends) {
|
|
||||||
t.Errorf("expected %#v, got %#v", test.expectedFrontends, actualConfig.Frontends)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ 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/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSwarmGetFrontendName(t *testing.T) {
|
func TestSwarmGetFrontendName(t *testing.T) {
|
||||||
|
@ -416,7 +418,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",
|
||||||
|
@ -451,7 +452,7 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
|
||||||
label.TraefikBackend: "foobar",
|
label.TraefikBackend: "foobar",
|
||||||
label.TraefikFrontendEntryPoints: "http,https",
|
label.TraefikFrontendEntryPoints: "http,https",
|
||||||
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.TraefikFrontendRedirect: "https",
|
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||||
}),
|
}),
|
||||||
withEndpointSpec(modeVIP),
|
withEndpointSpec(modeVIP),
|
||||||
withEndpoint(virtualIP("1", "127.0.0.1/24")),
|
withEndpoint(virtualIP("1", "127.0.0.1/24")),
|
||||||
|
@ -472,7 +473,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",
|
||||||
|
@ -484,7 +487,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",
|
||||||
|
@ -531,14 +533,12 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
|
||||||
ExposedByDefault: true,
|
ExposedByDefault: true,
|
||||||
SwarmMode: true,
|
SwarmMode: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
actualConfig := provider.buildConfiguration(dockerDataList)
|
actualConfig := provider.buildConfiguration(dockerDataList)
|
||||||
// Compare backends
|
require.NotNil(t, actualConfig, "actualConfig")
|
||||||
if !reflect.DeepEqual(actualConfig.Backends, test.expectedBackends) {
|
|
||||||
t.Errorf("expected %#v, got %#v", test.expectedBackends, actualConfig.Backends)
|
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||||
}
|
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
||||||
if !reflect.DeepEqual(actualConfig.Frontends, test.expectedFrontends) {
|
|
||||||
t.Errorf("expected %#v, got %#v", test.expectedFrontends, actualConfig.Frontends)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,16 @@ func getServicePort(container dockerData, serviceName string) string {
|
||||||
return getPort(container)
|
return getPort(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasServiceRedirect(container dockerData, serviceName string) bool {
|
||||||
|
serviceLabels := getServiceLabels(container, serviceName)
|
||||||
|
if len(serviceLabels) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return label.Has(serviceLabels, label.SuffixFrontendRedirectEntryPoint) ||
|
||||||
|
label.Has(serviceLabels, label.SuffixFrontendRedirectRegex) && label.Has(serviceLabels, label.SuffixFrontendRedirectReplacement)
|
||||||
|
}
|
||||||
|
|
||||||
// Service label functions
|
// Service label functions
|
||||||
|
|
||||||
func getFuncServiceMapLabel(labelSuffix string) func(container dockerData, serviceName string) map[string]string {
|
func getFuncServiceMapLabel(labelSuffix string) func(container dockerData, serviceName string) map[string]string {
|
||||||
|
|
|
@ -121,7 +121,7 @@ func TestDockerGetFuncServiceStringLabel(t *testing.T) {
|
||||||
|
|
||||||
actual := getFuncServiceStringLabel(test.suffixLabel, test.defaultValue)(dData, "myservice")
|
actual := getFuncServiceStringLabel(test.suffixLabel, test.defaultValue)(dData, "myservice")
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
t.Fatalf("got %q, expected %q", actual, test.expected)
|
t.Errorf("got %q, expected %q", actual, test.expected)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ func TestDockerGetFuncServiceSliceStringLabel(t *testing.T) {
|
||||||
actual := getFuncServiceSliceStringLabel(test.suffixLabel)(dData, "myservice")
|
actual := getFuncServiceSliceStringLabel(test.suffixLabel)(dData, "myservice")
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual, test.expected) {
|
if !reflect.DeepEqual(actual, test.expected) {
|
||||||
t.Fatalf("for container %q: got %q, expected %q", dData.Name, actual, test.expected)
|
t.Errorf("for container %q: got %q, expected %q", dData.Name, actual, test.expected)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -9,6 +8,8 @@ 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"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDockerGetServicePort(t *testing.T) {
|
func TestDockerGetServicePort(t *testing.T) {
|
||||||
|
@ -41,7 +42,7 @@ func TestDockerGetServicePort(t *testing.T) {
|
||||||
dData := parseContainer(test.container)
|
dData := parseContainer(test.container)
|
||||||
actual := getServicePort(dData, "myservice")
|
actual := getServicePort(dData, "myservice")
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
t.Fatalf("expected %q, got %q", test.expected, actual)
|
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -79,7 +80,7 @@ func TestDockerGetServiceFrontendRule(t *testing.T) {
|
||||||
dData := parseContainer(test.container)
|
dData := parseContainer(test.container)
|
||||||
actual := provider.getServiceFrontendRule(dData, "myservice")
|
actual := provider.getServiceFrontendRule(dData, "myservice")
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
t.Fatalf("expected %q, got %q", test.expected, actual)
|
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -115,7 +116,7 @@ func TestDockerGetServiceBackend(t *testing.T) {
|
||||||
dData := parseContainer(test.container)
|
dData := parseContainer(test.container)
|
||||||
actual := getServiceBackend(dData, "myservice")
|
actual := getServiceBackend(dData, "myservice")
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
t.Fatalf("expected %q, got %q", test.expected, actual)
|
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -140,7 +141,7 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
"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": {},
|
||||||
|
@ -154,7 +155,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",
|
||||||
|
@ -188,7 +191,7 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
"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": {},
|
||||||
|
@ -215,7 +218,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",
|
||||||
|
@ -227,7 +232,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,13 +278,10 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
actualConfig := provider.buildConfiguration(dockerDataList)
|
actualConfig := provider.buildConfiguration(dockerDataList)
|
||||||
// Compare backends
|
require.NotNil(t, actualConfig, "actualConfig")
|
||||||
if !reflect.DeepEqual(actualConfig.Backends, test.expectedBackends) {
|
|
||||||
t.Fatalf("expected %#v, got %#v", test.expectedBackends, actualConfig.Backends)
|
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||||
}
|
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
||||||
if !reflect.DeepEqual(actualConfig.Frontends, test.expectedFrontends) {
|
|
||||||
t.Fatalf("expected %#v, got %#v", test.expectedFrontends, actualConfig.Frontends)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,9 +152,22 @@ func priority(value int) func(*types.Frontend) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func redirect(value string) func(*types.Frontend) {
|
func redirectEntryPoint(name string) func(*types.Frontend) {
|
||||||
return func(f *types.Frontend) {
|
return func(f *types.Frontend) {
|
||||||
f.Redirect = value
|
if f.Redirect == nil {
|
||||||
|
f.Redirect = &types.Redirect{}
|
||||||
|
}
|
||||||
|
f.Redirect.EntryPoint = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func redirectRegex(regex, replacement string) func(*types.Frontend) {
|
||||||
|
return func(f *types.Frontend) {
|
||||||
|
if f.Redirect == nil {
|
||||||
|
f.Redirect = &types.Redirect{}
|
||||||
|
}
|
||||||
|
f.Redirect.Regex = regex
|
||||||
|
f.Redirect.Replacement = replacement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,8 +203,6 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
||||||
|
|
||||||
whitelistSourceRange := label.GetSliceStringValue(i.Annotations, annotationKubernetesWhitelistSourceRange)
|
whitelistSourceRange := label.GetSliceStringValue(i.Annotations, annotationKubernetesWhitelistSourceRange)
|
||||||
|
|
||||||
entryPointRedirect := i.Annotations[label.TraefikFrontendRedirect]
|
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -245,7 +243,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,
|
||||||
}
|
}
|
||||||
|
@ -470,3 +468,23 @@ func equalPorts(servicePort v1.ServicePort, ingressPort intstr.IntOrString) bool
|
||||||
func shouldProcessIngress(ingressClass string) bool {
|
func shouldProcessIngress(ingressClass string) bool {
|
||||||
return ingressClass == "" || ingressClass == "traefik"
|
return ingressClass == "" || ingressClass == "traefik"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getFrontendRedirect(i *v1beta1.Ingress) *types.Redirect {
|
||||||
|
frontendRedirectEntryPoint, ok := i.Annotations[label.TraefikFrontendRedirectEntryPoint]
|
||||||
|
frep := ok && len(frontendRedirectEntryPoint) > 0
|
||||||
|
|
||||||
|
frontendRedirectRegex, ok := i.Annotations[label.TraefikFrontendRedirectRegex]
|
||||||
|
frrg := ok && len(frontendRedirectRegex) > 0
|
||||||
|
|
||||||
|
frontendRedirectReplacement, ok := i.Annotations[label.TraefikFrontendRedirectReplacement]
|
||||||
|
frrp := ok && len(frontendRedirectReplacement) > 0
|
||||||
|
|
||||||
|
if frep || frrg && frrp {
|
||||||
|
return &types.Redirect{
|
||||||
|
EntryPoint: frontendRedirectEntryPoint,
|
||||||
|
Regex: frontendRedirectRegex,
|
||||||
|
Replacement: frontendRedirectReplacement,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -608,7 +608,7 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
buildIngress(
|
buildIngress(
|
||||||
iNamespace("testing"),
|
iNamespace("testing"),
|
||||||
iAnnotation(annotationKubernetesIngressClass, "traefik"),
|
iAnnotation(annotationKubernetesIngressClass, "traefik"),
|
||||||
iAnnotation(label.TraefikFrontendRedirect, "https"),
|
iAnnotation(label.TraefikFrontendRedirectEntryPoint, "https"),
|
||||||
iRules(
|
iRules(
|
||||||
iRule(
|
iRule(
|
||||||
iHost("redirect"),
|
iHost("redirect"),
|
||||||
|
@ -752,7 +752,7 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
),
|
),
|
||||||
frontend("redirect/https",
|
frontend("redirect/https",
|
||||||
passHostHeader(),
|
passHostHeader(),
|
||||||
redirect("https"),
|
redirectEntryPoint("https"),
|
||||||
routes(
|
routes(
|
||||||
route("/https", "PathPrefix:/https"),
|
route("/https", "PathPrefix:/https"),
|
||||||
route("redirect", "Host:redirect")),
|
route("redirect", "Host:redirect")),
|
||||||
|
@ -1182,6 +1182,7 @@ func TestBasicAuthInTemplate(t *testing.T) {
|
||||||
require.NoError(t, err, "error loading ingresses")
|
require.NoError(t, err, "error loading ingresses")
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -22,7 +22,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"
|
||||||
DefaultBackendLoadbalancerStickinessCookieName = ""
|
DefaultBackendLoadbalancerStickinessCookieName = ""
|
||||||
|
|
|
@ -48,7 +48,9 @@ const (
|
||||||
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
|
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
|
||||||
SuffixFrontendPassTLSCert = "frontend.passTLSCert"
|
SuffixFrontendPassTLSCert = "frontend.passTLSCert"
|
||||||
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"
|
||||||
SuffixFrontendRuleType = "frontend.rule.type"
|
SuffixFrontendRuleType = "frontend.rule.type"
|
||||||
SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange"
|
SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange"
|
||||||
|
@ -79,7 +81,9 @@ const (
|
||||||
TraefikFrontendPriority = Prefix + SuffixFrontendPriority
|
TraefikFrontendPriority = Prefix + SuffixFrontendPriority
|
||||||
TraefikFrontendRule = Prefix + SuffixFrontendRule
|
TraefikFrontendRule = Prefix + SuffixFrontendRule
|
||||||
TraefikFrontendRuleType = Prefix + SuffixFrontendRuleType
|
TraefikFrontendRuleType = Prefix + SuffixFrontendRuleType
|
||||||
TraefikFrontendRedirect = Prefix + SuffixFrontendRedirect
|
TraefikFrontendRedirectEntryPoint = Prefix + SuffixFrontendRedirectEntryPoint
|
||||||
|
TraefikFrontendRedirectRegex = Prefix + SuffixFrontendRedirectRegex
|
||||||
|
TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement
|
||||||
TraefikFrontendValue = Prefix + SuffixFrontendValue
|
TraefikFrontendValue = Prefix + SuffixFrontendValue
|
||||||
TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange
|
TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange
|
||||||
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
|
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
|
||||||
|
|
|
@ -27,15 +27,18 @@ func (p *Provider) buildConfiguration(services []rancherData) *types.Configurati
|
||||||
"getFrontendRule": p.getFrontendRule,
|
"getFrontendRule": p.getFrontendRule,
|
||||||
"hasCircuitBreakerLabel": hasFunc(label.TraefikBackendCircuitBreakerExpression),
|
"hasCircuitBreakerLabel": hasFunc(label.TraefikBackendCircuitBreakerExpression),
|
||||||
"getCircuitBreakerExpression": getFuncString(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
"getCircuitBreakerExpression": getFuncString(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
||||||
"hasLoadBalancerLabel": hasLoadBalancerLabel, // OK
|
"hasLoadBalancerLabel": hasLoadBalancerLabel,
|
||||||
"getLoadBalancerMethod": getFuncString(label.TraefikFrontendRedirect, label.DefaultBackendLoadBalancerMethod),
|
"getLoadBalancerMethod": getFuncString(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod),
|
||||||
"hasMaxConnLabels": hasMaxConnLabels, // OK
|
"hasMaxConnLabels": hasMaxConnLabels,
|
||||||
"getMaxConnAmount": getFuncInt64(label.TraefikBackendMaxConnAmount, math.MaxInt64),
|
"getMaxConnAmount": getFuncInt64(label.TraefikBackendMaxConnAmount, math.MaxInt64),
|
||||||
"getMaxConnExtractorFunc": getFuncString(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc),
|
"getMaxConnExtractorFunc": getFuncString(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc),
|
||||||
"getSticky": getSticky, // deprecated
|
"getSticky": getSticky, // deprecated
|
||||||
"hasStickinessLabel": hasFunc(label.TraefikBackendLoadBalancerStickiness),
|
"hasStickinessLabel": hasFunc(label.TraefikBackendLoadBalancerStickiness),
|
||||||
"getStickinessCookieName": getFuncString(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
"getStickinessCookieName": getFuncString(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
||||||
"getRedirect": getFuncString(label.TraefikFrontendRedirect, label.DefaultFrontendRedirect),
|
"hasRedirect": hasRedirect,
|
||||||
|
"getRedirectEntryPoint": getFuncString(label.TraefikFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint),
|
||||||
|
"getRedirectRegex": getFuncString(label.TraefikFrontendRedirectRegex, ""),
|
||||||
|
"getRedirectReplacement": getFuncString(label.TraefikFrontendRedirectReplacement, ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter services
|
// filter services
|
||||||
|
@ -116,7 +119,8 @@ func (p *Provider) getFrontendName(service rancherData) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Deprecated
|
// TODO: Deprecated
|
||||||
// Deprecated replaced by Stickiness
|
// replaced by Stickiness
|
||||||
|
// Deprecated
|
||||||
func getSticky(service rancherData) string {
|
func getSticky(service rancherData) string {
|
||||||
if label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky) {
|
if label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky) {
|
||||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
||||||
|
@ -143,6 +147,11 @@ func getBackend(service rancherData) string {
|
||||||
return provider.Normalize(backend)
|
return provider.Normalize(backend)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasRedirect(service rancherData) bool {
|
||||||
|
return label.Has(service.Labels, label.TraefikFrontendRedirectEntryPoint) ||
|
||||||
|
label.Has(service.Labels, label.TraefikFrontendRedirectRegex) && label.Has(service.Labels, label.TraefikFrontendRedirectReplacement)
|
||||||
|
}
|
||||||
|
|
||||||
// Label functions
|
// Label functions
|
||||||
|
|
||||||
func getFuncString(labelName string, defaultValue string) func(service rancherData) string {
|
func getFuncString(labelName string, defaultValue string) func(service rancherData) string {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/containous/traefik/provider/label"
|
"github.com/containous/traefik/provider/label"
|
||||||
"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) {
|
||||||
|
@ -363,7 +364,7 @@ func TestProviderLoadRancherConfig(t *testing.T) {
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
label.TraefikPort: "80",
|
label.TraefikPort: "80",
|
||||||
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.TraefikFrontendRedirect: "https",
|
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||||
},
|
},
|
||||||
Health: "healthy",
|
Health: "healthy",
|
||||||
Containers: []string{"127.0.0.1"},
|
Containers: []string{"127.0.0.1"},
|
||||||
|
@ -376,7 +377,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",
|
||||||
|
@ -405,9 +408,77 @@ func TestProviderLoadRancherConfig(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
actualConfig := provider.buildConfiguration(test.services)
|
actualConfig := provider.buildConfiguration(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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{
|
||||||
|
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "with Redirect regex label",
|
||||||
|
service: rancherData{
|
||||||
|
Name: "test-service",
|
||||||
|
Labels: map[string]string{
|
||||||
|
label.TraefikFrontendRedirectRegex: `(.+)`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "with Redirect replacement label",
|
||||||
|
service: rancherData{
|
||||||
|
Name: "test-service",
|
||||||
|
Labels: map[string]string{
|
||||||
|
label.TraefikFrontendRedirectReplacement: "$1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "with Redirect regex & replacement labels",
|
||||||
|
service: rancherData{
|
||||||
|
Name: "test-service",
|
||||||
|
Labels: map[string]string{
|
||||||
|
label.TraefikFrontendRedirectRegex: `(.+)`,
|
||||||
|
label.TraefikFrontendRedirectReplacement: "$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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -49,6 +49,10 @@ import (
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultRedirectRegex = `^(?:https?:\/\/)?([\w\._-]+)(?::\d+)?(.*)$`
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
httpServerLogger = stdlog.New(log.WriterLevel(logrus.DebugLevel), "", 0)
|
httpServerLogger = stdlog.New(log.WriterLevel(logrus.DebugLevel), "", 0)
|
||||||
)
|
)
|
||||||
|
@ -944,7 +948,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.loadEntryPointConfig(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
|
||||||
|
@ -1125,28 +1129,19 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
|
||||||
|
|
||||||
ipWhitelistMiddleware, err := configureIPWhitelistMiddleware(frontend.WhitelistSourceRange)
|
ipWhitelistMiddleware, err := configureIPWhitelistMiddleware(frontend.WhitelistSourceRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error creating IP Whitelister: %s", err)
|
log.Errorf("Error creating IP Whitelister: %s", err)
|
||||||
} else if ipWhitelistMiddleware != nil {
|
} else if ipWhitelistMiddleware != nil {
|
||||||
n.Use(ipWhitelistMiddleware)
|
n.Use(ipWhitelistMiddleware)
|
||||||
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 {
|
||||||
proto := "http"
|
rewrite, err := s.buildRedirectHandler(entryPointName, frontend.Redirect)
|
||||||
if s.globalConfiguration.EntryPoints[frontend.Redirect].TLS != nil {
|
|
||||||
proto = "https"
|
|
||||||
}
|
|
||||||
|
|
||||||
regex, replacement, err := s.buildRedirect(proto, entryPoint)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error creating Frontend Redirect: %v", err)
|
log.Errorf("Error creating Frontend Redirect: %v", err)
|
||||||
}
|
|
||||||
rewrite, err := middlewares.NewRewrite(regex, replacement, true)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error creating Frontend Redirect: %v", err)
|
|
||||||
}
|
}
|
||||||
n.Use(rewrite)
|
n.Use(rewrite)
|
||||||
log.Debugf("Creating frontend %s redirect to %s", frontendName, proto)
|
log.Debugf("Frontend %s redirect created", frontendName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(frontend.BasicAuth) > 0 {
|
if len(frontend.BasicAuth) > 0 {
|
||||||
|
@ -1296,41 +1291,57 @@ func (s *Server) wireFrontendBackend(serverRoute *serverRoute, handler http.Hand
|
||||||
serverRoute.route.Handler(handler)
|
serverRoute.route.Handler(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) loadEntryPointConfig(entryPointName string, entryPoint *configuration.EntryPoint) (negroni.Handler, error) {
|
func (s *Server) buildRedirectHandler(srcEntryPointName string, redirect *types.Redirect) (*middlewares.Rewrite, error) {
|
||||||
regex := entryPoint.Redirect.Regex
|
// entry point redirect
|
||||||
replacement := entryPoint.Redirect.Replacement
|
if len(redirect.EntryPoint) > 0 {
|
||||||
var err error
|
return s.buildEntryPointRedirect(srcEntryPointName, redirect.EntryPoint)
|
||||||
if len(entryPoint.Redirect.EntryPoint) > 0 {
|
|
||||||
var protocol = "http"
|
|
||||||
if s.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].TLS != nil {
|
|
||||||
protocol = "https"
|
|
||||||
}
|
}
|
||||||
regex, replacement, err = s.buildRedirect(protocol, entryPoint)
|
|
||||||
|
// regex redirect
|
||||||
|
rewrite, err := middlewares.NewRewrite(redirect.Regex, redirect.Replacement, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
log.Debugf("Creating entryPoint redirect %s -> %s -> %s", srcEntryPointName, redirect.Regex, redirect.Replacement)
|
||||||
rewrite, err := middlewares.NewRewrite(regex, replacement, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", entryPointName, entryPoint.Redirect.EntryPoint, regex, replacement)
|
|
||||||
|
|
||||||
return rewrite, nil
|
return rewrite, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) buildRedirect(protocol string, entryPoint *configuration.EntryPoint) (string, string, error) {
|
func (s *Server) buildEntryPointRedirect(srcEntryPointName string, redirectEntryPoint string) (*middlewares.Rewrite, error) {
|
||||||
regex := `^(?:https?:\/\/)?([\w\._-]+)(?::\d+)?(.*)$`
|
regex, replacement, err := s.buildRedirect(redirectEntryPoint)
|
||||||
if s.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint] == nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("unknown target entrypoint %q", entryPoint.Redirect.EntryPoint)
|
return nil, err
|
||||||
}
|
}
|
||||||
r, _ := regexp.Compile(`(:\d+)`)
|
|
||||||
match := r.FindStringSubmatch(s.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].Address)
|
rewrite, err := middlewares.NewRewrite(regex, replacement, true)
|
||||||
|
if err != nil {
|
||||||
|
// Impossible case because error is always nil
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", srcEntryPointName, redirectEntryPoint, regex, replacement)
|
||||||
|
|
||||||
|
return rewrite, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) buildRedirect(entryPointName string) (string, string, error) {
|
||||||
|
entryPoint := s.globalConfiguration.EntryPoints[entryPointName]
|
||||||
|
if entryPoint == nil {
|
||||||
|
return "", "", fmt.Errorf("unknown target entrypoint %q", entryPointName)
|
||||||
|
}
|
||||||
|
|
||||||
|
exp := regexp.MustCompile(`(:\d+)`)
|
||||||
|
match := exp.FindStringSubmatch(entryPoint.Address)
|
||||||
if len(match) == 0 {
|
if len(match) == 0 {
|
||||||
return "", "", fmt.Errorf("bad Address format %q", s.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].Address)
|
return "", "", fmt.Errorf("bad Address format %q", entryPoint.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var protocol = "http"
|
||||||
|
if s.globalConfiguration.EntryPoints[entryPointName].TLS != nil {
|
||||||
|
protocol = "https"
|
||||||
|
}
|
||||||
|
|
||||||
replacement := protocol + "://$1" + match[0] + "$2"
|
replacement := protocol + "://$1" + match[0] + "$2"
|
||||||
return regex, replacement, nil
|
return defaultRedirectRegex, replacement, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) buildDefaultHTTPRouter() *mux.Router {
|
func (s *Server) buildDefaultHTTPRouter() *mux.Router {
|
||||||
|
|
|
@ -924,55 +924,209 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerLoadConfigBuildRedirect(t *testing.T) {
|
func TestBuildEntryPointRedirect(t *testing.T) {
|
||||||
testCases := []struct {
|
srv := Server{
|
||||||
desc string
|
|
||||||
replacementProtocol string
|
|
||||||
globalConfiguration configuration.GlobalConfiguration
|
|
||||||
originEntryPointName string
|
|
||||||
expectedReplacement string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "Redirect endpoint http to https with HTTPS protocol",
|
|
||||||
replacementProtocol: "https",
|
|
||||||
originEntryPointName: "http",
|
|
||||||
globalConfiguration: configuration.GlobalConfiguration{
|
globalConfiguration: configuration.GlobalConfiguration{
|
||||||
EntryPoints: configuration.EntryPoints{
|
EntryPoints: configuration.EntryPoints{
|
||||||
"http": &configuration.EntryPoint{
|
"http": &configuration.EntryPoint{Address: ":80"},
|
||||||
|
"https": &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
srcEntryPointName string
|
||||||
|
url string
|
||||||
|
entryPoint *configuration.EntryPoint
|
||||||
|
redirect *types.Redirect
|
||||||
|
expectedURL string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "redirect regex",
|
||||||
|
srcEntryPointName: "http",
|
||||||
|
url: "http://foo.com",
|
||||||
|
redirect: &types.Redirect{
|
||||||
|
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
||||||
|
Replacement: "https://$1{{\"bar\"}}$2",
|
||||||
|
},
|
||||||
|
entryPoint: &configuration.EntryPoint{
|
||||||
Address: ":80",
|
Address: ":80",
|
||||||
Redirect: &configuration.Redirect{
|
Redirect: &types.Redirect{
|
||||||
|
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
||||||
|
Replacement: "https://$1{{\"bar\"}}$2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedURL: "https://foobar.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "redirect entry point",
|
||||||
|
srcEntryPointName: "http",
|
||||||
|
url: "http://foo:80",
|
||||||
|
redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
},
|
||||||
|
entryPoint: &configuration.EntryPoint{
|
||||||
|
Address: ":80",
|
||||||
|
Redirect: &types.Redirect{
|
||||||
EntryPoint: "https",
|
EntryPoint: "https",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"https": &configuration.EntryPoint{
|
expectedURL: "https://foo:443",
|
||||||
Address: ":443",
|
},
|
||||||
TLS: &tls.TLS{},
|
{
|
||||||
|
desc: "redirect entry point with regex (ignored)",
|
||||||
|
srcEntryPointName: "http",
|
||||||
|
url: "http://foo.com:80",
|
||||||
|
redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
||||||
|
Replacement: "https://$1{{\"bar\"}}$2",
|
||||||
|
},
|
||||||
|
entryPoint: &configuration.EntryPoint{
|
||||||
|
Address: ":80",
|
||||||
|
Redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
Regex: `^(?:http?:\/\/)(foo)(\.com)$`,
|
||||||
|
Replacement: "https://$1{{\"bar\"}}$2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
expectedURL: "https://foo.com:443",
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
rewrite, err := srv.buildRedirectHandler(test.srcEntryPointName, test.redirect)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
req := testhelpers.MustNewRequest(http.MethodGet, test.url, nil)
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
rewrite.ServeHTTP(recorder, req, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Location", "fail")
|
||||||
|
}))
|
||||||
|
|
||||||
|
location, err := recorder.Result().Location()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, test.expectedURL, location.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerBuildEntryPointRedirect(t *testing.T) {
|
||||||
|
srv := Server{
|
||||||
|
globalConfiguration: configuration.GlobalConfiguration{
|
||||||
|
EntryPoints: configuration.EntryPoints{
|
||||||
|
"http": &configuration.EntryPoint{Address: ":80"},
|
||||||
|
"https": &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
srcEntryPointName string
|
||||||
|
redirectEntryPoint string
|
||||||
|
url string
|
||||||
|
expectedURL string
|
||||||
|
errorExpected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "existing redirect entry point",
|
||||||
|
srcEntryPointName: "http",
|
||||||
|
redirectEntryPoint: "https",
|
||||||
|
url: "http://foo:80",
|
||||||
|
expectedURL: "https://foo:443",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "non-existing redirect entry point",
|
||||||
|
srcEntryPointName: "http",
|
||||||
|
redirectEntryPoint: "foo",
|
||||||
|
url: "http://foo:80",
|
||||||
|
errorExpected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
rewrite, err := srv.buildEntryPointRedirect(test.srcEntryPointName, test.redirectEntryPoint)
|
||||||
|
if test.errorExpected {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
r := testhelpers.MustNewRequest(http.MethodGet, test.url, nil)
|
||||||
|
rewrite.ServeHTTP(recorder, r, nil)
|
||||||
|
|
||||||
|
location, err := recorder.Result().Location()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, test.expectedURL, location.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerBuildRedirect(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
globalConfiguration configuration.GlobalConfiguration
|
||||||
|
redirectEntryPointName string
|
||||||
|
expectedReplacement string
|
||||||
|
errorExpected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "Redirect endpoint http to https with HTTPS protocol",
|
||||||
|
redirectEntryPointName: "https",
|
||||||
|
globalConfiguration: configuration.GlobalConfiguration{
|
||||||
|
EntryPoints: configuration.EntryPoints{
|
||||||
|
"http": &configuration.EntryPoint{Address: ":80"},
|
||||||
|
"https": &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
expectedReplacement: "https://$1:443$2",
|
expectedReplacement: "https://$1:443$2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Redirect endpoint http to http02 with HTTP protocol",
|
desc: "Redirect endpoint http to http02 with HTTP protocol",
|
||||||
replacementProtocol: "http",
|
redirectEntryPointName: "http02",
|
||||||
originEntryPointName: "http",
|
|
||||||
globalConfiguration: configuration.GlobalConfiguration{
|
globalConfiguration: configuration.GlobalConfiguration{
|
||||||
EntryPoints: configuration.EntryPoints{
|
EntryPoints: configuration.EntryPoints{
|
||||||
"http": &configuration.EntryPoint{
|
"http": &configuration.EntryPoint{Address: ":80"},
|
||||||
Address: ":80",
|
"http02": &configuration.EntryPoint{Address: ":88"},
|
||||||
Redirect: &configuration.Redirect{
|
|
||||||
EntryPoint: "http02",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"http02": &configuration.EntryPoint{
|
|
||||||
Address: ":88",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
expectedReplacement: "http://$1:88$2",
|
expectedReplacement: "http://$1:88$2",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "Redirect endpoint to non-existent entry point",
|
||||||
|
redirectEntryPointName: "foobar",
|
||||||
|
globalConfiguration: configuration.GlobalConfiguration{
|
||||||
|
EntryPoints: configuration.EntryPoints{
|
||||||
|
"http": &configuration.EntryPoint{Address: ":80"},
|
||||||
|
"http02": &configuration.EntryPoint{Address: ":88"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errorExpected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Redirect endpoint to an entry point with a malformed address",
|
||||||
|
redirectEntryPointName: "http02",
|
||||||
|
globalConfiguration: configuration.GlobalConfiguration{
|
||||||
|
EntryPoints: configuration.EntryPoints{
|
||||||
|
"http": &configuration.EntryPoint{Address: ":80"},
|
||||||
|
"http02": &configuration.EntryPoint{Address: "88"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errorExpected: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
@ -982,9 +1136,9 @@ func TestServerLoadConfigBuildRedirect(t *testing.T) {
|
||||||
|
|
||||||
srv := Server{globalConfiguration: test.globalConfiguration}
|
srv := Server{globalConfiguration: test.globalConfiguration}
|
||||||
|
|
||||||
_, replacement, err := srv.buildRedirect(test.replacementProtocol, srv.globalConfiguration.EntryPoints[test.originEntryPointName])
|
_, replacement, err := srv.buildRedirect(test.redirectEntryPointName)
|
||||||
|
|
||||||
require.NoError(t, err, "build redirect sent an unexpected error")
|
require.Equal(t, test.errorExpected, err != nil, "Expected an error but don't have error, or Expected no error but have an error: %v", err)
|
||||||
assert.Equal(t, test.expectedReplacement, replacement, "build redirect does not return the right replacement pattern")
|
assert.Equal(t, test.expectedReplacement, replacement, "build redirect does not return the right replacement pattern")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,6 +59,14 @@
|
||||||
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{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 "." "-"}}"]
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
||||||
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
||||||
{{if hasServiceRequestHeaders $container $serviceName}}
|
{{if hasServiceRequestHeaders $container $serviceName}}
|
||||||
|
@ -79,7 +86,6 @@
|
||||||
[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}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
|
@ -92,6 +98,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}}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
62
vendor/github.com/containous/traefik-extra-service-fabric/labels.go
generated
vendored
62
vendor/github.com/containous/traefik-extra-service-fabric/labels.go
generated
vendored
|
@ -1,23 +1,59 @@
|
||||||
package servicefabric
|
package servicefabric
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
func hasServiceLabel(service ServiceItemExtended, key string) bool {
|
func getFuncBoolLabel(labelName string, defaultValue bool) func(service ServiceItemExtended) bool {
|
||||||
_, exists := service.Labels[key]
|
|
||||||
return exists
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFuncBoolLabel(labelName string) func(service ServiceItemExtended) bool {
|
|
||||||
return func(service ServiceItemExtended) bool {
|
return func(service ServiceItemExtended) bool {
|
||||||
return getBoolLabel(service, labelName)
|
return getBoolValue(service.Labels, labelName, defaultValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBoolLabel(service ServiceItemExtended, labelName string) bool {
|
func getFuncServiceStringLabel(service ServiceItemExtended, labelName string, defaultValue string) string {
|
||||||
value, exists := service.Labels[labelName]
|
return getStringValue(service.Labels, labelName, defaultValue)
|
||||||
return exists && strings.EqualFold(strings.TrimSpace(value), "true")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServiceLabelValue(service ServiceItemExtended, key string) string {
|
func hasFuncService(service ServiceItemExtended, labelName string) bool {
|
||||||
return service.Labels[key]
|
return hasLabel(service.Labels, labelName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServiceLabelsWithPrefix(service ServiceItemExtended, prefix string) map[string]string {
|
||||||
|
results := make(map[string]string)
|
||||||
|
for k, v := range service.Labels {
|
||||||
|
if strings.HasPrefix(k, prefix) {
|
||||||
|
results[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be replace by label.Has()
|
||||||
|
// Deprecated
|
||||||
|
func hasLabel(labels map[string]string, labelName string) bool {
|
||||||
|
value, ok := labels[labelName]
|
||||||
|
return ok && len(value) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be replace by label.GetStringValue()
|
||||||
|
// Deprecated
|
||||||
|
func getStringValue(labels map[string]string, labelName string, defaultValue string) string {
|
||||||
|
if value, ok := labels[labelName]; ok && len(value) > 0 {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be replace by label.GetBoolValue()
|
||||||
|
// Deprecated
|
||||||
|
func getBoolValue(labels map[string]string, labelName string, defaultValue bool) bool {
|
||||||
|
rawValue, ok := labels[labelName]
|
||||||
|
if ok {
|
||||||
|
v, err := strconv.ParseBool(rawValue)
|
||||||
|
if err == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
190
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go
generated
vendored
190
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go
generated
vendored
|
@ -69,34 +69,7 @@ func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, po
|
||||||
log.Info("Checking service fabric config")
|
log.Info("Checking service fabric config")
|
||||||
}
|
}
|
||||||
|
|
||||||
services, err := getClusterServices(sfClient)
|
configuration, err := p.buildConfiguration(sfClient)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
templateObjects := struct {
|
|
||||||
Services []ServiceItemExtended
|
|
||||||
}{
|
|
||||||
services,
|
|
||||||
}
|
|
||||||
|
|
||||||
var sfFuncMap = template.FuncMap{
|
|
||||||
"isPrimary": isPrimary,
|
|
||||||
"getDefaultEndpoint": p.getDefaultEndpoint,
|
|
||||||
"getNamedEndpoint": p.getNamedEndpoint,
|
|
||||||
"getApplicationParameter": p.getApplicationParameter,
|
|
||||||
"doesAppParamContain": p.doesAppParamContain,
|
|
||||||
"hasServiceLabel": hasServiceLabel,
|
|
||||||
"getServiceLabelValue": getServiceLabelValue,
|
|
||||||
"getServiceLabelValueWithDefault": getServiceLabelValueWithDefault,
|
|
||||||
"getServiceLabelsWithPrefix": getServiceLabelsWithPrefix,
|
|
||||||
"getServicesWithLabelValueMap": getServicesWithLabelValueMap,
|
|
||||||
"getServicesWithLabelValue": getServicesWithLabelValue,
|
|
||||||
"isExposed": getFuncBoolLabel("expose"),
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration, err := p.GetConfiguration(tmpl, sfFuncMap, templateObjects)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -120,24 +93,39 @@ func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, po
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Provider) doesAppParamContain(app sf.ApplicationItem, key, shouldContain string) bool {
|
func (p *Provider) buildConfiguration(sfClient sfClient) (*types.Configuration, error) {
|
||||||
value := p.getApplicationParameter(app, key)
|
var sfFuncMap = template.FuncMap{
|
||||||
return strings.Contains(value, shouldContain)
|
"getServices": getServices,
|
||||||
|
"hasLabel": hasFuncService,
|
||||||
|
"getLabelValue": getFuncServiceStringLabel,
|
||||||
|
"getLabelsWithPrefix": getServiceLabelsWithPrefix,
|
||||||
|
"isPrimary": isPrimary,
|
||||||
|
"isExposed": getFuncBoolLabel("expose", false),
|
||||||
|
"getBackendName": getBackendName,
|
||||||
|
"getDefaultEndpoint": getDefaultEndpoint,
|
||||||
|
"getNamedEndpoint": getNamedEndpoint, // FIXME unused
|
||||||
|
"getApplicationParameter": getApplicationParameter, // FIXME unused
|
||||||
|
"doesAppParamContain": doesAppParamContain, // FIXME unused
|
||||||
|
"filterServicesByLabelValue": filterServicesByLabelValue, // FIXME unused
|
||||||
|
}
|
||||||
|
|
||||||
|
services, err := getClusterServices(sfClient)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
templateObjects := struct {
|
||||||
|
Services []ServiceItemExtended
|
||||||
|
}{
|
||||||
|
Services: services,
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.GetConfiguration(tmpl, sfFuncMap, templateObjects)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Provider) getApplicationParameter(app sf.ApplicationItem, key string) string {
|
func getDefaultEndpoint(instance replicaInstance) string {
|
||||||
for _, param := range app.Parameters {
|
|
||||||
if param.Key == key {
|
|
||||||
return param.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Errorf("Parameter %s doesn't exist in app %s", key, app.Name)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Provider) getDefaultEndpoint(instance replicaInstance) string {
|
|
||||||
id, data := instance.GetReplicaData()
|
id, data := instance.GetReplicaData()
|
||||||
endpoint, err := getDefaultEndpoint(data.Address)
|
endpoint, err := getReplicaDefaultEndpoint(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("No default endpoint for replica %s in service %s endpointData: %s", id, data.Address)
|
log.Warnf("No default endpoint for replica %s in service %s endpointData: %s", id, data.Address)
|
||||||
return ""
|
return ""
|
||||||
|
@ -145,16 +133,64 @@ func (p Provider) getDefaultEndpoint(instance replicaInstance) string {
|
||||||
return endpoint
|
return endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Provider) getNamedEndpoint(instance replicaInstance, endpointName string) string {
|
func getReplicaDefaultEndpoint(replicaData *sf.ReplicaItemBase) (string, error) {
|
||||||
id, data := instance.GetReplicaData()
|
endpoints, err := decodeEndpointData(replicaData.Address)
|
||||||
endpoint, err := getNamedEndpoint(data.Address, endpointName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("No names endpoint of %s for replica %s in endpointData: %s", endpointName, id, data.Address)
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultHTTPEndpoint string
|
||||||
|
for _, v := range endpoints {
|
||||||
|
if strings.Contains(v, "http") {
|
||||||
|
defaultHTTPEndpoint = v
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(defaultHTTPEndpoint) == 0 {
|
||||||
|
return "", errors.New("no default endpoint found")
|
||||||
|
}
|
||||||
|
return defaultHTTPEndpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedEndpoint(instance replicaInstance, endpointName string) string {
|
||||||
|
id, data := instance.GetReplicaData()
|
||||||
|
endpoint, err := getReplicaNamedEndpoint(data, endpointName)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("No names endpoint of %s for replica %s in endpointData: %s. Error: %v", endpointName, id, data.Address, err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return endpoint
|
return endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getReplicaNamedEndpoint(replicaData *sf.ReplicaItemBase, endpointName string) (string, error) {
|
||||||
|
endpoints, err := decodeEndpointData(replicaData.Address)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint, exists := endpoints[endpointName]
|
||||||
|
if !exists {
|
||||||
|
return "", errors.New("endpoint doesn't exist")
|
||||||
|
}
|
||||||
|
return endpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doesAppParamContain(app sf.ApplicationItem, key, shouldContain string) bool {
|
||||||
|
value := getApplicationParameter(app, key)
|
||||||
|
return strings.Contains(value, shouldContain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getApplicationParameter(app sf.ApplicationItem, key string) string {
|
||||||
|
for _, param := range app.Parameters {
|
||||||
|
if param.Key == key {
|
||||||
|
return param.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Errorf("Parameter %s doesn't exist in app %s", key, app.Name)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func getClusterServices(sfClient sfClient) ([]ServiceItemExtended, error) {
|
func getClusterServices(sfClient sfClient) ([]ServiceItemExtended, error) {
|
||||||
apps, err := sfClient.GetApplications()
|
apps, err := sfClient.GetApplications()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -236,7 +272,7 @@ func getValidInstances(sfClient sfClient, app sf.ApplicationItem, service sf.Ser
|
||||||
return validInstances
|
return validInstances
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServicesWithLabelValueMap(services []ServiceItemExtended, key string) map[string][]ServiceItemExtended {
|
func getServices(services []ServiceItemExtended, key string) map[string][]ServiceItemExtended {
|
||||||
result := map[string][]ServiceItemExtended{}
|
result := map[string][]ServiceItemExtended{}
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
if value, exists := service.Labels[key]; exists {
|
if value, exists := service.Labels[key]; exists {
|
||||||
|
@ -250,7 +286,7 @@ func getServicesWithLabelValueMap(services []ServiceItemExtended, key string) ma
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServicesWithLabelValue(services []ServiceItemExtended, key, expectedValue string) []ServiceItemExtended {
|
func filterServicesByLabelValue(services []ServiceItemExtended, key, expectedValue string) []ServiceItemExtended {
|
||||||
var srvWithLabel []ServiceItemExtended
|
var srvWithLabel []ServiceItemExtended
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
value, exists := service.Labels[key]
|
value, exists := service.Labels[key]
|
||||||
|
@ -261,25 +297,6 @@ func getServicesWithLabelValue(services []ServiceItemExtended, key, expectedValu
|
||||||
return srvWithLabel
|
return srvWithLabel
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServiceLabelValueWithDefault(service ServiceItemExtended, key, defaultValue string) string {
|
|
||||||
value, exists := service.Labels[key]
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func getServiceLabelsWithPrefix(service ServiceItemExtended, prefix string) map[string]string {
|
|
||||||
results := make(map[string]string)
|
|
||||||
for k, v := range service.Labels {
|
|
||||||
if strings.HasPrefix(k, prefix) {
|
|
||||||
results[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
func isPrimary(instance replicaInstance) bool {
|
func isPrimary(instance replicaInstance) bool {
|
||||||
_, data := instance.GetReplicaData()
|
_, data := instance.GetReplicaData()
|
||||||
return data.ReplicaRole == "Primary"
|
return data.ReplicaRole == "Primary"
|
||||||
|
@ -290,7 +307,7 @@ func isHealthy(instanceData *sf.ReplicaItemBase) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasHTTPEndpoint(instanceData *sf.ReplicaItemBase) bool {
|
func hasHTTPEndpoint(instanceData *sf.ReplicaItemBase) bool {
|
||||||
_, err := getDefaultEndpoint(instanceData.Address)
|
_, err := getReplicaDefaultEndpoint(instanceData)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,37 +331,6 @@ func decodeEndpointData(endpointData string) (map[string]string, error) {
|
||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultEndpoint(endpointData string) (string, error) {
|
func getBackendName(service ServiceItemExtended, partition PartitionItemExtended) string {
|
||||||
endpoints, err := decodeEndpointData(endpointData)
|
return provider.Normalize(service.Name + partition.PartitionInformation.ID)
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultHTTPEndpointExists bool
|
|
||||||
var defaultHTTPEndpoint string
|
|
||||||
for _, v := range endpoints {
|
|
||||||
if strings.Contains(v, "http") {
|
|
||||||
defaultHTTPEndpoint = v
|
|
||||||
defaultHTTPEndpointExists = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !defaultHTTPEndpointExists {
|
|
||||||
return "", errors.New("no default endpoint found")
|
|
||||||
}
|
|
||||||
return defaultHTTPEndpoint, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNamedEndpoint(endpointData string, endpointName string) (string, error) {
|
|
||||||
endpoints, err := decodeEndpointData(endpointData)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
endpoint, exists := endpoints[endpointName]
|
|
||||||
if !exists {
|
|
||||||
return "", errors.New("endpoint doesn't exist")
|
|
||||||
}
|
|
||||||
return endpoint, nil
|
|
||||||
}
|
}
|
||||||
|
|
64
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go
generated
vendored
64
vendor/github.com/containous/traefik-extra-service-fabric/servicefabric_tmpl.go
generated
vendored
|
@ -1,8 +1,8 @@
|
||||||
package servicefabric
|
package servicefabric
|
||||||
|
|
||||||
const tmpl = `
|
const tmpl = `
|
||||||
|
{{$groupedServiceMap := getServices .Services "backend.group.name"}}
|
||||||
[backends]
|
[backends]
|
||||||
{{$groupedServiceMap := getServicesWithLabelValueMap .Services "backend.group.name"}}
|
|
||||||
{{range $aggName, $aggServices := $groupedServiceMap }}
|
{{range $aggName, $aggServices := $groupedServiceMap }}
|
||||||
[backends."{{$aggName}}"]
|
[backends."{{$aggName}}"]
|
||||||
{{range $service := $aggServices}}
|
{{range $service := $aggServices}}
|
||||||
|
@ -10,7 +10,7 @@ const tmpl = `
|
||||||
{{range $instance := $partition.Instances}}
|
{{range $instance := $partition.Instances}}
|
||||||
[backends."{{$aggName}}".servers."{{$service.ID}}-{{$instance.ID}}"]
|
[backends."{{$aggName}}".servers."{{$service.ID}}-{{$instance.ID}}"]
|
||||||
url = "{{getDefaultEndpoint $instance}}"
|
url = "{{getDefaultEndpoint $instance}}"
|
||||||
weight = {{getServiceLabelValueWithDefault $service "backend.group.weight" "1"}}
|
weight = {{getLabelValue $service "backend.group.weight" "1"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -20,45 +20,45 @@ const tmpl = `
|
||||||
{{if eq $partition.ServiceKind "Stateless"}}
|
{{if eq $partition.ServiceKind "Stateless"}}
|
||||||
[backends."{{$service.Name}}"]
|
[backends."{{$service.Name}}"]
|
||||||
[backends."{{$service.Name}}".LoadBalancer]
|
[backends."{{$service.Name}}".LoadBalancer]
|
||||||
{{if hasServiceLabel $service "backend.loadbalancer.method"}}
|
{{if hasLabel $service "backend.loadbalancer.method"}}
|
||||||
method = "{{getServiceLabelValue $service "backend.loadbalancer.method" }}"
|
method = "{{getLabelValue $service "backend.loadbalancer.method" "" }}"
|
||||||
{{else}}
|
{{else}}
|
||||||
method = "drr"
|
method = "drr"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if hasServiceLabel $service "backend.healthcheck"}}
|
{{if hasLabel $service "backend.healthcheck"}}
|
||||||
[backends."{{$service.Name}}".healthcheck]
|
[backends."{{$service.Name}}".healthcheck]
|
||||||
path = "{{getServiceLabelValue $service "backend.healthcheck"}}"
|
path = "{{getLabelValue $service "backend.healthcheck" ""}}"
|
||||||
interval = "{{getServiceLabelValueWithDefault $service "backend.healthcheck.interval" "10s"}}"
|
interval = "{{getLabelValue $service "backend.healthcheck.interval" "10s"}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if hasServiceLabel $service "backend.loadbalancer.stickiness"}}
|
{{if hasLabel $service "backend.loadbalancer.stickiness"}}
|
||||||
[backends."{{$service.Name}}".LoadBalancer.stickiness]
|
[backends."{{$service.Name}}".LoadBalancer.stickiness]
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if hasServiceLabel $service "backend.circuitbreaker"}}
|
{{if hasLabel $service "backend.circuitbreaker"}}
|
||||||
[backends."{{$service.Name}}".circuitbreaker]
|
[backends."{{$service.Name}}".circuitbreaker]
|
||||||
expression = "{{getServiceLabelValue $service "backend.circuitbreaker"}}"
|
expression = "{{getLabelValue $service "backend.circuitbreaker" ""}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if hasServiceLabel $service "backend.maxconn.amount"}}
|
{{if hasLabel $service "backend.maxconn.amount"}}
|
||||||
[backends."{{$service.Name}}".maxconn]
|
[backends."{{$service.Name}}".maxconn]
|
||||||
amount = {{getServiceLabelValue $service "backend.maxconn.amount"}}
|
amount = {{getLabelValue $service "backend.maxconn.amount" ""}}
|
||||||
{{if hasServiceLabel $service "backend.maxconn.extractorfunc"}}
|
{{if hasLabel $service "backend.maxconn.extractorfunc"}}
|
||||||
extractorfunc = "{{getServiceLabelValue $service "backend.maxconn.extractorfunc"}}"
|
extractorfunc = "{{getLabelValue $service "backend.maxconn.extractorfunc" ""}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{range $instance := $partition.Instances}}
|
{{range $instance := $partition.Instances}}
|
||||||
[backends."{{$service.Name}}".servers."{{$instance.ID}}"]
|
[backends."{{$service.Name}}".servers."{{$instance.ID}}"]
|
||||||
url = "{{getDefaultEndpoint $instance}}"
|
url = "{{getDefaultEndpoint $instance}}"
|
||||||
weight = {{getServiceLabelValueWithDefault $service "backend.weight" "1"}}
|
weight = {{getLabelValue $service "backend.weight" "1"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else if eq $partition.ServiceKind "Stateful"}}
|
{{else if eq $partition.ServiceKind "Stateful"}}
|
||||||
{{range $replica := $partition.Replicas}}
|
{{range $replica := $partition.Replicas}}
|
||||||
{{if isPrimary $replica}}
|
{{if isPrimary $replica}}
|
||||||
|
|
||||||
{{$backendName := (print $service.Name $partition.PartitionInformation.ID)}}
|
{{$backendName := getBackendName $service.Name $partition}}
|
||||||
[backends."{{$backendName}}".servers."{{$replica.ID}}"]
|
[backends."{{$backendName}}".servers."{{$replica.ID}}"]
|
||||||
url = "{{getDefaultEndpoint $replica}}"
|
url = "{{getDefaultEndpoint $replica}}"
|
||||||
weight = 1
|
weight = 1
|
||||||
|
@ -81,11 +81,11 @@ const tmpl = `
|
||||||
[frontends."{{$groupName}}"]
|
[frontends."{{$groupName}}"]
|
||||||
backend = "{{$groupName}}"
|
backend = "{{$groupName}}"
|
||||||
|
|
||||||
{{if hasServiceLabel $service "frontend.priority"}}
|
{{if hasLabel $service "frontend.priority"}}
|
||||||
priority = 100
|
priority = 100
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{range $key, $value := getServiceLabelsWithPrefix $service "frontend.rule"}}
|
{{range $key, $value := getLabelsWithPrefix $service "frontend.rule"}}
|
||||||
[frontends."{{$groupName}}".routes."{{$key}}"]
|
[frontends."{{$groupName}}".routes."{{$key}}"]
|
||||||
rule = "{{$value}}"
|
rule = "{{$value}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -97,27 +97,27 @@ const tmpl = `
|
||||||
[frontends."{{$service.Name}}"]
|
[frontends."{{$service.Name}}"]
|
||||||
backend = "{{$service.Name}}"
|
backend = "{{$service.Name}}"
|
||||||
|
|
||||||
{{if hasServiceLabel $service "frontend.passHostHeader"}}
|
{{if hasLabel $service "frontend.passHostHeader"}}
|
||||||
passHostHeader = {{getServiceLabelValue $service "frontend.passHostHeader" }}
|
passHostHeader = {{getLabelValue $service "frontend.passHostHeader" ""}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if hasServiceLabel $service "frontend.whitelistSourceRange"}}
|
{{if hasLabel $service "frontend.whitelistSourceRange"}}
|
||||||
whitelistSourceRange = {{getServiceLabelValue $service "frontend.whitelistSourceRange" }}
|
whitelistSourceRange = {{getLabelValue $service "frontend.whitelistSourceRange" ""}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if hasServiceLabel $service "frontend.priority"}}
|
{{if hasLabel $service "frontend.priority"}}
|
||||||
priority = {{getServiceLabelValue $service "frontend.priority"}}
|
priority = {{getLabelValue $service "frontend.priority" ""}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if hasServiceLabel $service "frontend.basicAuth"}}
|
{{if hasLabel $service "frontend.basicAuth"}}
|
||||||
basicAuth = {{getServiceLabelValue $service "frontend.basicAuth"}}
|
basicAuth = {{getLabelValue $service "frontend.basicAuth" ""}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if hasServiceLabel $service "frontend.entryPoints"}}
|
{{if hasLabel $service "frontend.entryPoints"}}
|
||||||
entryPoints = {{getServiceLabelValue $service "frontend.entryPoints"}}
|
entryPoints = {{getLabelValue $service "frontend.entryPoints" ""}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{range $key, $value := getServiceLabelsWithPrefix $service "frontend.rule"}}
|
{{range $key, $value := getLabelsWithPrefix $service "frontend.rule"}}
|
||||||
[frontends."{{$service.Name}}".routes."{{$key}}"]
|
[frontends."{{$service.Name}}".routes."{{$key}}"]
|
||||||
rule = "{{$value}}"
|
rule = "{{$value}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -126,11 +126,11 @@ const tmpl = `
|
||||||
{{range $partition := $service.Partitions}}
|
{{range $partition := $service.Partitions}}
|
||||||
{{$partitionId := $partition.PartitionInformation.ID}}
|
{{$partitionId := $partition.PartitionInformation.ID}}
|
||||||
|
|
||||||
{{if hasServiceLabel $service "frontend.rule"}}
|
{{if hasLabel $service "frontend.rule"}}
|
||||||
[frontends."{{$service.Name}}/{{$partitionId}}"]
|
[frontends."{{$service.Name}}/{{$partitionId}}"]
|
||||||
backend = "{{$service.Name}}/{{$partitionId}}"
|
backend = "{{getBackendName $service.Name $partition}}"
|
||||||
[frontends."{{$service.Name}}/{{$partitionId}}".routes.default]
|
[frontends."{{$service.Name}}/{{$partitionId}}".routes.default]
|
||||||
rule = {{getServiceLabelValue $service "frontend.rule.partition.$partitionId"}}
|
rule = {{getLabelValue $service "frontend.rule.partition.$partitionId" ""}}
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
1
vendor/github.com/jjcollinge/servicefabric/servicefabric.go
generated
vendored
1
vendor/github.com/jjcollinge/servicefabric/servicefabric.go
generated
vendored
|
@ -12,6 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultAPIVersion is a default Service Fabric REST API version
|
||||||
const DefaultAPIVersion = "3.0"
|
const DefaultAPIVersion = "3.0"
|
||||||
|
|
||||||
// Client for Service Fabric.
|
// Client for Service Fabric.
|
||||||
|
|
Loading…
Reference in a new issue