Segments Labels: Rancher & Marathon
This commit is contained in:
parent
16bb9b6836
commit
0ea007b26f
31 changed files with 4288 additions and 3656 deletions
|
@ -7,9 +7,11 @@
|
|||
// templates/eureka.tmpl
|
||||
// templates/kubernetes.tmpl
|
||||
// templates/kv.tmpl
|
||||
// templates/marathon-v1.tmpl
|
||||
// templates/marathon.tmpl
|
||||
// templates/mesos.tmpl
|
||||
// templates/notFound.tmpl
|
||||
// templates/rancher-v1.tmpl
|
||||
// templates/rancher.tmpl
|
||||
// DO NOT EDIT!
|
||||
|
||||
|
@ -1265,22 +1267,106 @@ func templatesKvTmpl() (*asset, error) {
|
|||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesMarathonV1Tmpl = []byte(`{{$apps := .Applications}}
|
||||
|
||||
{{range $app := $apps }}
|
||||
{{range $task := $app.Tasks }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".servers."server-{{ $task.ID | replace "." "-"}}{{getServiceNameSuffix $serviceName }}"]
|
||||
url = "{{ getProtocol $app $serviceName }}://{{ getBackendServer $task $app }}:{{ getPort $task $app $serviceName }}"
|
||||
weight = {{ getWeight $app $serviceName }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{range $app := $apps }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app }}
|
||||
|
||||
[backends."{{ getBackend $app $serviceName }}"]
|
||||
{{if hasMaxConnLabels $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".maxConn]
|
||||
amount = {{ getMaxConnAmount $app }}
|
||||
extractorFunc = "{{ getMaxConnExtractorFunc $app }}"
|
||||
{{end}}
|
||||
|
||||
{{if hasLoadBalancerLabels $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".loadBalancer]
|
||||
method = "{{ getLoadBalancerMethod $app }}"
|
||||
sticky = {{ getSticky $app }}
|
||||
{{if hasStickinessLabel $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ getStickinessCookieName $app }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if hasCircuitBreakerLabels $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".circuitBreaker]
|
||||
expression = "{{ getCircuitBreakerExpression $app }}"
|
||||
{{end}}
|
||||
|
||||
{{if hasHealthCheckLabels $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".healthCheck]
|
||||
path = "{{ getHealthCheckPath $app }}"
|
||||
interval = "{{ getHealthCheckInterval $app }}"
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends]
|
||||
{{range $app := $apps }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames . }}
|
||||
|
||||
[frontends."{{ getFrontendName $app $serviceName | normalize }}"]
|
||||
backend = "{{ getBackend $app $serviceName }}"
|
||||
passHostHeader = {{ getPassHostHeader $app $serviceName }}
|
||||
priority = {{ getPriority $app $serviceName }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $app $serviceName }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
basicAuth = [{{range getBasicAuth $app $serviceName }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
[frontends."{{ getFrontendName $app $serviceName | normalize }}".routes."route-host{{ $app.ID | replace "/" "-" }}{{ getServiceNameSuffix $serviceName }}"]
|
||||
rule = "{{ getFrontendRule $app $serviceName }}"
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
func templatesMarathonV1TmplBytes() ([]byte, error) {
|
||||
return _templatesMarathonV1Tmpl, nil
|
||||
}
|
||||
|
||||
func templatesMarathonV1Tmpl() (*asset, error) {
|
||||
bytes, err := templatesMarathonV1TmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/marathon-v1.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
||||
|
||||
[backends]
|
||||
{{range $app := $apps }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app }}
|
||||
{{ $backendName := getBackend $app $serviceName}}
|
||||
{{ $backendName := getBackendName $app }}
|
||||
|
||||
[backends."{{ $backendName }}"]
|
||||
|
||||
{{ $circuitBreaker := getCircuitBreaker $app }}
|
||||
{{ $circuitBreaker := getCircuitBreaker $app.SegmentLabels }}
|
||||
{{if $circuitBreaker }}
|
||||
[backends."{{ $backendName }}".circuitBreaker]
|
||||
expression = "{{ $circuitBreaker.Expression }}"
|
||||
{{end}}
|
||||
|
||||
{{ $loadBalancer := getLoadBalancer $app }}
|
||||
{{ $loadBalancer := getLoadBalancer $app.SegmentLabels }}
|
||||
{{if $loadBalancer }}
|
||||
[backends."{{ $backendName }}".loadBalancer]
|
||||
method = "{{ $loadBalancer.Method }}"
|
||||
|
@ -1291,14 +1377,14 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $maxConn := getMaxConn $app }}
|
||||
{{ $maxConn := getMaxConn $app.SegmentLabels }}
|
||||
{{if $maxConn }}
|
||||
[backends."{{ $backendName }}".maxConn]
|
||||
extractorFunc = "{{ $maxConn.ExtractorFunc }}"
|
||||
amount = {{ $maxConn.Amount }}
|
||||
{{end}}
|
||||
|
||||
{{ $healthCheck := getHealthCheck $app }}
|
||||
{{ $healthCheck := getHealthCheck $app.SegmentLabels }}
|
||||
{{if $healthCheck }}
|
||||
[backends."{{ $backendName }}".healthCheck]
|
||||
path = "{{ $healthCheck.Path }}"
|
||||
|
@ -1306,7 +1392,7 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
|||
interval = "{{ $healthCheck.Interval }}"
|
||||
{{end}}
|
||||
|
||||
{{ $buffering := getBuffering $app }}
|
||||
{{ $buffering := getBuffering $app.SegmentLabels }}
|
||||
{{if $buffering }}
|
||||
[backends."{{ $backendName }}".buffering]
|
||||
maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }}
|
||||
|
@ -1316,35 +1402,33 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
|||
retryExpression = "{{ $buffering.RetryExpression }}"
|
||||
{{end}}
|
||||
|
||||
{{range $serverName, $server := getServers $app $serviceName }}
|
||||
{{range $serverName, $server := getServers $app }}
|
||||
[backends."{{ $backendName }}".servers."{{ $serverName }}"]
|
||||
url = "{{ $server.URL }}"
|
||||
weight = {{ $server.Weight }}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends]
|
||||
{{range $app := $apps }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app }}
|
||||
{{ $frontendName := getFrontendName $app $serviceName }}
|
||||
{{ $frontendName := getFrontendName $app }}
|
||||
|
||||
[frontends."{{ $frontendName }}"]
|
||||
backend = "{{ getBackend $app $serviceName }}"
|
||||
priority = {{ getPriority $app $serviceName }}
|
||||
passHostHeader = {{ getPassHostHeader $app $serviceName }}
|
||||
passTLSCert = {{ getPassTLSCert $app $serviceName }}
|
||||
backend = "{{ getBackendName $app }}"
|
||||
priority = {{ getPriority $app.SegmentLabels }}
|
||||
passHostHeader = {{ getPassHostHeader $app.SegmentLabels }}
|
||||
passTLSCert = {{ getPassTLSCert $app.SegmentLabels }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $app $serviceName }}
|
||||
entryPoints = [{{range getEntryPoints $app.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
basicAuth = [{{range getBasicAuth $app $serviceName }}
|
||||
basicAuth = [{{range getBasicAuth $app.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $whitelist := getWhiteList $app $serviceName }}
|
||||
{{ $whitelist := getWhiteList $app.SegmentLabels }}
|
||||
{{if $whitelist }}
|
||||
[frontends."{{ $frontendName }}".whiteList]
|
||||
sourceRange = [{{range $whitelist.SourceRange }}
|
||||
|
@ -1353,7 +1437,7 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
|||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||
{{end}}
|
||||
|
||||
{{ $redirect := getRedirect $app $serviceName }}
|
||||
{{ $redirect := getRedirect $app.SegmentLabels }}
|
||||
{{if $redirect }}
|
||||
[frontends."{{ $frontendName }}".redirect]
|
||||
entryPoint = "{{ $redirect.EntryPoint }}"
|
||||
|
@ -1362,7 +1446,7 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
|||
permanent = {{ $redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{ $errorPages := getErrorPages $app $serviceName }}
|
||||
{{ $errorPages := getErrorPages $app.SegmentLabels }}
|
||||
{{if $errorPages }}
|
||||
[frontends."{{ $frontendName }}".errors]
|
||||
{{range $pageName, $page := $errorPages }}
|
||||
|
@ -1375,7 +1459,7 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $rateLimit := getRateLimit $app $serviceName }}
|
||||
{{ $rateLimit := getRateLimit $app.SegmentLabels }}
|
||||
{{if $rateLimit }}
|
||||
[frontends."{{ $frontendName }}".rateLimit]
|
||||
extractorFunc = "{{ $rateLimit.ExtractorFunc }}"
|
||||
|
@ -1388,7 +1472,7 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $headers := getHeaders $app $serviceName }}
|
||||
{{ $headers := getHeaders $app.SegmentLabels }}
|
||||
{{if $headers }}
|
||||
[frontends."{{ $frontendName }}".headers]
|
||||
SSLRedirect = {{ $headers.SSLRedirect }}
|
||||
|
@ -1442,10 +1526,9 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends."{{ $frontendName }}".routes."route-host{{ $app.ID | replace "/" "-" }}{{ getServiceNameSuffix $serviceName }}"]
|
||||
rule = "{{ getFrontendRule $app $serviceName }}"
|
||||
[frontends."{{ $frontendName }}".routes."route-host{{ $app.ID | replace "/" "-" }}{{ getSegmentNameSuffix $app.SegmentName }}"]
|
||||
rule = "{{ getFrontendRule $app }}"
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
|
@ -1682,19 +1765,94 @@ func templatesNotfoundTmpl() (*asset, error) {
|
|||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesRancherV1Tmpl = []byte(`{{$backendServers := .Backends}}
|
||||
|
||||
[backends]
|
||||
{{range $backendName, $backend := .Backends }}
|
||||
{{if hasCircuitBreakerLabel $backend }}
|
||||
[backends."backend-{{ $backendName }}".circuitBreaker]
|
||||
expression = "{{ getCircuitBreakerExpression $backend }}"
|
||||
{{end}}
|
||||
|
||||
{{if hasLoadBalancerLabel $backend }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer]
|
||||
method = "{{ getLoadBalancerMethod $backend }}"
|
||||
sticky = {{ getSticky $backend }}
|
||||
{{if hasStickinessLabel $backend }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ getStickinessCookieName $backend }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if hasMaxConnLabels $backend }}
|
||||
[backends."backend-{{ $backendName }}".maxConn]
|
||||
amount = {{ getMaxConnAmount $backend }}
|
||||
extractorFunc = "{{ getMaxConnExtractorFunc $backend }}"
|
||||
{{end}}
|
||||
|
||||
{{range $index, $ip := $backend.Containers }}
|
||||
[backends."backend-{{ $backendName }}".servers."server-{{ $index }}"]
|
||||
url = "{{ getProtocol $backend }}://{{ $ip }}:{{ getPort $backend }}"
|
||||
weight = {{ getWeight $backend }}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
[frontends]
|
||||
{{range $frontendName, $service := .Frontends }}
|
||||
[frontends."frontend-{{ $frontendName }}"]
|
||||
backend = "backend-{{ getBackend $service }}"
|
||||
passHostHeader = {{ getPassHostHeader $service }}
|
||||
priority = {{ getPriority $service }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $service }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
basicAuth = [{{range getBasicAuth $service }}
|
||||
"{{.}}",
|
||||
{{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 }}"]
|
||||
rule = "{{ getFrontendRule $service }}"
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
func templatesRancherV1TmplBytes() ([]byte, error) {
|
||||
return _templatesRancherV1Tmpl, nil
|
||||
}
|
||||
|
||||
func templatesRancherV1Tmpl() (*asset, error) {
|
||||
bytes, err := templatesRancherV1TmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/rancher-v1.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
||||
[backends]
|
||||
{{range $backendName, $backend := .Backends }}
|
||||
|
||||
[backends."backend-{{ $backendName }}"]
|
||||
|
||||
{{ $circuitBreaker := getCircuitBreaker $backend }}
|
||||
{{ $circuitBreaker := getCircuitBreaker $backend.SegmentLabels }}
|
||||
{{if $circuitBreaker }}
|
||||
[backends."backend-{{ $backendName }}".circuitBreaker]
|
||||
expression = "{{ $circuitBreaker.Expression }}"
|
||||
{{end}}
|
||||
|
||||
{{ $loadBalancer := getLoadBalancer $backend }}
|
||||
{{ $loadBalancer := getLoadBalancer $backend.SegmentLabels }}
|
||||
{{if $loadBalancer }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer]
|
||||
method = "{{ $loadBalancer.Method }}"
|
||||
|
@ -1705,14 +1863,14 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $maxConn := getMaxConn $backend }}
|
||||
{{ $maxConn := getMaxConn $backend.SegmentLabels }}
|
||||
{{if $maxConn }}
|
||||
[backends."backend-{{ $backendName }}".maxConn]
|
||||
extractorFunc = "{{ $maxConn.ExtractorFunc }}"
|
||||
amount = {{ $maxConn.Amount }}
|
||||
{{end}}
|
||||
|
||||
{{ $healthCheck := getHealthCheck $backend }}
|
||||
{{ $healthCheck := getHealthCheck $backend.SegmentLabels }}
|
||||
{{if $healthCheck }}
|
||||
[backends."backend-{{ $backendName }}".healthCheck]
|
||||
path = "{{ $healthCheck.Path }}"
|
||||
|
@ -1720,7 +1878,7 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
|||
interval = "{{ $healthCheck.Interval }}"
|
||||
{{end}}
|
||||
|
||||
{{ $buffering := getBuffering $backend }}
|
||||
{{ $buffering := getBuffering $backend.SegmentLabels }}
|
||||
{{if $buffering }}
|
||||
[backends."backend-{{ $backendName }}".buffering]
|
||||
maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }}
|
||||
|
@ -1743,19 +1901,19 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
|||
|
||||
[frontends."frontend-{{ $frontendName }}"]
|
||||
backend = "backend-{{ getBackendName $service }}"
|
||||
priority = {{ getPriority $service }}
|
||||
passHostHeader = {{ getPassHostHeader $service }}
|
||||
passTLSCert = {{ getPassTLSCert $service }}
|
||||
priority = {{ getPriority $service.SegmentLabels }}
|
||||
passHostHeader = {{ getPassHostHeader $service.SegmentLabels }}
|
||||
passTLSCert = {{ getPassTLSCert $service.SegmentLabels }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $service }}
|
||||
entryPoints = [{{range getEntryPoints $service.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
basicAuth = [{{range getBasicAuth $service }}
|
||||
basicAuth = [{{range getBasicAuth $service.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $whitelist := getWhiteList $service }}
|
||||
{{ $whitelist := getWhiteList $service.SegmentLabels }}
|
||||
{{if $whitelist }}
|
||||
[frontends."frontend-{{ $frontendName }}".whiteList]
|
||||
sourceRange = [{{range $whitelist.SourceRange }}
|
||||
|
@ -1764,7 +1922,7 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
|||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||
{{end}}
|
||||
|
||||
{{ $redirect := getRedirect $service }}
|
||||
{{ $redirect := getRedirect $service.SegmentLabels }}
|
||||
{{if $redirect }}
|
||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||
entryPoint = "{{ $redirect.EntryPoint }}"
|
||||
|
@ -1773,7 +1931,7 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
|||
permanent = {{ $redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{ $errorPages := getErrorPages $service }}
|
||||
{{ $errorPages := getErrorPages $service.SegmentLabels }}
|
||||
{{if $errorPages }}
|
||||
[frontends."frontend-{{ $frontendName }}".errors]
|
||||
{{range $pageName, $page := $errorPages }}
|
||||
|
@ -1786,7 +1944,7 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $rateLimit := getRateLimit $service }}
|
||||
{{ $rateLimit := getRateLimit $service.SegmentLabels }}
|
||||
{{if $rateLimit }}
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit]
|
||||
extractorFunc = "{{ $rateLimit.ExtractorFunc }}"
|
||||
|
@ -1799,7 +1957,7 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $headers := getHeaders $service }}
|
||||
{{ $headers := getHeaders $service.SegmentLabels }}
|
||||
{{if $headers }}
|
||||
[frontends."frontend-{{ $frontendName }}".headers]
|
||||
SSLRedirect = {{ $headers.SSLRedirect }}
|
||||
|
@ -1933,9 +2091,11 @@ var _bindata = map[string]func() (*asset, error){
|
|||
"templates/eureka.tmpl": templatesEurekaTmpl,
|
||||
"templates/kubernetes.tmpl": templatesKubernetesTmpl,
|
||||
"templates/kv.tmpl": templatesKvTmpl,
|
||||
"templates/marathon-v1.tmpl": templatesMarathonV1Tmpl,
|
||||
"templates/marathon.tmpl": templatesMarathonTmpl,
|
||||
"templates/mesos.tmpl": templatesMesosTmpl,
|
||||
"templates/notFound.tmpl": templatesNotfoundTmpl,
|
||||
"templates/rancher-v1.tmpl": templatesRancherV1Tmpl,
|
||||
"templates/rancher.tmpl": templatesRancherTmpl,
|
||||
}
|
||||
|
||||
|
@ -1988,9 +2148,11 @@ var _bintree = &bintree{nil, map[string]*bintree{
|
|||
"eureka.tmpl": {templatesEurekaTmpl, map[string]*bintree{}},
|
||||
"kubernetes.tmpl": {templatesKubernetesTmpl, map[string]*bintree{}},
|
||||
"kv.tmpl": {templatesKvTmpl, map[string]*bintree{}},
|
||||
"marathon-v1.tmpl": {templatesMarathonV1Tmpl, map[string]*bintree{}},
|
||||
"marathon.tmpl": {templatesMarathonTmpl, map[string]*bintree{}},
|
||||
"mesos.tmpl": {templatesMesosTmpl, map[string]*bintree{}},
|
||||
"notFound.tmpl": {templatesNotfoundTmpl, map[string]*bintree{}},
|
||||
"rancher-v1.tmpl": {templatesRancherV1Tmpl, map[string]*bintree{}},
|
||||
"rancher.tmpl": {templatesRancherTmpl, map[string]*bintree{}},
|
||||
}},
|
||||
}}
|
||||
|
|
|
@ -214,12 +214,22 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
|||
|
||||
if gc.Docker != nil {
|
||||
if len(gc.Docker.Filename) != 0 && gc.Docker.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Docker.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Docker.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Marathon != nil {
|
||||
if len(gc.Marathon.Filename) != 0 && gc.Marathon.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Marathon.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Marathon.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Eureka != nil {
|
||||
if gc.Eureka.Delay != 0 {
|
||||
log.Warn("Delay has been deprecated -- please use RefreshSeconds")
|
||||
|
@ -228,6 +238,13 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
|||
}
|
||||
|
||||
if gc.Rancher != nil {
|
||||
if len(gc.Rancher.Filename) != 0 && gc.Rancher.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Rancher.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Rancher.TemplateVersion = 2
|
||||
}
|
||||
|
||||
// Ensure backwards compatibility for now
|
||||
if len(gc.Rancher.AccessKey) > 0 ||
|
||||
len(gc.Rancher.Endpoint) > 0 ||
|
||||
|
|
|
@ -161,9 +161,9 @@ exposedbydefault = false
|
|||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
|
||||
## Labels: overriding default behaviour
|
||||
## Labels: overriding default behavior
|
||||
|
||||
#### Using Docker with Swarm Mode
|
||||
### Using Docker with Swarm Mode
|
||||
|
||||
If you use a compose file with the Swarm mode, labels should be defined in the `deploy` part of your service.
|
||||
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/#labels-1)).
|
||||
|
@ -177,7 +177,7 @@ services:
|
|||
traefik.docker.network: traefik
|
||||
```
|
||||
|
||||
#### Using Docker Compose
|
||||
### Using Docker Compose
|
||||
|
||||
If you are intending to use only Docker Compose commands (e.g. `docker-compose up --scale whoami=2 -d`), labels should be under your service, otherwise they will be ignored.
|
||||
|
||||
|
|
|
@ -45,6 +45,15 @@ domain = "marathon.localhost"
|
|||
#
|
||||
# filename = "marathon.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = "2"
|
||||
|
||||
# Expose Marathon apps by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
|
@ -241,6 +250,8 @@ Segment labels are used to define routes to an application exposing multiple por
|
|||
A segment is a group of labels that apply to a port exposed by an application.
|
||||
You can define as many segments as ports exposed in an application.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.portIndex=1` | Create a service binding with frontend/backend using this port index. Overrides `traefik.portIndex`. |
|
||||
|
@ -266,7 +277,7 @@ You can define as many segments as ports exposed in an application.
|
|||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Overrides `traefik.frontend.rule`. Default: `{service_name}.{sub_domain}.{domain}` |
|
||||
| `traefik.<segment_name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
#### Custom Headers
|
||||
|
|
|
@ -46,6 +46,22 @@ exposedByDefault = false
|
|||
# Default: false
|
||||
#
|
||||
enableServiceHealthFilter = true
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "rancher.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = "2"
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
|
@ -116,9 +132,11 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|||
io.rancher.container.create_agent: true
|
||||
```
|
||||
|
||||
## Labels: overriding default behaviour
|
||||
## Labels: overriding default behavior
|
||||
|
||||
Labels can be used on task containers to override default behaviour:
|
||||
### On Containers
|
||||
|
||||
Labels can be used on task containers to override default behavior:
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
@ -162,19 +180,19 @@ Labels can be used on task containers to override default behaviour:
|
|||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
|
@ -192,3 +210,68 @@ Labels can be used on task containers to override default behaviour:
|
|||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
|
||||
### On containers with Multiple Ports (segment labels)
|
||||
|
||||
Segment labels are used to define routes to a container exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by a container.
|
||||
You can define as many segments as ports exposed in a container.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the segment labels could be used. |
|
||||
| `traefik.<segment_name>.protocol` | Overrides `traefik.protocol`. |
|
||||
| `traefik.<segment_name>.weight` | Assign this segment weight. Overrides `traefik.weight`. |
|
||||
| `traefik.<segment_name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
|
||||
| `traefik.<segment_name>.frontend.backend=BACKEND` | Assign this segment frontend to `BACKEND`. Default is to assign to the segment backend. |
|
||||
| `traefik.<segment_name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert` | Overrides `traefik.frontend.passTLSCert`. |
|
||||
| `traefik.<segment_name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.<segment_name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Overrides `traefik.frontend.whiteList.useXForwardedFor`. |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|-----------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | overrides `traefik.frontend.headers.customRequestHeaders=EXPR ` |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | overrides `traefik.frontend.headers.customResponseHeaders=EXPR` |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|--------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | overrides `traefik.frontend.headers.allowedHosts=EXPR` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | overrides `traefik.frontend.headers.hostsProxyHeaders=EXPR` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | overrides `traefik.frontend.headers.SSLRedirect=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | overrides `traefik.frontend.headers.SSLTemporaryRedirect=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | overrides `traefik.frontend.headers.SSLHost=HOST` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | overrides `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | overrides `traefik.frontend.headers.STSSeconds=315360000` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | overrides `traefik.frontend.headers.STSIncludeSubdomains=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | overrides `traefik.frontend.headers.STSPreload=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | overrides `traefik.frontend.headers.forceSTSHeader=false` |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | overrides `traefik.frontend.headers.frameDeny=false` |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | overrides `traefik.frontend.headers.customFrameOptionsValue=VALUE` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | overrides `traefik.frontend.headers.contentTypeNosniff=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | overrides `traefik.frontend.headers.browserXSSFilter=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | overrides `traefik.frontend.headers.customBrowserXSSValue=VALUE` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | overrides `traefik.frontend.headers.contentSecurityPolicy=VALUE` |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | overrides `traefik.frontend.headers.publicKey=VALUE` |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | overrides `traefik.frontend.headers.referrerPolicy=VALUE` |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | overrides `traefik.frontend.headers.isDevelopment=false` |
|
||||
|
|
|
@ -3,7 +3,6 @@ package docker
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
@ -28,30 +27,30 @@ func (p *Provider) buildConfigurationV2(containersInspected []dockerData) *types
|
|||
"getLabelValue": label.GetStringValue,
|
||||
"getSubDomain": getSubDomain,
|
||||
"isBackendLBSwarm": isBackendLBSwarm,
|
||||
"getDomain": getFuncStringLabel(label.TraefikDomain, p.Domain),
|
||||
"getDomain": label.GetFuncString(label.TraefikDomain, p.Domain),
|
||||
|
||||
// Backend functions
|
||||
"getIPAddress": p.getIPAddress,
|
||||
"getServers": p.getServers,
|
||||
"getMaxConn": getMaxConn,
|
||||
"getHealthCheck": getHealthCheck,
|
||||
"getBuffering": getBuffering,
|
||||
"getCircuitBreaker": getCircuitBreaker,
|
||||
"getLoadBalancer": getLoadBalancer,
|
||||
"getMaxConn": label.GetMaxConn,
|
||||
"getHealthCheck": label.GetHealthCheck,
|
||||
"getBuffering": label.GetBuffering,
|
||||
"getCircuitBreaker": label.GetCircuitBreaker,
|
||||
"getLoadBalancer": label.GetLoadBalancer,
|
||||
|
||||
// Frontend functions
|
||||
"getBackendName": getBackendName,
|
||||
"getPriority": getFuncIntLabel(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getPassHostHeader": getFuncBoolLabel(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getPassTLSCert": getFuncBoolLabel(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getEntryPoints": getFuncSliceStringLabel(label.TraefikFrontendEntryPoints),
|
||||
"getBasicAuth": getFuncSliceStringLabel(label.TraefikFrontendAuthBasic),
|
||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic),
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
"getRedirect": getRedirect,
|
||||
"getErrorPages": getErrorPages,
|
||||
"getRateLimit": getRateLimit,
|
||||
"getHeaders": getHeaders,
|
||||
"getWhiteList": getWhiteList,
|
||||
"getRedirect": label.GetRedirect,
|
||||
"getErrorPages": label.GetErrorPages,
|
||||
"getRateLimit": label.GetRateLimit,
|
||||
"getHeaders": label.GetHeaders,
|
||||
"getWhiteList": label.GetWhiteList,
|
||||
}
|
||||
|
||||
// filter containers
|
||||
|
@ -276,105 +275,6 @@ func getBackendName(container dockerData) string {
|
|||
return getDefaultBackendName(container)
|
||||
}
|
||||
|
||||
func getWhiteList(labels map[string]string) *types.WhiteList {
|
||||
if label.Has(labels, label.TraefikFrontendWhitelistSourceRange) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikFrontendWhitelistSourceRange, label.TraefikFrontendWhiteListSourceRange)
|
||||
}
|
||||
|
||||
ranges := label.GetSliceStringValue(labels, label.TraefikFrontendWhiteListSourceRange)
|
||||
if len(ranges) > 0 {
|
||||
return &types.WhiteList{
|
||||
SourceRange: ranges,
|
||||
UseXForwardedFor: label.GetBoolValue(labels, label.TraefikFrontendWhiteListUseXForwardedFor, false),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
values := label.GetSliceStringValue(labels, label.TraefikFrontendWhitelistSourceRange)
|
||||
if len(values) > 0 {
|
||||
return &types.WhiteList{
|
||||
SourceRange: values,
|
||||
UseXForwardedFor: false,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRedirect(labels map[string]string) *types.Redirect {
|
||||
permanent := label.GetBoolValue(labels, label.TraefikFrontendRedirectPermanent, false)
|
||||
|
||||
if label.Has(labels, label.TraefikFrontendRedirectEntryPoint) {
|
||||
return &types.Redirect{
|
||||
EntryPoint: label.GetStringValue(labels, label.TraefikFrontendRedirectEntryPoint, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
if label.Has(labels, label.TraefikFrontendRedirectRegex) &&
|
||||
label.Has(labels, label.TraefikFrontendRedirectReplacement) {
|
||||
return &types.Redirect{
|
||||
Regex: label.GetStringValue(labels, label.TraefikFrontendRedirectRegex, ""),
|
||||
Replacement: label.GetStringValue(labels, label.TraefikFrontendRedirectReplacement, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getErrorPages(labels map[string]string) map[string]*types.ErrorPage {
|
||||
prefix := label.Prefix + label.BaseFrontendErrorPage
|
||||
return label.ParseErrorPages(labels, prefix, label.RegexpFrontendErrorPage)
|
||||
}
|
||||
|
||||
func getRateLimit(labels map[string]string) *types.RateLimit {
|
||||
extractorFunc := label.GetStringValue(labels, label.TraefikFrontendRateLimitExtractorFunc, "")
|
||||
if len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
prefix := label.Prefix + label.BaseFrontendRateLimit
|
||||
limits := label.ParseRateSets(labels, prefix, label.RegexpFrontendRateLimit)
|
||||
|
||||
return &types.RateLimit{
|
||||
ExtractorFunc: extractorFunc,
|
||||
RateSet: limits,
|
||||
}
|
||||
}
|
||||
|
||||
func getHeaders(labels map[string]string) *types.Headers {
|
||||
headers := &types.Headers{
|
||||
CustomRequestHeaders: label.GetMapValue(labels, label.TraefikFrontendRequestHeaders),
|
||||
CustomResponseHeaders: label.GetMapValue(labels, label.TraefikFrontendResponseHeaders),
|
||||
SSLProxyHeaders: label.GetMapValue(labels, label.TraefikFrontendSSLProxyHeaders),
|
||||
AllowedHosts: label.GetSliceStringValue(labels, label.TraefikFrontendAllowedHosts),
|
||||
HostsProxyHeaders: label.GetSliceStringValue(labels, label.TraefikFrontendHostsProxyHeaders),
|
||||
STSSeconds: label.GetInt64Value(labels, label.TraefikFrontendSTSSeconds, 0),
|
||||
SSLRedirect: label.GetBoolValue(labels, label.TraefikFrontendSSLRedirect, false),
|
||||
SSLTemporaryRedirect: label.GetBoolValue(labels, label.TraefikFrontendSSLTemporaryRedirect, false),
|
||||
STSIncludeSubdomains: label.GetBoolValue(labels, label.TraefikFrontendSTSIncludeSubdomains, false),
|
||||
STSPreload: label.GetBoolValue(labels, label.TraefikFrontendSTSPreload, false),
|
||||
ForceSTSHeader: label.GetBoolValue(labels, label.TraefikFrontendForceSTSHeader, false),
|
||||
FrameDeny: label.GetBoolValue(labels, label.TraefikFrontendFrameDeny, false),
|
||||
ContentTypeNosniff: label.GetBoolValue(labels, label.TraefikFrontendContentTypeNosniff, false),
|
||||
BrowserXSSFilter: label.GetBoolValue(labels, label.TraefikFrontendBrowserXSSFilter, false),
|
||||
IsDevelopment: label.GetBoolValue(labels, label.TraefikFrontendIsDevelopment, false),
|
||||
SSLHost: label.GetStringValue(labels, label.TraefikFrontendSSLHost, ""),
|
||||
CustomFrameOptionsValue: label.GetStringValue(labels, label.TraefikFrontendCustomFrameOptionsValue, ""),
|
||||
ContentSecurityPolicy: label.GetStringValue(labels, label.TraefikFrontendContentSecurityPolicy, ""),
|
||||
PublicKey: label.GetStringValue(labels, label.TraefikFrontendPublicKey, ""),
|
||||
ReferrerPolicy: label.GetStringValue(labels, label.TraefikFrontendReferrerPolicy, ""),
|
||||
CustomBrowserXSSValue: label.GetStringValue(labels, label.TraefikFrontendCustomBrowserXSSValue, ""),
|
||||
}
|
||||
|
||||
if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func getPort(container dockerData) string {
|
||||
if value := label.GetStringValue(container.SegmentLabels, label.TraefikPort, ""); len(value) != 0 {
|
||||
return value
|
||||
|
@ -399,89 +299,6 @@ func getPort(container dockerData) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func getMaxConn(labels map[string]string) *types.MaxConn {
|
||||
amount := label.GetInt64Value(labels, label.TraefikBackendMaxConnAmount, math.MinInt64)
|
||||
extractorFunc := label.GetStringValue(labels, label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc)
|
||||
|
||||
if amount == math.MinInt64 || len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.MaxConn{
|
||||
Amount: amount,
|
||||
ExtractorFunc: extractorFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func getHealthCheck(labels map[string]string) *types.HealthCheck {
|
||||
path := label.GetStringValue(labels, label.TraefikBackendHealthCheckPath, "")
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
port := label.GetIntValue(labels, label.TraefikBackendHealthCheckPort, label.DefaultBackendHealthCheckPort)
|
||||
interval := label.GetStringValue(labels, label.TraefikBackendHealthCheckInterval, "")
|
||||
|
||||
return &types.HealthCheck{
|
||||
Path: path,
|
||||
Port: port,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
func getBuffering(labels map[string]string) *types.Buffering {
|
||||
if !label.HasPrefix(labels, label.TraefikBackendBuffering) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.Buffering{
|
||||
MaxRequestBodyBytes: label.GetInt64Value(labels, label.TraefikBackendBufferingMaxRequestBodyBytes, 0),
|
||||
MaxResponseBodyBytes: label.GetInt64Value(labels, label.TraefikBackendBufferingMaxResponseBodyBytes, 0),
|
||||
MemRequestBodyBytes: label.GetInt64Value(labels, label.TraefikBackendBufferingMemRequestBodyBytes, 0),
|
||||
MemResponseBodyBytes: label.GetInt64Value(labels, label.TraefikBackendBufferingMemResponseBodyBytes, 0),
|
||||
RetryExpression: label.GetStringValue(labels, label.TraefikBackendBufferingRetryExpression, ""),
|
||||
}
|
||||
}
|
||||
|
||||
func getCircuitBreaker(labels map[string]string) *types.CircuitBreaker {
|
||||
circuitBreaker := label.GetStringValue(labels, label.TraefikBackendCircuitBreakerExpression, "")
|
||||
if len(circuitBreaker) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &types.CircuitBreaker{Expression: circuitBreaker}
|
||||
}
|
||||
|
||||
func getLoadBalancer(labels map[string]string) *types.LoadBalancer {
|
||||
if !label.HasPrefix(labels, label.TraefikBackendLoadBalancer) {
|
||||
return nil
|
||||
}
|
||||
|
||||
method := label.GetStringValue(labels, label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod)
|
||||
|
||||
lb := &types.LoadBalancer{
|
||||
Method: method,
|
||||
Sticky: getSticky(labels),
|
||||
}
|
||||
|
||||
if label.GetBoolValue(labels, label.TraefikBackendLoadBalancerStickiness, false) {
|
||||
cookieName := label.GetStringValue(labels, label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName)
|
||||
lb.Stickiness = &types.Stickiness{CookieName: cookieName}
|
||||
}
|
||||
|
||||
return lb
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
// replaced by Stickiness
|
||||
// Deprecated
|
||||
func getSticky(labels map[string]string) bool {
|
||||
if label.Has(labels, label.TraefikBackendLoadBalancerSticky) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
||||
}
|
||||
|
||||
return label.GetBoolValue(labels, label.TraefikBackendLoadBalancerSticky, false)
|
||||
}
|
||||
|
||||
func (p *Provider) getServers(containers []dockerData) map[string]types.Server {
|
||||
var servers map[string]types.Server
|
||||
|
||||
|
@ -507,27 +324,3 @@ func (p *Provider) getServers(containers []dockerData) map[string]types.Server {
|
|||
|
||||
return servers
|
||||
}
|
||||
|
||||
func getFuncStringLabel(labelName string, defaultValue string) func(map[string]string) string {
|
||||
return func(labels map[string]string) string {
|
||||
return label.GetStringValue(labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncIntLabel(labelName string, defaultValue int) func(map[string]string) int {
|
||||
return func(labels map[string]string) int {
|
||||
return label.GetIntValue(labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncBoolLabel(labelName string, defaultValue bool) func(map[string]string) bool {
|
||||
return func(labels map[string]string) bool {
|
||||
return label.GetBoolValue(labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncSliceStringLabel(labelName string) func(map[string]string) []string {
|
||||
return func(labels map[string]string) []string {
|
||||
return label.GetSliceStringValue(labels, labelName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -645,104 +645,6 @@ func TestDockerTraefikFilter(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDockerGetFuncStringLabel(t *testing.T) {
|
||||
testCases := []struct {
|
||||
labels map[string]string
|
||||
labelName string
|
||||
defaultValue string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
labels: nil,
|
||||
labelName: label.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
expected: "0",
|
||||
},
|
||||
{
|
||||
labels: map[string]string{
|
||||
label.TraefikWeight: "10",
|
||||
},
|
||||
labelName: label.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
expected: "10",
|
||||
},
|
||||
}
|
||||
|
||||
for containerID, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.labelName+strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getFuncStringLabel(test.labelName, test.defaultValue)(test.labels)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetSliceStringLabel(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
labelName string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
desc: "no whitelist-label",
|
||||
labels: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with empty string",
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListSourceRange: "",
|
||||
},
|
||||
labelName: label.TraefikFrontendWhiteListSourceRange,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with IPv4 mask",
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListSourceRange: "1.2.3.4/16",
|
||||
},
|
||||
labelName: label.TraefikFrontendWhiteListSourceRange,
|
||||
expected: []string{
|
||||
"1.2.3.4/16",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with IPv6 mask",
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListSourceRange: "fe80::/16",
|
||||
},
|
||||
labelName: label.TraefikFrontendWhiteListSourceRange,
|
||||
expected: []string{
|
||||
"fe80::/16",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with multiple masks",
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListSourceRange: "1.1.1.1/24, 1234:abcd::42/32",
|
||||
},
|
||||
labelName: label.TraefikFrontendWhiteListSourceRange,
|
||||
expected: []string{
|
||||
"1.1.1.1/24",
|
||||
"1234:abcd::42/32",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getFuncSliceStringLabel(test.labelName)(test.labels)
|
||||
assert.EqualValues(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetFrontendName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
container docker.ContainerJSON
|
||||
|
@ -1027,85 +929,3 @@ func TestDockerGetPort(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhiteList(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.WhiteList
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no white list labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when deprecated label",
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhitelistSourceRange: "10.10.10.10",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when only range",
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when range and UseXForwardedFor",
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when mix deprecated label and new labels",
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhitelistSourceRange: "20.20.20.20",
|
||||
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return nil when only UseXForwardedFor",
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getWhiteList(test.labels)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,7 +26,6 @@ const (
|
|||
DefaultFrontendPriority = "0" // TODO [breaking] int value
|
||||
DefaultFrontendPriorityInt = 0 // TODO rename to DefaultFrontendPriority
|
||||
DefaultCircuitBreakerExpression = "NetworkErrorRatio() > 1"
|
||||
DefaultFrontendRedirectEntryPoint = ""
|
||||
DefaultBackendLoadBalancerMethod = "wrr"
|
||||
DefaultBackendMaxconnExtractorFunc = "request.host"
|
||||
DefaultBackendLoadbalancerStickinessCookieName = ""
|
||||
|
@ -57,14 +54,6 @@ func GetStringValue(labels map[string]string, labelName string, defaultValue str
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
// GetStringValueP get string value associated to a label
|
||||
func GetStringValueP(labels *map[string]string, labelName string, defaultValue string) string {
|
||||
if labels == nil {
|
||||
return defaultValue
|
||||
}
|
||||
return GetStringValue(*labels, labelName, defaultValue)
|
||||
}
|
||||
|
||||
// GetBoolValue get bool value associated to a label
|
||||
func GetBoolValue(labels map[string]string, labelName string, defaultValue bool) bool {
|
||||
rawValue, ok := labels[labelName]
|
||||
|
@ -77,14 +66,6 @@ func GetBoolValue(labels map[string]string, labelName string, defaultValue bool)
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
// GetBoolValueP get bool value associated to a label
|
||||
func GetBoolValueP(labels *map[string]string, labelName string, defaultValue bool) bool {
|
||||
if labels == nil {
|
||||
return defaultValue
|
||||
}
|
||||
return GetBoolValue(*labels, labelName, defaultValue)
|
||||
}
|
||||
|
||||
// GetIntValue get int value associated to a label
|
||||
func GetIntValue(labels map[string]string, labelName string, defaultValue int) int {
|
||||
if rawValue, ok := labels[labelName]; ok {
|
||||
|
@ -97,14 +78,6 @@ func GetIntValue(labels map[string]string, labelName string, defaultValue int) i
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
// GetIntValueP get int value associated to a label
|
||||
func GetIntValueP(labels *map[string]string, labelName string, defaultValue int) int {
|
||||
if labels == nil {
|
||||
return defaultValue
|
||||
}
|
||||
return GetIntValue(*labels, labelName, defaultValue)
|
||||
}
|
||||
|
||||
// GetInt64Value get int64 value associated to a label
|
||||
func GetInt64Value(labels map[string]string, labelName string, defaultValue int64) int64 {
|
||||
if rawValue, ok := labels[labelName]; ok {
|
||||
|
@ -117,14 +90,6 @@ func GetInt64Value(labels map[string]string, labelName string, defaultValue int6
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
// GetInt64ValueP get int64 value associated to a label
|
||||
func GetInt64ValueP(labels *map[string]string, labelName string, defaultValue int64) int64 {
|
||||
if labels == nil {
|
||||
return defaultValue
|
||||
}
|
||||
return GetInt64Value(*labels, labelName, defaultValue)
|
||||
}
|
||||
|
||||
// GetSliceStringValue get a slice of string associated to a label
|
||||
func GetSliceStringValue(labels map[string]string, labelName string) []string {
|
||||
var value []string
|
||||
|
@ -139,14 +104,6 @@ func GetSliceStringValue(labels map[string]string, labelName string) []string {
|
|||
return value
|
||||
}
|
||||
|
||||
// GetSliceStringValueP get a slice of string value associated to a label
|
||||
func GetSliceStringValueP(labels *map[string]string, labelName string) []string {
|
||||
if labels == nil {
|
||||
return nil
|
||||
}
|
||||
return GetSliceStringValue(*labels, labelName)
|
||||
}
|
||||
|
||||
// ParseMapValue get Map value for a label value
|
||||
func ParseMapValue(labelName, values string) map[string]string {
|
||||
mapValue := make(map[string]string)
|
||||
|
@ -203,14 +160,6 @@ func Has(labels map[string]string, labelName string) bool {
|
|||
return ok && len(value) > 0
|
||||
}
|
||||
|
||||
// HasP Check if a value is associated to a label
|
||||
func HasP(labels *map[string]string, labelName string) bool {
|
||||
if labels == nil {
|
||||
return false
|
||||
}
|
||||
return Has(*labels, labelName)
|
||||
}
|
||||
|
||||
// HasPrefix Check if a value is associated to a less one label with a prefix
|
||||
func HasPrefix(labels map[string]string, prefix string) bool {
|
||||
for name, value := range labels {
|
||||
|
@ -221,124 +170,11 @@ func HasPrefix(labels map[string]string, prefix string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// HasPrefixP Check if a value is associated to a less one label with a prefix
|
||||
func HasPrefixP(labels *map[string]string, prefix string) bool {
|
||||
if labels == nil {
|
||||
return false
|
||||
}
|
||||
return HasPrefix(*labels, prefix)
|
||||
}
|
||||
|
||||
// ParseErrorPages parse error pages to create ErrorPage struct
|
||||
func ParseErrorPages(labels map[string]string, labelPrefix string, labelRegex *regexp.Regexp) map[string]*types.ErrorPage {
|
||||
var errorPages map[string]*types.ErrorPage
|
||||
|
||||
for lblName, value := range labels {
|
||||
if strings.HasPrefix(lblName, labelPrefix) {
|
||||
submatch := labelRegex.FindStringSubmatch(lblName)
|
||||
if len(submatch) != 3 {
|
||||
log.Errorf("Invalid page error label: %s, sub-match: %v", lblName, submatch)
|
||||
continue
|
||||
}
|
||||
|
||||
if errorPages == nil {
|
||||
errorPages = make(map[string]*types.ErrorPage)
|
||||
}
|
||||
|
||||
pageName := submatch[1]
|
||||
|
||||
ep, ok := errorPages[pageName]
|
||||
if !ok {
|
||||
ep = &types.ErrorPage{}
|
||||
errorPages[pageName] = ep
|
||||
}
|
||||
|
||||
switch submatch[2] {
|
||||
case SuffixErrorPageStatus:
|
||||
ep.Status = SplitAndTrimString(value, ",")
|
||||
case SuffixErrorPageQuery:
|
||||
ep.Query = value
|
||||
case SuffixErrorPageBackend:
|
||||
ep.Backend = value
|
||||
default:
|
||||
log.Errorf("Invalid page error label: %s", lblName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errorPages
|
||||
}
|
||||
|
||||
// ParseRateSets parse rate limits to create Rate struct
|
||||
func ParseRateSets(labels map[string]string, labelPrefix string, labelRegex *regexp.Regexp) map[string]*types.Rate {
|
||||
var rateSets map[string]*types.Rate
|
||||
|
||||
for lblName, rawValue := range labels {
|
||||
if strings.HasPrefix(lblName, labelPrefix) && len(rawValue) > 0 {
|
||||
submatch := labelRegex.FindStringSubmatch(lblName)
|
||||
if len(submatch) != 3 {
|
||||
log.Errorf("Invalid rate limit label: %s, sub-match: %v", lblName, submatch)
|
||||
continue
|
||||
}
|
||||
|
||||
if rateSets == nil {
|
||||
rateSets = make(map[string]*types.Rate)
|
||||
}
|
||||
|
||||
limitName := submatch[1]
|
||||
|
||||
ep, ok := rateSets[limitName]
|
||||
if !ok {
|
||||
ep = &types.Rate{}
|
||||
rateSets[limitName] = ep
|
||||
}
|
||||
|
||||
switch submatch[2] {
|
||||
case "period":
|
||||
var d flaeg.Duration
|
||||
err := d.Set(rawValue)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to parse %q: %q. %v", lblName, rawValue, err)
|
||||
continue
|
||||
}
|
||||
ep.Period = d
|
||||
case "average":
|
||||
value, err := strconv.ParseInt(rawValue, 10, 64)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to parse %q: %q. %v", lblName, rawValue, err)
|
||||
continue
|
||||
}
|
||||
ep.Average = value
|
||||
case "burst":
|
||||
value, err := strconv.ParseInt(rawValue, 10, 64)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to parse %q: %q. %v", lblName, rawValue, err)
|
||||
continue
|
||||
}
|
||||
ep.Burst = value
|
||||
default:
|
||||
log.Errorf("Invalid rate limit label: %s", lblName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return rateSets
|
||||
}
|
||||
|
||||
// IsEnabled Check if a container is enabled in Træfik
|
||||
func IsEnabled(labels map[string]string, exposedByDefault bool) bool {
|
||||
return GetBoolValue(labels, TraefikEnable, exposedByDefault)
|
||||
}
|
||||
|
||||
// IsEnabledP Check if a container is enabled in Træfik
|
||||
func IsEnabledP(labels *map[string]string, exposedByDefault bool) bool {
|
||||
if labels == nil {
|
||||
return exposedByDefault
|
||||
}
|
||||
return IsEnabled(*labels, exposedByDefault)
|
||||
}
|
||||
|
||||
// SplitAndTrimString splits separatedString at the separator character and trims each
|
||||
// piece, filtering out empty pieces. Returns the list of pieces or nil if the input
|
||||
// did not contain a non-empty piece.
|
||||
|
@ -354,3 +190,31 @@ func SplitAndTrimString(base string, sep string) []string {
|
|||
|
||||
return trimmedStrings
|
||||
}
|
||||
|
||||
// GetFuncString a func related to GetStringValue
|
||||
func GetFuncString(labelName string, defaultValue string) func(map[string]string) string {
|
||||
return func(labels map[string]string) string {
|
||||
return GetStringValue(labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// GetFuncInt a func related to GetIntValue
|
||||
func GetFuncInt(labelName string, defaultValue int) func(map[string]string) int {
|
||||
return func(labels map[string]string) int {
|
||||
return GetIntValue(labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// GetFuncBool a func related to GetBoolValue
|
||||
func GetFuncBool(labelName string, defaultValue bool) func(map[string]string) bool {
|
||||
return func(labels map[string]string) bool {
|
||||
return GetBoolValue(labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// GetFuncSliceString a func related to GetSliceStringValue
|
||||
func GetFuncSliceString(labelName string) func(map[string]string) []string {
|
||||
return func(labels map[string]string) []string {
|
||||
return GetSliceStringValue(labels, labelName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
package label
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -108,51 +106,6 @@ func TestGetStringValue(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetStringValueP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
labelName string
|
||||
defaultValue string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
labels: nil,
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: "default",
|
||||
},
|
||||
{
|
||||
desc: "existing label",
|
||||
labels: &map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: "bar",
|
||||
},
|
||||
{
|
||||
desc: "non existing label",
|
||||
labels: &map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
labelName: "fii",
|
||||
defaultValue: "default",
|
||||
expected: "default",
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := GetStringValueP(test.labels, test.labelName, test.defaultValue)
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBoolValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -255,60 +208,6 @@ func TestGetIntValue(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetIntValueP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
labelName string
|
||||
defaultValue int
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
desc: "nil map",
|
||||
labels: nil,
|
||||
labelName: "foo",
|
||||
defaultValue: 666,
|
||||
expected: 666,
|
||||
},
|
||||
{
|
||||
desc: "invalid int value",
|
||||
labelName: "foo",
|
||||
labels: &map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
defaultValue: 666,
|
||||
expected: 666,
|
||||
},
|
||||
{
|
||||
desc: "negative int value",
|
||||
labelName: "foo",
|
||||
labels: &map[string]string{
|
||||
"foo": "-1",
|
||||
},
|
||||
defaultValue: 666,
|
||||
expected: -1,
|
||||
},
|
||||
{
|
||||
desc: "positive int value",
|
||||
labelName: "foo",
|
||||
labels: &map[string]string{
|
||||
"foo": "1",
|
||||
},
|
||||
defaultValue: 666,
|
||||
expected: 1,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := GetIntValueP(test.labels, test.labelName, test.defaultValue)
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInt64Value(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -360,60 +259,6 @@ func TestGetInt64Value(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetInt64ValueP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
labelName string
|
||||
defaultValue int64
|
||||
expected int64
|
||||
}{
|
||||
{
|
||||
desc: "nil map",
|
||||
labels: nil,
|
||||
labelName: "foo",
|
||||
defaultValue: 666,
|
||||
expected: 666,
|
||||
},
|
||||
{
|
||||
desc: "invalid int value",
|
||||
labelName: "foo",
|
||||
labels: &map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
defaultValue: 666,
|
||||
expected: 666,
|
||||
},
|
||||
{
|
||||
desc: "negative int value",
|
||||
labelName: "foo",
|
||||
labels: &map[string]string{
|
||||
"foo": "-1",
|
||||
},
|
||||
defaultValue: 666,
|
||||
expected: -1,
|
||||
},
|
||||
{
|
||||
desc: "positive int value",
|
||||
labelName: "foo",
|
||||
labels: &map[string]string{
|
||||
"foo": "1",
|
||||
},
|
||||
defaultValue: 666,
|
||||
expected: 1,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := GetInt64ValueP(test.labels, test.labelName, test.defaultValue)
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSliceStringValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -461,47 +306,6 @@ func TestGetSliceStringValue(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetSliceStringValueP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
labelName string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
desc: "nil map",
|
||||
labels: nil,
|
||||
labelName: "foo",
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "one value, not split",
|
||||
labels: &map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
labelName: "foo",
|
||||
expected: []string{"bar"},
|
||||
},
|
||||
{
|
||||
desc: "several values",
|
||||
labels: &map[string]string{
|
||||
"foo": "bar,bir ,bur",
|
||||
},
|
||||
labelName: "foo",
|
||||
expected: []string{"bar", "bir", "bur"},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := GetSliceStringValueP(test.labels, test.labelName)
|
||||
assert.EqualValues(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMapValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -680,149 +484,6 @@ func TestHas(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHasP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
labelName string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
labelName: "foo",
|
||||
},
|
||||
{
|
||||
desc: "nonexistent label",
|
||||
labels: &map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
labelName: "fii",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "existent label",
|
||||
labels: &map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
labelName: "foo",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "existent label with empty value",
|
||||
labels: &map[string]string{
|
||||
"foo": "",
|
||||
},
|
||||
labelName: "foo",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := HasP(test.labels, test.labelName)
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractServiceProperties(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "empty labels map",
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
{
|
||||
desc: "valid label names",
|
||||
labels: map[string]string{
|
||||
"traefik.foo.port": "bar",
|
||||
"traefik.foo.frontend.bar": "1bar",
|
||||
"traefik.foo.backend": "3bar",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"foo": SegmentPropertyValues{
|
||||
"port": "bar",
|
||||
"frontend.bar": "1bar",
|
||||
"backend": "3bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid label names",
|
||||
labels: map[string]string{
|
||||
"foo.frontend.bar": "1bar",
|
||||
"traefik.foo.frontend.": "2bar",
|
||||
"traefik.foo.port.bar": "barbar",
|
||||
"traefik.foo.frontend": "0bar",
|
||||
"traefik.frontend.foo.backend": "0bar",
|
||||
},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := ExtractServiceProperties(test.labels)
|
||||
assert.EqualValues(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractServicePropertiesP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
{
|
||||
desc: "valid label names",
|
||||
labels: &map[string]string{
|
||||
"traefik.foo.port": "bar",
|
||||
"traefik.foo.frontend.bar": "1bar",
|
||||
"traefik.foo.backend": "3bar",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"foo": SegmentPropertyValues{
|
||||
"port": "bar",
|
||||
"frontend.bar": "1bar",
|
||||
"backend": "3bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid label names",
|
||||
labels: &map[string]string{
|
||||
"foo.frontend.bar": "1bar",
|
||||
"traefik.foo.frontend.": "2bar",
|
||||
"traefik.foo.port.bar": "barbar",
|
||||
"traefik.foo.frontend": "0bar",
|
||||
"traefik.frontend.foo.backend": "0bar",
|
||||
},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := ExtractServicePropertiesP(test.labels)
|
||||
assert.EqualValues(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEnabled(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -884,97 +545,6 @@ func TestIsEnabled(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIsEnabledP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
exposedByDefault bool
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map & exposedByDefault true",
|
||||
exposedByDefault: true,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "nil labels map & exposedByDefault false",
|
||||
exposedByDefault: false,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "exposedByDefault false and label enable true",
|
||||
labels: &map[string]string{
|
||||
TraefikEnable: "true",
|
||||
},
|
||||
exposedByDefault: false,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "exposedByDefault false and label enable false",
|
||||
labels: &map[string]string{
|
||||
TraefikEnable: "false",
|
||||
},
|
||||
exposedByDefault: false,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "exposedByDefault true and label enable false",
|
||||
labels: &map[string]string{
|
||||
TraefikEnable: "false",
|
||||
},
|
||||
exposedByDefault: true,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "exposedByDefault true and label enable true",
|
||||
labels: &map[string]string{
|
||||
TraefikEnable: "true",
|
||||
},
|
||||
exposedByDefault: true,
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := IsEnabledP(test.labels, test.exposedByDefault)
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServiceLabel(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labelName string
|
||||
serviceName string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "without service name",
|
||||
labelName: TraefikPort,
|
||||
expected: TraefikPort,
|
||||
},
|
||||
{
|
||||
desc: "with service name",
|
||||
labelName: TraefikPort,
|
||||
serviceName: "bar",
|
||||
expected: "traefik.bar.port",
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := GetServiceLabel(test.labelName, test.serviceName)
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPrefix(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -1023,205 +593,100 @@ func TestHasPrefix(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseErrorPages(t *testing.T) {
|
||||
func TestGetFuncString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected map[string]*types.ErrorPage
|
||||
labels map[string]string
|
||||
labelName string
|
||||
defaultValue string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "2 errors pages",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageStatus: "404",
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageBackend: "foo_backend",
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageQuery: "foo_query",
|
||||
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageStatus: "500,600",
|
||||
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageBackend: "bar_backend",
|
||||
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageQuery: "bar_query",
|
||||
},
|
||||
expected: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
Query: "foo_query",
|
||||
Backend: "foo_backend",
|
||||
},
|
||||
"bar": {
|
||||
Status: []string{"500", "600"},
|
||||
Query: "bar_query",
|
||||
Backend: "bar_backend",
|
||||
},
|
||||
},
|
||||
labels: nil,
|
||||
labelName: TraefikWeight,
|
||||
defaultValue: DefaultWeight,
|
||||
expected: "0",
|
||||
},
|
||||
{
|
||||
desc: "only status field",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageStatus: "404",
|
||||
TraefikWeight: "10",
|
||||
},
|
||||
expected: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid field",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendErrorPage + "foo." + "courgette": "404",
|
||||
},
|
||||
expected: map[string]*types.ErrorPage{"foo": {}},
|
||||
},
|
||||
{
|
||||
desc: "no error pages labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
labelName: TraefikWeight,
|
||||
defaultValue: DefaultWeight,
|
||||
expected: "10",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
for containerID, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Run(test.labelName+strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pages := ParseErrorPages(test.labels, Prefix+BaseFrontendErrorPage, RegexpFrontendErrorPage)
|
||||
|
||||
assert.EqualValues(t, test.expected, pages)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRateSets(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected map[string]*types.Rate
|
||||
}{
|
||||
{
|
||||
desc: "2 rate limits",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitPeriod: "6",
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitAverage: "12",
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitBurst: "18",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitPeriod: "3",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitAverage: "6",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitBurst: "9",
|
||||
},
|
||||
expected: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
desc: "no rate limits labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rateSets := ParseRateSets(test.labels, Prefix+BaseFrontendRateLimit, RegexpFrontendRateLimit)
|
||||
|
||||
assert.EqualValues(t, test.expected, rateSets)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractTraefikLabels(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
prefix string
|
||||
originLabels map[string]string
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
prefix: "traefik",
|
||||
originLabels: nil,
|
||||
expected: SegmentProperties{"": {}},
|
||||
},
|
||||
{
|
||||
desc: "container labels",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"frontend.priority": "foo", // missing prefix: skip
|
||||
"traefik.port": "bar",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"": {
|
||||
"traefik.port": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: only segment no default",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.goo.frontend.priority": "A",
|
||||
"traefik.goo.port": "D",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"goo": {
|
||||
"traefik.frontend.priority": "A",
|
||||
"traefik.port": "D",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: use default",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.guu.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"guu": {
|
||||
"traefik.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: several segments",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.goo.frontend.priority": "A",
|
||||
"traefik.goo.port": "D",
|
||||
"traefik.guu.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"goo": {
|
||||
"traefik.frontend.priority": "A",
|
||||
"traefik.port": "D",
|
||||
},
|
||||
"guu": {
|
||||
"traefik.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := ExtractTraefikLabels(test.originLabels)
|
||||
actual := GetFuncString(test.labelName, test.defaultValue)(test.labels)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSliceString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
labelName string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
desc: "no whitelist-label",
|
||||
labels: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with empty string",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendWhiteListSourceRange: "",
|
||||
},
|
||||
labelName: TraefikFrontendWhiteListSourceRange,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with IPv4 mask",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendWhiteListSourceRange: "1.2.3.4/16",
|
||||
},
|
||||
labelName: TraefikFrontendWhiteListSourceRange,
|
||||
expected: []string{
|
||||
"1.2.3.4/16",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with IPv6 mask",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendWhiteListSourceRange: "fe80::/16",
|
||||
},
|
||||
labelName: TraefikFrontendWhiteListSourceRange,
|
||||
expected: []string{
|
||||
"fe80::/16",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with multiple masks",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendWhiteListSourceRange: "1.1.1.1/24, 1234:abcd::42/32",
|
||||
},
|
||||
labelName: TraefikFrontendWhiteListSourceRange,
|
||||
expected: []string{
|
||||
"1.1.1.1/24",
|
||||
"1234:abcd::42/32",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetFuncSliceString(test.labelName)(test.labels)
|
||||
assert.EqualValues(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
301
provider/label/partial.go
Normal file
301
provider/label/partial.go
Normal file
|
@ -0,0 +1,301 @@
|
|||
package label
|
||||
|
||||
import (
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
// GetWhiteList Create white list from labels
|
||||
func GetWhiteList(labels map[string]string) *types.WhiteList {
|
||||
if Has(labels, TraefikFrontendWhitelistSourceRange) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", TraefikFrontendWhitelistSourceRange, TraefikFrontendWhiteListSourceRange)
|
||||
}
|
||||
|
||||
ranges := GetSliceStringValue(labels, TraefikFrontendWhiteListSourceRange)
|
||||
if len(ranges) > 0 {
|
||||
return &types.WhiteList{
|
||||
SourceRange: ranges,
|
||||
UseXForwardedFor: GetBoolValue(labels, TraefikFrontendWhiteListUseXForwardedFor, false),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
values := GetSliceStringValue(labels, TraefikFrontendWhitelistSourceRange)
|
||||
if len(values) > 0 {
|
||||
return &types.WhiteList{
|
||||
SourceRange: values,
|
||||
UseXForwardedFor: false,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRedirect Create redirect from labels
|
||||
func GetRedirect(labels map[string]string) *types.Redirect {
|
||||
permanent := GetBoolValue(labels, TraefikFrontendRedirectPermanent, false)
|
||||
|
||||
if Has(labels, TraefikFrontendRedirectEntryPoint) {
|
||||
return &types.Redirect{
|
||||
EntryPoint: GetStringValue(labels, TraefikFrontendRedirectEntryPoint, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
if Has(labels, TraefikFrontendRedirectRegex) &&
|
||||
Has(labels, TraefikFrontendRedirectReplacement) {
|
||||
return &types.Redirect{
|
||||
Regex: GetStringValue(labels, TraefikFrontendRedirectRegex, ""),
|
||||
Replacement: GetStringValue(labels, TraefikFrontendRedirectReplacement, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetErrorPages Create error pages from labels
|
||||
func GetErrorPages(labels map[string]string) map[string]*types.ErrorPage {
|
||||
prefix := Prefix + BaseFrontendErrorPage
|
||||
return ParseErrorPages(labels, prefix, RegexpFrontendErrorPage)
|
||||
}
|
||||
|
||||
// ParseErrorPages parse error pages to create ErrorPage struct
|
||||
func ParseErrorPages(labels map[string]string, labelPrefix string, labelRegex *regexp.Regexp) map[string]*types.ErrorPage {
|
||||
var errorPages map[string]*types.ErrorPage
|
||||
|
||||
for lblName, value := range labels {
|
||||
if strings.HasPrefix(lblName, labelPrefix) {
|
||||
submatch := labelRegex.FindStringSubmatch(lblName)
|
||||
if len(submatch) != 3 {
|
||||
log.Errorf("Invalid page error label: %s, sub-match: %v", lblName, submatch)
|
||||
continue
|
||||
}
|
||||
|
||||
if errorPages == nil {
|
||||
errorPages = make(map[string]*types.ErrorPage)
|
||||
}
|
||||
|
||||
pageName := submatch[1]
|
||||
|
||||
ep, ok := errorPages[pageName]
|
||||
if !ok {
|
||||
ep = &types.ErrorPage{}
|
||||
errorPages[pageName] = ep
|
||||
}
|
||||
|
||||
switch submatch[2] {
|
||||
case SuffixErrorPageStatus:
|
||||
ep.Status = SplitAndTrimString(value, ",")
|
||||
case SuffixErrorPageQuery:
|
||||
ep.Query = value
|
||||
case SuffixErrorPageBackend:
|
||||
ep.Backend = value
|
||||
default:
|
||||
log.Errorf("Invalid page error label: %s", lblName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errorPages
|
||||
}
|
||||
|
||||
// GetRateLimit Create rate limits from labels
|
||||
func GetRateLimit(labels map[string]string) *types.RateLimit {
|
||||
extractorFunc := GetStringValue(labels, TraefikFrontendRateLimitExtractorFunc, "")
|
||||
if len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
prefix := Prefix + BaseFrontendRateLimit
|
||||
limits := ParseRateSets(labels, prefix, RegexpFrontendRateLimit)
|
||||
|
||||
return &types.RateLimit{
|
||||
ExtractorFunc: extractorFunc,
|
||||
RateSet: limits,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseRateSets parse rate limits to create Rate struct
|
||||
func ParseRateSets(labels map[string]string, labelPrefix string, labelRegex *regexp.Regexp) map[string]*types.Rate {
|
||||
var rateSets map[string]*types.Rate
|
||||
|
||||
for lblName, rawValue := range labels {
|
||||
if strings.HasPrefix(lblName, labelPrefix) && len(rawValue) > 0 {
|
||||
submatch := labelRegex.FindStringSubmatch(lblName)
|
||||
if len(submatch) != 3 {
|
||||
log.Errorf("Invalid rate limit label: %s, sub-match: %v", lblName, submatch)
|
||||
continue
|
||||
}
|
||||
|
||||
if rateSets == nil {
|
||||
rateSets = make(map[string]*types.Rate)
|
||||
}
|
||||
|
||||
limitName := submatch[1]
|
||||
|
||||
ep, ok := rateSets[limitName]
|
||||
if !ok {
|
||||
ep = &types.Rate{}
|
||||
rateSets[limitName] = ep
|
||||
}
|
||||
|
||||
switch submatch[2] {
|
||||
case "period":
|
||||
var d flaeg.Duration
|
||||
err := d.Set(rawValue)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to parse %q: %q. %v", lblName, rawValue, err)
|
||||
continue
|
||||
}
|
||||
ep.Period = d
|
||||
case "average":
|
||||
value, err := strconv.ParseInt(rawValue, 10, 64)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to parse %q: %q. %v", lblName, rawValue, err)
|
||||
continue
|
||||
}
|
||||
ep.Average = value
|
||||
case "burst":
|
||||
value, err := strconv.ParseInt(rawValue, 10, 64)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to parse %q: %q. %v", lblName, rawValue, err)
|
||||
continue
|
||||
}
|
||||
ep.Burst = value
|
||||
default:
|
||||
log.Errorf("Invalid rate limit label: %s", lblName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return rateSets
|
||||
}
|
||||
|
||||
// GetHeaders Create headers from labels
|
||||
func GetHeaders(labels map[string]string) *types.Headers {
|
||||
headers := &types.Headers{
|
||||
CustomRequestHeaders: GetMapValue(labels, TraefikFrontendRequestHeaders),
|
||||
CustomResponseHeaders: GetMapValue(labels, TraefikFrontendResponseHeaders),
|
||||
SSLProxyHeaders: GetMapValue(labels, TraefikFrontendSSLProxyHeaders),
|
||||
AllowedHosts: GetSliceStringValue(labels, TraefikFrontendAllowedHosts),
|
||||
HostsProxyHeaders: GetSliceStringValue(labels, TraefikFrontendHostsProxyHeaders),
|
||||
STSSeconds: GetInt64Value(labels, TraefikFrontendSTSSeconds, 0),
|
||||
SSLRedirect: GetBoolValue(labels, TraefikFrontendSSLRedirect, false),
|
||||
SSLTemporaryRedirect: GetBoolValue(labels, TraefikFrontendSSLTemporaryRedirect, false),
|
||||
STSIncludeSubdomains: GetBoolValue(labels, TraefikFrontendSTSIncludeSubdomains, false),
|
||||
STSPreload: GetBoolValue(labels, TraefikFrontendSTSPreload, false),
|
||||
ForceSTSHeader: GetBoolValue(labels, TraefikFrontendForceSTSHeader, false),
|
||||
FrameDeny: GetBoolValue(labels, TraefikFrontendFrameDeny, false),
|
||||
ContentTypeNosniff: GetBoolValue(labels, TraefikFrontendContentTypeNosniff, false),
|
||||
BrowserXSSFilter: GetBoolValue(labels, TraefikFrontendBrowserXSSFilter, false),
|
||||
IsDevelopment: GetBoolValue(labels, TraefikFrontendIsDevelopment, false),
|
||||
SSLHost: GetStringValue(labels, TraefikFrontendSSLHost, ""),
|
||||
CustomFrameOptionsValue: GetStringValue(labels, TraefikFrontendCustomFrameOptionsValue, ""),
|
||||
ContentSecurityPolicy: GetStringValue(labels, TraefikFrontendContentSecurityPolicy, ""),
|
||||
PublicKey: GetStringValue(labels, TraefikFrontendPublicKey, ""),
|
||||
ReferrerPolicy: GetStringValue(labels, TraefikFrontendReferrerPolicy, ""),
|
||||
CustomBrowserXSSValue: GetStringValue(labels, TraefikFrontendCustomBrowserXSSValue, ""),
|
||||
}
|
||||
|
||||
if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
// GetMaxConn Create max connection from labels
|
||||
func GetMaxConn(labels map[string]string) *types.MaxConn {
|
||||
amount := GetInt64Value(labels, TraefikBackendMaxConnAmount, math.MinInt64)
|
||||
extractorFunc := GetStringValue(labels, TraefikBackendMaxConnExtractorFunc, DefaultBackendMaxconnExtractorFunc)
|
||||
|
||||
if amount == math.MinInt64 || len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.MaxConn{
|
||||
Amount: amount,
|
||||
ExtractorFunc: extractorFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// GetHealthCheck Create health check from labels
|
||||
func GetHealthCheck(labels map[string]string) *types.HealthCheck {
|
||||
path := GetStringValue(labels, TraefikBackendHealthCheckPath, "")
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
port := GetIntValue(labels, TraefikBackendHealthCheckPort, DefaultBackendHealthCheckPort)
|
||||
interval := GetStringValue(labels, TraefikBackendHealthCheckInterval, "")
|
||||
|
||||
return &types.HealthCheck{
|
||||
Path: path,
|
||||
Port: port,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBuffering Create buffering from labels
|
||||
func GetBuffering(labels map[string]string) *types.Buffering {
|
||||
if !HasPrefix(labels, TraefikBackendBuffering) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.Buffering{
|
||||
MaxRequestBodyBytes: GetInt64Value(labels, TraefikBackendBufferingMaxRequestBodyBytes, 0),
|
||||
MaxResponseBodyBytes: GetInt64Value(labels, TraefikBackendBufferingMaxResponseBodyBytes, 0),
|
||||
MemRequestBodyBytes: GetInt64Value(labels, TraefikBackendBufferingMemRequestBodyBytes, 0),
|
||||
MemResponseBodyBytes: GetInt64Value(labels, TraefikBackendBufferingMemResponseBodyBytes, 0),
|
||||
RetryExpression: GetStringValue(labels, TraefikBackendBufferingRetryExpression, ""),
|
||||
}
|
||||
}
|
||||
|
||||
// GetCircuitBreaker Create circuit breaker from labels
|
||||
func GetCircuitBreaker(labels map[string]string) *types.CircuitBreaker {
|
||||
circuitBreaker := GetStringValue(labels, TraefikBackendCircuitBreakerExpression, "")
|
||||
if len(circuitBreaker) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &types.CircuitBreaker{Expression: circuitBreaker}
|
||||
}
|
||||
|
||||
// GetLoadBalancer Create load balancer from labels
|
||||
func GetLoadBalancer(labels map[string]string) *types.LoadBalancer {
|
||||
if !HasPrefix(labels, TraefikBackendLoadBalancer) {
|
||||
return nil
|
||||
}
|
||||
|
||||
method := GetStringValue(labels, TraefikBackendLoadBalancerMethod, DefaultBackendLoadBalancerMethod)
|
||||
|
||||
lb := &types.LoadBalancer{
|
||||
Method: method,
|
||||
Sticky: getSticky(labels),
|
||||
}
|
||||
|
||||
if GetBoolValue(labels, TraefikBackendLoadBalancerStickiness, false) {
|
||||
cookieName := GetStringValue(labels, TraefikBackendLoadBalancerStickinessCookieName, DefaultBackendLoadbalancerStickinessCookieName)
|
||||
lb.Stickiness = &types.Stickiness{CookieName: cookieName}
|
||||
}
|
||||
|
||||
return lb
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
// replaced by Stickiness
|
||||
// Deprecated
|
||||
func getSticky(labels map[string]string) bool {
|
||||
if Has(labels, TraefikBackendLoadBalancerSticky) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", TraefikBackendLoadBalancerSticky, TraefikBackendLoadBalancerStickiness)
|
||||
}
|
||||
|
||||
return GetBoolValue(labels, TraefikBackendLoadBalancerSticky, false)
|
||||
}
|
711
provider/label/partial_test.go
Normal file
711
provider/label/partial_test.go
Normal file
|
@ -0,0 +1,711 @@
|
|||
package label
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseErrorPages(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected map[string]*types.ErrorPage
|
||||
}{
|
||||
{
|
||||
desc: "2 errors pages",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageStatus: "404",
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageBackend: "foo_backend",
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageQuery: "foo_query",
|
||||
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageStatus: "500,600",
|
||||
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageBackend: "bar_backend",
|
||||
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageQuery: "bar_query",
|
||||
},
|
||||
expected: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
Query: "foo_query",
|
||||
Backend: "foo_backend",
|
||||
},
|
||||
"bar": {
|
||||
Status: []string{"500", "600"},
|
||||
Query: "bar_query",
|
||||
Backend: "bar_backend",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "only status field",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageStatus: "404",
|
||||
},
|
||||
expected: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid field",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendErrorPage + "foo." + "courgette": "404",
|
||||
},
|
||||
expected: map[string]*types.ErrorPage{"foo": {}},
|
||||
},
|
||||
{
|
||||
desc: "no error pages labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pages := ParseErrorPages(test.labels, Prefix+BaseFrontendErrorPage, RegexpFrontendErrorPage)
|
||||
|
||||
assert.EqualValues(t, test.expected, pages)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRateSets(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected map[string]*types.Rate
|
||||
}{
|
||||
{
|
||||
desc: "2 rate limits",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitPeriod: "6",
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitAverage: "12",
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitBurst: "18",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitPeriod: "3",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitAverage: "6",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitBurst: "9",
|
||||
},
|
||||
expected: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "no rate limits labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rateSets := ParseRateSets(test.labels, Prefix+BaseFrontendRateLimit, RegexpFrontendRateLimit)
|
||||
|
||||
assert.EqualValues(t, test.expected, rateSets)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhiteList(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.WhiteList
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no white list labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when deprecated label",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendWhitelistSourceRange: "10.10.10.10",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when only range",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when range and UseXForwardedFor",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||
TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when mix deprecated label and new labels",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendWhitelistSourceRange: "20.20.20.20",
|
||||
TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||
TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return nil when only UseXForwardedFor",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetWhiteList(test.labels)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCircuitBreaker(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.CircuitBreaker
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no CB label",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when CB label is set",
|
||||
labels: map[string]string{
|
||||
TraefikBackendCircuitBreakerExpression: "NetworkErrorRatio() > 0.5",
|
||||
},
|
||||
expected: &types.CircuitBreaker{
|
||||
Expression: "NetworkErrorRatio() > 0.5",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetCircuitBreaker(test.labels)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLoadBalancer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.LoadBalancer
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no LB labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when labels are set",
|
||||
labels: map[string]string{
|
||||
TraefikBackendLoadBalancerMethod: "drr",
|
||||
TraefikBackendLoadBalancerSticky: "true",
|
||||
TraefikBackendLoadBalancerStickiness: "true",
|
||||
TraefikBackendLoadBalancerStickinessCookieName: "foo",
|
||||
},
|
||||
expected: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a nil Stickiness when Stickiness is not set",
|
||||
labels: map[string]string{
|
||||
TraefikBackendLoadBalancerMethod: "drr",
|
||||
TraefikBackendLoadBalancerSticky: "true",
|
||||
TraefikBackendLoadBalancerStickinessCookieName: "foo",
|
||||
},
|
||||
expected: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetLoadBalancer(test.labels)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMaxConn(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.MaxConn
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no max conn labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return nil when no amount label",
|
||||
labels: map[string]string{
|
||||
TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return default when no empty extractorFunc label",
|
||||
labels: map[string]string{
|
||||
TraefikBackendMaxConnExtractorFunc: "",
|
||||
TraefikBackendMaxConnAmount: "666",
|
||||
},
|
||||
expected: &types.MaxConn{
|
||||
ExtractorFunc: "request.host",
|
||||
Amount: 666,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when max conn labels are set",
|
||||
labels: map[string]string{
|
||||
TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
TraefikBackendMaxConnAmount: "666",
|
||||
},
|
||||
expected: &types.MaxConn{
|
||||
ExtractorFunc: "client.ip",
|
||||
Amount: 666,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetMaxConn(test.labels)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHealthCheck(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.HealthCheck
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no health check labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return nil when no health check Path label",
|
||||
labels: map[string]string{
|
||||
TraefikBackendHealthCheckPort: "80",
|
||||
TraefikBackendHealthCheckInterval: "6",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when health check labels are set",
|
||||
labels: map[string]string{
|
||||
TraefikBackendHealthCheckPath: "/health",
|
||||
TraefikBackendHealthCheckPort: "80",
|
||||
TraefikBackendHealthCheckInterval: "6",
|
||||
},
|
||||
expected: &types.HealthCheck{
|
||||
Path: "/health",
|
||||
Port: 80,
|
||||
Interval: "6",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetHealthCheck(test.labels)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBuffering(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.Buffering
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no buffering labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when buffering labels are set",
|
||||
labels: map[string]string{
|
||||
TraefikBackendBufferingMaxResponseBodyBytes: "10485760",
|
||||
TraefikBackendBufferingMemResponseBodyBytes: "2097152",
|
||||
TraefikBackendBufferingMaxRequestBodyBytes: "10485760",
|
||||
TraefikBackendBufferingMemRequestBodyBytes: "2097152",
|
||||
TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2",
|
||||
},
|
||||
expected: &types.Buffering{
|
||||
MaxResponseBodyBytes: 10485760,
|
||||
MemResponseBodyBytes: 2097152,
|
||||
MaxRequestBodyBytes: 10485760,
|
||||
MemRequestBodyBytes: 2097152,
|
||||
RetryExpression: "IsNetworkError() && Attempts() <= 2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetBuffering(test.labels)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRedirect(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.Redirect
|
||||
}{
|
||||
|
||||
{
|
||||
desc: "should return nil when no redirect labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should use only entry point tag when mix regex redirect and entry point redirect",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendRedirectEntryPoint: "https",
|
||||
TraefikFrontendRedirectRegex: "(.*)",
|
||||
TraefikFrontendRedirectReplacement: "$1",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when entry point redirect label",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendRedirectEntryPoint: "https",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when entry point redirect label (permanent)",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendRedirectEntryPoint: "https",
|
||||
TraefikFrontendRedirectPermanent: "true",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Permanent: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when regex redirect labels",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendRedirectRegex: "(.*)",
|
||||
TraefikFrontendRedirectReplacement: "$1",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
Regex: "(.*)",
|
||||
Replacement: "$1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when regex redirect labels (permanent)",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendRedirectRegex: "(.*)",
|
||||
TraefikFrontendRedirectReplacement: "$1",
|
||||
TraefikFrontendRedirectPermanent: "true",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
Regex: "(.*)",
|
||||
Replacement: "$1",
|
||||
Permanent: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetRedirect(test.labels)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRateLimit(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.RateLimit
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no rate limit labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when rate limit labels are defined",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendRateLimitExtractorFunc: "client.ip",
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitPeriod: "6",
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitAverage: "12",
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitBurst: "18",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitPeriod: "3",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitAverage: "6",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitBurst: "9",
|
||||
},
|
||||
expected: &types.RateLimit{
|
||||
ExtractorFunc: "client.ip",
|
||||
RateSet: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return nil when ExtractorFunc is missing",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitPeriod: "6",
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitAverage: "12",
|
||||
Prefix + BaseFrontendRateLimit + "foo." + SuffixRateLimitBurst: "18",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitPeriod: "3",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitAverage: "6",
|
||||
Prefix + BaseFrontendRateLimit + "bar." + SuffixRateLimitBurst: "9",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetRateLimit(test.labels)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHeaders(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected *types.Headers
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no custom headers options are set",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when all custom headers options are set",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
TraefikFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
TraefikFrontendSSLProxyHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
TraefikFrontendAllowedHosts: "foo,bar,bor",
|
||||
TraefikFrontendHostsProxyHeaders: "foo,bar,bor",
|
||||
TraefikFrontendSSLHost: "foo",
|
||||
TraefikFrontendCustomFrameOptionsValue: "foo",
|
||||
TraefikFrontendContentSecurityPolicy: "foo",
|
||||
TraefikFrontendPublicKey: "foo",
|
||||
TraefikFrontendReferrerPolicy: "foo",
|
||||
TraefikFrontendCustomBrowserXSSValue: "foo",
|
||||
TraefikFrontendSTSSeconds: "666",
|
||||
TraefikFrontendSSLRedirect: "true",
|
||||
TraefikFrontendSSLTemporaryRedirect: "true",
|
||||
TraefikFrontendSTSIncludeSubdomains: "true",
|
||||
TraefikFrontendSTSPreload: "true",
|
||||
TraefikFrontendForceSTSHeader: "true",
|
||||
TraefikFrontendFrameDeny: "true",
|
||||
TraefikFrontendContentTypeNosniff: "true",
|
||||
TraefikFrontendBrowserXSSFilter: "true",
|
||||
TraefikFrontendIsDevelopment: "true",
|
||||
},
|
||||
expected: &types.Headers{
|
||||
CustomRequestHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
CustomResponseHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
SSLProxyHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
AllowedHosts: []string{"foo", "bar", "bor"},
|
||||
HostsProxyHeaders: []string{"foo", "bar", "bor"},
|
||||
SSLHost: "foo",
|
||||
CustomFrameOptionsValue: "foo",
|
||||
ContentSecurityPolicy: "foo",
|
||||
PublicKey: "foo",
|
||||
ReferrerPolicy: "foo",
|
||||
CustomBrowserXSSValue: "foo",
|
||||
STSSeconds: 666,
|
||||
SSLRedirect: true,
|
||||
SSLTemporaryRedirect: true,
|
||||
STSIncludeSubdomains: true,
|
||||
STSPreload: true,
|
||||
ForceSTSHeader: true,
|
||||
FrameDeny: true,
|
||||
ContentTypeNosniff: true,
|
||||
BrowserXSSFilter: true,
|
||||
IsDevelopment: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := GetHeaders(test.labels)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderGetErrorPages(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected map[string]*types.ErrorPage
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no tags",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a map when tags are present",
|
||||
labels: map[string]string{
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageStatus: "404",
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageBackend: "foo_backend",
|
||||
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageQuery: "foo_query",
|
||||
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageStatus: "500,600",
|
||||
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageBackend: "bar_backend",
|
||||
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageQuery: "bar_query",
|
||||
},
|
||||
expected: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
Query: "foo_query",
|
||||
Backend: "foo_backend",
|
||||
},
|
||||
"bar": {
|
||||
Status: []string{"500", "600"},
|
||||
Query: "bar_query",
|
||||
Backend: "bar_backend",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result := GetErrorPages(test.labels)
|
||||
|
||||
assert.Equal(t, test.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
221
provider/label/segment_test.go
Normal file
221
provider/label/segment_test.go
Normal file
|
@ -0,0 +1,221 @@
|
|||
package label
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestExtractTraefikLabels(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
prefix string
|
||||
originLabels map[string]string
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
prefix: "traefik",
|
||||
originLabels: nil,
|
||||
expected: SegmentProperties{"": {}},
|
||||
},
|
||||
{
|
||||
desc: "container labels",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"frontend.priority": "foo", // missing prefix: skip
|
||||
"traefik.port": "bar",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"": {
|
||||
"traefik.port": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: only segment no default",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.goo.frontend.priority": "A",
|
||||
"traefik.goo.port": "D",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"goo": {
|
||||
"traefik.frontend.priority": "A",
|
||||
"traefik.port": "D",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: use default",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.guu.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"guu": {
|
||||
"traefik.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: several segments",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.goo.frontend.priority": "A",
|
||||
"traefik.goo.port": "D",
|
||||
"traefik.guu.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"goo": {
|
||||
"traefik.frontend.priority": "A",
|
||||
"traefik.port": "D",
|
||||
},
|
||||
"guu": {
|
||||
"traefik.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := ExtractTraefikLabels(test.originLabels)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractServiceProperties(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "empty labels map",
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
{
|
||||
desc: "valid label names",
|
||||
labels: map[string]string{
|
||||
"traefik.foo.port": "bar",
|
||||
"traefik.foo.frontend.bar": "1bar",
|
||||
"traefik.foo.backend": "3bar",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"foo": SegmentPropertyValues{
|
||||
"port": "bar",
|
||||
"frontend.bar": "1bar",
|
||||
"backend": "3bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid label names",
|
||||
labels: map[string]string{
|
||||
"foo.frontend.bar": "1bar",
|
||||
"traefik.foo.frontend.": "2bar",
|
||||
"traefik.foo.port.bar": "barbar",
|
||||
"traefik.foo.frontend": "0bar",
|
||||
"traefik.frontend.foo.backend": "0bar",
|
||||
},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := ExtractServiceProperties(test.labels)
|
||||
assert.EqualValues(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractServicePropertiesP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
{
|
||||
desc: "valid label names",
|
||||
labels: &map[string]string{
|
||||
"traefik.foo.port": "bar",
|
||||
"traefik.foo.frontend.bar": "1bar",
|
||||
"traefik.foo.backend": "3bar",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"foo": SegmentPropertyValues{
|
||||
"port": "bar",
|
||||
"frontend.bar": "1bar",
|
||||
"backend": "3bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid label names",
|
||||
labels: &map[string]string{
|
||||
"foo.frontend.bar": "1bar",
|
||||
"traefik.foo.frontend.": "2bar",
|
||||
"traefik.foo.port.bar": "barbar",
|
||||
"traefik.foo.frontend": "0bar",
|
||||
"traefik.frontend.foo.backend": "0bar",
|
||||
},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := ExtractServicePropertiesP(test.labels)
|
||||
assert.EqualValues(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServiceLabel(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labelName string
|
||||
serviceName string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "without service name",
|
||||
labelName: TraefikPort,
|
||||
expected: TraefikPort,
|
||||
},
|
||||
{
|
||||
desc: "with service name",
|
||||
labelName: TraefikPort,
|
||||
serviceName: "bar",
|
||||
expected: "traefik.bar.port",
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := GetServiceLabel(test.labelName, test.serviceName)
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -12,6 +12,10 @@ const testTaskName = "taskID"
|
|||
|
||||
// Functions related to building applications.
|
||||
|
||||
func withApplications(apps ...marathon.Application) *marathon.Applications {
|
||||
return &marathon.Applications{Apps: apps}
|
||||
}
|
||||
|
||||
func application(ops ...func(*marathon.Application)) marathon.Application {
|
||||
app := marathon.Application{}
|
||||
app.EmptyLabels()
|
||||
|
|
|
@ -4,12 +4,10 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
|
@ -19,99 +17,79 @@ import (
|
|||
|
||||
const defaultService = ""
|
||||
|
||||
func (p *Provider) buildConfiguration() *types.Configuration {
|
||||
type appData struct {
|
||||
marathon.Application
|
||||
SegmentLabels map[string]string
|
||||
SegmentName string
|
||||
}
|
||||
|
||||
func (p *Provider) buildConfigurationV2(applications *marathon.Applications) *types.Configuration {
|
||||
var MarathonFuncMap = template.FuncMap{
|
||||
"getBackend": p.getBackend,
|
||||
"getDomain": getFuncStringService(label.SuffixDomain, p.Domain), // see https://github.com/containous/traefik/pull/1693
|
||||
"getSubDomain": p.getSubDomain, // see https://github.com/containous/traefik/pull/1693
|
||||
"getDomain": label.GetFuncString(label.TraefikDomain, p.Domain), // see https://github.com/containous/traefik/pull/1693
|
||||
"getSubDomain": p.getSubDomain, // see https://github.com/containous/traefik/pull/1693
|
||||
"getBackendName": p.getBackendName,
|
||||
|
||||
// Backend functions
|
||||
"getBackendServer": p.getBackendServer,
|
||||
"getPort": getPort,
|
||||
"getCircuitBreaker": getCircuitBreaker,
|
||||
"getLoadBalancer": getLoadBalancer,
|
||||
"getMaxConn": getMaxConn,
|
||||
"getHealthCheck": getHealthCheck,
|
||||
"getBuffering": getBuffering,
|
||||
"getCircuitBreaker": label.GetCircuitBreaker,
|
||||
"getLoadBalancer": label.GetLoadBalancer,
|
||||
"getMaxConn": label.GetMaxConn,
|
||||
"getHealthCheck": label.GetHealthCheck,
|
||||
"getBuffering": label.GetBuffering,
|
||||
"getServers": p.getServers,
|
||||
|
||||
// TODO Deprecated [breaking]
|
||||
"getWeight": getFuncIntService(label.SuffixWeight, label.DefaultWeightInt),
|
||||
// TODO Deprecated [breaking]
|
||||
"getProtocol": getFuncStringService(label.SuffixProtocol, label.DefaultProtocol),
|
||||
// TODO Deprecated [breaking]
|
||||
"hasCircuitBreakerLabels": hasFunc(label.TraefikBackendCircuitBreakerExpression),
|
||||
// TODO Deprecated [breaking]
|
||||
"getCircuitBreakerExpression": getFuncString(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
||||
// TODO Deprecated [breaking]
|
||||
"hasLoadBalancerLabels": hasLoadBalancerLabels,
|
||||
// TODO Deprecated [breaking]
|
||||
"getLoadBalancerMethod": getFuncString(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod),
|
||||
// TODO Deprecated [breaking]
|
||||
"getSticky": getSticky,
|
||||
// TODO Deprecated [breaking]
|
||||
"hasStickinessLabel": hasFunc(label.TraefikBackendLoadBalancerStickiness),
|
||||
// TODO Deprecated [breaking]
|
||||
"getStickinessCookieName": getFuncString(label.TraefikBackendLoadBalancerStickinessCookieName, ""),
|
||||
// TODO Deprecated [breaking]
|
||||
"hasMaxConnLabels": hasMaxConnLabels,
|
||||
// TODO Deprecated [breaking]
|
||||
"getMaxConnExtractorFunc": getFuncString(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc),
|
||||
// TODO Deprecated [breaking]
|
||||
"getMaxConnAmount": getFuncInt64(label.TraefikBackendMaxConnAmount, math.MaxInt64),
|
||||
// TODO Deprecated [breaking]
|
||||
"hasHealthCheckLabels": hasFunc(label.TraefikBackendHealthCheckPath),
|
||||
// TODO Deprecated [breaking]
|
||||
"getHealthCheckPath": getFuncString(label.TraefikBackendHealthCheckPath, ""),
|
||||
// TODO Deprecated [breaking]
|
||||
"getHealthCheckInterval": getFuncString(label.TraefikBackendHealthCheckInterval, ""),
|
||||
|
||||
// Frontend functions
|
||||
"getServiceNames": getServiceNames,
|
||||
"getServiceNameSuffix": getServiceNameSuffix,
|
||||
"getPassHostHeader": getFuncBoolService(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getPassTLSCert": getFuncBoolService(label.SuffixFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPriority": getFuncIntService(label.SuffixFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getEntryPoints": getFuncSliceStringService(label.SuffixFrontendEntryPoints),
|
||||
"getSegmentNameSuffix": getSegmentNameSuffix,
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
"getFrontendName": p.getFrontendName,
|
||||
"getBasicAuth": getFuncSliceStringService(label.SuffixFrontendAuthBasic),
|
||||
"getRedirect": getRedirect,
|
||||
"getErrorPages": getErrorPages,
|
||||
"getRateLimit": getRateLimit,
|
||||
"getHeaders": getHeaders,
|
||||
"getWhiteList": getWhiteList,
|
||||
|
||||
// TODO Deprecated [breaking]
|
||||
"getWhitelistSourceRange": getFuncSliceStringService(label.SuffixFrontendWhitelistSourceRange),
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic),
|
||||
"getRedirect": label.GetRedirect,
|
||||
"getErrorPages": label.GetErrorPages,
|
||||
"getRateLimit": label.GetRateLimit,
|
||||
"getHeaders": label.GetHeaders,
|
||||
"getWhiteList": label.GetWhiteList,
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Add("embed", "apps.tasks")
|
||||
v.Add("embed", "apps.deployments")
|
||||
v.Add("embed", "apps.readiness")
|
||||
applications, err := p.marathonClient.Applications(v)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to retrieve Marathon applications: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
filteredApps := fun.Filter(p.applicationFilter, applications.Apps).([]marathon.Application)
|
||||
for i, app := range filteredApps {
|
||||
filteredApps[i].Tasks = fun.Filter(func(task *marathon.Task) bool {
|
||||
filtered := p.taskFilter(*task, app)
|
||||
if filtered {
|
||||
logIllegalServices(*task, app)
|
||||
var apps []*appData
|
||||
for _, app := range applications.Apps {
|
||||
if p.applicationFilter(app) {
|
||||
// Tasks
|
||||
var filteredTasks []*marathon.Task
|
||||
for _, task := range app.Tasks {
|
||||
if p.taskFilter(*task, app) {
|
||||
filteredTasks = append(filteredTasks, task)
|
||||
logIllegalServices(*task, app)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}, app.Tasks).([]*marathon.Task)
|
||||
|
||||
if len(filteredTasks) == 0 {
|
||||
log.Warnf("No valid tasks for application %s", app.ID)
|
||||
continue
|
||||
}
|
||||
app.Tasks = filteredTasks
|
||||
|
||||
// segments
|
||||
segmentProperties := label.ExtractTraefikLabels(stringValueMap(app.Labels))
|
||||
for segmentName, labels := range segmentProperties {
|
||||
data := &appData{
|
||||
Application: app,
|
||||
SegmentLabels: labels,
|
||||
SegmentName: segmentName,
|
||||
}
|
||||
apps = append(apps, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
templateObjects := struct {
|
||||
Applications []marathon.Application
|
||||
Applications []*appData
|
||||
Domain string
|
||||
}{
|
||||
Applications: filteredApps,
|
||||
Applications: apps,
|
||||
Domain: p.Domain,
|
||||
}
|
||||
|
||||
|
@ -124,15 +102,15 @@ func (p *Provider) buildConfiguration() *types.Configuration {
|
|||
|
||||
func (p *Provider) applicationFilter(app marathon.Application) bool {
|
||||
// Filter disabled application.
|
||||
if !label.IsEnabledP(app.Labels, p.ExposedByDefault) {
|
||||
if !label.IsEnabled(stringValueMap(app.Labels), p.ExposedByDefault) {
|
||||
log.Debugf("Filtering disabled Marathon application %s", app.ID)
|
||||
return false
|
||||
}
|
||||
|
||||
// Filter by constraints.
|
||||
constraintTags := label.GetSliceStringValueP(app.Labels, label.TraefikTags)
|
||||
constraintTags := label.GetSliceStringValue(stringValueMap(app.Labels), label.TraefikTags)
|
||||
if p.MarathonLBCompatibility {
|
||||
if haGroup := label.GetStringValueP(app.Labels, labelLbCompatibilityGroup, ""); len(haGroup) > 0 {
|
||||
if haGroup := label.GetStringValue(stringValueMap(app.Labels), labelLbCompatibilityGroup, ""); len(haGroup) > 0 {
|
||||
constraintTags = append(constraintTags, haGroup)
|
||||
}
|
||||
}
|
||||
|
@ -164,40 +142,32 @@ func (p *Provider) taskFilter(task marathon.Task, application marathon.Applicati
|
|||
return true
|
||||
}
|
||||
|
||||
// getFrontendRule returns the frontend rule for the specified application, using
|
||||
// its label. If service is provided, it will look for serviceName label before generic one.
|
||||
// It returns a default one (Host) if the label is not present.
|
||||
func (p *Provider) getFrontendRule(application marathon.Application, serviceName string) string {
|
||||
labels := getLabels(application, serviceName)
|
||||
lblFrontendRule := getLabelName(serviceName, label.SuffixFrontendRule)
|
||||
if value := label.GetStringValue(labels, lblFrontendRule, ""); len(value) > 0 {
|
||||
return value
|
||||
}
|
||||
// logIllegalServices logs illegal service configurations.
|
||||
// While we cannot filter on the service level, they will eventually get
|
||||
// rejected once the server configuration is rendered.
|
||||
func logIllegalServices(task marathon.Task, app marathon.Application) {
|
||||
segmentProperties := label.ExtractTraefikLabels(stringValueMap(app.Labels))
|
||||
for segmentName, labels := range segmentProperties {
|
||||
// Check for illegal/missing ports.
|
||||
if _, err := processPorts(app, task, labels); err != nil {
|
||||
log.Warnf("%s has an illegal configuration: no proper port available", identifier(app, task, segmentName))
|
||||
continue
|
||||
}
|
||||
|
||||
if p.MarathonLBCompatibility {
|
||||
if value := label.GetStringValueP(application.Labels, labelLbCompatibility, ""); len(value) > 0 {
|
||||
return "Host:" + value
|
||||
// Check for illegal port label combinations.
|
||||
hasPortLabel := label.Has(labels, label.TraefikPort)
|
||||
hasPortIndexLabel := label.Has(labels, label.TraefikPortIndex)
|
||||
if hasPortLabel && hasPortIndexLabel {
|
||||
log.Warnf("%s has both port and port index specified; port will take precedence", identifier(app, task, segmentName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getSegmentNameSuffix(serviceName string) string {
|
||||
if len(serviceName) > 0 {
|
||||
return "Host:" + strings.ToLower(provider.Normalize(serviceName)) + "." + p.getSubDomain(application.ID) + "." + p.Domain
|
||||
return "-service-" + provider.Normalize(serviceName)
|
||||
}
|
||||
return "Host:" + p.getSubDomain(application.ID) + "." + p.Domain
|
||||
}
|
||||
|
||||
func (p *Provider) getBackend(application marathon.Application, serviceName string) string {
|
||||
labels := getLabels(application, serviceName)
|
||||
lblBackend := getLabelName(serviceName, label.SuffixBackend)
|
||||
value := label.GetStringValue(labels, lblBackend, "")
|
||||
if len(value) > 0 {
|
||||
return provider.Normalize("backend" + value)
|
||||
}
|
||||
return provider.Normalize("backend" + application.ID + getServiceNameSuffix(serviceName))
|
||||
}
|
||||
|
||||
func (p *Provider) getFrontendName(application marathon.Application, serviceName string) string {
|
||||
return provider.Normalize("frontend" + application.ID + getServiceNameSuffix(serviceName))
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Provider) getSubDomain(name string) string {
|
||||
|
@ -210,120 +180,43 @@ func (p *Provider) getSubDomain(name string) string {
|
|||
return strings.Replace(strings.TrimPrefix(name, "/"), "/", "-", -1)
|
||||
}
|
||||
|
||||
func (p *Provider) getBackendServer(task marathon.Task, application marathon.Application) string {
|
||||
if application.IPAddressPerTask == nil || p.ForceTaskHostname {
|
||||
return task.Host
|
||||
}
|
||||
|
||||
numTaskIPAddresses := len(task.IPAddresses)
|
||||
switch numTaskIPAddresses {
|
||||
case 0:
|
||||
log.Errorf("Missing IP address for Marathon application %s on task %s", application.ID, task.ID)
|
||||
return ""
|
||||
case 1:
|
||||
return task.IPAddresses[0].IPAddress
|
||||
default:
|
||||
ipAddressIdx := label.GetIntValueP(application.Labels, labelIPAddressIdx, math.MinInt32)
|
||||
|
||||
if ipAddressIdx == math.MinInt32 {
|
||||
log.Errorf("Found %d task IP addresses but missing IP address index for Marathon application %s on task %s",
|
||||
numTaskIPAddresses, application.ID, task.ID)
|
||||
return ""
|
||||
}
|
||||
if ipAddressIdx < 0 || ipAddressIdx > numTaskIPAddresses {
|
||||
log.Errorf("Cannot use IP address index to select from %d task IP addresses for Marathon application %s on task %s",
|
||||
numTaskIPAddresses, application.ID, task.ID)
|
||||
return ""
|
||||
}
|
||||
|
||||
return task.IPAddresses[ipAddressIdx].IPAddress
|
||||
func (p *Provider) getBackendName(app appData) string {
|
||||
|
||||
value := label.GetStringValue(app.SegmentLabels, label.TraefikBackend, "")
|
||||
if len(value) > 0 {
|
||||
return provider.Normalize("backend" + value)
|
||||
}
|
||||
return provider.Normalize("backend" + app.ID + getSegmentNameSuffix(app.SegmentName))
|
||||
}
|
||||
|
||||
func identifier(app marathon.Application, task marathon.Task, serviceName string) string {
|
||||
id := fmt.Sprintf("Marathon task %s from application %s", task.ID, app.ID)
|
||||
if serviceName != "" {
|
||||
id += fmt.Sprintf(" (service: %s)", serviceName)
|
||||
}
|
||||
return id
|
||||
func (p *Provider) getFrontendName(app appData) string {
|
||||
return provider.Normalize("frontend" + app.ID + getSegmentNameSuffix(app.SegmentName))
|
||||
}
|
||||
|
||||
// getServiceNames returns a list of service names for a given application
|
||||
// An empty name "" will be added if no service specific properties exist,
|
||||
// as an indication that there are no sub-services, but only main application
|
||||
func getServiceNames(application marathon.Application) []string {
|
||||
labelServiceProperties := label.ExtractServicePropertiesP(application.Labels)
|
||||
|
||||
var names []string
|
||||
for k := range labelServiceProperties {
|
||||
names = append(names, k)
|
||||
// getFrontendRule returns the frontend rule for the specified application, using
|
||||
// its label. If service is provided, it will look for serviceName label before generic one.
|
||||
// It returns a default one (Host) if the label is not present.
|
||||
func (p *Provider) getFrontendRule(app appData) string {
|
||||
if value := label.GetStringValue(app.SegmentLabels, label.TraefikFrontendRule, ""); len(value) > 0 {
|
||||
return value
|
||||
}
|
||||
|
||||
// An empty name "" will be added if no service specific properties exist,
|
||||
// as an indication that there are no sub-services, but only main application
|
||||
if len(names) == 0 {
|
||||
names = append(names, defaultService)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func getServiceNameSuffix(serviceName string) string {
|
||||
if len(serviceName) > 0 {
|
||||
return "-service-" + provider.Normalize(serviceName)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// logIllegalServices logs illegal service configurations.
|
||||
// While we cannot filter on the service level, they will eventually get
|
||||
// rejected once the server configuration is rendered.
|
||||
func logIllegalServices(task marathon.Task, application marathon.Application) {
|
||||
for _, serviceName := range getServiceNames(application) {
|
||||
// Check for illegal/missing ports.
|
||||
if _, err := processPorts(application, task, serviceName); err != nil {
|
||||
log.Warnf("%s has an illegal configuration: no proper port available", identifier(application, task, serviceName))
|
||||
continue
|
||||
}
|
||||
|
||||
// Check for illegal port label combinations.
|
||||
labels := getLabels(application, serviceName)
|
||||
hasPortLabel := label.Has(labels, getLabelName(serviceName, label.SuffixPort))
|
||||
hasPortIndexLabel := label.Has(labels, getLabelName(serviceName, label.SuffixPortIndex))
|
||||
if hasPortLabel && hasPortIndexLabel {
|
||||
log.Warnf("%s has both port and port index specified; port will take precedence", identifier(application, task, serviceName))
|
||||
if p.MarathonLBCompatibility {
|
||||
if value := label.GetStringValue(stringValueMap(app.Labels), labelLbCompatibility, ""); len(value) > 0 {
|
||||
return "Host:" + value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasLoadBalancerLabels(application marathon.Application) bool {
|
||||
method := label.HasP(application.Labels, label.TraefikBackendLoadBalancerMethod)
|
||||
sticky := label.HasP(application.Labels, label.TraefikBackendLoadBalancerSticky)
|
||||
stickiness := label.HasP(application.Labels, label.TraefikBackendLoadBalancerStickiness)
|
||||
return method || sticky || stickiness
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasMaxConnLabels(application marathon.Application) bool {
|
||||
mca := label.HasP(application.Labels, label.TraefikBackendMaxConnAmount)
|
||||
mcef := label.HasP(application.Labels, label.TraefikBackendMaxConnExtractorFunc)
|
||||
return mca && mcef
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
// replaced by Stickiness
|
||||
// Deprecated
|
||||
func getSticky(application marathon.Application) bool {
|
||||
if label.HasP(application.Labels, label.TraefikBackendLoadBalancerSticky) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
||||
if len(app.SegmentName) > 0 {
|
||||
return "Host:" + strings.ToLower(provider.Normalize(app.SegmentName)) + "." + p.getSubDomain(app.ID) + "." + p.Domain
|
||||
}
|
||||
return label.GetBoolValueP(application.Labels, label.TraefikBackendLoadBalancerSticky, false)
|
||||
return "Host:" + p.getSubDomain(app.ID) + "." + p.Domain
|
||||
}
|
||||
|
||||
func getPort(task marathon.Task, application marathon.Application, serviceName string) string {
|
||||
port, err := processPorts(application, task, serviceName)
|
||||
func getPort(task marathon.Task, app appData) string {
|
||||
port, err := processPorts(app.Application, task, app.SegmentLabels)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to process ports for %s: %s", identifier(application, task, serviceName), err)
|
||||
log.Errorf("Unable to process ports for %s: %s", identifier(app.Application, task, app.SegmentName), err)
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -334,12 +227,9 @@ func getPort(task marathon.Task, application marathon.Application, serviceName s
|
|||
// An explicitly specified port is preferred. If none is specified, it selects
|
||||
// one of the available port. The first such found port is returned unless an
|
||||
// optional index is provided.
|
||||
func processPorts(application marathon.Application, task marathon.Task, serviceName string) (int, error) {
|
||||
labels := getLabels(application, serviceName)
|
||||
lblPort := getLabelName(serviceName, label.SuffixPort)
|
||||
|
||||
if label.Has(labels, lblPort) {
|
||||
port := label.GetIntValue(labels, lblPort, 0)
|
||||
func processPorts(app marathon.Application, task marathon.Task, labels map[string]string) (int, error) {
|
||||
if label.Has(labels, label.TraefikPort) {
|
||||
port := label.GetIntValue(labels, label.TraefikPort, 0)
|
||||
|
||||
if port <= 0 {
|
||||
return 0, fmt.Errorf("explicitly specified port %d must be larger than zero", port)
|
||||
|
@ -348,39 +238,39 @@ func processPorts(application marathon.Application, task marathon.Task, serviceN
|
|||
}
|
||||
}
|
||||
|
||||
ports := retrieveAvailablePorts(application, task)
|
||||
ports := retrieveAvailablePorts(app, task)
|
||||
if len(ports) == 0 {
|
||||
return 0, errors.New("no port found")
|
||||
}
|
||||
|
||||
lblPortIndex := getLabelName(serviceName, label.SuffixPortIndex)
|
||||
portIndex := label.GetIntValue(labels, lblPortIndex, 0)
|
||||
portIndex := label.GetIntValue(labels, label.TraefikPortIndex, 0)
|
||||
if portIndex < 0 || portIndex > len(ports)-1 {
|
||||
return 0, fmt.Errorf("index %d must be within range (0, %d)", portIndex, len(ports)-1)
|
||||
}
|
||||
return ports[portIndex], nil
|
||||
}
|
||||
|
||||
func retrieveAvailablePorts(application marathon.Application, task marathon.Task) []int {
|
||||
func retrieveAvailablePorts(app marathon.Application, task marathon.Task) []int {
|
||||
// Using default port configuration
|
||||
if len(task.Ports) > 0 {
|
||||
return task.Ports
|
||||
}
|
||||
|
||||
// Using port definition if available
|
||||
if application.PortDefinitions != nil && len(*application.PortDefinitions) > 0 {
|
||||
if app.PortDefinitions != nil && len(*app.PortDefinitions) > 0 {
|
||||
var ports []int
|
||||
for _, def := range *application.PortDefinitions {
|
||||
for _, def := range *app.PortDefinitions {
|
||||
if def.Port != nil {
|
||||
ports = append(ports, *def.Port)
|
||||
}
|
||||
}
|
||||
return ports
|
||||
}
|
||||
|
||||
// If using IP-per-task using this port definition
|
||||
if application.IPAddressPerTask != nil && len(*(application.IPAddressPerTask.Discovery).Ports) > 0 {
|
||||
if app.IPAddressPerTask != nil && app.IPAddressPerTask.Discovery != nil && len(*(app.IPAddressPerTask.Discovery.Ports)) > 0 {
|
||||
var ports []int
|
||||
for _, def := range *(application.IPAddressPerTask.Discovery).Ports {
|
||||
for _, def := range *(app.IPAddressPerTask.Discovery.Ports) {
|
||||
ports = append(ports, def.Number)
|
||||
}
|
||||
return ports
|
||||
|
@ -389,83 +279,19 @@ func retrieveAvailablePorts(application marathon.Application, task marathon.Task
|
|||
return []int{}
|
||||
}
|
||||
|
||||
func getCircuitBreaker(application marathon.Application) *types.CircuitBreaker {
|
||||
circuitBreaker := label.GetStringValueP(application.Labels, label.TraefikBackendCircuitBreakerExpression, "")
|
||||
if len(circuitBreaker) == 0 {
|
||||
return nil
|
||||
func identifier(app marathon.Application, task marathon.Task, segmentName string) string {
|
||||
id := fmt.Sprintf("Marathon task %s from application %s", task.ID, app.ID)
|
||||
if segmentName != "" {
|
||||
id += fmt.Sprintf(" (segment: %s)", segmentName)
|
||||
}
|
||||
return &types.CircuitBreaker{Expression: circuitBreaker}
|
||||
return id
|
||||
}
|
||||
|
||||
func getLoadBalancer(application marathon.Application) *types.LoadBalancer {
|
||||
if !label.HasPrefixP(application.Labels, label.TraefikBackendLoadBalancer) {
|
||||
return nil
|
||||
}
|
||||
|
||||
method := label.GetStringValueP(application.Labels, label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod)
|
||||
|
||||
lb := &types.LoadBalancer{
|
||||
Method: method,
|
||||
Sticky: getSticky(application),
|
||||
}
|
||||
|
||||
if label.GetBoolValueP(application.Labels, label.TraefikBackendLoadBalancerStickiness, false) {
|
||||
cookieName := label.GetStringValueP(application.Labels, label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName)
|
||||
lb.Stickiness = &types.Stickiness{CookieName: cookieName}
|
||||
}
|
||||
|
||||
return lb
|
||||
}
|
||||
|
||||
func getMaxConn(application marathon.Application) *types.MaxConn {
|
||||
amount := label.GetInt64ValueP(application.Labels, label.TraefikBackendMaxConnAmount, math.MinInt64)
|
||||
extractorFunc := label.GetStringValueP(application.Labels, label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc)
|
||||
|
||||
if amount == math.MinInt64 || len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.MaxConn{
|
||||
Amount: amount,
|
||||
ExtractorFunc: extractorFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func getHealthCheck(application marathon.Application) *types.HealthCheck {
|
||||
path := label.GetStringValueP(application.Labels, label.TraefikBackendHealthCheckPath, "")
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
port := label.GetIntValueP(application.Labels, label.TraefikBackendHealthCheckPort, label.DefaultBackendHealthCheckPort)
|
||||
interval := label.GetStringValueP(application.Labels, label.TraefikBackendHealthCheckInterval, "")
|
||||
|
||||
return &types.HealthCheck{
|
||||
Path: path,
|
||||
Port: port,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
func getBuffering(application marathon.Application) *types.Buffering {
|
||||
if !label.HasPrefixP(application.Labels, label.TraefikBackendBuffering) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.Buffering{
|
||||
MaxRequestBodyBytes: label.GetInt64ValueP(application.Labels, label.TraefikBackendBufferingMaxRequestBodyBytes, 0),
|
||||
MaxResponseBodyBytes: label.GetInt64ValueP(application.Labels, label.TraefikBackendBufferingMaxResponseBodyBytes, 0),
|
||||
MemRequestBodyBytes: label.GetInt64ValueP(application.Labels, label.TraefikBackendBufferingMemRequestBodyBytes, 0),
|
||||
MemResponseBodyBytes: label.GetInt64ValueP(application.Labels, label.TraefikBackendBufferingMemResponseBodyBytes, 0),
|
||||
RetryExpression: label.GetStringValueP(application.Labels, label.TraefikBackendBufferingRetryExpression, ""),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) getServers(application marathon.Application, serviceName string) map[string]types.Server {
|
||||
func (p *Provider) getServers(app appData) map[string]types.Server {
|
||||
var servers map[string]types.Server
|
||||
|
||||
for _, task := range application.Tasks {
|
||||
host := p.getBackendServer(*task, application)
|
||||
for _, task := range app.Tasks {
|
||||
host := p.getBackendServer(*task, app)
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
|
@ -474,197 +300,45 @@ func (p *Provider) getServers(application marathon.Application, serviceName stri
|
|||
servers = make(map[string]types.Server)
|
||||
}
|
||||
|
||||
labels := getLabels(application, serviceName)
|
||||
port := getPort(*task, app)
|
||||
protocol := label.GetStringValue(app.SegmentLabels, label.TraefikProtocol, label.DefaultProtocol)
|
||||
|
||||
port := getPort(*task, application, serviceName)
|
||||
protocol := label.GetStringValue(labels, getLabelName(serviceName, label.SuffixProtocol), label.DefaultProtocol)
|
||||
|
||||
serverName := provider.Normalize("server-" + task.ID + getServiceNameSuffix(serviceName))
|
||||
serverName := provider.Normalize("server-" + task.ID + getSegmentNameSuffix(app.SegmentName))
|
||||
servers[serverName] = types.Server{
|
||||
URL: fmt.Sprintf("%s://%s:%v", protocol, host, port),
|
||||
Weight: label.GetIntValue(labels, getLabelName(serviceName, label.SuffixWeight), label.DefaultWeightInt),
|
||||
Weight: label.GetIntValue(app.SegmentLabels, label.TraefikWeight, label.DefaultWeightInt),
|
||||
}
|
||||
}
|
||||
|
||||
return servers
|
||||
}
|
||||
|
||||
func getWhiteList(application marathon.Application, serviceName string) *types.WhiteList {
|
||||
labels := getLabels(application, serviceName)
|
||||
func (p *Provider) getBackendServer(task marathon.Task, app appData) string {
|
||||
if app.IPAddressPerTask == nil || p.ForceTaskHostname {
|
||||
return task.Host
|
||||
}
|
||||
|
||||
ranges := label.GetSliceStringValue(labels, getLabelName(serviceName, label.SuffixFrontendWhiteListSourceRange))
|
||||
if len(ranges) > 0 {
|
||||
return &types.WhiteList{
|
||||
SourceRange: ranges,
|
||||
UseXForwardedFor: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendWhiteListUseXForwardedFor), false),
|
||||
numTaskIPAddresses := len(task.IPAddresses)
|
||||
switch numTaskIPAddresses {
|
||||
case 0:
|
||||
log.Errorf("Missing IP address for Marathon application %s on task %s", app.ID, task.ID)
|
||||
return ""
|
||||
case 1:
|
||||
return task.IPAddresses[0].IPAddress
|
||||
default:
|
||||
ipAddressIdx := label.GetIntValue(stringValueMap(app.Labels), labelIPAddressIdx, math.MinInt32)
|
||||
|
||||
if ipAddressIdx == math.MinInt32 {
|
||||
log.Errorf("Found %d task IP addresses but missing IP address index for Marathon application %s on task %s",
|
||||
numTaskIPAddresses, app.ID, task.ID)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRedirect(application marathon.Application, serviceName string) *types.Redirect {
|
||||
labels := getLabels(application, serviceName)
|
||||
|
||||
permanent := label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendRedirectPermanent), false)
|
||||
|
||||
if label.Has(labels, getLabelName(serviceName, label.SuffixFrontendRedirectEntryPoint)) {
|
||||
return &types.Redirect{
|
||||
EntryPoint: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendRedirectEntryPoint), ""),
|
||||
Permanent: permanent,
|
||||
if ipAddressIdx < 0 || ipAddressIdx > numTaskIPAddresses {
|
||||
log.Errorf("Cannot use IP address index to select from %d task IP addresses for Marathon application %s on task %s",
|
||||
numTaskIPAddresses, app.ID, task.ID)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
if label.Has(labels, getLabelName(serviceName, label.SuffixFrontendRedirectRegex)) &&
|
||||
label.Has(labels, getLabelName(serviceName, label.SuffixFrontendRedirectReplacement)) {
|
||||
return &types.Redirect{
|
||||
Regex: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendRedirectRegex), ""),
|
||||
Replacement: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendRedirectReplacement), ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getErrorPages(application marathon.Application, serviceName string) map[string]*types.ErrorPage {
|
||||
labels := getLabels(application, serviceName)
|
||||
prefix := getLabelName(serviceName, label.BaseFrontendErrorPage)
|
||||
|
||||
if len(serviceName) > 0 {
|
||||
return label.ParseErrorPages(labels, prefix, label.RegexpBaseFrontendErrorPage)
|
||||
}
|
||||
return label.ParseErrorPages(labels, prefix, label.RegexpFrontendErrorPage)
|
||||
}
|
||||
|
||||
func getRateLimit(application marathon.Application, serviceName string) *types.RateLimit {
|
||||
labels := getLabels(application, serviceName)
|
||||
|
||||
extractorFunc := label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendRateLimitExtractorFunc), "")
|
||||
if len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
limits := getRateSet(labels, serviceName)
|
||||
if len(limits) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.RateLimit{
|
||||
ExtractorFunc: extractorFunc,
|
||||
RateSet: limits,
|
||||
}
|
||||
}
|
||||
|
||||
func getRateSet(labels map[string]string, serviceName string) map[string]*types.Rate {
|
||||
rateSetPrefix := getLabelName(serviceName, label.BaseFrontendRateLimit)
|
||||
|
||||
if len(serviceName) > 0 {
|
||||
return label.ParseRateSets(labels, rateSetPrefix, label.RegexpBaseFrontendRateLimit)
|
||||
}
|
||||
return label.ParseRateSets(labels, rateSetPrefix, label.RegexpFrontendRateLimit)
|
||||
}
|
||||
|
||||
func getHeaders(application marathon.Application, serviceName string) *types.Headers {
|
||||
labels := getLabels(application, serviceName)
|
||||
|
||||
headers := &types.Headers{
|
||||
CustomRequestHeaders: label.GetMapValue(labels, getLabelName(serviceName, label.SuffixFrontendRequestHeaders)),
|
||||
CustomResponseHeaders: label.GetMapValue(labels, getLabelName(serviceName, label.SuffixFrontendResponseHeaders)),
|
||||
SSLProxyHeaders: label.GetMapValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersSSLProxyHeaders)),
|
||||
AllowedHosts: label.GetSliceStringValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersAllowedHosts)),
|
||||
HostsProxyHeaders: label.GetSliceStringValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersHostsProxyHeaders)),
|
||||
STSSeconds: label.GetInt64Value(labels, getLabelName(serviceName, label.SuffixFrontendHeadersSTSSeconds), 0),
|
||||
SSLRedirect: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersSSLRedirect), false),
|
||||
SSLTemporaryRedirect: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersSSLTemporaryRedirect), false),
|
||||
STSIncludeSubdomains: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersSTSIncludeSubdomains), false),
|
||||
STSPreload: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersSTSPreload), false),
|
||||
ForceSTSHeader: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersForceSTSHeader), false),
|
||||
FrameDeny: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersFrameDeny), false),
|
||||
ContentTypeNosniff: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersContentTypeNosniff), false),
|
||||
BrowserXSSFilter: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersBrowserXSSFilter), false),
|
||||
IsDevelopment: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersIsDevelopment), false),
|
||||
SSLHost: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersSSLHost), ""),
|
||||
CustomFrameOptionsValue: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersCustomFrameOptionsValue), ""),
|
||||
ContentSecurityPolicy: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersContentSecurityPolicy), ""),
|
||||
PublicKey: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersPublicKey), ""),
|
||||
ReferrerPolicy: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersReferrerPolicy), ""),
|
||||
CustomBrowserXSSValue: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendHeadersCustomBrowserXSSValue), ""),
|
||||
}
|
||||
|
||||
if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
// Label functions
|
||||
|
||||
func getLabels(application marathon.Application, serviceName string) map[string]string {
|
||||
if len(serviceName) > 0 {
|
||||
return label.ExtractServicePropertiesP(application.Labels)[serviceName]
|
||||
}
|
||||
|
||||
if application.Labels != nil {
|
||||
return *application.Labels
|
||||
}
|
||||
|
||||
return make(map[string]string)
|
||||
}
|
||||
|
||||
func getLabelName(serviceName string, suffix string) string {
|
||||
if len(serviceName) != 0 {
|
||||
return suffix
|
||||
}
|
||||
return label.Prefix + suffix
|
||||
}
|
||||
|
||||
func hasFunc(labelName string) func(application marathon.Application) bool {
|
||||
return func(application marathon.Application) bool {
|
||||
return label.HasP(application.Labels, labelName)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncStringService(labelName string, defaultValue string) func(application marathon.Application, serviceName string) string {
|
||||
return func(application marathon.Application, serviceName string) string {
|
||||
labels := getLabels(application, serviceName)
|
||||
lbName := getLabelName(serviceName, labelName)
|
||||
return label.GetStringValue(labels, lbName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncBoolService(labelName string, defaultValue bool) func(application marathon.Application, serviceName string) bool {
|
||||
return func(application marathon.Application, serviceName string) bool {
|
||||
labels := getLabels(application, serviceName)
|
||||
lbName := getLabelName(serviceName, labelName)
|
||||
return label.GetBoolValue(labels, lbName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncIntService(labelName string, defaultValue int) func(application marathon.Application, serviceName string) int {
|
||||
return func(application marathon.Application, serviceName string) int {
|
||||
labels := getLabels(application, serviceName)
|
||||
lbName := getLabelName(serviceName, labelName)
|
||||
return label.GetIntValue(labels, lbName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncSliceStringService(labelName string) func(application marathon.Application, serviceName string) []string {
|
||||
return func(application marathon.Application, serviceName string) []string {
|
||||
labels := getLabels(application, serviceName)
|
||||
return label.GetSliceStringValue(labels, getLabelName(serviceName, labelName))
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncString(labelName string, defaultValue string) func(application marathon.Application) string {
|
||||
return func(application marathon.Application) string {
|
||||
return label.GetStringValueP(application.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncInt64(labelName string, defaultValue int64) func(application marathon.Application) int64 {
|
||||
return func(application marathon.Application) int64 {
|
||||
return label.GetInt64ValueP(application.Labels, labelName, defaultValue)
|
||||
return task.IPAddresses[ipAddressIdx].IPAddress
|
||||
}
|
||||
}
|
||||
|
|
13
provider/marathon/config_root.go
Normal file
13
provider/marathon/config_root.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package marathon
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/gambol99/go-marathon"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfiguration(applications *marathon.Applications) *types.Configuration {
|
||||
if p.TemplateVersion == 1 {
|
||||
return p.buildConfigurationV1(applications)
|
||||
}
|
||||
return p.buildConfigurationV2(applications)
|
||||
}
|
File diff suppressed because it is too large
Load diff
8
provider/marathon/convert_types.go
Normal file
8
provider/marathon/convert_types.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package marathon
|
||||
|
||||
func stringValueMap(mp *map[string]string) map[string]string {
|
||||
if mp != nil {
|
||||
return *mp
|
||||
}
|
||||
return make(map[string]string)
|
||||
}
|
425
provider/marathon/deprecated_config.go
Normal file
425
provider/marathon/deprecated_config.go
Normal file
|
@ -0,0 +1,425 @@
|
|||
package marathon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/gambol99/go-marathon"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfigurationV1(applications *marathon.Applications) *types.Configuration {
|
||||
var MarathonFuncMap = template.FuncMap{
|
||||
"getBackend": p.getBackendNameV1,
|
||||
"getDomain": getFuncStringServiceV1(label.SuffixDomain, p.Domain), // see https://github.com/containous/traefik/pull/1693
|
||||
"getSubDomain": p.getSubDomain, // see https://github.com/containous/traefik/pull/1693
|
||||
|
||||
// Backend functions
|
||||
"getBackendServer": p.getBackendServerV1,
|
||||
"getPort": getPortV1,
|
||||
"getServers": p.getServersV1,
|
||||
|
||||
"getWeight": getFuncIntServiceV1(label.SuffixWeight, label.DefaultWeightInt),
|
||||
"getProtocol": getFuncStringServiceV1(label.SuffixProtocol, label.DefaultProtocol),
|
||||
"hasCircuitBreakerLabels": hasFuncV1(label.TraefikBackendCircuitBreakerExpression),
|
||||
"getCircuitBreakerExpression": getFuncStringV1(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
||||
"hasLoadBalancerLabels": hasLoadBalancerLabelsV1,
|
||||
"getLoadBalancerMethod": getFuncStringV1(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod),
|
||||
"getSticky": getStickyV1,
|
||||
"hasStickinessLabel": hasFuncV1(label.TraefikBackendLoadBalancerStickiness),
|
||||
"getStickinessCookieName": getFuncStringV1(label.TraefikBackendLoadBalancerStickinessCookieName, ""),
|
||||
"hasMaxConnLabels": hasMaxConnLabelsV1,
|
||||
"getMaxConnExtractorFunc": getFuncStringV1(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc),
|
||||
"getMaxConnAmount": getFuncInt64V1(label.TraefikBackendMaxConnAmount, math.MaxInt64),
|
||||
"hasHealthCheckLabels": hasFuncV1(label.TraefikBackendHealthCheckPath),
|
||||
"getHealthCheckPath": getFuncStringV1(label.TraefikBackendHealthCheckPath, ""),
|
||||
"getHealthCheckInterval": getFuncStringV1(label.TraefikBackendHealthCheckInterval, ""),
|
||||
|
||||
// Frontend functions
|
||||
"getServiceNames": getServiceNamesV1,
|
||||
"getServiceNameSuffix": getSegmentNameSuffix,
|
||||
"getPassHostHeader": getFuncBoolServiceV1(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getPassTLSCert": getFuncBoolServiceV1(label.SuffixFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPriority": getFuncIntServiceV1(label.SuffixFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getEntryPoints": getFuncSliceStringServiceV1(label.SuffixFrontendEntryPoints),
|
||||
"getFrontendRule": p.getFrontendRuleV1,
|
||||
"getFrontendName": p.getFrontendNameV1,
|
||||
"getBasicAuth": getFuncSliceStringServiceV1(label.SuffixFrontendAuthBasic),
|
||||
"getWhitelistSourceRange": getFuncSliceStringServiceV1(label.SuffixFrontendWhitelistSourceRange),
|
||||
"getWhiteList": getWhiteListV1,
|
||||
}
|
||||
|
||||
filteredApps := fun.Filter(p.applicationFilter, applications.Apps).([]marathon.Application)
|
||||
for i, app := range filteredApps {
|
||||
filteredApps[i].Tasks = fun.Filter(func(task *marathon.Task) bool {
|
||||
filtered := p.taskFilter(*task, app)
|
||||
if filtered {
|
||||
logIllegalServicesV1(*task, app)
|
||||
}
|
||||
return filtered
|
||||
}, app.Tasks).([]*marathon.Task)
|
||||
}
|
||||
|
||||
templateObjects := struct {
|
||||
Applications []marathon.Application
|
||||
Domain string
|
||||
}{
|
||||
Applications: filteredApps,
|
||||
Domain: p.Domain,
|
||||
}
|
||||
|
||||
configuration, err := p.GetConfiguration("templates/marathon-v1.tmpl", MarathonFuncMap, templateObjects)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to render Marathon configuration template: %v", err)
|
||||
}
|
||||
return configuration
|
||||
}
|
||||
|
||||
// logIllegalServicesV1 logs illegal service configurations.
|
||||
// While we cannot filter on the service level, they will eventually get
|
||||
// rejected once the server configuration is rendered.
|
||||
// Deprecated
|
||||
func logIllegalServicesV1(task marathon.Task, app marathon.Application) {
|
||||
for _, serviceName := range getServiceNamesV1(app) {
|
||||
// Check for illegal/missing ports.
|
||||
if _, err := processPortsV1(app, task, serviceName); err != nil {
|
||||
log.Warnf("%s has an illegal configuration: no proper port available", identifierV1(app, task, serviceName))
|
||||
continue
|
||||
}
|
||||
|
||||
// Check for illegal port label combinations.
|
||||
labels := getLabelsV1(app, serviceName)
|
||||
hasPortLabel := label.Has(labels, getLabelNameV1(serviceName, label.SuffixPort))
|
||||
hasPortIndexLabel := label.Has(labels, getLabelNameV1(serviceName, label.SuffixPortIndex))
|
||||
if hasPortLabel && hasPortIndexLabel {
|
||||
log.Warnf("%s has both port and port index specified; port will take precedence", identifierV1(app, task, serviceName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (p *Provider) getBackendNameV1(application marathon.Application, serviceName string) string {
|
||||
labels := getLabelsV1(application, serviceName)
|
||||
lblBackend := getLabelNameV1(serviceName, label.SuffixBackend)
|
||||
value := label.GetStringValue(labels, lblBackend, "")
|
||||
if len(value) > 0 {
|
||||
return provider.Normalize("backend" + value)
|
||||
}
|
||||
return provider.Normalize("backend" + application.ID + getSegmentNameSuffix(serviceName))
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (p *Provider) getFrontendNameV1(application marathon.Application, serviceName string) string {
|
||||
return provider.Normalize("frontend" + application.ID + getSegmentNameSuffix(serviceName))
|
||||
}
|
||||
|
||||
// getFrontendRuleV1 returns the frontend rule for the specified application, using
|
||||
// its label. If service is provided, it will look for serviceName label before generic one.
|
||||
// It returns a default one (Host) if the label is not present.
|
||||
// Deprecated
|
||||
func (p *Provider) getFrontendRuleV1(application marathon.Application, serviceName string) string {
|
||||
labels := getLabelsV1(application, serviceName)
|
||||
lblFrontendRule := getLabelNameV1(serviceName, label.SuffixFrontendRule)
|
||||
if value := label.GetStringValue(labels, lblFrontendRule, ""); len(value) > 0 {
|
||||
return value
|
||||
}
|
||||
|
||||
if p.MarathonLBCompatibility {
|
||||
if value := label.GetStringValue(stringValueMap(application.Labels), labelLbCompatibility, ""); len(value) > 0 {
|
||||
return "Host:" + value
|
||||
}
|
||||
}
|
||||
|
||||
if len(serviceName) > 0 {
|
||||
return "Host:" + strings.ToLower(provider.Normalize(serviceName)) + "." + p.getSubDomain(application.ID) + "." + p.Domain
|
||||
}
|
||||
return "Host:" + p.getSubDomain(application.ID) + "." + p.Domain
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (p *Provider) getBackendServerV1(task marathon.Task, application marathon.Application) string {
|
||||
if application.IPAddressPerTask == nil || p.ForceTaskHostname {
|
||||
return task.Host
|
||||
}
|
||||
|
||||
numTaskIPAddresses := len(task.IPAddresses)
|
||||
switch numTaskIPAddresses {
|
||||
case 0:
|
||||
log.Errorf("Missing IP address for Marathon application %s on task %s", application.ID, task.ID)
|
||||
return ""
|
||||
case 1:
|
||||
return task.IPAddresses[0].IPAddress
|
||||
default:
|
||||
ipAddressIdx := label.GetIntValue(stringValueMap(application.Labels), labelIPAddressIdx, math.MinInt32)
|
||||
|
||||
if ipAddressIdx == math.MinInt32 {
|
||||
log.Errorf("Found %d task IP addresses but missing IP address index for Marathon application %s on task %s",
|
||||
numTaskIPAddresses, application.ID, task.ID)
|
||||
return ""
|
||||
}
|
||||
if ipAddressIdx < 0 || ipAddressIdx > numTaskIPAddresses {
|
||||
log.Errorf("Cannot use IP address index to select from %d task IP addresses for Marathon application %s on task %s",
|
||||
numTaskIPAddresses, application.ID, task.ID)
|
||||
return ""
|
||||
}
|
||||
|
||||
return task.IPAddresses[ipAddressIdx].IPAddress
|
||||
}
|
||||
}
|
||||
|
||||
// getServiceNamesV1 returns a list of service names for a given application
|
||||
// An empty name "" will be added if no service specific properties exist,
|
||||
// as an indication that there are no sub-services, but only main application
|
||||
// Deprecated
|
||||
func getServiceNamesV1(application marathon.Application) []string {
|
||||
labelServiceProperties := label.ExtractServicePropertiesP(application.Labels)
|
||||
|
||||
var names []string
|
||||
for k := range labelServiceProperties {
|
||||
names = append(names, k)
|
||||
}
|
||||
|
||||
// An empty name "" will be added if no service specific properties exist,
|
||||
// as an indication that there are no sub-services, but only main application
|
||||
if len(names) == 0 {
|
||||
names = append(names, defaultService)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func identifierV1(app marathon.Application, task marathon.Task, serviceName string) string {
|
||||
id := fmt.Sprintf("Marathon task %s from application %s", task.ID, app.ID)
|
||||
if serviceName != "" {
|
||||
id += fmt.Sprintf(" (service: %s)", serviceName)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasLoadBalancerLabelsV1(application marathon.Application) bool {
|
||||
method := label.Has(stringValueMap(application.Labels), label.TraefikBackendLoadBalancerMethod)
|
||||
sticky := label.Has(stringValueMap(application.Labels), label.TraefikBackendLoadBalancerSticky)
|
||||
stickiness := label.Has(stringValueMap(application.Labels), label.TraefikBackendLoadBalancerStickiness)
|
||||
return method || sticky || stickiness
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasMaxConnLabelsV1(application marathon.Application) bool {
|
||||
mca := label.Has(stringValueMap(application.Labels), label.TraefikBackendMaxConnAmount)
|
||||
mcef := label.Has(stringValueMap(application.Labels), label.TraefikBackendMaxConnExtractorFunc)
|
||||
return mca && mcef
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
// replaced by Stickiness
|
||||
// Deprecated
|
||||
func getStickyV1(application marathon.Application) bool {
|
||||
if label.Has(stringValueMap(application.Labels), label.TraefikBackendLoadBalancerSticky) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
||||
}
|
||||
return label.GetBoolValue(stringValueMap(application.Labels), label.TraefikBackendLoadBalancerSticky, false)
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getPortV1(task marathon.Task, application marathon.Application, serviceName string) string {
|
||||
port, err := processPortsV1(application, task, serviceName)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to process ports for %s: %s", identifierV1(application, task, serviceName), err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return strconv.Itoa(port)
|
||||
}
|
||||
|
||||
// processPortsV1 returns the configured port.
|
||||
// An explicitly specified port is preferred. If none is specified, it selects
|
||||
// one of the available port. The first such found port is returned unless an
|
||||
// optional index is provided.
|
||||
// Deprecated
|
||||
func processPortsV1(application marathon.Application, task marathon.Task, serviceName string) (int, error) {
|
||||
labels := getLabelsV1(application, serviceName)
|
||||
lblPort := getLabelNameV1(serviceName, label.SuffixPort)
|
||||
|
||||
if label.Has(labels, lblPort) {
|
||||
port := label.GetIntValue(labels, lblPort, 0)
|
||||
|
||||
if port <= 0 {
|
||||
return 0, fmt.Errorf("explicitly specified port %d must be larger than zero", port)
|
||||
} else if port > 0 {
|
||||
return port, nil
|
||||
}
|
||||
}
|
||||
|
||||
ports := retrieveAvailablePortsV1(application, task)
|
||||
if len(ports) == 0 {
|
||||
return 0, errors.New("no port found")
|
||||
}
|
||||
|
||||
lblPortIndex := getLabelNameV1(serviceName, label.SuffixPortIndex)
|
||||
portIndex := label.GetIntValue(labels, lblPortIndex, 0)
|
||||
if portIndex < 0 || portIndex > len(ports)-1 {
|
||||
return 0, fmt.Errorf("index %d must be within range (0, %d)", portIndex, len(ports)-1)
|
||||
}
|
||||
return ports[portIndex], nil
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func retrieveAvailablePortsV1(application marathon.Application, task marathon.Task) []int {
|
||||
// Using default port configuration
|
||||
if len(task.Ports) > 0 {
|
||||
return task.Ports
|
||||
}
|
||||
|
||||
// Using port definition if available
|
||||
if application.PortDefinitions != nil && len(*application.PortDefinitions) > 0 {
|
||||
var ports []int
|
||||
for _, def := range *application.PortDefinitions {
|
||||
if def.Port != nil {
|
||||
ports = append(ports, *def.Port)
|
||||
}
|
||||
}
|
||||
return ports
|
||||
}
|
||||
// If using IP-per-task using this port definition
|
||||
if application.IPAddressPerTask != nil && len(*(application.IPAddressPerTask.Discovery).Ports) > 0 {
|
||||
var ports []int
|
||||
for _, def := range *(application.IPAddressPerTask.Discovery).Ports {
|
||||
ports = append(ports, def.Number)
|
||||
}
|
||||
return ports
|
||||
}
|
||||
|
||||
return []int{}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (p *Provider) getServersV1(application marathon.Application, serviceName string) map[string]types.Server {
|
||||
var servers map[string]types.Server
|
||||
|
||||
for _, task := range application.Tasks {
|
||||
host := p.getBackendServerV1(*task, application)
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if servers == nil {
|
||||
servers = make(map[string]types.Server)
|
||||
}
|
||||
|
||||
labels := getLabelsV1(application, serviceName)
|
||||
|
||||
port := getPortV1(*task, application, serviceName)
|
||||
protocol := label.GetStringValue(labels, getLabelNameV1(serviceName, label.SuffixProtocol), label.DefaultProtocol)
|
||||
|
||||
serverName := provider.Normalize("server-" + task.ID + getSegmentNameSuffix(serviceName))
|
||||
servers[serverName] = types.Server{
|
||||
URL: fmt.Sprintf("%s://%s:%v", protocol, host, port),
|
||||
Weight: label.GetIntValue(labels, getLabelNameV1(serviceName, label.SuffixWeight), label.DefaultWeightInt),
|
||||
}
|
||||
}
|
||||
|
||||
return servers
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getWhiteListV1(application marathon.Application, serviceName string) *types.WhiteList {
|
||||
labels := getLabelsV1(application, serviceName)
|
||||
|
||||
ranges := label.GetSliceStringValue(labels, getLabelNameV1(serviceName, label.SuffixFrontendWhiteListSourceRange))
|
||||
if len(ranges) > 0 {
|
||||
return &types.WhiteList{
|
||||
SourceRange: ranges,
|
||||
UseXForwardedFor: label.GetBoolValue(labels, getLabelNameV1(serviceName, label.SuffixFrontendWhiteListUseXForwardedFor), false),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Label functions
|
||||
|
||||
// Deprecated
|
||||
func getLabelsV1(application marathon.Application, serviceName string) map[string]string {
|
||||
if len(serviceName) > 0 {
|
||||
return label.ExtractServicePropertiesP(application.Labels)[serviceName]
|
||||
}
|
||||
|
||||
if application.Labels != nil {
|
||||
return *application.Labels
|
||||
}
|
||||
|
||||
return make(map[string]string)
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getLabelNameV1(serviceName string, suffix string) string {
|
||||
if len(serviceName) != 0 {
|
||||
return suffix
|
||||
}
|
||||
return label.Prefix + suffix
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasFuncV1(labelName string) func(application marathon.Application) bool {
|
||||
return func(application marathon.Application) bool {
|
||||
return label.Has(stringValueMap(application.Labels), labelName)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncStringServiceV1(labelName string, defaultValue string) func(application marathon.Application, serviceName string) string {
|
||||
return func(application marathon.Application, serviceName string) string {
|
||||
labels := getLabelsV1(application, serviceName)
|
||||
lbName := getLabelNameV1(serviceName, labelName)
|
||||
return label.GetStringValue(labels, lbName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncBoolServiceV1(labelName string, defaultValue bool) func(application marathon.Application, serviceName string) bool {
|
||||
return func(application marathon.Application, serviceName string) bool {
|
||||
labels := getLabelsV1(application, serviceName)
|
||||
lbName := getLabelNameV1(serviceName, labelName)
|
||||
return label.GetBoolValue(labels, lbName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncIntServiceV1(labelName string, defaultValue int) func(application marathon.Application, serviceName string) int {
|
||||
return func(application marathon.Application, serviceName string) int {
|
||||
labels := getLabelsV1(application, serviceName)
|
||||
lbName := getLabelNameV1(serviceName, labelName)
|
||||
return label.GetIntValue(labels, lbName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncSliceStringServiceV1(labelName string) func(application marathon.Application, serviceName string) []string {
|
||||
return func(application marathon.Application, serviceName string) []string {
|
||||
labels := getLabelsV1(application, serviceName)
|
||||
return label.GetSliceStringValue(labels, getLabelNameV1(serviceName, labelName))
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncStringV1(labelName string, defaultValue string) func(application marathon.Application) string {
|
||||
return func(application marathon.Application) string {
|
||||
return label.GetStringValue(stringValueMap(application.Labels), labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncInt64V1(labelName string, defaultValue int64) func(application marathon.Application) int64 {
|
||||
return func(application marathon.Application) int64 {
|
||||
return label.GetInt64Value(stringValueMap(application.Labels), labelName, defaultValue)
|
||||
}
|
||||
}
|
762
provider/marathon/deprecated_config_test.go
Normal file
762
provider/marathon/deprecated_config_test.go
Normal file
|
@ -0,0 +1,762 @@
|
|||
package marathon
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/gambol99/go-marathon"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetConfigurationAPIErrorsV1(t *testing.T) {
|
||||
fakeClient := newFakeClient(true, marathon.Applications{})
|
||||
|
||||
p := &Provider{
|
||||
marathonClient: fakeClient,
|
||||
}
|
||||
p.TemplateVersion = 1
|
||||
|
||||
actualConfig := p.getConfiguration()
|
||||
fakeClient.AssertExpectations(t)
|
||||
|
||||
if actualConfig != nil {
|
||||
t.Errorf("configuration should have been nil, got %v", actualConfig)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildConfigurationV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
application marathon.Application
|
||||
expectedFrontends map[string]*types.Frontend
|
||||
expectedBackends map[string]*types.Backend
|
||||
}{
|
||||
{
|
||||
desc: "simple application",
|
||||
application: application(
|
||||
appPorts(80),
|
||||
withTasks(localhostTask(taskPorts(80))),
|
||||
),
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-app": {
|
||||
Backend: "backend-app",
|
||||
Routes: map[string]types.Route{
|
||||
"route-host-app": {
|
||||
Rule: "Host:app.marathon.localhost",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
BasicAuth: []string{},
|
||||
EntryPoints: []string{},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-app": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-task": {
|
||||
URL: "http://localhost:80",
|
||||
Weight: 0,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "filtered task",
|
||||
application: application(
|
||||
appPorts(80),
|
||||
withTasks(localhostTask(taskPorts(80), state(taskStateStaging))),
|
||||
),
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-app": {
|
||||
Backend: "backend-app",
|
||||
Routes: map[string]types.Route{
|
||||
"route-host-app": {
|
||||
Rule: "Host:app.marathon.localhost",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
BasicAuth: []string{},
|
||||
EntryPoints: []string{},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-app": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "max connection extractor function label only",
|
||||
application: application(
|
||||
appPorts(80),
|
||||
withTasks(localhostTask(taskPorts(80))),
|
||||
|
||||
withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"),
|
||||
),
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-app": {
|
||||
Backend: "backend-app",
|
||||
Routes: map[string]types.Route{
|
||||
"route-host-app": {
|
||||
Rule: "Host:app.marathon.localhost",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
BasicAuth: []string{},
|
||||
EntryPoints: []string{},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-app": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-task": {
|
||||
URL: "http://localhost:80",
|
||||
Weight: 0,
|
||||
},
|
||||
},
|
||||
MaxConn: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "multiple ports",
|
||||
application: application(
|
||||
appPorts(80, 81),
|
||||
withTasks(localhostTask(taskPorts(80, 81))),
|
||||
),
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-app": {
|
||||
Backend: "backend-app",
|
||||
Routes: map[string]types.Route{
|
||||
"route-host-app": {
|
||||
Rule: "Host:app.marathon.localhost",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
BasicAuth: []string{},
|
||||
EntryPoints: []string{},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-app": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-task": {
|
||||
URL: "http://localhost:80",
|
||||
Weight: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with all labels",
|
||||
application: application(
|
||||
appPorts(80),
|
||||
withTasks(task(host("127.0.0.1"), taskPorts(80))),
|
||||
|
||||
withLabel(label.TraefikPort, "666"),
|
||||
withLabel(label.TraefikProtocol, "https"),
|
||||
withLabel(label.TraefikWeight, "12"),
|
||||
|
||||
withLabel(label.TraefikBackend, "foobar"),
|
||||
|
||||
withLabel(label.TraefikBackendCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"),
|
||||
withLabel(label.TraefikBackendHealthCheckPath, "/health"),
|
||||
withLabel(label.TraefikBackendHealthCheckInterval, "6"),
|
||||
withLabel(label.TraefikBackendLoadBalancerMethod, "drr"),
|
||||
withLabel(label.TraefikBackendLoadBalancerSticky, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickiness, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"),
|
||||
withLabel(label.TraefikBackendMaxConnAmount, "666"),
|
||||
withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"),
|
||||
|
||||
withLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||
withLabel(label.TraefikFrontendEntryPoints, "http,https"),
|
||||
withLabel(label.TraefikFrontendPassHostHeader, "true"),
|
||||
withLabel(label.TraefikFrontendPriority, "666"),
|
||||
withLabel(label.TraefikFrontendRule, "Host:traefik.io"),
|
||||
),
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-app": {
|
||||
EntryPoints: []string{
|
||||
"http",
|
||||
"https",
|
||||
},
|
||||
Backend: "backendfoobar",
|
||||
Routes: map[string]types.Route{
|
||||
"route-host-app": {
|
||||
Rule: "Host:traefik.io",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
Priority: 666,
|
||||
BasicAuth: []string{
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backendfoobar": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-task": {
|
||||
URL: "https://127.0.0.1:666",
|
||||
Weight: 12,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: &types.CircuitBreaker{
|
||||
Expression: "NetworkErrorRatio() > 0.5",
|
||||
},
|
||||
LoadBalancer: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
Amount: 666,
|
||||
ExtractorFunc: "client.ip",
|
||||
},
|
||||
HealthCheck: &types.HealthCheck{
|
||||
Path: "/health",
|
||||
Interval: "6",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
test.application.ID = "/app"
|
||||
|
||||
for _, task := range test.application.Tasks {
|
||||
task.ID = "task"
|
||||
if task.State == "" {
|
||||
task.State = "TASK_RUNNING"
|
||||
}
|
||||
}
|
||||
|
||||
p := &Provider{
|
||||
Domain: "marathon.localhost",
|
||||
ExposedByDefault: true,
|
||||
}
|
||||
|
||||
actualConfig := p.buildConfigurationV1(withApplications(test.application))
|
||||
|
||||
assert.NotNil(t, actualConfig)
|
||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
||||
assert.Equal(t, test.expectedFrontends, actualConfig.Frontends)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildConfigurationServicesV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
application marathon.Application
|
||||
expectedFrontends map[string]*types.Frontend
|
||||
expectedBackends map[string]*types.Backend
|
||||
}{
|
||||
{
|
||||
desc: "multiple ports with services",
|
||||
application: application(
|
||||
appPorts(80, 81),
|
||||
withTasks(localhostTask(taskPorts(80, 81))),
|
||||
|
||||
withLabel(label.TraefikBackendMaxConnAmount, "1000"),
|
||||
withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"),
|
||||
withServiceLabel(label.TraefikPort, "80", "web"),
|
||||
withServiceLabel(label.TraefikPort, "81", "admin"),
|
||||
withLabel("traefik..port", "82"), // This should be ignored, as it fails to match the servicesPropertiesRegexp regex.
|
||||
withServiceLabel(label.TraefikFrontendRule, "Host:web.app.marathon.localhost", "web"),
|
||||
withServiceLabel(label.TraefikFrontendRule, "Host:admin.app.marathon.localhost", "admin"),
|
||||
),
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-app-service-web": {
|
||||
Backend: "backend-app-service-web",
|
||||
Routes: map[string]types.Route{
|
||||
`route-host-app-service-web`: {
|
||||
Rule: "Host:web.app.marathon.localhost",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
BasicAuth: []string{},
|
||||
EntryPoints: []string{},
|
||||
},
|
||||
"frontend-app-service-admin": {
|
||||
Backend: "backend-app-service-admin",
|
||||
Routes: map[string]types.Route{
|
||||
`route-host-app-service-admin`: {
|
||||
Rule: "Host:admin.app.marathon.localhost",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
BasicAuth: []string{},
|
||||
EntryPoints: []string{},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-app-service-web": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-task-service-web": {
|
||||
URL: "http://localhost:80",
|
||||
Weight: 0,
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
Amount: 1000,
|
||||
ExtractorFunc: "client.ip",
|
||||
},
|
||||
},
|
||||
"backend-app-service-admin": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-task-service-admin": {
|
||||
URL: "http://localhost:81",
|
||||
Weight: 0,
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
Amount: 1000,
|
||||
ExtractorFunc: "client.ip",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "when all labels are set",
|
||||
application: application(
|
||||
appPorts(80, 81),
|
||||
withTasks(localhostTask(taskPorts(80, 81))),
|
||||
|
||||
withLabel(label.TraefikBackendCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"),
|
||||
withLabel(label.TraefikBackendHealthCheckPath, "/health"),
|
||||
withLabel(label.TraefikBackendHealthCheckInterval, "6"),
|
||||
withLabel(label.TraefikBackendLoadBalancerMethod, "drr"),
|
||||
withLabel(label.TraefikBackendLoadBalancerSticky, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickiness, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"),
|
||||
withLabel(label.TraefikBackendMaxConnAmount, "666"),
|
||||
withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"),
|
||||
|
||||
withServiceLabel(label.TraefikPort, "80", "containous"),
|
||||
withServiceLabel(label.TraefikProtocol, "https", "containous"),
|
||||
withServiceLabel(label.TraefikWeight, "12", "containous"),
|
||||
|
||||
withServiceLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"),
|
||||
withServiceLabel(label.TraefikFrontendEntryPoints, "http,https", "containous"),
|
||||
withServiceLabel(label.TraefikFrontendPassHostHeader, "true", "containous"),
|
||||
withServiceLabel(label.TraefikFrontendPriority, "666", "containous"),
|
||||
withServiceLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"),
|
||||
),
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-app-service-containous": {
|
||||
EntryPoints: []string{
|
||||
"http",
|
||||
"https",
|
||||
},
|
||||
Backend: "backend-app-service-containous",
|
||||
Routes: map[string]types.Route{
|
||||
"route-host-app-service-containous": {
|
||||
Rule: "Host:traefik.io",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
Priority: 666,
|
||||
BasicAuth: []string{
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-app-service-containous": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-task-service-containous": {
|
||||
URL: "https://localhost:80",
|
||||
Weight: 12,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: &types.CircuitBreaker{
|
||||
Expression: "NetworkErrorRatio() > 0.5",
|
||||
},
|
||||
LoadBalancer: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
Amount: 666,
|
||||
ExtractorFunc: "client.ip",
|
||||
},
|
||||
HealthCheck: &types.HealthCheck{
|
||||
Path: "/health",
|
||||
Interval: "6",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
test.application.ID = "/app"
|
||||
|
||||
for _, task := range test.application.Tasks {
|
||||
task.ID = "task"
|
||||
if task.State == "" {
|
||||
task.State = "TASK_RUNNING"
|
||||
}
|
||||
}
|
||||
|
||||
p := &Provider{
|
||||
Domain: "marathon.localhost",
|
||||
ExposedByDefault: true,
|
||||
}
|
||||
|
||||
actualConfig := p.buildConfigurationV1(withApplications(test.application))
|
||||
|
||||
assert.NotNil(t, actualConfig)
|
||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
||||
assert.Equal(t, test.expectedFrontends, actualConfig.Frontends)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPortV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
application marathon.Application
|
||||
task marathon.Task
|
||||
serviceName string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "port missing",
|
||||
application: application(),
|
||||
task: task(),
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "numeric port",
|
||||
application: application(withLabel(label.TraefikPort, "80")),
|
||||
task: task(),
|
||||
expected: "80",
|
||||
},
|
||||
{
|
||||
desc: "string port",
|
||||
application: application(withLabel(label.TraefikPort, "foobar")),
|
||||
task: task(taskPorts(80)),
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "negative port",
|
||||
application: application(withLabel(label.TraefikPort, "-1")),
|
||||
task: task(taskPorts(80)),
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "task port available",
|
||||
application: application(),
|
||||
task: task(taskPorts(80)),
|
||||
expected: "80",
|
||||
},
|
||||
{
|
||||
desc: "port definition available",
|
||||
application: application(
|
||||
portDefinition(443),
|
||||
),
|
||||
task: task(),
|
||||
expected: "443",
|
||||
},
|
||||
{
|
||||
desc: "IP-per-task port available",
|
||||
application: application(ipAddrPerTask(8000)),
|
||||
task: task(),
|
||||
expected: "8000",
|
||||
},
|
||||
{
|
||||
desc: "multiple task ports available",
|
||||
application: application(),
|
||||
task: task(taskPorts(80, 443)),
|
||||
expected: "80",
|
||||
},
|
||||
{
|
||||
desc: "numeric port index specified",
|
||||
application: application(withLabel(label.TraefikPortIndex, "1")),
|
||||
task: task(taskPorts(80, 443)),
|
||||
expected: "443",
|
||||
},
|
||||
{
|
||||
desc: "string port index specified",
|
||||
application: application(withLabel(label.TraefikPortIndex, "foobar")),
|
||||
task: task(taskPorts(80)),
|
||||
expected: "80",
|
||||
},
|
||||
{
|
||||
desc: "port and port index specified",
|
||||
application: application(
|
||||
withLabel(label.TraefikPort, "80"),
|
||||
withLabel(label.TraefikPortIndex, "1"),
|
||||
),
|
||||
task: task(taskPorts(80, 443)),
|
||||
expected: "80",
|
||||
},
|
||||
{
|
||||
desc: "task and application ports specified",
|
||||
application: application(appPorts(9999)),
|
||||
task: task(taskPorts(7777)),
|
||||
expected: "7777",
|
||||
},
|
||||
{
|
||||
desc: "multiple task ports with service index available",
|
||||
application: application(withLabel(label.Prefix+"http.portIndex", "0")),
|
||||
task: task(taskPorts(80, 443)),
|
||||
serviceName: "http",
|
||||
expected: "80",
|
||||
},
|
||||
{
|
||||
desc: "multiple task ports with service port available",
|
||||
application: application(withLabel(label.Prefix+"https.port", "443")),
|
||||
task: task(taskPorts(80, 443)),
|
||||
serviceName: "https",
|
||||
expected: "443",
|
||||
},
|
||||
{
|
||||
desc: "multiple task ports with services but default port available",
|
||||
application: application(withLabel(label.Prefix+"http.weight", "100")),
|
||||
task: task(taskPorts(80, 443)),
|
||||
serviceName: "http",
|
||||
expected: "80",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getPortV1(test.task, test.application, test.serviceName)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFrontendRuleV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
application marathon.Application
|
||||
serviceName string
|
||||
expected string
|
||||
marathonLBCompatibility bool
|
||||
}{
|
||||
{
|
||||
desc: "label missing",
|
||||
application: application(appID("test")),
|
||||
marathonLBCompatibility: true,
|
||||
expected: "Host:test.marathon.localhost",
|
||||
},
|
||||
{
|
||||
desc: "HAProxy vhost available and LB compat disabled",
|
||||
application: application(
|
||||
appID("test"),
|
||||
withLabel("HAPROXY_0_VHOST", "foo.bar"),
|
||||
),
|
||||
marathonLBCompatibility: false,
|
||||
expected: "Host:test.marathon.localhost",
|
||||
},
|
||||
{
|
||||
desc: "HAProxy vhost available and LB compat enabled",
|
||||
application: application(withLabel("HAPROXY_0_VHOST", "foo.bar")),
|
||||
marathonLBCompatibility: true,
|
||||
expected: "Host:foo.bar",
|
||||
},
|
||||
{
|
||||
desc: "frontend rule available",
|
||||
|
||||
application: application(
|
||||
withLabel(label.TraefikFrontendRule, "Host:foo.bar"),
|
||||
withLabel("HAPROXY_0_VHOST", "unused"),
|
||||
),
|
||||
marathonLBCompatibility: true,
|
||||
expected: "Host:foo.bar",
|
||||
},
|
||||
{
|
||||
desc: "service label existing",
|
||||
application: application(withServiceLabel(label.TraefikFrontendRule, "Host:foo.bar", "app")),
|
||||
serviceName: "app",
|
||||
marathonLBCompatibility: true,
|
||||
expected: "Host:foo.bar",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := &Provider{
|
||||
Domain: "marathon.localhost",
|
||||
MarathonLBCompatibility: test.marathonLBCompatibility,
|
||||
}
|
||||
|
||||
actual := p.getFrontendRuleV1(test.application, test.serviceName)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBackendV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
application marathon.Application
|
||||
serviceName string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "label missing",
|
||||
application: application(appID("/group/app")),
|
||||
expected: "backend-group-app",
|
||||
},
|
||||
{
|
||||
desc: "label existing",
|
||||
application: application(withLabel(label.TraefikBackend, "bar")),
|
||||
expected: "backendbar",
|
||||
},
|
||||
{
|
||||
desc: "service label existing",
|
||||
application: application(withServiceLabel(label.TraefikBackend, "bar", "app")),
|
||||
serviceName: "app",
|
||||
expected: "backendbar",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := &Provider{}
|
||||
|
||||
actual := p.getBackendNameV1(test.application, test.serviceName)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBackendServerV1(t *testing.T) {
|
||||
host := "host"
|
||||
testCases := []struct {
|
||||
desc string
|
||||
application marathon.Application
|
||||
task marathon.Task
|
||||
forceTaskHostname bool
|
||||
expectedServer string
|
||||
}{
|
||||
{
|
||||
desc: "application without IP-per-task",
|
||||
application: application(),
|
||||
expectedServer: host,
|
||||
},
|
||||
{
|
||||
desc: "task hostname override",
|
||||
application: application(ipAddrPerTask(8000)),
|
||||
forceTaskHostname: true,
|
||||
expectedServer: host,
|
||||
},
|
||||
{
|
||||
desc: "task IP address missing",
|
||||
application: application(ipAddrPerTask(8000)),
|
||||
task: task(),
|
||||
expectedServer: "",
|
||||
},
|
||||
{
|
||||
desc: "single task IP address",
|
||||
application: application(ipAddrPerTask(8000)),
|
||||
task: task(ipAddresses("1.1.1.1")),
|
||||
expectedServer: "1.1.1.1",
|
||||
},
|
||||
{
|
||||
desc: "multiple task IP addresses without index label",
|
||||
application: application(ipAddrPerTask(8000)),
|
||||
task: task(ipAddresses("1.1.1.1", "2.2.2.2")),
|
||||
expectedServer: "",
|
||||
},
|
||||
{
|
||||
desc: "multiple task IP addresses with invalid index label",
|
||||
application: application(
|
||||
withLabel("traefik.ipAddressIdx", "invalid"),
|
||||
ipAddrPerTask(8000),
|
||||
),
|
||||
task: task(ipAddresses("1.1.1.1", "2.2.2.2")),
|
||||
expectedServer: "",
|
||||
},
|
||||
{
|
||||
desc: "multiple task IP addresses with valid index label",
|
||||
application: application(
|
||||
withLabel("traefik.ipAddressIdx", "1"),
|
||||
ipAddrPerTask(8000),
|
||||
),
|
||||
task: task(ipAddresses("1.1.1.1", "2.2.2.2")),
|
||||
expectedServer: "2.2.2.2",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := &Provider{ForceTaskHostname: test.forceTaskHostname}
|
||||
test.task.Host = host
|
||||
|
||||
actualServer := p.getBackendServerV1(test.task, test.application)
|
||||
|
||||
assert.Equal(t, test.expectedServer, actualServer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStickyV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
application marathon.Application
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "label missing",
|
||||
application: application(),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "label existing",
|
||||
application: application(withLabel(label.TraefikBackendLoadBalancerSticky, "true")),
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getStickyV1(test.application)
|
||||
if actual != test.expected {
|
||||
t.Errorf("actual %v, expected %v", actual, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
24
provider/marathon/fake_client_test.go
Normal file
24
provider/marathon/fake_client_test.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package marathon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/containous/traefik/provider/marathon/mocks"
|
||||
"github.com/gambol99/go-marathon"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
mocks.Marathon
|
||||
}
|
||||
|
||||
func newFakeClient(applicationsError bool, applications marathon.Applications) *fakeClient {
|
||||
// create an instance of our test object
|
||||
fakeClient := new(fakeClient)
|
||||
if applicationsError {
|
||||
fakeClient.On("Applications", mock.Anything).Return(nil, errors.New("fake Marathon server error"))
|
||||
} else {
|
||||
fakeClient.On("Applications", mock.Anything).Return(&applications, nil)
|
||||
}
|
||||
return fakeClient
|
||||
}
|
|
@ -3,6 +3,7 @@ package marathon
|
|||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/cenk/backoff"
|
||||
|
@ -128,7 +129,8 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
|||
return
|
||||
case event := <-update:
|
||||
log.Debugf("Received provider event %s", event)
|
||||
configuration := p.buildConfiguration()
|
||||
|
||||
configuration := p.getConfiguration()
|
||||
if configuration != nil {
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "marathon",
|
||||
|
@ -139,7 +141,8 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
|||
}
|
||||
})
|
||||
}
|
||||
configuration := p.buildConfiguration()
|
||||
|
||||
configuration := p.getConfiguration()
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "marathon",
|
||||
Configuration: configuration,
|
||||
|
@ -156,3 +159,22 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) getConfiguration() *types.Configuration {
|
||||
applications, err := p.getApplications()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to retrieve Marathon applications: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.buildConfiguration(applications)
|
||||
}
|
||||
|
||||
func (p *Provider) getApplications() (*marathon.Applications, error) {
|
||||
v := url.Values{}
|
||||
v.Add("embed", "apps.tasks")
|
||||
v.Add("embed", "apps.deployments")
|
||||
v.Add("embed", "apps.readiness")
|
||||
|
||||
return p.marathonClient.Applications(v)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package rancher
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
@ -14,63 +13,32 @@ import (
|
|||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfiguration(services []rancherData) *types.Configuration {
|
||||
|
||||
func (p *Provider) buildConfigurationV2(services []rancherData) *types.Configuration {
|
||||
var RancherFuncMap = template.FuncMap{
|
||||
"getDomain": getFuncString(label.TraefikDomain, p.Domain),
|
||||
"getLabelValue": label.GetStringValue,
|
||||
"getDomain": label.GetFuncString(label.TraefikDomain, p.Domain),
|
||||
|
||||
// Backend functions
|
||||
"getCircuitBreaker": getCircuitBreaker,
|
||||
"getLoadBalancer": getLoadBalancer,
|
||||
"getMaxConn": getMaxConn,
|
||||
"getHealthCheck": getHealthCheck,
|
||||
"getBuffering": getBuffering,
|
||||
"getCircuitBreaker": label.GetCircuitBreaker,
|
||||
"getLoadBalancer": label.GetLoadBalancer,
|
||||
"getMaxConn": label.GetMaxConn,
|
||||
"getHealthCheck": label.GetHealthCheck,
|
||||
"getBuffering": label.GetBuffering,
|
||||
"getServers": getServers,
|
||||
|
||||
// TODO Deprecated [breaking]
|
||||
"getPort": getFuncString(label.TraefikPort, ""),
|
||||
// TODO Deprecated [breaking]
|
||||
"getProtocol": getFuncString(label.TraefikProtocol, label.DefaultProtocol),
|
||||
// TODO Deprecated [breaking]
|
||||
"getWeight": getFuncInt(label.TraefikWeight, label.DefaultWeightInt),
|
||||
// TODO Deprecated [breaking]
|
||||
"hasCircuitBreakerLabel": hasFunc(label.TraefikBackendCircuitBreakerExpression),
|
||||
// TODO Deprecated [breaking]
|
||||
"getCircuitBreakerExpression": getFuncString(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
||||
// TODO Deprecated [breaking]
|
||||
"hasLoadBalancerLabel": hasLoadBalancerLabel,
|
||||
// TODO Deprecated [breaking]
|
||||
"getLoadBalancerMethod": getFuncString(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod),
|
||||
// TODO Deprecated [breaking]
|
||||
"hasMaxConnLabels": hasMaxConnLabels,
|
||||
// TODO Deprecated [breaking]
|
||||
"getMaxConnAmount": getFuncInt64(label.TraefikBackendMaxConnAmount, 0),
|
||||
// TODO Deprecated [breaking]
|
||||
"getMaxConnExtractorFunc": getFuncString(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc),
|
||||
// TODO Deprecated [breaking]
|
||||
"getSticky": getSticky,
|
||||
// TODO Deprecated [breaking]
|
||||
"hasStickinessLabel": hasFunc(label.TraefikBackendLoadBalancerStickiness),
|
||||
// TODO Deprecated [breaking]
|
||||
"getStickinessCookieName": getFuncString(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
||||
|
||||
// Frontend functions
|
||||
"getBackend": getBackendName, // TODO Deprecated [breaking] replaced by getBackendName
|
||||
"getBackendName": getBackendName,
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
"getPriority": getFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getPassHostHeader": getFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getPassTLSCert": getFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getEntryPoints": getFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||
"getBasicAuth": getFuncSliceString(label.TraefikFrontendAuthBasic),
|
||||
"getErrorPages": getErrorPages,
|
||||
"getRateLimit": getRateLimit,
|
||||
"getRedirect": getRedirect,
|
||||
"getHeaders": getHeaders,
|
||||
"getWhiteList": getWhiteList,
|
||||
|
||||
// TODO Deprecated [breaking]
|
||||
"getWhitelistSourceRange": getFuncSliceString(label.TraefikFrontendWhitelistSourceRange),
|
||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic),
|
||||
"getErrorPages": label.GetErrorPages,
|
||||
"getRateLimit": label.GetRateLimit,
|
||||
"getRedirect": label.GetRedirect,
|
||||
"getHeaders": label.GetHeaders,
|
||||
"getWhiteList": label.GetWhiteList,
|
||||
}
|
||||
|
||||
// filter services
|
||||
|
@ -80,10 +48,16 @@ func (p *Provider) buildConfiguration(services []rancherData) *types.Configurati
|
|||
backends := map[string]rancherData{}
|
||||
|
||||
for _, service := range filteredServices {
|
||||
frontendName := p.getFrontendName(service)
|
||||
frontends[frontendName] = service
|
||||
backendName := getBackendName(service)
|
||||
backends[backendName] = service
|
||||
segmentProperties := label.ExtractTraefikLabels(service.Labels)
|
||||
for segmentName, labels := range segmentProperties {
|
||||
service.SegmentLabels = labels
|
||||
service.SegmentName = segmentName
|
||||
|
||||
frontendName := p.getFrontendName(service)
|
||||
frontends[frontendName] = service
|
||||
backendName := getBackendName(service)
|
||||
backends[backendName] = service
|
||||
}
|
||||
}
|
||||
|
||||
templateObjects := struct {
|
||||
|
@ -105,9 +79,19 @@ func (p *Provider) buildConfiguration(services []rancherData) *types.Configurati
|
|||
}
|
||||
|
||||
func (p *Provider) serviceFilter(service rancherData) bool {
|
||||
if service.Labels[label.TraefikPort] == "" {
|
||||
log.Debugf("Filtering service %s without traefik.port label", service.Name)
|
||||
return false
|
||||
segmentProperties := label.ExtractTraefikLabels(service.Labels)
|
||||
|
||||
for segmentName, labels := range segmentProperties {
|
||||
_, err := checkSegmentPort(labels, segmentName)
|
||||
if err != nil {
|
||||
log.Debugf("Filtering service %s %s without traefik.port label", service.Name, segmentName)
|
||||
return false
|
||||
}
|
||||
|
||||
if len(p.getFrontendRule(service)) == 0 {
|
||||
log.Debugf("Filtering container with empty frontend rule %s %s", service.Name, segmentName)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if !label.IsEnabled(service.Labels, p.ExposedByDefault) {
|
||||
|
@ -145,112 +129,37 @@ func (p *Provider) getFrontendRule(service rancherData) string {
|
|||
}
|
||||
|
||||
func (p *Provider) getFrontendName(service rancherData) string {
|
||||
return provider.Normalize(p.getFrontendRule(service))
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
// replaced by Stickiness
|
||||
// Deprecated
|
||||
func getSticky(service rancherData) bool {
|
||||
if label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
||||
var name string
|
||||
if len(service.SegmentName) > 0 {
|
||||
name = getBackendName(service)
|
||||
} else {
|
||||
name = p.getFrontendRule(service)
|
||||
}
|
||||
return label.GetBoolValue(service.Labels, label.TraefikBackendLoadBalancerSticky, false)
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasLoadBalancerLabel(service rancherData) bool {
|
||||
method := label.Has(service.Labels, label.TraefikBackendLoadBalancerMethod)
|
||||
sticky := label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky)
|
||||
stickiness := label.Has(service.Labels, label.TraefikBackendLoadBalancerStickiness)
|
||||
cookieName := label.Has(service.Labels, label.TraefikBackendLoadBalancerStickinessCookieName)
|
||||
return method || sticky || stickiness || cookieName
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasMaxConnLabels(service rancherData) bool {
|
||||
mca := label.Has(service.Labels, label.TraefikBackendMaxConnAmount)
|
||||
mcef := label.Has(service.Labels, label.TraefikBackendMaxConnExtractorFunc)
|
||||
return mca && mcef
|
||||
return provider.Normalize(name)
|
||||
}
|
||||
|
||||
func getBackendName(service rancherData) string {
|
||||
backend := label.GetStringValue(service.Labels, label.TraefikBackend, service.Name)
|
||||
if len(service.SegmentName) > 0 {
|
||||
return getSegmentBackendName(service)
|
||||
}
|
||||
|
||||
return getDefaultBackendName(service)
|
||||
}
|
||||
|
||||
func getSegmentBackendName(service rancherData) string {
|
||||
if value := label.GetStringValue(service.SegmentLabels, label.TraefikFrontendBackend, ""); len(value) > 0 {
|
||||
return provider.Normalize(service.Name + "-" + value)
|
||||
}
|
||||
|
||||
return provider.Normalize(service.Name + "-" + getDefaultBackendName(service) + "-" + service.SegmentName)
|
||||
}
|
||||
|
||||
func getDefaultBackendName(service rancherData) string {
|
||||
backend := label.GetStringValue(service.SegmentLabels, label.TraefikBackend, service.Name)
|
||||
return provider.Normalize(backend)
|
||||
}
|
||||
|
||||
func getCircuitBreaker(service rancherData) *types.CircuitBreaker {
|
||||
circuitBreaker := label.GetStringValue(service.Labels, label.TraefikBackendCircuitBreakerExpression, "")
|
||||
if len(circuitBreaker) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &types.CircuitBreaker{Expression: circuitBreaker}
|
||||
}
|
||||
|
||||
func getLoadBalancer(service rancherData) *types.LoadBalancer {
|
||||
if !label.HasPrefix(service.Labels, label.TraefikBackendLoadBalancer) {
|
||||
return nil
|
||||
}
|
||||
|
||||
method := label.GetStringValue(service.Labels, label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod)
|
||||
|
||||
lb := &types.LoadBalancer{
|
||||
Method: method,
|
||||
Sticky: getSticky(service),
|
||||
}
|
||||
|
||||
if label.GetBoolValue(service.Labels, label.TraefikBackendLoadBalancerStickiness, false) {
|
||||
cookieName := label.GetStringValue(service.Labels, label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName)
|
||||
lb.Stickiness = &types.Stickiness{CookieName: cookieName}
|
||||
}
|
||||
|
||||
return lb
|
||||
}
|
||||
|
||||
func getMaxConn(service rancherData) *types.MaxConn {
|
||||
amount := label.GetInt64Value(service.Labels, label.TraefikBackendMaxConnAmount, math.MinInt64)
|
||||
extractorFunc := label.GetStringValue(service.Labels, label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc)
|
||||
|
||||
if amount == math.MinInt64 || len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.MaxConn{
|
||||
Amount: amount,
|
||||
ExtractorFunc: extractorFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func getHealthCheck(service rancherData) *types.HealthCheck {
|
||||
path := label.GetStringValue(service.Labels, label.TraefikBackendHealthCheckPath, "")
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
port := label.GetIntValue(service.Labels, label.TraefikBackendHealthCheckPort, label.DefaultBackendHealthCheckPort)
|
||||
interval := label.GetStringValue(service.Labels, label.TraefikBackendHealthCheckInterval, "")
|
||||
|
||||
return &types.HealthCheck{
|
||||
Path: path,
|
||||
Port: port,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
func getBuffering(service rancherData) *types.Buffering {
|
||||
if !label.HasPrefix(service.Labels, label.TraefikBackendBuffering) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.Buffering{
|
||||
MaxRequestBodyBytes: label.GetInt64Value(service.Labels, label.TraefikBackendBufferingMaxRequestBodyBytes, 0),
|
||||
MaxResponseBodyBytes: label.GetInt64Value(service.Labels, label.TraefikBackendBufferingMaxResponseBodyBytes, 0),
|
||||
MemRequestBodyBytes: label.GetInt64Value(service.Labels, label.TraefikBackendBufferingMemRequestBodyBytes, 0),
|
||||
MemResponseBodyBytes: label.GetInt64Value(service.Labels, label.TraefikBackendBufferingMemResponseBodyBytes, 0),
|
||||
RetryExpression: label.GetStringValue(service.Labels, label.TraefikBackendBufferingRetryExpression, ""),
|
||||
}
|
||||
}
|
||||
|
||||
func getServers(service rancherData) map[string]types.Server {
|
||||
var servers map[string]types.Server
|
||||
|
||||
|
@ -259,9 +168,9 @@ func getServers(service rancherData) map[string]types.Server {
|
|||
servers = make(map[string]types.Server)
|
||||
}
|
||||
|
||||
protocol := label.GetStringValue(service.Labels, label.TraefikProtocol, label.DefaultProtocol)
|
||||
port := label.GetStringValue(service.Labels, label.TraefikPort, "")
|
||||
weight := label.GetIntValue(service.Labels, label.TraefikWeight, label.DefaultWeightInt)
|
||||
protocol := label.GetStringValue(service.SegmentLabels, label.TraefikProtocol, label.DefaultProtocol)
|
||||
port := label.GetStringValue(service.SegmentLabels, label.TraefikPort, "")
|
||||
weight := label.GetIntValue(service.SegmentLabels, label.TraefikWeight, label.DefaultWeightInt)
|
||||
|
||||
serverName := "server-" + strconv.Itoa(index)
|
||||
servers[serverName] = types.Server{
|
||||
|
@ -273,127 +182,14 @@ func getServers(service rancherData) map[string]types.Server {
|
|||
return servers
|
||||
}
|
||||
|
||||
func getWhiteList(service rancherData) *types.WhiteList {
|
||||
ranges := label.GetSliceStringValue(service.Labels, label.TraefikFrontendWhiteListSourceRange)
|
||||
|
||||
if len(ranges) > 0 {
|
||||
return &types.WhiteList{
|
||||
SourceRange: ranges,
|
||||
UseXForwardedFor: label.GetBoolValue(service.Labels, label.TraefikFrontendWhiteListUseXForwardedFor, false),
|
||||
func checkSegmentPort(labels map[string]string, segmentName string) (int, error) {
|
||||
if rawPort, ok := labels[label.TraefikPort]; ok {
|
||||
port, err := strconv.Atoi(rawPort)
|
||||
if err != nil {
|
||||
return port, fmt.Errorf("invalid port value %q for the segment %q: %v", rawPort, segmentName, err)
|
||||
}
|
||||
} else {
|
||||
return 0, fmt.Errorf("port label is missing, please use %s as default value or define port label for all segments ('traefik.<segment_name>.port')", label.TraefikPort)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRedirect(service rancherData) *types.Redirect {
|
||||
permanent := label.GetBoolValue(service.Labels, label.TraefikFrontendRedirectPermanent, false)
|
||||
|
||||
if label.Has(service.Labels, label.TraefikFrontendRedirectEntryPoint) {
|
||||
return &types.Redirect{
|
||||
EntryPoint: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectEntryPoint, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
if label.Has(service.Labels, label.TraefikFrontendRedirectRegex) &&
|
||||
label.Has(service.Labels, label.TraefikFrontendRedirectReplacement) {
|
||||
return &types.Redirect{
|
||||
Regex: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectRegex, ""),
|
||||
Replacement: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectReplacement, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getErrorPages(service rancherData) map[string]*types.ErrorPage {
|
||||
prefix := label.Prefix + label.BaseFrontendErrorPage
|
||||
return label.ParseErrorPages(service.Labels, prefix, label.RegexpFrontendErrorPage)
|
||||
}
|
||||
|
||||
func getRateLimit(service rancherData) *types.RateLimit {
|
||||
extractorFunc := label.GetStringValue(service.Labels, label.TraefikFrontendRateLimitExtractorFunc, "")
|
||||
if len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
prefix := label.Prefix + label.BaseFrontendRateLimit
|
||||
limits := label.ParseRateSets(service.Labels, prefix, label.RegexpFrontendRateLimit)
|
||||
|
||||
return &types.RateLimit{
|
||||
ExtractorFunc: extractorFunc,
|
||||
RateSet: limits,
|
||||
}
|
||||
}
|
||||
|
||||
func getHeaders(service rancherData) *types.Headers {
|
||||
headers := &types.Headers{
|
||||
CustomRequestHeaders: label.GetMapValue(service.Labels, label.TraefikFrontendRequestHeaders),
|
||||
CustomResponseHeaders: label.GetMapValue(service.Labels, label.TraefikFrontendResponseHeaders),
|
||||
SSLProxyHeaders: label.GetMapValue(service.Labels, label.TraefikFrontendSSLProxyHeaders),
|
||||
AllowedHosts: label.GetSliceStringValue(service.Labels, label.TraefikFrontendAllowedHosts),
|
||||
HostsProxyHeaders: label.GetSliceStringValue(service.Labels, label.TraefikFrontendHostsProxyHeaders),
|
||||
STSSeconds: label.GetInt64Value(service.Labels, label.TraefikFrontendSTSSeconds, 0),
|
||||
SSLRedirect: label.GetBoolValue(service.Labels, label.TraefikFrontendSSLRedirect, false),
|
||||
SSLTemporaryRedirect: label.GetBoolValue(service.Labels, label.TraefikFrontendSSLTemporaryRedirect, false),
|
||||
STSIncludeSubdomains: label.GetBoolValue(service.Labels, label.TraefikFrontendSTSIncludeSubdomains, false),
|
||||
STSPreload: label.GetBoolValue(service.Labels, label.TraefikFrontendSTSPreload, false),
|
||||
ForceSTSHeader: label.GetBoolValue(service.Labels, label.TraefikFrontendForceSTSHeader, false),
|
||||
FrameDeny: label.GetBoolValue(service.Labels, label.TraefikFrontendFrameDeny, false),
|
||||
ContentTypeNosniff: label.GetBoolValue(service.Labels, label.TraefikFrontendContentTypeNosniff, false),
|
||||
BrowserXSSFilter: label.GetBoolValue(service.Labels, label.TraefikFrontendBrowserXSSFilter, false),
|
||||
IsDevelopment: label.GetBoolValue(service.Labels, label.TraefikFrontendIsDevelopment, false),
|
||||
SSLHost: label.GetStringValue(service.Labels, label.TraefikFrontendSSLHost, ""),
|
||||
CustomFrameOptionsValue: label.GetStringValue(service.Labels, label.TraefikFrontendCustomFrameOptionsValue, ""),
|
||||
ContentSecurityPolicy: label.GetStringValue(service.Labels, label.TraefikFrontendContentSecurityPolicy, ""),
|
||||
PublicKey: label.GetStringValue(service.Labels, label.TraefikFrontendPublicKey, ""),
|
||||
ReferrerPolicy: label.GetStringValue(service.Labels, label.TraefikFrontendReferrerPolicy, ""),
|
||||
CustomBrowserXSSValue: label.GetStringValue(service.Labels, label.TraefikFrontendCustomBrowserXSSValue, ""),
|
||||
}
|
||||
|
||||
if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
// Label functions
|
||||
|
||||
func getFuncString(labelName string, defaultValue string) func(service rancherData) string {
|
||||
return func(service rancherData) string {
|
||||
return label.GetStringValue(service.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncBool(labelName string, defaultValue bool) func(service rancherData) bool {
|
||||
return func(service rancherData) bool {
|
||||
return label.GetBoolValue(service.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncInt(labelName string, defaultValue int) func(service rancherData) int {
|
||||
return func(service rancherData) int {
|
||||
return label.GetIntValue(service.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncInt64(labelName string, defaultValue int64) func(service rancherData) int64 {
|
||||
return func(service rancherData) int64 {
|
||||
return label.GetInt64Value(service.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncSliceString(labelName string) func(service rancherData) []string {
|
||||
return func(service rancherData) []string {
|
||||
return label.GetSliceStringValue(service.Labels, labelName)
|
||||
}
|
||||
}
|
||||
|
||||
func hasFunc(labelName string) func(service rancherData) bool {
|
||||
return func(service rancherData) bool {
|
||||
return label.Has(service.Labels, labelName)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
|
12
provider/rancher/config_root.go
Normal file
12
provider/rancher/config_root.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package rancher
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfiguration(containersInspected []rancherData) *types.Configuration {
|
||||
if p.TemplateVersion == 1 {
|
||||
return p.buildConfigurationV1(containersInspected)
|
||||
}
|
||||
return p.buildConfigurationV2(containersInspected)
|
||||
}
|
|
@ -251,6 +251,172 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "when all segment labels are set",
|
||||
services: []rancherData{
|
||||
{
|
||||
Labels: map[string]string{
|
||||
label.Prefix + "sauternes." + label.SuffixPort: "666",
|
||||
label.Prefix + "sauternes." + label.SuffixProtocol: "https",
|
||||
label.Prefix + "sauternes." + label.SuffixWeight: "12",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendWhiteListSourceRange: "10.10.10.10",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendWhiteListUseXForwardedFor: "true",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSSLProxyHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersAllowedHosts: "foo,bar,bor",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersHostsProxyHeaders: "foo,bar,bor",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSSLHost: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersCustomFrameOptionsValue: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersContentSecurityPolicy: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersPublicKey: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersReferrerPolicy: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersCustomBrowserXSSValue: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSTSSeconds: "666",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSSLRedirect: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSSLTemporaryRedirect: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSTSIncludeSubdomains: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSTSPreload: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersForceSTSHeader: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersFrameDeny: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersContentTypeNosniff: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersBrowserXSSFilter: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersIsDevelopment: "true",
|
||||
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageStatus: "404",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageBackend: "foobar",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageQuery: "foo_query",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageStatus: "500,600",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageBackend: "foobar",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageQuery: "bar_query",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRateLimitExtractorFunc: "client.ip",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
},
|
||||
Health: "healthy",
|
||||
Containers: []string{"10.0.0.1", "10.0.0.2"},
|
||||
},
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-sauternes": {
|
||||
EntryPoints: []string{"http", "https"},
|
||||
Backend: "backend-sauternes",
|
||||
Routes: map[string]types.Route{
|
||||
"route-frontend-sauternes": {
|
||||
Rule: "Host:.rancher.localhost",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
BasicAuth: []string{
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
},
|
||||
WhiteList: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: true,
|
||||
},
|
||||
Headers: &types.Headers{
|
||||
CustomRequestHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
CustomResponseHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
AllowedHosts: []string{"foo", "bar", "bor"},
|
||||
HostsProxyHeaders: []string{"foo", "bar", "bor"},
|
||||
SSLRedirect: true,
|
||||
SSLTemporaryRedirect: true,
|
||||
SSLHost: "foo",
|
||||
SSLProxyHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
STSSeconds: 666,
|
||||
STSIncludeSubdomains: true,
|
||||
STSPreload: true,
|
||||
ForceSTSHeader: true,
|
||||
FrameDeny: true,
|
||||
CustomFrameOptionsValue: "foo",
|
||||
ContentTypeNosniff: true,
|
||||
BrowserXSSFilter: true,
|
||||
CustomBrowserXSSValue: "foo",
|
||||
ContentSecurityPolicy: "foo",
|
||||
PublicKey: "foo",
|
||||
ReferrerPolicy: "foo",
|
||||
IsDevelopment: true,
|
||||
},
|
||||
Errors: map[string]*types.ErrorPage{
|
||||
"bar": {
|
||||
Status: []string{"500", "600"},
|
||||
Backend: "foobar",
|
||||
Query: "bar_query",
|
||||
},
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
Backend: "foobar",
|
||||
Query: "foo_query",
|
||||
},
|
||||
},
|
||||
RateLimit: &types.RateLimit{
|
||||
ExtractorFunc: "client.ip",
|
||||
RateSet: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Regex: "",
|
||||
Replacement: "",
|
||||
Permanent: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-sauternes": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-0": {
|
||||
URL: "https://10.0.0.1:666",
|
||||
Weight: 12,
|
||||
},
|
||||
"server-1": {
|
||||
URL: "https://10.0.0.2:666",
|
||||
Weight: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with services",
|
||||
services: []rancherData{
|
||||
|
@ -298,7 +464,6 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -634,298 +799,15 @@ func TestGetBackendName(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
segmentProperties := label.ExtractTraefikLabels(test.service.Labels)
|
||||
test.service.SegmentLabels = segmentProperties[""]
|
||||
|
||||
actual := getBackendName(test.service)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCircuitBreaker(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected *types.CircuitBreaker
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no CB label",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when CB label is set",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackendCircuitBreakerExpression: "NetworkErrorRatio() > 0.5",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.CircuitBreaker{
|
||||
Expression: "NetworkErrorRatio() > 0.5",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getCircuitBreaker(test.service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLoadBalancer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected *types.LoadBalancer
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no LB labels",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when labels are set",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackendLoadBalancerMethod: "drr",
|
||||
label.TraefikBackendLoadBalancerSticky: "true",
|
||||
label.TraefikBackendLoadBalancerStickiness: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: "foo",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a nil Stickiness when Stickiness is not set",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackendLoadBalancerMethod: "drr",
|
||||
label.TraefikBackendLoadBalancerSticky: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: "foo",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getLoadBalancer(test.service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMaxConn(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected *types.MaxConn
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no max conn labels",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return nil when no amount label",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return default when no empty extractorFunc label",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackendMaxConnExtractorFunc: "",
|
||||
label.TraefikBackendMaxConnAmount: "666",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.MaxConn{
|
||||
ExtractorFunc: "request.host",
|
||||
Amount: 666,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when max conn labels are set",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
label.TraefikBackendMaxConnAmount: "666",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.MaxConn{
|
||||
ExtractorFunc: "client.ip",
|
||||
Amount: 666,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getMaxConn(test.service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHealthCheck(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected *types.HealthCheck
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no health check labels",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return nil when no health check Path label",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackendHealthCheckPort: "80",
|
||||
label.TraefikBackendHealthCheckInterval: "6",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when health check labels are set",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackendHealthCheckPath: "/health",
|
||||
label.TraefikBackendHealthCheckPort: "80",
|
||||
label.TraefikBackendHealthCheckInterval: "6",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.HealthCheck{
|
||||
Path: "/health",
|
||||
Port: 80,
|
||||
Interval: "6",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getHealthCheck(test.service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBuffering(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected *types.Buffering
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no buffering labels",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when buffering labels are set",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackendBufferingMaxResponseBodyBytes: "10485760",
|
||||
label.TraefikBackendBufferingMemResponseBodyBytes: "2097152",
|
||||
label.TraefikBackendBufferingMaxRequestBodyBytes: "10485760",
|
||||
label.TraefikBackendBufferingMemRequestBodyBytes: "2097152",
|
||||
label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.Buffering{
|
||||
MaxResponseBodyBytes: 10485760,
|
||||
MemResponseBodyBytes: 2097152,
|
||||
MaxRequestBodyBytes: 10485760,
|
||||
MemRequestBodyBytes: 2097152,
|
||||
RetryExpression: "IsNetworkError() && Attempts() <= 2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getBuffering(test.service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServers(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -998,352 +880,10 @@ func TestGetServers(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
segmentProperties := label.ExtractTraefikLabels(test.service.Labels)
|
||||
test.service.SegmentLabels = segmentProperties[""]
|
||||
|
||||
actual := getServers(test.service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhiteList(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected *types.WhiteList
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no white list labels",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when only range",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when range and UseXForwardedFor",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.10.10.10",
|
||||
},
|
||||
UseXForwardedFor: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return nil when only UseXForwardedFor",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getWhiteList(test.service)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRedirect(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected *types.Redirect
|
||||
}{
|
||||
|
||||
{
|
||||
desc: "should return nil when no redirect labels",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should use only entry point tag when mix regex redirect and entry point redirect",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||
label.TraefikFrontendRedirectRegex: "(.*)",
|
||||
label.TraefikFrontendRedirectReplacement: "$1",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when entry point redirect label",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when entry point redirect label (permanent)",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||
label.TraefikFrontendRedirectPermanent: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Permanent: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when regex redirect labels",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRedirectRegex: "(.*)",
|
||||
label.TraefikFrontendRedirectReplacement: "$1",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
Regex: "(.*)",
|
||||
Replacement: "$1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when regex redirect labels (permanent)",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRedirectRegex: "(.*)",
|
||||
label.TraefikFrontendRedirectReplacement: "$1",
|
||||
label.TraefikFrontendRedirectPermanent: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.Redirect{
|
||||
Regex: "(.*)",
|
||||
Replacement: "$1",
|
||||
Permanent: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getRedirect(test.service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRateLimit(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected *types.RateLimit
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no rate limit labels",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when rate limit labels are defined",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRateLimitExtractorFunc: "client.ip",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.RateLimit{
|
||||
ExtractorFunc: "client.ip",
|
||||
RateSet: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return nil when ExtractorFunc is missing",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getRateLimit(test.service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHeaders(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected *types.Headers
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no custom headers options are set",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when all custom headers options are set",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.TraefikFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.TraefikFrontendSSLProxyHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.TraefikFrontendAllowedHosts: "foo,bar,bor",
|
||||
label.TraefikFrontendHostsProxyHeaders: "foo,bar,bor",
|
||||
label.TraefikFrontendSSLHost: "foo",
|
||||
label.TraefikFrontendCustomFrameOptionsValue: "foo",
|
||||
label.TraefikFrontendContentSecurityPolicy: "foo",
|
||||
label.TraefikFrontendPublicKey: "foo",
|
||||
label.TraefikFrontendReferrerPolicy: "foo",
|
||||
label.TraefikFrontendCustomBrowserXSSValue: "foo",
|
||||
label.TraefikFrontendSTSSeconds: "666",
|
||||
label.TraefikFrontendSSLRedirect: "true",
|
||||
label.TraefikFrontendSSLTemporaryRedirect: "true",
|
||||
label.TraefikFrontendSTSIncludeSubdomains: "true",
|
||||
label.TraefikFrontendSTSPreload: "true",
|
||||
label.TraefikFrontendForceSTSHeader: "true",
|
||||
label.TraefikFrontendFrameDeny: "true",
|
||||
label.TraefikFrontendContentTypeNosniff: "true",
|
||||
label.TraefikFrontendBrowserXSSFilter: "true",
|
||||
label.TraefikFrontendIsDevelopment: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: &types.Headers{
|
||||
CustomRequestHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
CustomResponseHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
SSLProxyHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
AllowedHosts: []string{"foo", "bar", "bor"},
|
||||
HostsProxyHeaders: []string{"foo", "bar", "bor"},
|
||||
SSLHost: "foo",
|
||||
CustomFrameOptionsValue: "foo",
|
||||
ContentSecurityPolicy: "foo",
|
||||
PublicKey: "foo",
|
||||
ReferrerPolicy: "foo",
|
||||
CustomBrowserXSSValue: "foo",
|
||||
STSSeconds: 666,
|
||||
SSLRedirect: true,
|
||||
SSLTemporaryRedirect: true,
|
||||
STSIncludeSubdomains: true,
|
||||
STSPreload: true,
|
||||
ForceSTSHeader: true,
|
||||
FrameDeny: true,
|
||||
ContentTypeNosniff: true,
|
||||
BrowserXSSFilter: true,
|
||||
IsDevelopment: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getHeaders(test.service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
|
|
226
provider/rancher/deprecated_config.go
Normal file
226
provider/rancher/deprecated_config.go
Normal file
|
@ -0,0 +1,226 @@
|
|||
package rancher
|
||||
|
||||
import (
|
||||
"text/template"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfigurationV1(services []rancherData) *types.Configuration {
|
||||
var RancherFuncMap = template.FuncMap{
|
||||
"getDomain": getFuncStringV1(label.TraefikDomain, p.Domain),
|
||||
|
||||
// Backend functions
|
||||
"getPort": getFuncStringV1(label.TraefikPort, ""),
|
||||
"getProtocol": getFuncStringV1(label.TraefikProtocol, label.DefaultProtocol),
|
||||
"getWeight": getFuncIntV1(label.TraefikWeight, label.DefaultWeightInt),
|
||||
"hasCircuitBreakerLabel": hasFuncV1(label.TraefikBackendCircuitBreakerExpression),
|
||||
"getCircuitBreakerExpression": getFuncStringV1(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
||||
"hasLoadBalancerLabel": hasLoadBalancerLabel,
|
||||
"getLoadBalancerMethod": getFuncStringV1(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod),
|
||||
"hasMaxConnLabels": hasMaxConnLabels,
|
||||
"getMaxConnAmount": getFuncInt64V1(label.TraefikBackendMaxConnAmount, 0),
|
||||
"getMaxConnExtractorFunc": getFuncStringV1(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc),
|
||||
"getSticky": getStickyV1,
|
||||
"hasStickinessLabel": hasFuncV1(label.TraefikBackendLoadBalancerStickiness),
|
||||
"getStickinessCookieName": getFuncStringV1(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
||||
|
||||
// Frontend functions
|
||||
"getBackend": getBackendNameV1,
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
"getPriority": getFuncIntV1(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getPassHostHeader": getFuncBoolV1(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getEntryPoints": getFuncSliceStringV1(label.TraefikFrontendEntryPoints),
|
||||
"getBasicAuth": getFuncSliceStringV1(label.TraefikFrontendAuthBasic),
|
||||
"hasRedirect": hasRedirect,
|
||||
"getRedirectEntryPoint": getRedirectEntryPoint,
|
||||
"getRedirectRegex": getRedirectRegex,
|
||||
"getRedirectReplacement": getRedirectReplacement,
|
||||
}
|
||||
|
||||
// filter services
|
||||
filteredServices := fun.Filter(p.serviceFilterV1, services).([]rancherData)
|
||||
|
||||
frontends := map[string]rancherData{}
|
||||
backends := map[string]rancherData{}
|
||||
|
||||
for _, service := range filteredServices {
|
||||
frontendName := p.getFrontendNameV1(service)
|
||||
frontends[frontendName] = service
|
||||
backendName := getBackendNameV1(service)
|
||||
backends[backendName] = service
|
||||
}
|
||||
|
||||
templateObjects := struct {
|
||||
Frontends map[string]rancherData
|
||||
Backends map[string]rancherData
|
||||
Domain string
|
||||
}{
|
||||
Frontends: frontends,
|
||||
Backends: backends,
|
||||
Domain: p.Domain,
|
||||
}
|
||||
|
||||
configuration, err := p.GetConfiguration("templates/rancher-v1.tmpl", RancherFuncMap, templateObjects)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (p *Provider) serviceFilterV1(service rancherData) bool {
|
||||
if service.Labels[label.TraefikPort] == "" {
|
||||
log.Debugf("Filtering service %s without traefik.port label", service.Name)
|
||||
return false
|
||||
}
|
||||
|
||||
if !label.IsEnabled(service.Labels, p.ExposedByDefault) {
|
||||
log.Debugf("Filtering disabled service %s", service.Name)
|
||||
return false
|
||||
}
|
||||
|
||||
constraintTags := label.GetSliceStringValue(service.Labels, label.TraefikTags)
|
||||
if ok, failingConstraint := p.MatchConstraints(constraintTags); !ok {
|
||||
if failingConstraint != nil {
|
||||
log.Debugf("Filtering service %s with constraint %s", service.Name, failingConstraint.String())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Only filter services by Health (HealthState) and State if EnableServiceHealthFilter is true
|
||||
if p.EnableServiceHealthFilter {
|
||||
|
||||
if service.Health != "" && service.Health != healthy && service.Health != updatingHealthy {
|
||||
log.Debugf("Filtering service %s with healthState of %s", service.Name, service.Health)
|
||||
return false
|
||||
}
|
||||
if service.State != "" && service.State != active && service.State != updatingActive && service.State != upgraded && service.State != upgrading {
|
||||
log.Debugf("Filtering service %s with state of %s", service.Name, service.State)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (p *Provider) getFrontendNameV1(service rancherData) string {
|
||||
return provider.Normalize(p.getFrontendRule(service))
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getBackendNameV1(service rancherData) string {
|
||||
backend := label.GetStringValue(service.Labels, label.TraefikBackend, service.Name)
|
||||
return provider.Normalize(backend)
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
// replaced by Stickiness
|
||||
// Deprecated
|
||||
func getStickyV1(service rancherData) bool {
|
||||
if label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
||||
}
|
||||
return label.GetBoolValue(service.Labels, label.TraefikBackendLoadBalancerSticky, false)
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasLoadBalancerLabel(service rancherData) bool {
|
||||
method := label.Has(service.Labels, label.TraefikBackendLoadBalancerMethod)
|
||||
sticky := label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky)
|
||||
stickiness := label.Has(service.Labels, label.TraefikBackendLoadBalancerStickiness)
|
||||
cookieName := label.Has(service.Labels, label.TraefikBackendLoadBalancerStickinessCookieName)
|
||||
return method || sticky || stickiness || cookieName
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasMaxConnLabels(service rancherData) bool {
|
||||
mca := label.Has(service.Labels, label.TraefikBackendMaxConnAmount)
|
||||
mcef := label.Has(service.Labels, label.TraefikBackendMaxConnExtractorFunc)
|
||||
return mca && mcef
|
||||
}
|
||||
|
||||
func hasRedirect(service rancherData) bool {
|
||||
value := label.GetStringValue(service.Labels, label.TraefikFrontendRedirectEntryPoint, "")
|
||||
frep := len(value) > 0
|
||||
value = label.GetStringValue(service.Labels, label.TraefikFrontendRedirectRegex, "")
|
||||
frrg := len(value) > 0
|
||||
value = label.GetStringValue(service.Labels, label.TraefikFrontendRedirectReplacement, "")
|
||||
frrp := len(value) > 0
|
||||
|
||||
return frep || frrg && frrp
|
||||
}
|
||||
|
||||
func getRedirectEntryPoint(service rancherData) string {
|
||||
value := label.GetStringValue(service.Labels, label.TraefikFrontendRedirectEntryPoint, "")
|
||||
if len(value) == 0 {
|
||||
return ""
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func getRedirectRegex(service rancherData) string {
|
||||
value := label.GetStringValue(service.Labels, label.TraefikFrontendRedirectRegex, "")
|
||||
if len(value) == 0 {
|
||||
return ""
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func getRedirectReplacement(service rancherData) string {
|
||||
value := label.GetStringValue(service.Labels, label.TraefikFrontendRedirectReplacement, "")
|
||||
if len(value) == 0 {
|
||||
return ""
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Label functions
|
||||
|
||||
// Deprecated
|
||||
func getFuncStringV1(labelName string, defaultValue string) func(service rancherData) string {
|
||||
return func(service rancherData) string {
|
||||
return label.GetStringValue(service.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncBoolV1(labelName string, defaultValue bool) func(service rancherData) bool {
|
||||
return func(service rancherData) bool {
|
||||
return label.GetBoolValue(service.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncIntV1(labelName string, defaultValue int) func(service rancherData) int {
|
||||
return func(service rancherData) int {
|
||||
return label.GetIntValue(service.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncInt64V1(labelName string, defaultValue int64) func(service rancherData) int64 {
|
||||
return func(service rancherData) int64 {
|
||||
return label.GetInt64Value(service.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getFuncSliceStringV1(labelName string) func(service rancherData) []string {
|
||||
return func(service rancherData) []string {
|
||||
return label.GetSliceStringValue(service.Labels, labelName)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasFuncV1(labelName string) func(service rancherData) bool {
|
||||
return func(service rancherData) bool {
|
||||
return label.Has(service.Labels, labelName)
|
||||
}
|
||||
}
|
503
provider/rancher/deprecated_config_test.go
Normal file
503
provider/rancher/deprecated_config_test.go
Normal file
|
@ -0,0 +1,503 @@
|
|||
package rancher
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestProviderBuildConfigurationV1(t *testing.T) {
|
||||
provider := &Provider{
|
||||
Domain: "rancher.localhost",
|
||||
ExposedByDefault: true,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
services []rancherData
|
||||
expectedFrontends map[string]*types.Frontend
|
||||
expectedBackends map[string]*types.Backend
|
||||
}{
|
||||
{
|
||||
desc: "without services",
|
||||
services: []rancherData{},
|
||||
expectedFrontends: map[string]*types.Frontend{},
|
||||
expectedBackends: map[string]*types.Backend{},
|
||||
},
|
||||
{
|
||||
desc: "when all labels are set",
|
||||
services: []rancherData{
|
||||
{
|
||||
Labels: map[string]string{
|
||||
label.TraefikPort: "666",
|
||||
label.TraefikProtocol: "https",
|
||||
label.TraefikWeight: "12",
|
||||
|
||||
label.TraefikBackend: "foobar",
|
||||
|
||||
label.TraefikBackendCircuitBreakerExpression: "NetworkErrorRatio() > 0.5",
|
||||
label.TraefikBackendLoadBalancerMethod: "drr",
|
||||
label.TraefikBackendLoadBalancerSticky: "true",
|
||||
label.TraefikBackendLoadBalancerStickiness: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: "chocolate",
|
||||
label.TraefikBackendMaxConnAmount: "666",
|
||||
label.TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
|
||||
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
label.TraefikFrontendEntryPoints: "http,https",
|
||||
label.TraefikFrontendPassHostHeader: "true",
|
||||
label.TraefikFrontendPriority: "666",
|
||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||
label.TraefikFrontendRedirectRegex: "nope",
|
||||
label.TraefikFrontendRedirectReplacement: "nope",
|
||||
label.TraefikFrontendRule: "Host:traefik.io",
|
||||
},
|
||||
Health: "healthy",
|
||||
Containers: []string{"10.0.0.1", "10.0.0.2"},
|
||||
},
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-Host-traefik-io": {
|
||||
EntryPoints: []string{
|
||||
"http",
|
||||
"https",
|
||||
},
|
||||
Backend: "backend-foobar",
|
||||
Routes: map[string]types.Route{
|
||||
"route-frontend-Host-traefik-io": {
|
||||
Rule: "Host:traefik.io",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
Priority: 666,
|
||||
BasicAuth: []string{
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
},
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Regex: "nope",
|
||||
Replacement: "nope",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-foobar": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-0": {
|
||||
URL: "https://10.0.0.1:666",
|
||||
Weight: 12,
|
||||
},
|
||||
"server-1": {
|
||||
URL: "https://10.0.0.2:666",
|
||||
Weight: 12,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: &types.CircuitBreaker{
|
||||
Expression: "NetworkErrorRatio() > 0.5",
|
||||
},
|
||||
LoadBalancer: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
Amount: 666,
|
||||
ExtractorFunc: "client.ip",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with services",
|
||||
services: []rancherData{
|
||||
{
|
||||
Name: "test/service",
|
||||
Labels: map[string]string{
|
||||
label.TraefikPort: "80",
|
||||
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||
},
|
||||
Health: "healthy",
|
||||
Containers: []string{"127.0.0.1"},
|
||||
},
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-Host-test-service-rancher-localhost": {
|
||||
Backend: "backend-test-service",
|
||||
PassHostHeader: true,
|
||||
EntryPoints: []string{},
|
||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
||||
Priority: 0,
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
Routes: map[string]types.Route{
|
||||
"route-frontend-Host-test-service-rancher-localhost": {
|
||||
Rule: "Host:test.service.rancher.localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-test-service": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-0": {
|
||||
URL: "http://127.0.0.1:80",
|
||||
Weight: 0,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actualConfig := provider.buildConfigurationV1(test.services)
|
||||
require.NotNil(t, actualConfig)
|
||||
|
||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderServiceFilterV1(t *testing.T) {
|
||||
provider := &Provider{
|
||||
Domain: "rancher.localhost",
|
||||
EnableServiceHealthFilter: true,
|
||||
}
|
||||
|
||||
constraint, _ := types.NewConstraint("tag==ch*se")
|
||||
provider.Constraints = types.Constraints{constraint}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "missing Port labels, don't respect constraint",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikEnable: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "don't respect constraint",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikPort: "80",
|
||||
label.TraefikEnable: "false",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "unhealthy",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikTags: "cheese",
|
||||
label.TraefikPort: "80",
|
||||
label.TraefikEnable: "true",
|
||||
},
|
||||
Health: "unhealthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "inactive",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikTags: "not-cheesy",
|
||||
label.TraefikPort: "80",
|
||||
label.TraefikEnable: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "inactive",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "healthy & active, tag: cheese",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikTags: "cheese",
|
||||
label.TraefikPort: "80",
|
||||
label.TraefikEnable: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "healthy & active, tag: chose",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikTags: "chose",
|
||||
label.TraefikPort: "80",
|
||||
label.TraefikEnable: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "active",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "healthy & upgraded",
|
||||
service: rancherData{
|
||||
Labels: map[string]string{
|
||||
label.TraefikTags: "cheeeeese",
|
||||
label.TraefikPort: "80",
|
||||
label.TraefikEnable: "true",
|
||||
},
|
||||
Health: "healthy",
|
||||
State: "upgraded",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := provider.serviceFilterV1(test.service)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerFilterV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
healthState string
|
||||
state string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
healthState: "unhealthy",
|
||||
state: "running",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
healthState: "healthy",
|
||||
state: "stopped",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
state: "stopped",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
healthState: "healthy",
|
||||
state: "running",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
healthState: "updating-healthy",
|
||||
state: "updating-running",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.healthState+" "+test.state, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := containerFilter(test.name, test.healthState, test.state)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderGetFrontendNameV1(t *testing.T) {
|
||||
provider := &Provider{Domain: "rancher.localhost"}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "default",
|
||||
service: rancherData{
|
||||
Name: "foo",
|
||||
},
|
||||
expected: "Host-foo-rancher-localhost",
|
||||
},
|
||||
{
|
||||
desc: "with Headers label",
|
||||
service: rancherData{
|
||||
Name: "test-service",
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRule: "Headers:User-Agent,bat/0.1.0",
|
||||
},
|
||||
},
|
||||
expected: "Headers-User-Agent-bat-0-1-0",
|
||||
},
|
||||
{
|
||||
desc: "with Host label",
|
||||
service: rancherData{
|
||||
Name: "test-service",
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRule: "Host:foo.bar",
|
||||
},
|
||||
},
|
||||
expected: "Host-foo-bar",
|
||||
},
|
||||
{
|
||||
desc: "with Path label",
|
||||
service: rancherData{
|
||||
Name: "test-service",
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRule: "Path:/test",
|
||||
},
|
||||
},
|
||||
expected: "Path-test",
|
||||
},
|
||||
{
|
||||
desc: "with PathPrefix label",
|
||||
service: rancherData{
|
||||
Name: "test-service",
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRule: "PathPrefix:/test2",
|
||||
},
|
||||
},
|
||||
expected: "PathPrefix-test2",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := provider.getFrontendNameV1(test.service)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderGetFrontendRuleV1(t *testing.T) {
|
||||
provider := &Provider{Domain: "rancher.localhost"}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "host",
|
||||
service: rancherData{
|
||||
Name: "foo",
|
||||
},
|
||||
expected: "Host:foo.rancher.localhost",
|
||||
},
|
||||
{
|
||||
desc: "host with /",
|
||||
service: rancherData{
|
||||
Name: "foo/bar",
|
||||
},
|
||||
expected: "Host:foo.bar.rancher.localhost",
|
||||
},
|
||||
{
|
||||
desc: "with Host label",
|
||||
service: rancherData{
|
||||
Name: "test-service",
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRule: "Host:foo.bar.com",
|
||||
},
|
||||
},
|
||||
expected: "Host:foo.bar.com",
|
||||
},
|
||||
{
|
||||
desc: "with Path label",
|
||||
service: rancherData{
|
||||
Name: "test-service",
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRule: "Path:/test",
|
||||
},
|
||||
},
|
||||
expected: "Path:/test",
|
||||
},
|
||||
{
|
||||
desc: "with PathPrefix label",
|
||||
service: rancherData{
|
||||
Name: "test-service",
|
||||
Labels: map[string]string{
|
||||
label.TraefikFrontendRule: "PathPrefix:/test2",
|
||||
},
|
||||
},
|
||||
expected: "PathPrefix:/test2",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := provider.getFrontendRule(test.service)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBackendNameV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
service rancherData
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "without label",
|
||||
service: rancherData{
|
||||
Name: "test-service",
|
||||
},
|
||||
expected: "test-service",
|
||||
},
|
||||
{
|
||||
desc: "with label",
|
||||
service: rancherData{
|
||||
Name: "test-service",
|
||||
Labels: map[string]string{
|
||||
label.TraefikBackend: "foobar",
|
||||
},
|
||||
},
|
||||
|
||||
expected: "foobar",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getBackendNameV1(test.service)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -38,11 +38,13 @@ type Provider struct {
|
|||
}
|
||||
|
||||
type rancherData struct {
|
||||
Name string
|
||||
Labels map[string]string // List of labels set to container or service
|
||||
Containers []string
|
||||
Health string
|
||||
State string
|
||||
Name string
|
||||
Labels map[string]string // List of labels set to container or service
|
||||
Containers []string
|
||||
Health string
|
||||
State string
|
||||
SegmentLabels map[string]string
|
||||
SegmentName string
|
||||
}
|
||||
|
||||
func (r rancherData) String() string {
|
||||
|
|
68
templates/marathon-v1.tmpl
Normal file
68
templates/marathon-v1.tmpl
Normal file
|
@ -0,0 +1,68 @@
|
|||
{{$apps := .Applications}}
|
||||
|
||||
{{range $app := $apps }}
|
||||
{{range $task := $app.Tasks }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".servers."server-{{ $task.ID | replace "." "-"}}{{getServiceNameSuffix $serviceName }}"]
|
||||
url = "{{ getProtocol $app $serviceName }}://{{ getBackendServer $task $app }}:{{ getPort $task $app $serviceName }}"
|
||||
weight = {{ getWeight $app $serviceName }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{range $app := $apps }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app }}
|
||||
|
||||
[backends."{{ getBackend $app $serviceName }}"]
|
||||
{{if hasMaxConnLabels $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".maxConn]
|
||||
amount = {{ getMaxConnAmount $app }}
|
||||
extractorFunc = "{{ getMaxConnExtractorFunc $app }}"
|
||||
{{end}}
|
||||
|
||||
{{if hasLoadBalancerLabels $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".loadBalancer]
|
||||
method = "{{ getLoadBalancerMethod $app }}"
|
||||
sticky = {{ getSticky $app }}
|
||||
{{if hasStickinessLabel $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ getStickinessCookieName $app }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if hasCircuitBreakerLabels $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".circuitBreaker]
|
||||
expression = "{{ getCircuitBreakerExpression $app }}"
|
||||
{{end}}
|
||||
|
||||
{{if hasHealthCheckLabels $app }}
|
||||
[backends."{{ getBackend $app $serviceName }}".healthCheck]
|
||||
path = "{{ getHealthCheckPath $app }}"
|
||||
interval = "{{ getHealthCheckInterval $app }}"
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends]
|
||||
{{range $app := $apps }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames . }}
|
||||
|
||||
[frontends."{{ getFrontendName $app $serviceName | normalize }}"]
|
||||
backend = "{{ getBackend $app $serviceName }}"
|
||||
passHostHeader = {{ getPassHostHeader $app $serviceName }}
|
||||
priority = {{ getPriority $app $serviceName }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $app $serviceName }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
basicAuth = [{{range getBasicAuth $app $serviceName }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
[frontends."{{ getFrontendName $app $serviceName | normalize }}".routes."route-host{{ $app.ID | replace "/" "-" }}{{ getServiceNameSuffix $serviceName }}"]
|
||||
rule = "{{ getFrontendRule $app $serviceName }}"
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -2,18 +2,17 @@
|
|||
|
||||
[backends]
|
||||
{{range $app := $apps }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app }}
|
||||
{{ $backendName := getBackend $app $serviceName}}
|
||||
{{ $backendName := getBackendName $app }}
|
||||
|
||||
[backends."{{ $backendName }}"]
|
||||
|
||||
{{ $circuitBreaker := getCircuitBreaker $app }}
|
||||
{{ $circuitBreaker := getCircuitBreaker $app.SegmentLabels }}
|
||||
{{if $circuitBreaker }}
|
||||
[backends."{{ $backendName }}".circuitBreaker]
|
||||
expression = "{{ $circuitBreaker.Expression }}"
|
||||
{{end}}
|
||||
|
||||
{{ $loadBalancer := getLoadBalancer $app }}
|
||||
{{ $loadBalancer := getLoadBalancer $app.SegmentLabels }}
|
||||
{{if $loadBalancer }}
|
||||
[backends."{{ $backendName }}".loadBalancer]
|
||||
method = "{{ $loadBalancer.Method }}"
|
||||
|
@ -24,14 +23,14 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $maxConn := getMaxConn $app }}
|
||||
{{ $maxConn := getMaxConn $app.SegmentLabels }}
|
||||
{{if $maxConn }}
|
||||
[backends."{{ $backendName }}".maxConn]
|
||||
extractorFunc = "{{ $maxConn.ExtractorFunc }}"
|
||||
amount = {{ $maxConn.Amount }}
|
||||
{{end}}
|
||||
|
||||
{{ $healthCheck := getHealthCheck $app }}
|
||||
{{ $healthCheck := getHealthCheck $app.SegmentLabels }}
|
||||
{{if $healthCheck }}
|
||||
[backends."{{ $backendName }}".healthCheck]
|
||||
path = "{{ $healthCheck.Path }}"
|
||||
|
@ -39,7 +38,7 @@
|
|||
interval = "{{ $healthCheck.Interval }}"
|
||||
{{end}}
|
||||
|
||||
{{ $buffering := getBuffering $app }}
|
||||
{{ $buffering := getBuffering $app.SegmentLabels }}
|
||||
{{if $buffering }}
|
||||
[backends."{{ $backendName }}".buffering]
|
||||
maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }}
|
||||
|
@ -49,35 +48,33 @@
|
|||
retryExpression = "{{ $buffering.RetryExpression }}"
|
||||
{{end}}
|
||||
|
||||
{{range $serverName, $server := getServers $app $serviceName }}
|
||||
{{range $serverName, $server := getServers $app }}
|
||||
[backends."{{ $backendName }}".servers."{{ $serverName }}"]
|
||||
url = "{{ $server.URL }}"
|
||||
weight = {{ $server.Weight }}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends]
|
||||
{{range $app := $apps }}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app }}
|
||||
{{ $frontendName := getFrontendName $app $serviceName }}
|
||||
{{ $frontendName := getFrontendName $app }}
|
||||
|
||||
[frontends."{{ $frontendName }}"]
|
||||
backend = "{{ getBackend $app $serviceName }}"
|
||||
priority = {{ getPriority $app $serviceName }}
|
||||
passHostHeader = {{ getPassHostHeader $app $serviceName }}
|
||||
passTLSCert = {{ getPassTLSCert $app $serviceName }}
|
||||
backend = "{{ getBackendName $app }}"
|
||||
priority = {{ getPriority $app.SegmentLabels }}
|
||||
passHostHeader = {{ getPassHostHeader $app.SegmentLabels }}
|
||||
passTLSCert = {{ getPassTLSCert $app.SegmentLabels }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $app $serviceName }}
|
||||
entryPoints = [{{range getEntryPoints $app.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
basicAuth = [{{range getBasicAuth $app $serviceName }}
|
||||
basicAuth = [{{range getBasicAuth $app.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $whitelist := getWhiteList $app $serviceName }}
|
||||
{{ $whitelist := getWhiteList $app.SegmentLabels }}
|
||||
{{if $whitelist }}
|
||||
[frontends."{{ $frontendName }}".whiteList]
|
||||
sourceRange = [{{range $whitelist.SourceRange }}
|
||||
|
@ -86,7 +83,7 @@
|
|||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||
{{end}}
|
||||
|
||||
{{ $redirect := getRedirect $app $serviceName }}
|
||||
{{ $redirect := getRedirect $app.SegmentLabels }}
|
||||
{{if $redirect }}
|
||||
[frontends."{{ $frontendName }}".redirect]
|
||||
entryPoint = "{{ $redirect.EntryPoint }}"
|
||||
|
@ -95,7 +92,7 @@
|
|||
permanent = {{ $redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{ $errorPages := getErrorPages $app $serviceName }}
|
||||
{{ $errorPages := getErrorPages $app.SegmentLabels }}
|
||||
{{if $errorPages }}
|
||||
[frontends."{{ $frontendName }}".errors]
|
||||
{{range $pageName, $page := $errorPages }}
|
||||
|
@ -108,7 +105,7 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $rateLimit := getRateLimit $app $serviceName }}
|
||||
{{ $rateLimit := getRateLimit $app.SegmentLabels }}
|
||||
{{if $rateLimit }}
|
||||
[frontends."{{ $frontendName }}".rateLimit]
|
||||
extractorFunc = "{{ $rateLimit.ExtractorFunc }}"
|
||||
|
@ -121,7 +118,7 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $headers := getHeaders $app $serviceName }}
|
||||
{{ $headers := getHeaders $app.SegmentLabels }}
|
||||
{{if $headers }}
|
||||
[frontends."{{ $frontendName }}".headers]
|
||||
SSLRedirect = {{ $headers.SSLRedirect }}
|
||||
|
@ -175,8 +172,7 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends."{{ $frontendName }}".routes."route-host{{ $app.ID | replace "/" "-" }}{{ getServiceNameSuffix $serviceName }}"]
|
||||
rule = "{{ getFrontendRule $app $serviceName }}"
|
||||
[frontends."{{ $frontendName }}".routes."route-host{{ $app.ID | replace "/" "-" }}{{ getSegmentNameSuffix $app.SegmentName }}"]
|
||||
rule = "{{ getFrontendRule $app }}"
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
58
templates/rancher-v1.tmpl
Normal file
58
templates/rancher-v1.tmpl
Normal file
|
@ -0,0 +1,58 @@
|
|||
{{$backendServers := .Backends}}
|
||||
|
||||
[backends]
|
||||
{{range $backendName, $backend := .Backends }}
|
||||
{{if hasCircuitBreakerLabel $backend }}
|
||||
[backends."backend-{{ $backendName }}".circuitBreaker]
|
||||
expression = "{{ getCircuitBreakerExpression $backend }}"
|
||||
{{end}}
|
||||
|
||||
{{if hasLoadBalancerLabel $backend }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer]
|
||||
method = "{{ getLoadBalancerMethod $backend }}"
|
||||
sticky = {{ getSticky $backend }}
|
||||
{{if hasStickinessLabel $backend }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ getStickinessCookieName $backend }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if hasMaxConnLabels $backend }}
|
||||
[backends."backend-{{ $backendName }}".maxConn]
|
||||
amount = {{ getMaxConnAmount $backend }}
|
||||
extractorFunc = "{{ getMaxConnExtractorFunc $backend }}"
|
||||
{{end}}
|
||||
|
||||
{{range $index, $ip := $backend.Containers }}
|
||||
[backends."backend-{{ $backendName }}".servers."server-{{ $index }}"]
|
||||
url = "{{ getProtocol $backend }}://{{ $ip }}:{{ getPort $backend }}"
|
||||
weight = {{ getWeight $backend }}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
[frontends]
|
||||
{{range $frontendName, $service := .Frontends }}
|
||||
[frontends."frontend-{{ $frontendName }}"]
|
||||
backend = "backend-{{ getBackend $service }}"
|
||||
passHostHeader = {{ getPassHostHeader $service }}
|
||||
priority = {{ getPriority $service }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $service }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
basicAuth = [{{range getBasicAuth $service }}
|
||||
"{{.}}",
|
||||
{{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 }}"]
|
||||
rule = "{{ getFrontendRule $service }}"
|
||||
{{end}}
|
|
@ -4,13 +4,13 @@
|
|||
|
||||
[backends."backend-{{ $backendName }}"]
|
||||
|
||||
{{ $circuitBreaker := getCircuitBreaker $backend }}
|
||||
{{ $circuitBreaker := getCircuitBreaker $backend.SegmentLabels }}
|
||||
{{if $circuitBreaker }}
|
||||
[backends."backend-{{ $backendName }}".circuitBreaker]
|
||||
expression = "{{ $circuitBreaker.Expression }}"
|
||||
{{end}}
|
||||
|
||||
{{ $loadBalancer := getLoadBalancer $backend }}
|
||||
{{ $loadBalancer := getLoadBalancer $backend.SegmentLabels }}
|
||||
{{if $loadBalancer }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer]
|
||||
method = "{{ $loadBalancer.Method }}"
|
||||
|
@ -21,14 +21,14 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $maxConn := getMaxConn $backend }}
|
||||
{{ $maxConn := getMaxConn $backend.SegmentLabels }}
|
||||
{{if $maxConn }}
|
||||
[backends."backend-{{ $backendName }}".maxConn]
|
||||
extractorFunc = "{{ $maxConn.ExtractorFunc }}"
|
||||
amount = {{ $maxConn.Amount }}
|
||||
{{end}}
|
||||
|
||||
{{ $healthCheck := getHealthCheck $backend }}
|
||||
{{ $healthCheck := getHealthCheck $backend.SegmentLabels }}
|
||||
{{if $healthCheck }}
|
||||
[backends."backend-{{ $backendName }}".healthCheck]
|
||||
path = "{{ $healthCheck.Path }}"
|
||||
|
@ -36,7 +36,7 @@
|
|||
interval = "{{ $healthCheck.Interval }}"
|
||||
{{end}}
|
||||
|
||||
{{ $buffering := getBuffering $backend }}
|
||||
{{ $buffering := getBuffering $backend.SegmentLabels }}
|
||||
{{if $buffering }}
|
||||
[backends."backend-{{ $backendName }}".buffering]
|
||||
maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }}
|
||||
|
@ -59,19 +59,19 @@
|
|||
|
||||
[frontends."frontend-{{ $frontendName }}"]
|
||||
backend = "backend-{{ getBackendName $service }}"
|
||||
priority = {{ getPriority $service }}
|
||||
passHostHeader = {{ getPassHostHeader $service }}
|
||||
passTLSCert = {{ getPassTLSCert $service }}
|
||||
priority = {{ getPriority $service.SegmentLabels }}
|
||||
passHostHeader = {{ getPassHostHeader $service.SegmentLabels }}
|
||||
passTLSCert = {{ getPassTLSCert $service.SegmentLabels }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $service }}
|
||||
entryPoints = [{{range getEntryPoints $service.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
basicAuth = [{{range getBasicAuth $service }}
|
||||
basicAuth = [{{range getBasicAuth $service.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $whitelist := getWhiteList $service }}
|
||||
{{ $whitelist := getWhiteList $service.SegmentLabels }}
|
||||
{{if $whitelist }}
|
||||
[frontends."frontend-{{ $frontendName }}".whiteList]
|
||||
sourceRange = [{{range $whitelist.SourceRange }}
|
||||
|
@ -80,7 +80,7 @@
|
|||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||
{{end}}
|
||||
|
||||
{{ $redirect := getRedirect $service }}
|
||||
{{ $redirect := getRedirect $service.SegmentLabels }}
|
||||
{{if $redirect }}
|
||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||
entryPoint = "{{ $redirect.EntryPoint }}"
|
||||
|
@ -89,7 +89,7 @@
|
|||
permanent = {{ $redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{ $errorPages := getErrorPages $service }}
|
||||
{{ $errorPages := getErrorPages $service.SegmentLabels }}
|
||||
{{if $errorPages }}
|
||||
[frontends."frontend-{{ $frontendName }}".errors]
|
||||
{{range $pageName, $page := $errorPages }}
|
||||
|
@ -102,7 +102,7 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $rateLimit := getRateLimit $service }}
|
||||
{{ $rateLimit := getRateLimit $service.SegmentLabels }}
|
||||
{{if $rateLimit }}
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit]
|
||||
extractorFunc = "{{ $rateLimit.ExtractorFunc }}"
|
||||
|
@ -115,7 +115,7 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $headers := getHeaders $service }}
|
||||
{{ $headers := getHeaders $service.SegmentLabels }}
|
||||
{{if $headers }}
|
||||
[frontends."frontend-{{ $frontendName }}".headers]
|
||||
SSLRedirect = {{ $headers.SSLRedirect }}
|
||||
|
|
Loading…
Reference in a new issue