Removes templates
This commit is contained in:
parent
d8f69700e6
commit
f611ef0edd
42 changed files with 37 additions and 8397 deletions
|
@ -1,20 +1,14 @@
|
||||||
// Code generated by go-bindata.
|
// Code generated by go-bindata.
|
||||||
// sources:
|
// sources:
|
||||||
// templates/consul_catalog-v1.tmpl
|
|
||||||
// templates/consul_catalog.tmpl
|
// templates/consul_catalog.tmpl
|
||||||
// templates/docker-v1.tmpl
|
|
||||||
// templates/docker.tmpl
|
// templates/docker.tmpl
|
||||||
// templates/ecs-v1.tmpl
|
|
||||||
// templates/ecs.tmpl
|
// templates/ecs.tmpl
|
||||||
// templates/eureka.tmpl
|
// templates/eureka.tmpl
|
||||||
// templates/kubernetes.tmpl
|
// templates/kubernetes.tmpl
|
||||||
// templates/kv.tmpl
|
// templates/kv.tmpl
|
||||||
// templates/marathon-v1.tmpl
|
|
||||||
// templates/marathon.tmpl
|
// templates/marathon.tmpl
|
||||||
// templates/mesos-v1.tmpl
|
|
||||||
// templates/mesos.tmpl
|
// templates/mesos.tmpl
|
||||||
// templates/notFound.tmpl
|
// templates/notFound.tmpl
|
||||||
// templates/rancher-v1.tmpl
|
|
||||||
// templates/rancher.tmpl
|
// templates/rancher.tmpl
|
||||||
// DO NOT EDIT!
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
@ -60,79 +54,6 @@ func (fi bindataFileInfo) Sys() interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _templatesConsul_catalogV1Tmpl = []byte(`[backends]
|
|
||||||
{{range $index, $node := .Nodes }}
|
|
||||||
[backends."backend-{{ getBackend $node }}".servers."{{ getBackendName $node $index }}"]
|
|
||||||
url = "{{ getAttribute "protocol" $node.Service.Tags "http" }}://{{ getBackendAddress $node }}:{{ $node.Service.Port }}"
|
|
||||||
{{ $weight := getAttribute "backend.weight" $node.Service.Tags "0" }}
|
|
||||||
{{with $weight }}
|
|
||||||
weight = {{ $weight }}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .Services }}
|
|
||||||
{{ $service := .ServiceName }}
|
|
||||||
|
|
||||||
{{ $circuitBreaker := getAttribute "backend.circuitbreaker" .Attributes "" }}
|
|
||||||
{{with $circuitBreaker }}
|
|
||||||
[backends."backend-{{ $service }}".circuitbreaker]
|
|
||||||
expression = "{{ $circuitBreaker }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[backends."backend-{{ $service }}".loadbalancer]
|
|
||||||
method = "{{ getAttribute "backend.loadbalancer" .Attributes "wrr" }}"
|
|
||||||
sticky = {{ getSticky .Attributes }}
|
|
||||||
{{if hasStickinessLabel .Attributes }}
|
|
||||||
[backends."backend-{{ $service }}".loadbalancer.stickiness]
|
|
||||||
cookieName = "{{ getStickinessCookieName .Attributes }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if hasMaxconnAttributes .Attributes }}
|
|
||||||
[backends."backend-{{ $service }}".maxconn]
|
|
||||||
amount = {{ getAttribute "backend.maxconn.amount" .Attributes "" }}
|
|
||||||
extractorfunc = "{{ getAttribute "backend.maxconn.extractorfunc" .Attributes "" }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends]
|
|
||||||
{{range .Services }}
|
|
||||||
[frontends."frontend-{{ .ServiceName }}"]
|
|
||||||
backend = "backend-{{ .ServiceName }}"
|
|
||||||
passHostHeader = {{ getAttribute "frontend.passHostHeader" .Attributes "true" }}
|
|
||||||
priority = {{ getAttribute "frontend.priority" .Attributes "0" }}
|
|
||||||
|
|
||||||
{{ $entryPoints := getAttribute "frontend.entrypoints" .Attributes "" }}
|
|
||||||
{{with $entryPoints }}
|
|
||||||
entrypoints = [{{range getEntryPoints $entryPoints }}
|
|
||||||
"{{ . }}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth .Attributes }}
|
|
||||||
"{{ . }}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
[frontends."frontend-{{ .ServiceName }}".routes."route-host-{{ .ServiceName }}"]
|
|
||||||
rule = "{{ getFrontendRule . }}"
|
|
||||||
{{end}}
|
|
||||||
`)
|
|
||||||
|
|
||||||
func templatesConsul_catalogV1TmplBytes() ([]byte, error) {
|
|
||||||
return _templatesConsul_catalogV1Tmpl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func templatesConsul_catalogV1Tmpl() (*asset, error) {
|
|
||||||
bytes, err := templatesConsul_catalogV1TmplBytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
info := bindataFileInfo{name: "templates/consul_catalog-v1.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
|
||||||
a := &asset{bytes: bytes, info: info}
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _templatesConsul_catalogTmpl = []byte(`[backends]
|
var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||||
{{range $service := .Services}}
|
{{range $service := .Services}}
|
||||||
{{ $backendName := getServiceBackendName $service }}
|
{{ $backendName := getServiceBackendName $service }}
|
||||||
|
@ -373,215 +294,6 @@ func templatesConsul_catalogTmpl() (*asset, error) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _templatesDockerV1Tmpl = []byte(`{{$backendServers := .Servers}}
|
|
||||||
|
|
||||||
[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}}
|
|
||||||
|
|
||||||
{{ $servers := index $backendServers $backendName }}
|
|
||||||
{{range $serverName, $server := $servers }}
|
|
||||||
{{if hasServices $server }}
|
|
||||||
{{$services := getServiceNames $server }}
|
|
||||||
{{range $serviceIndex, $serviceName := $services }}
|
|
||||||
[backends."backend-{{ getServiceBackend $server $serviceName }}".servers."service-{{ $serverName }}"]
|
|
||||||
url = "{{ getServiceProtocol $server $serviceName }}://{{ getIPAddress $server }}:{{ getServicePort $server $serviceName }}"
|
|
||||||
weight = {{ getServiceWeight $server $serviceName }}
|
|
||||||
{{end}}
|
|
||||||
{{else}}
|
|
||||||
[backends."backend-{{ $backendName }}".servers."server-{{$server.Name | replace "/" "" | replace "." "-"}}"]
|
|
||||||
url = "{{ getProtocol $server }}://{{ getIPAddress $server }}:{{ getPort $server }}"
|
|
||||||
weight = {{ getWeight $server }}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends]
|
|
||||||
{{range $frontend, $containers := .Frontends}}
|
|
||||||
{{$container := index $containers 0}}
|
|
||||||
|
|
||||||
{{if hasServices $container }}
|
|
||||||
{{ $services := getServiceNames $container }}
|
|
||||||
{{range $serviceIndex, $serviceName := $services }}
|
|
||||||
[frontends."frontend-{{ getServiceBackend $container $serviceName }}"]
|
|
||||||
backend = "backend-{{ getServiceBackend $container $serviceName }}"
|
|
||||||
passHostHeader = {{ getServicePassHostHeader $container $serviceName }}
|
|
||||||
passTLSCert = {{ getServicePassTLSCert $container $serviceName }}
|
|
||||||
|
|
||||||
{{if getWhitelistSourceRange $container }}
|
|
||||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
priority = {{ getServicePriority $container $serviceName }}
|
|
||||||
|
|
||||||
entryPoints = [{{range getServiceEntryPoints $container $serviceName }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
basicAuth = [{{range getServiceBasicAuth $container $serviceName }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
{{if hasServiceRedirect $container $serviceName }}
|
|
||||||
[frontends."frontend-{{ getServiceBackend $container $serviceName }}".redirect]
|
|
||||||
entryPoint = "{{ getServiceRedirectEntryPoint $container $serviceName }}"
|
|
||||||
regex = "{{ getServiceRedirectRegex $container $serviceName }}"
|
|
||||||
replacement = "{{ getServiceRedirectReplacement $container $serviceName }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends."frontend-{{ getServiceBackend $container $serviceName }}".routes."service-{{ $serviceName | replace "/" "" | replace "." "-" }}"]
|
|
||||||
rule = "{{ getServiceFrontendRule $container $serviceName }}"
|
|
||||||
{{end}}
|
|
||||||
{{else}}
|
|
||||||
[frontends."frontend-{{ $frontend }}"]
|
|
||||||
backend = "backend-{{ getBackend $container }}"
|
|
||||||
passHostHeader = {{ getPassHostHeader $container}}
|
|
||||||
passTLSCert = {{ getPassTLSCert $container }}
|
|
||||||
priority = {{ getPriority $container }}
|
|
||||||
|
|
||||||
{{if getWhitelistSourceRange $container}}
|
|
||||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
entryPoints = [{{range getEntryPoints $container }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $container }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
{{if hasRedirect $container}}
|
|
||||||
[frontends."frontend-{{$frontend}}".redirect]
|
|
||||||
entryPoint = "{{getRedirectEntryPoint $container}}"
|
|
||||||
regex = "{{getRedirectRegex $container}}"
|
|
||||||
replacement = "{{getRedirectReplacement $container}}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if hasHeaders $container }}
|
|
||||||
[frontends."frontend-{{ $frontend }}".headers]
|
|
||||||
{{if hasSSLRedirectHeaders $container}}
|
|
||||||
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSSLTemporaryRedirectHeaders $container}}
|
|
||||||
SSLTemporaryRedirect = {{getSSLTemporaryRedirectHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSSLHostHeaders $container}}
|
|
||||||
SSLHost = "{{getSSLHostHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasSTSSecondsHeaders $container}}
|
|
||||||
STSSeconds = {{getSTSSecondsHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSTSIncludeSubdomainsHeaders $container}}
|
|
||||||
STSIncludeSubdomains = {{getSTSIncludeSubdomainsHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSTSPreloadHeaders $container}}
|
|
||||||
STSPreload = {{getSTSPreloadHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasForceSTSHeaderHeaders $container}}
|
|
||||||
ForceSTSHeader = {{getForceSTSHeaderHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasFrameDenyHeaders $container}}
|
|
||||||
FrameDeny = {{getFrameDenyHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasCustomFrameOptionsValueHeaders $container}}
|
|
||||||
CustomFrameOptionsValue = "{{getCustomFrameOptionsValueHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasContentTypeNosniffHeaders $container}}
|
|
||||||
ContentTypeNosniff = {{getContentTypeNosniffHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasBrowserXSSFilterHeaders $container}}
|
|
||||||
BrowserXSSFilter = {{getBrowserXSSFilterHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasContentSecurityPolicyHeaders $container}}
|
|
||||||
ContentSecurityPolicy = "{{getContentSecurityPolicyHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasPublicKeyHeaders $container}}
|
|
||||||
PublicKey = "{{getPublicKeyHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasReferrerPolicyHeaders $container}}
|
|
||||||
ReferrerPolicy = "{{getReferrerPolicyHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasIsDevelopmentHeaders $container}}
|
|
||||||
IsDevelopment = {{getIsDevelopmentHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasAllowedHostsHeaders $container}}
|
|
||||||
AllowedHosts = [{{range getAllowedHostsHeaders $container}}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
{{if hasHostsProxyHeaders $container}}
|
|
||||||
HostsProxyHeaders = [{{range getHostsProxyHeaders $container}}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
{{if hasRequestHeaders $container}}
|
|
||||||
[frontends."frontend-{{$frontend}}".headers.customrequestheaders]
|
|
||||||
{{range $k, $v := getRequestHeaders $container}}
|
|
||||||
{{$k}} = "{{$v}}"
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasResponseHeaders $container}}
|
|
||||||
[frontends."frontend-{{$frontend}}".headers.customresponseheaders]
|
|
||||||
{{range $k, $v := getResponseHeaders $container}}
|
|
||||||
{{$k}} = "{{$v}}"
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSSLProxyHeaders $container}}
|
|
||||||
[frontends."frontend-{{$frontend}}".headers.SSLProxyHeaders]
|
|
||||||
{{range $k, $v := getSSLProxyHeaders $container}}
|
|
||||||
{{$k}} = "{{$v}}"
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends."frontend-{{$frontend}}".routes."route-frontend-{{$frontend}}"]
|
|
||||||
rule = "{{getFrontendRule $container}}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
`)
|
|
||||||
|
|
||||||
func templatesDockerV1TmplBytes() ([]byte, error) {
|
|
||||||
return _templatesDockerV1Tmpl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func templatesDockerV1Tmpl() (*asset, error) {
|
|
||||||
bytes, err := templatesDockerV1TmplBytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
info := bindataFileInfo{name: "templates/docker-v1.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
|
||||||
a := &asset{bytes: bytes, info: info}
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
[backends]
|
[backends]
|
||||||
{{range $backendName, $servers := .Servers}}
|
{{range $backendName, $servers := .Servers}}
|
||||||
|
@ -823,66 +535,6 @@ func templatesDockerTmpl() (*asset, error) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _templatesEcsV1Tmpl = []byte(`[backends]
|
|
||||||
{{range $serviceName, $instances := .Services }}
|
|
||||||
[backends."backend-{{ $serviceName }}".loadBalancer]
|
|
||||||
method = "{{ getLoadBalancerMethod $instances }}"
|
|
||||||
sticky = {{ getLoadBalancerSticky $instances }}
|
|
||||||
|
|
||||||
{{if hasStickinessLabel $instances }}
|
|
||||||
[backends."backend-{{ $serviceName }}".loadBalancer.stickiness]
|
|
||||||
cookieName = "{{ getStickinessCookieName $instances }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{ if hasHealthCheckLabels $instances }}
|
|
||||||
[backends."backend-{{ $serviceName }}".healthCheck]
|
|
||||||
path = "{{ getHealthCheckPath $instances }}"
|
|
||||||
interval = "{{ getHealthCheckInterval $instances }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range $index, $i := $instances }}
|
|
||||||
[backends."backend-{{ $serviceName }}".servers."server-{{ $i.Name }}{{ $i.ID }}"]
|
|
||||||
url = "{{ getProtocol $i }}://{{ getHost $i }}:{{ getPort $i }}"
|
|
||||||
weight = {{ getWeight $i }}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends]
|
|
||||||
{{range $serviceName, $instances := .Services}}
|
|
||||||
{{range filterFrontends $instances }}
|
|
||||||
[frontends."frontend-{{ $serviceName }}"]
|
|
||||||
backend = "backend-{{ $serviceName }}"
|
|
||||||
passHostHeader = {{ getPassHostHeader . }}
|
|
||||||
priority = {{ getPriority . }}
|
|
||||||
|
|
||||||
entryPoints = [{{range getEntryPoints . }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth . }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
[frontends."frontend-{{ $serviceName }}".routes."route-frontend-{{ $serviceName }}"]
|
|
||||||
rule = "{{getFrontendRule .}}"
|
|
||||||
{{end}}
|
|
||||||
{{end}}`)
|
|
||||||
|
|
||||||
func templatesEcsV1TmplBytes() ([]byte, error) {
|
|
||||||
return _templatesEcsV1Tmpl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func templatesEcsV1Tmpl() (*asset, error) {
|
|
||||||
bytes, err := templatesEcsV1TmplBytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
info := bindataFileInfo{name: "templates/ecs-v1.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
|
||||||
a := &asset{bytes: bytes, info: info}
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _templatesEcsTmpl = []byte(`[backends]
|
var _templatesEcsTmpl = []byte(`[backends]
|
||||||
{{range $serviceName, $instances := .Services }}
|
{{range $serviceName, $instances := .Services }}
|
||||||
{{ $firstInstance := index $instances 0 }}
|
{{ $firstInstance := index $instances 0 }}
|
||||||
|
@ -1630,91 +1282,6 @@ func templatesKvTmpl() (*asset, error) {
|
||||||
return a, nil
|
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 }}
|
var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
||||||
|
|
||||||
[backends]
|
[backends]
|
||||||
|
@ -1957,50 +1524,6 @@ func templatesMarathonTmpl() (*asset, error) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _templatesMesosV1Tmpl = []byte(`{{$apps := .Applications}}
|
|
||||||
|
|
||||||
[backends]
|
|
||||||
{{range .Tasks}}
|
|
||||||
|
|
||||||
[backends."backend-{{ getBackend . $apps }}".servers."server-{{ getID . }}"]
|
|
||||||
url = "{{ getProtocol . $apps }}://{{ getHost . }}:{{ getPort . $apps }}"
|
|
||||||
weight = {{ getWeight . $apps }}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends]
|
|
||||||
{{range .Applications}}
|
|
||||||
|
|
||||||
[frontends."frontend-{{getFrontEndName . }}"]
|
|
||||||
backend = "backend-{{ getFrontendBackend . }}"
|
|
||||||
passHostHeader = {{ getPassHostHeader . }}
|
|
||||||
priority = {{ getPriority . }}
|
|
||||||
|
|
||||||
entryPoints = [{{range getEntryPoints . }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
[frontends."frontend-{{ getFrontEndName . }}".routes."route-host-{{ getFrontEndName . }}"]
|
|
||||||
rule = "{{ getFrontendRule . }}"
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
`)
|
|
||||||
|
|
||||||
func templatesMesosV1TmplBytes() ([]byte, error) {
|
|
||||||
return _templatesMesosV1Tmpl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func templatesMesosV1Tmpl() (*asset, error) {
|
|
||||||
bytes, err := templatesMesosV1TmplBytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
info := bindataFileInfo{name: "templates/mesos-v1.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
|
||||||
a := &asset{bytes: bytes, info: info}
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _templatesMesosTmpl = []byte(`[backends]
|
var _templatesMesosTmpl = []byte(`[backends]
|
||||||
{{range $applicationName, $tasks := .ApplicationsTasks }}
|
{{range $applicationName, $tasks := .ApplicationsTasks }}
|
||||||
{{ $app := index $tasks 0 }}
|
{{ $app := index $tasks 0 }}
|
||||||
|
@ -2267,81 +1790,6 @@ func templatesNotfoundTmpl() (*asset, error) {
|
||||||
return a, nil
|
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 }}
|
var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
||||||
[backends]
|
[backends]
|
||||||
{{range $backendName, $backend := .Backends }}
|
{{range $backendName, $backend := .Backends }}
|
||||||
|
@ -2634,22 +2082,16 @@ func AssetNames() []string {
|
||||||
|
|
||||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
var _bindata = map[string]func() (*asset, error){
|
var _bindata = map[string]func() (*asset, error){
|
||||||
"templates/consul_catalog-v1.tmpl": templatesConsul_catalogV1Tmpl,
|
"templates/consul_catalog.tmpl": templatesConsul_catalogTmpl,
|
||||||
"templates/consul_catalog.tmpl": templatesConsul_catalogTmpl,
|
"templates/docker.tmpl": templatesDockerTmpl,
|
||||||
"templates/docker-v1.tmpl": templatesDockerV1Tmpl,
|
"templates/ecs.tmpl": templatesEcsTmpl,
|
||||||
"templates/docker.tmpl": templatesDockerTmpl,
|
"templates/eureka.tmpl": templatesEurekaTmpl,
|
||||||
"templates/ecs-v1.tmpl": templatesEcsV1Tmpl,
|
"templates/kubernetes.tmpl": templatesKubernetesTmpl,
|
||||||
"templates/ecs.tmpl": templatesEcsTmpl,
|
"templates/kv.tmpl": templatesKvTmpl,
|
||||||
"templates/eureka.tmpl": templatesEurekaTmpl,
|
"templates/marathon.tmpl": templatesMarathonTmpl,
|
||||||
"templates/kubernetes.tmpl": templatesKubernetesTmpl,
|
"templates/mesos.tmpl": templatesMesosTmpl,
|
||||||
"templates/kv.tmpl": templatesKvTmpl,
|
"templates/notFound.tmpl": templatesNotfoundTmpl,
|
||||||
"templates/marathon-v1.tmpl": templatesMarathonV1Tmpl,
|
"templates/rancher.tmpl": templatesRancherTmpl,
|
||||||
"templates/marathon.tmpl": templatesMarathonTmpl,
|
|
||||||
"templates/mesos-v1.tmpl": templatesMesosV1Tmpl,
|
|
||||||
"templates/mesos.tmpl": templatesMesosTmpl,
|
|
||||||
"templates/notFound.tmpl": templatesNotfoundTmpl,
|
|
||||||
"templates/rancher-v1.tmpl": templatesRancherV1Tmpl,
|
|
||||||
"templates/rancher.tmpl": templatesRancherTmpl,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetDir returns the file names below a certain
|
// AssetDir returns the file names below a certain
|
||||||
|
@ -2694,22 +2136,16 @@ type bintree struct {
|
||||||
|
|
||||||
var _bintree = &bintree{nil, map[string]*bintree{
|
var _bintree = &bintree{nil, map[string]*bintree{
|
||||||
"templates": {nil, map[string]*bintree{
|
"templates": {nil, map[string]*bintree{
|
||||||
"consul_catalog-v1.tmpl": {templatesConsul_catalogV1Tmpl, map[string]*bintree{}},
|
"consul_catalog.tmpl": {templatesConsul_catalogTmpl, map[string]*bintree{}},
|
||||||
"consul_catalog.tmpl": {templatesConsul_catalogTmpl, map[string]*bintree{}},
|
"docker.tmpl": {templatesDockerTmpl, map[string]*bintree{}},
|
||||||
"docker-v1.tmpl": {templatesDockerV1Tmpl, map[string]*bintree{}},
|
"ecs.tmpl": {templatesEcsTmpl, map[string]*bintree{}},
|
||||||
"docker.tmpl": {templatesDockerTmpl, map[string]*bintree{}},
|
"eureka.tmpl": {templatesEurekaTmpl, map[string]*bintree{}},
|
||||||
"ecs-v1.tmpl": {templatesEcsV1Tmpl, map[string]*bintree{}},
|
"kubernetes.tmpl": {templatesKubernetesTmpl, map[string]*bintree{}},
|
||||||
"ecs.tmpl": {templatesEcsTmpl, map[string]*bintree{}},
|
"kv.tmpl": {templatesKvTmpl, map[string]*bintree{}},
|
||||||
"eureka.tmpl": {templatesEurekaTmpl, map[string]*bintree{}},
|
"marathon.tmpl": {templatesMarathonTmpl, map[string]*bintree{}},
|
||||||
"kubernetes.tmpl": {templatesKubernetesTmpl, map[string]*bintree{}},
|
"mesos.tmpl": {templatesMesosTmpl, map[string]*bintree{}},
|
||||||
"kv.tmpl": {templatesKvTmpl, map[string]*bintree{}},
|
"notFound.tmpl": {templatesNotfoundTmpl, map[string]*bintree{}},
|
||||||
"marathon-v1.tmpl": {templatesMarathonV1Tmpl, map[string]*bintree{}},
|
"rancher.tmpl": {templatesRancherTmpl, map[string]*bintree{}},
|
||||||
"marathon.tmpl": {templatesMarathonTmpl, map[string]*bintree{}},
|
|
||||||
"mesos-v1.tmpl": {templatesMesosV1Tmpl, 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{}},
|
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *Provider) buildConfigurationV2(catalog []catalogUpdate) *types.Configuration {
|
func (p *Provider) buildConfiguration(catalog []catalogUpdate) *types.Configuration {
|
||||||
var funcMap = template.FuncMap{
|
var funcMap = template.FuncMap{
|
||||||
"getAttribute": p.getAttribute,
|
"getAttribute": p.getAttribute,
|
||||||
"getTag": getTag,
|
"getTag": getTag,
|
||||||
|
@ -194,7 +194,8 @@ func getServerName(node *api.ServiceEntry, index int) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) getWeight(tags []string) int {
|
func (p *Provider) getWeight(tags []string) int {
|
||||||
weight := p.getIntAttribute(label.SuffixWeight, tags, label.DefaultWeight)
|
labels := tagsToNeutralLabels(tags, p.Prefix)
|
||||||
|
weight := label.GetIntValue(labels, p.getPrefixedName(label.SuffixWeight), label.DefaultWeight)
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
deprecatedWeightTag := "backend." + label.SuffixWeight
|
deprecatedWeightTag := "backend." + label.SuffixWeight
|
||||||
|
@ -202,7 +203,7 @@ func (p *Provider) getWeight(tags []string) int {
|
||||||
log.Warnf("Deprecated configuration found: %s. Please use %s.",
|
log.Warnf("Deprecated configuration found: %s. Please use %s.",
|
||||||
p.getPrefixedName(deprecatedWeightTag), p.getPrefixedName(label.SuffixWeight))
|
p.getPrefixedName(deprecatedWeightTag), p.getPrefixedName(label.SuffixWeight))
|
||||||
|
|
||||||
weight = p.getIntAttribute(deprecatedWeightTag, tags, label.DefaultWeight)
|
weight = label.GetIntValue(labels, p.getPrefixedName(deprecatedWeightTag), label.DefaultWeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
return weight
|
return weight
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
package consulcatalog
|
|
||||||
|
|
||||||
import "github.com/containous/traefik/types"
|
|
||||||
|
|
||||||
func (p *Provider) buildConfiguration(catalog []catalogUpdate) *types.Configuration {
|
|
||||||
if p.TemplateVersion == 1 {
|
|
||||||
return p.buildConfigurationV1(catalog)
|
|
||||||
}
|
|
||||||
return p.buildConfigurationV2(catalog)
|
|
||||||
}
|
|
|
@ -708,7 +708,7 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
|
|
||||||
nodes := fakeLoadTraefikLabelsSlice(test.nodes, p.Prefix)
|
nodes := fakeLoadTraefikLabelsSlice(test.nodes, p.Prefix)
|
||||||
|
|
||||||
actualConfig := p.buildConfigurationV2(nodes)
|
actualConfig := p.buildConfiguration(nodes)
|
||||||
assert.NotNil(t, actualConfig)
|
assert.NotNil(t, actualConfig)
|
||||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
||||||
assert.Equal(t, test.expectedFrontends, actualConfig.Frontends)
|
assert.Equal(t, test.expectedFrontends, actualConfig.Frontends)
|
||||||
|
|
|
@ -1,228 +0,0 @@
|
||||||
package consulcatalog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/log"
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
"github.com/hashicorp/consul/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) buildConfigurationV1(catalog []catalogUpdate) *types.Configuration {
|
|
||||||
var FuncMap = template.FuncMap{
|
|
||||||
"getAttribute": p.getAttribute,
|
|
||||||
"getTag": getTag,
|
|
||||||
"hasTag": hasTag,
|
|
||||||
|
|
||||||
// Backend functions
|
|
||||||
"getBackend": getNodeBackendName,
|
|
||||||
"getServiceBackendName": getServiceBackendName,
|
|
||||||
"getBackendAddress": getBackendAddress,
|
|
||||||
"getBackendName": getServerName,
|
|
||||||
"hasMaxconnAttributes": p.hasMaxConnAttributesV1,
|
|
||||||
"getSticky": p.getStickyV1,
|
|
||||||
"hasStickinessLabel": p.hasStickinessLabelV1,
|
|
||||||
"getStickinessCookieName": p.getStickinessCookieNameV1,
|
|
||||||
"getWeight": p.getWeight,
|
|
||||||
"getProtocol": p.getFuncStringAttribute(label.SuffixProtocol, label.DefaultProtocol),
|
|
||||||
|
|
||||||
// Frontend functions
|
|
||||||
"getFrontendRule": p.getFrontendRuleV1,
|
|
||||||
"getBasicAuth": p.getFuncSliceAttribute(label.SuffixFrontendAuthBasic),
|
|
||||||
"getEntryPoints": getEntryPointsV1,
|
|
||||||
"getPriority": p.getFuncIntAttribute(label.SuffixFrontendPriority, label.DefaultFrontendPriority),
|
|
||||||
"getPassHostHeader": p.getFuncBoolAttribute(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeader),
|
|
||||||
"getPassTLSCert": p.getFuncBoolAttribute(label.SuffixFrontendPassTLSCert, label.DefaultPassTLSCert),
|
|
||||||
}
|
|
||||||
|
|
||||||
var allNodes []*api.ServiceEntry
|
|
||||||
var services []*serviceUpdate
|
|
||||||
for _, info := range catalog {
|
|
||||||
if len(info.Nodes) > 0 {
|
|
||||||
services = append(services, info.Service)
|
|
||||||
allNodes = append(allNodes, info.Nodes...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ensure a stable ordering of nodes so that identical configurations may be detected
|
|
||||||
sort.Sort(nodeSorter(allNodes))
|
|
||||||
|
|
||||||
templateObjects := struct {
|
|
||||||
Services []*serviceUpdate
|
|
||||||
Nodes []*api.ServiceEntry
|
|
||||||
}{
|
|
||||||
Services: services,
|
|
||||||
Nodes: allNodes,
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration, err := p.GetConfiguration("templates/consul_catalog-v1.tmpl", FuncMap, templateObjects)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to create config")
|
|
||||||
}
|
|
||||||
|
|
||||||
return configuration
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specific functions
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getFrontendRuleV1(service serviceUpdate) string {
|
|
||||||
customFrontendRule := p.getAttribute(label.SuffixFrontendRule, service.Attributes, "")
|
|
||||||
if customFrontendRule == "" {
|
|
||||||
customFrontendRule = p.FrontEndRule
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpl := p.frontEndRuleTemplate
|
|
||||||
tmpl, err := tmpl.Parse(customFrontendRule)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to parse Consul Catalog custom frontend rule: %v", err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
templateObjects := struct {
|
|
||||||
ServiceName string
|
|
||||||
Domain string
|
|
||||||
Attributes []string
|
|
||||||
}{
|
|
||||||
ServiceName: service.ServiceName,
|
|
||||||
Domain: p.Domain,
|
|
||||||
Attributes: service.Attributes,
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
err = tmpl.Execute(&buffer, templateObjects)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to execute Consul Catalog custom frontend rule template: %v", err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) hasMaxConnAttributesV1(attributes []string) bool {
|
|
||||||
amount := p.getAttribute(label.SuffixBackendMaxConnAmount, attributes, "")
|
|
||||||
extractorFunc := p.getAttribute(label.SuffixBackendMaxConnExtractorFunc, attributes, "")
|
|
||||||
return amount != "" && extractorFunc != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getEntryPointsV1(list string) []string {
|
|
||||||
return strings.Split(list, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Deprecated
|
|
||||||
// replaced by Stickiness
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getStickyV1(tags []string) string {
|
|
||||||
stickyTag := p.getAttribute(label.SuffixBackendLoadBalancerSticky, tags, "")
|
|
||||||
if len(stickyTag) > 0 {
|
|
||||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
|
||||||
} else {
|
|
||||||
stickyTag = "false"
|
|
||||||
}
|
|
||||||
return stickyTag
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) hasStickinessLabelV1(tags []string) bool {
|
|
||||||
stickinessTag := p.getAttribute(label.SuffixBackendLoadBalancerStickiness, tags, "")
|
|
||||||
return len(stickinessTag) > 0 && strings.EqualFold(strings.TrimSpace(stickinessTag), "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getStickinessCookieNameV1(tags []string) string {
|
|
||||||
return p.getAttribute(label.SuffixBackendLoadBalancerStickinessCookieName, tags, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base functions
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getFuncStringAttribute(name string, defaultValue string) func(tags []string) string {
|
|
||||||
return func(tags []string) string {
|
|
||||||
return p.getAttribute(name, tags, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getFuncSliceAttribute(name string) func(tags []string) []string {
|
|
||||||
return func(tags []string) []string {
|
|
||||||
return p.getSliceAttribute(name, tags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getFuncIntAttribute(name string, defaultValue int) func(tags []string) int {
|
|
||||||
return func(tags []string) int {
|
|
||||||
return p.getIntAttribute(name, tags, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Provider) getFuncBoolAttribute(name string, defaultValue bool) func(tags []string) bool {
|
|
||||||
return func(tags []string) bool {
|
|
||||||
return p.getBoolAttribute(name, tags, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getInt64Attribute(name string, tags []string, defaultValue int64) int64 {
|
|
||||||
rawValue := getTag(p.getPrefixedName(name), tags, "")
|
|
||||||
|
|
||||||
if len(rawValue) == 0 {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := strconv.ParseInt(rawValue, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Invalid value for %s: %s", name, rawValue)
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getIntAttribute(name string, tags []string, defaultValue int) int {
|
|
||||||
rawValue := getTag(p.getPrefixedName(name), tags, "")
|
|
||||||
|
|
||||||
if len(rawValue) == 0 {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := strconv.Atoi(rawValue)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Invalid value for %s: %s", name, rawValue)
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getSliceAttribute(name string, tags []string) []string {
|
|
||||||
rawValue := getTag(p.getPrefixedName(name), tags, "")
|
|
||||||
|
|
||||||
if len(rawValue) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return label.SplitAndTrimString(rawValue, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getBoolAttribute(name string, tags []string, defaultValue bool) bool {
|
|
||||||
rawValue := getTag(p.getPrefixedName(name), tags, "")
|
|
||||||
|
|
||||||
if len(rawValue) == 0 {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := strconv.ParseBool(rawValue)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Invalid value for %s: %s", name, rawValue)
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
|
@ -1,444 +0,0 @@
|
||||||
package consulcatalog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
"github.com/hashicorp/consul/api"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProviderBuildConfigurationV1(t *testing.T) {
|
|
||||||
p := &Provider{
|
|
||||||
Domain: "localhost",
|
|
||||||
Prefix: "traefik",
|
|
||||||
ExposedByDefault: false,
|
|
||||||
FrontEndRule: "Host:{{.ServiceName}}.{{.Domain}}",
|
|
||||||
frontEndRuleTemplate: template.New("consul catalog frontend rule"),
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
nodes []catalogUpdate
|
|
||||||
expectedFrontends map[string]*types.Frontend
|
|
||||||
expectedBackends map[string]*types.Backend
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "Should build config of nothing",
|
|
||||||
nodes: []catalogUpdate{},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{},
|
|
||||||
expectedBackends: map[string]*types.Backend{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Should build config with no frontend and backend",
|
|
||||||
nodes: []catalogUpdate{
|
|
||||||
{
|
|
||||||
Service: &serviceUpdate{
|
|
||||||
ServiceName: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{},
|
|
||||||
expectedBackends: map[string]*types.Backend{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Should build config who contains one frontend and one backend",
|
|
||||||
nodes: []catalogUpdate{
|
|
||||||
{
|
|
||||||
Service: &serviceUpdate{
|
|
||||||
ServiceName: "test",
|
|
||||||
Attributes: []string{
|
|
||||||
"random.foo=bar",
|
|
||||||
label.TraefikBackendLoadBalancer + "=drr",
|
|
||||||
label.TraefikBackendCircuitBreaker + "=NetworkErrorRatio() > 0.5",
|
|
||||||
label.TraefikBackendMaxConnAmount + "=1000",
|
|
||||||
label.TraefikBackendMaxConnExtractorFunc + "=client.ip",
|
|
||||||
label.TraefikFrontendAuthBasic + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Nodes: []*api.ServiceEntry{
|
|
||||||
{
|
|
||||||
Service: &api.AgentService{
|
|
||||||
Service: "test",
|
|
||||||
Address: "127.0.0.1",
|
|
||||||
Port: 80,
|
|
||||||
Tags: []string{
|
|
||||||
"random.foo=bar",
|
|
||||||
label.Prefix + "backend.weight=42",
|
|
||||||
label.TraefikFrontendPassHostHeader + "=true",
|
|
||||||
label.TraefikProtocol + "=https",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Node: &api.Node{
|
|
||||||
Node: "localhost",
|
|
||||||
Address: "127.0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-test": {
|
|
||||||
Backend: "backend-test",
|
|
||||||
PassHostHeader: true,
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-host-test": {
|
|
||||||
Rule: "Host:test.localhost",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-test": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"test-0-us4-27hAOu2ARV7nNrmv6GoKlcA": {
|
|
||||||
URL: "https://127.0.0.1:80",
|
|
||||||
Weight: 42,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: &types.CircuitBreaker{
|
|
||||||
Expression: "NetworkErrorRatio() > 0.5",
|
|
||||||
},
|
|
||||||
LoadBalancer: &types.LoadBalancer{
|
|
||||||
Method: "drr",
|
|
||||||
},
|
|
||||||
MaxConn: &types.MaxConn{
|
|
||||||
Amount: 1000,
|
|
||||||
ExtractorFunc: "client.ip",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actualConfig := p.buildConfigurationV1(test.nodes)
|
|
||||||
assert.NotNil(t, actualConfig)
|
|
||||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
|
||||||
assert.Equal(t, test.expectedFrontends, actualConfig.Frontends)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProviderGetIntAttributeV1(t *testing.T) {
|
|
||||||
p := &Provider{
|
|
||||||
Prefix: "traefik",
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
name string
|
|
||||||
tags []string
|
|
||||||
defaultValue int
|
|
||||||
expected int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "should return default value when empty name",
|
|
||||||
name: "",
|
|
||||||
tags: []string{"traefik.foo=10"},
|
|
||||||
defaultValue: 666,
|
|
||||||
expected: 666,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return default value when empty tags",
|
|
||||||
name: "traefik.foo",
|
|
||||||
tags: nil,
|
|
||||||
expected: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return default value when value is not a int",
|
|
||||||
name: "foo",
|
|
||||||
tags: []string{"traefik.foo=bar"},
|
|
||||||
expected: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return a value when tag exist",
|
|
||||||
name: "foo",
|
|
||||||
tags: []string{"traefik.foo=10"},
|
|
||||||
expected: 10,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
result := p.getIntAttribute(test.name, test.tags, test.defaultValue)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProviderGetInt64AttributeV1(t *testing.T) {
|
|
||||||
p := &Provider{
|
|
||||||
Prefix: "traefik",
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
name string
|
|
||||||
tags []string
|
|
||||||
defaultValue int64
|
|
||||||
expected int64
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "should return default value when empty name",
|
|
||||||
name: "",
|
|
||||||
tags: []string{"traefik.foo=10"},
|
|
||||||
defaultValue: 666,
|
|
||||||
expected: 666,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return default value when empty tags",
|
|
||||||
name: "traefik.foo",
|
|
||||||
tags: nil,
|
|
||||||
expected: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return default value when value is not a int",
|
|
||||||
name: "foo",
|
|
||||||
tags: []string{"traefik.foo=bar"},
|
|
||||||
expected: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return a value when tag exist",
|
|
||||||
name: "foo",
|
|
||||||
tags: []string{"traefik.foo=10"},
|
|
||||||
expected: 10,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
result := p.getInt64Attribute(test.name, test.tags, test.defaultValue)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProviderGetBoolAttributeV1(t *testing.T) {
|
|
||||||
p := &Provider{
|
|
||||||
Prefix: "traefik",
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
name string
|
|
||||||
tags []string
|
|
||||||
defaultValue bool
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "should return default value when empty name",
|
|
||||||
name: "",
|
|
||||||
tags: []string{"traefik.foo=true"},
|
|
||||||
defaultValue: true,
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return default value when empty tags",
|
|
||||||
name: "traefik.foo",
|
|
||||||
tags: nil,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return default value when value is not a bool",
|
|
||||||
name: "foo",
|
|
||||||
tags: []string{"traefik.foo=bar"},
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return a value when tag exist",
|
|
||||||
name: "foo",
|
|
||||||
tags: []string{"traefik.foo=true"},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
result := p.getBoolAttribute(test.name, test.tags, test.defaultValue)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProviderGetSliceAttributeV1(t *testing.T) {
|
|
||||||
p := &Provider{
|
|
||||||
Prefix: "traefik",
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
name string
|
|
||||||
tags []string
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "should return nil when empty name",
|
|
||||||
name: "",
|
|
||||||
tags: []string{"traefik.foo=bar,bor,bir"},
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return nil when empty tags",
|
|
||||||
name: "foo",
|
|
||||||
tags: nil,
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return nil when tag doesn't have value",
|
|
||||||
name: "",
|
|
||||||
tags: []string{"traefik.foo="},
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return a slice when tag contains comma separated values",
|
|
||||||
name: "foo",
|
|
||||||
tags: []string{"traefik.foo=bar,bor,bir"},
|
|
||||||
expected: []string{"bar", "bor", "bir"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return a slice when tag contains one value",
|
|
||||||
name: "foo",
|
|
||||||
tags: []string{"traefik.foo=bar"},
|
|
||||||
expected: []string{"bar"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
result := p.getSliceAttribute(test.name, test.tags)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProviderGetFrontendRuleV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
service serviceUpdate
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "Should return default host foo.localhost",
|
|
||||||
service: serviceUpdate{
|
|
||||||
ServiceName: "foo",
|
|
||||||
Attributes: []string{},
|
|
||||||
},
|
|
||||||
expected: "Host:foo.localhost",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Should return host *.example.com",
|
|
||||||
service: serviceUpdate{
|
|
||||||
ServiceName: "foo",
|
|
||||||
Attributes: []string{
|
|
||||||
"traefik.frontend.rule=Host:*.example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: "Host:*.example.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Should return host foo.example.com",
|
|
||||||
service: serviceUpdate{
|
|
||||||
ServiceName: "foo",
|
|
||||||
Attributes: []string{
|
|
||||||
"traefik.frontend.rule=Host:{{.ServiceName}}.example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: "Host:foo.example.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Should return path prefix /bar",
|
|
||||||
service: serviceUpdate{
|
|
||||||
ServiceName: "foo",
|
|
||||||
Attributes: []string{
|
|
||||||
"traefik.frontend.rule=PathPrefix:{{getTag \"contextPath\" .Attributes \"/\"}}",
|
|
||||||
"contextPath=/bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: "PathPrefix:/bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
p := &Provider{
|
|
||||||
Domain: "localhost",
|
|
||||||
Prefix: "traefik",
|
|
||||||
FrontEndRule: "Host:{{.ServiceName}}.{{.Domain}}",
|
|
||||||
frontEndRuleTemplate: template.New("consul catalog frontend rule"),
|
|
||||||
}
|
|
||||||
p.setupFrontEndRuleTemplate()
|
|
||||||
|
|
||||||
actual := p.getFrontendRuleV1(test.service)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHasStickinessLabelV1(t *testing.T) {
|
|
||||||
p := &Provider{
|
|
||||||
Prefix: "traefik",
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
tags []string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "label missing",
|
|
||||||
tags: []string{},
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "stickiness=true",
|
|
||||||
tags: []string{
|
|
||||||
label.TraefikBackendLoadBalancerStickiness + "=true",
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "stickiness=false",
|
|
||||||
tags: []string{
|
|
||||||
label.TraefikBackendLoadBalancerStickiness + "=false",
|
|
||||||
},
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actual := p.hasStickinessLabelV1(test.tags)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,7 +25,7 @@ const (
|
||||||
labelDockerComposeService = "com.docker.compose.service"
|
labelDockerComposeService = "com.docker.compose.service"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *Provider) buildConfigurationV2(containersInspected []dockerData) *types.Configuration {
|
func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.Configuration {
|
||||||
dockerFuncMap := template.FuncMap{
|
dockerFuncMap := template.FuncMap{
|
||||||
"getLabelValue": label.GetStringValue,
|
"getLabelValue": label.GetStringValue,
|
||||||
"getSubDomain": getSubDomain,
|
"getSubDomain": getSubDomain,
|
||||||
|
|
|
@ -713,7 +713,7 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
||||||
ExposedByDefault: true,
|
ExposedByDefault: true,
|
||||||
Network: "webnet",
|
Network: "webnet",
|
||||||
}
|
}
|
||||||
actualConfig := provider.buildConfigurationV2(dockerDataList)
|
actualConfig := provider.buildConfiguration(dockerDataList)
|
||||||
require.NotNil(t, actualConfig, "actualConfig")
|
require.NotNil(t, actualConfig, "actualConfig")
|
||||||
|
|
||||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||||
|
|
|
@ -571,7 +571,7 @@ func TestSwarmBuildConfiguration(t *testing.T) {
|
||||||
SwarmMode: true,
|
SwarmMode: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
actualConfig := provider.buildConfigurationV2(dockerDataList)
|
actualConfig := provider.buildConfiguration(dockerDataList)
|
||||||
require.NotNil(t, actualConfig, "actualConfig")
|
require.NotNil(t, actualConfig, "actualConfig")
|
||||||
|
|
||||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.Configuration {
|
|
||||||
if p.TemplateVersion == 1 {
|
|
||||||
return p.buildConfigurationV1(containersInspected)
|
|
||||||
}
|
|
||||||
return p.buildConfigurationV2(containersInspected)
|
|
||||||
}
|
|
|
@ -721,7 +721,7 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
||||||
dockerDataList = append(dockerDataList, dData)
|
dockerDataList = append(dockerDataList, dData)
|
||||||
}
|
}
|
||||||
|
|
||||||
actualConfig := provider.buildConfigurationV2(dockerDataList)
|
actualConfig := provider.buildConfiguration(dockerDataList)
|
||||||
require.NotNil(t, actualConfig, "actualConfig")
|
require.NotNil(t, actualConfig, "actualConfig")
|
||||||
|
|
||||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||||
|
|
|
@ -1,263 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/BurntSushi/ty/fun"
|
|
||||||
"github.com/containous/traefik/log"
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) buildConfigurationV1(containersInspected []dockerData) *types.Configuration {
|
|
||||||
var DockerFuncMap = template.FuncMap{
|
|
||||||
"getDomain": getFuncStringLabelV1(label.TraefikDomain, p.Domain),
|
|
||||||
"getSubDomain": getSubDomain,
|
|
||||||
"isBackendLBSwarm": isBackendLBSwarm,
|
|
||||||
|
|
||||||
// Backend functions
|
|
||||||
"getIPAddress": p.getIPAddressV1,
|
|
||||||
"getPort": getPortV1,
|
|
||||||
"getWeight": getFuncIntLabelV1(label.TraefikWeight, label.DefaultWeight),
|
|
||||||
"getProtocol": getFuncStringLabelV1(label.TraefikProtocol, label.DefaultProtocol),
|
|
||||||
|
|
||||||
"hasCircuitBreakerLabel": hasFuncV1(label.TraefikBackendCircuitBreakerExpression),
|
|
||||||
"getCircuitBreakerExpression": getFuncStringLabelV1(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
|
||||||
"hasLoadBalancerLabel": hasLoadBalancerLabelV1,
|
|
||||||
"getLoadBalancerMethod": getFuncStringLabelV1(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod),
|
|
||||||
"hasMaxConnLabels": hasMaxConnLabelsV1,
|
|
||||||
"getMaxConnAmount": getFuncInt64LabelV1(label.TraefikBackendMaxConnAmount, math.MaxInt64),
|
|
||||||
"getMaxConnExtractorFunc": getFuncStringLabelV1(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc),
|
|
||||||
"getSticky": getStickyV1,
|
|
||||||
"hasStickinessLabel": hasFuncV1(label.TraefikBackendLoadBalancerStickiness),
|
|
||||||
"getStickinessCookieName": getFuncStringLabelV1(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
|
||||||
|
|
||||||
// Frontend functions
|
|
||||||
"getBackend": getBackendNameV1,
|
|
||||||
"getBackendName": getBackendNameV1,
|
|
||||||
"getPriority": getFuncIntLabelV1(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
|
||||||
"getPassHostHeader": getFuncBoolLabelV1(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
|
||||||
"getPassTLSCert": getFuncBoolLabelV1(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
|
||||||
"getEntryPoints": getFuncSliceStringLabelV1(label.TraefikFrontendEntryPoints),
|
|
||||||
"getBasicAuth": getFuncSliceStringLabelV1(label.TraefikFrontendAuthBasic),
|
|
||||||
"getWhitelistSourceRange": getFuncSliceStringLabelV1(label.TraefikFrontendWhitelistSourceRange),
|
|
||||||
"getFrontendRule": p.getFrontendRuleV1,
|
|
||||||
"hasRedirect": hasRedirectV1,
|
|
||||||
"getRedirectEntryPoint": getFuncStringLabelV1(label.TraefikFrontendRedirectEntryPoint, ""),
|
|
||||||
"getRedirectRegex": getFuncStringLabelV1(label.TraefikFrontendRedirectRegex, ""),
|
|
||||||
"getRedirectReplacement": getFuncStringLabelV1(label.TraefikFrontendRedirectReplacement, ""),
|
|
||||||
|
|
||||||
"hasHeaders": hasHeadersV1,
|
|
||||||
"hasRequestHeaders": hasLabelV1(label.TraefikFrontendRequestHeaders),
|
|
||||||
"getRequestHeaders": getFuncMapLabelV1(label.TraefikFrontendRequestHeaders),
|
|
||||||
"hasResponseHeaders": hasLabelV1(label.TraefikFrontendResponseHeaders),
|
|
||||||
"getResponseHeaders": getFuncMapLabelV1(label.TraefikFrontendResponseHeaders),
|
|
||||||
"hasAllowedHostsHeaders": hasLabelV1(label.TraefikFrontendAllowedHosts),
|
|
||||||
"getAllowedHostsHeaders": getFuncSliceStringLabelV1(label.TraefikFrontendAllowedHosts),
|
|
||||||
"hasHostsProxyHeaders": hasLabelV1(label.TraefikFrontendHostsProxyHeaders),
|
|
||||||
"getHostsProxyHeaders": getFuncSliceStringLabelV1(label.TraefikFrontendHostsProxyHeaders),
|
|
||||||
"hasSSLRedirectHeaders": hasLabelV1(label.TraefikFrontendSSLRedirect),
|
|
||||||
"getSSLRedirectHeaders": getFuncBoolLabelV1(label.TraefikFrontendSSLRedirect, false),
|
|
||||||
"hasSSLTemporaryRedirectHeaders": hasLabelV1(label.TraefikFrontendSSLTemporaryRedirect),
|
|
||||||
"getSSLTemporaryRedirectHeaders": getFuncBoolLabelV1(label.TraefikFrontendSSLTemporaryRedirect, false),
|
|
||||||
"hasSSLHostHeaders": hasLabelV1(label.TraefikFrontendSSLHost),
|
|
||||||
"getSSLHostHeaders": getFuncStringLabelV1(label.TraefikFrontendSSLHost, ""),
|
|
||||||
"hasSSLProxyHeaders": hasLabelV1(label.TraefikFrontendSSLProxyHeaders),
|
|
||||||
"getSSLProxyHeaders": getFuncMapLabelV1(label.TraefikFrontendSSLProxyHeaders),
|
|
||||||
"hasSTSSecondsHeaders": hasLabelV1(label.TraefikFrontendSTSSeconds),
|
|
||||||
"getSTSSecondsHeaders": getFuncInt64LabelV1(label.TraefikFrontendSTSSeconds, 0),
|
|
||||||
"hasSTSIncludeSubdomainsHeaders": hasLabelV1(label.TraefikFrontendSTSIncludeSubdomains),
|
|
||||||
"getSTSIncludeSubdomainsHeaders": getFuncBoolLabelV1(label.TraefikFrontendSTSIncludeSubdomains, false),
|
|
||||||
"hasSTSPreloadHeaders": hasLabelV1(label.TraefikFrontendSTSPreload),
|
|
||||||
"getSTSPreloadHeaders": getFuncBoolLabelV1(label.TraefikFrontendSTSPreload, false),
|
|
||||||
"hasForceSTSHeaderHeaders": hasLabelV1(label.TraefikFrontendForceSTSHeader),
|
|
||||||
"getForceSTSHeaderHeaders": getFuncBoolLabelV1(label.TraefikFrontendForceSTSHeader, false),
|
|
||||||
"hasFrameDenyHeaders": hasLabelV1(label.TraefikFrontendFrameDeny),
|
|
||||||
"getFrameDenyHeaders": getFuncBoolLabelV1(label.TraefikFrontendFrameDeny, false),
|
|
||||||
"hasCustomFrameOptionsValueHeaders": hasLabelV1(label.TraefikFrontendCustomFrameOptionsValue),
|
|
||||||
"getCustomFrameOptionsValueHeaders": getFuncStringLabelV1(label.TraefikFrontendCustomFrameOptionsValue, ""),
|
|
||||||
"hasContentTypeNosniffHeaders": hasLabelV1(label.TraefikFrontendContentTypeNosniff),
|
|
||||||
"getContentTypeNosniffHeaders": getFuncBoolLabelV1(label.TraefikFrontendContentTypeNosniff, false),
|
|
||||||
"hasBrowserXSSFilterHeaders": hasLabelV1(label.TraefikFrontendBrowserXSSFilter),
|
|
||||||
"getBrowserXSSFilterHeaders": getFuncBoolLabelV1(label.TraefikFrontendBrowserXSSFilter, false),
|
|
||||||
"hasContentSecurityPolicyHeaders": hasLabelV1(label.TraefikFrontendContentSecurityPolicy),
|
|
||||||
"getContentSecurityPolicyHeaders": getFuncStringLabelV1(label.TraefikFrontendContentSecurityPolicy, ""),
|
|
||||||
"hasPublicKeyHeaders": hasLabelV1(label.TraefikFrontendPublicKey),
|
|
||||||
"getPublicKeyHeaders": getFuncStringLabelV1(label.TraefikFrontendPublicKey, ""),
|
|
||||||
"hasReferrerPolicyHeaders": hasLabelV1(label.TraefikFrontendReferrerPolicy),
|
|
||||||
"getReferrerPolicyHeaders": getFuncStringLabelV1(label.TraefikFrontendReferrerPolicy, ""),
|
|
||||||
"hasIsDevelopmentHeaders": hasLabelV1(label.TraefikFrontendIsDevelopment),
|
|
||||||
"getIsDevelopmentHeaders": getFuncBoolLabelV1(label.TraefikFrontendIsDevelopment, false),
|
|
||||||
|
|
||||||
// Services
|
|
||||||
"hasServices": hasServicesV1,
|
|
||||||
"getServiceNames": getServiceNamesV1,
|
|
||||||
"getServiceBackend": getServiceBackendNameV1,
|
|
||||||
"getServiceBackendName": getServiceBackendNameV1,
|
|
||||||
// Services - Backend server functions
|
|
||||||
"getServicePort": getServicePortV1,
|
|
||||||
"getServiceProtocol": getFuncServiceStringLabelV1(label.SuffixProtocol, label.DefaultProtocol),
|
|
||||||
"getServiceWeight": getFuncServiceIntLabelV1(label.SuffixWeight, label.DefaultWeight),
|
|
||||||
// Services - Frontend functions
|
|
||||||
"getServiceEntryPoints": getFuncServiceSliceStringLabelV1(label.SuffixFrontendEntryPoints),
|
|
||||||
"getServiceWhitelistSourceRange": getFuncServiceSliceStringLabelV1(label.SuffixFrontendWhiteListSourceRange),
|
|
||||||
"getServiceBasicAuth": getFuncServiceSliceStringLabelV1(label.SuffixFrontendAuthBasic),
|
|
||||||
"getServiceFrontendRule": p.getServiceFrontendRuleV1,
|
|
||||||
"getServicePassHostHeader": getFuncServiceBoolLabelV1(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeader),
|
|
||||||
"getServicePassTLSCert": getFuncServiceBoolLabelV1(label.SuffixFrontendPassTLSCert, label.DefaultPassTLSCert),
|
|
||||||
"getServicePriority": getFuncServiceIntLabelV1(label.SuffixFrontendPriority, label.DefaultFrontendPriority),
|
|
||||||
"hasServiceRedirect": hasServiceRedirectV1,
|
|
||||||
"getServiceRedirectEntryPoint": getFuncServiceStringLabelV1(label.SuffixFrontendRedirectEntryPoint, ""),
|
|
||||||
"getServiceRedirectReplacement": getFuncServiceStringLabelV1(label.SuffixFrontendRedirectReplacement, ""),
|
|
||||||
"getServiceRedirectRegex": getFuncServiceStringLabelV1(label.SuffixFrontendRedirectRegex, ""),
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter containers
|
|
||||||
filteredContainers := fun.Filter(func(container dockerData) bool {
|
|
||||||
return p.containerFilterV1(container)
|
|
||||||
}, containersInspected).([]dockerData)
|
|
||||||
|
|
||||||
frontends := map[string][]dockerData{}
|
|
||||||
backends := map[string]dockerData{}
|
|
||||||
servers := map[string][]dockerData{}
|
|
||||||
serviceNames := make(map[string]struct{})
|
|
||||||
for idx, container := range filteredContainers {
|
|
||||||
|
|
||||||
serviceNamesKey := getServiceNameKey(container, p.SwarmMode, "")
|
|
||||||
|
|
||||||
if _, exists := serviceNames[serviceNamesKey]; !exists {
|
|
||||||
frontendName := p.getFrontendNameV1(container, idx)
|
|
||||||
frontends[frontendName] = append(frontends[frontendName], container)
|
|
||||||
if len(serviceNamesKey) > 0 {
|
|
||||||
serviceNames[serviceNamesKey] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
backendName := getBackendNameV1(container)
|
|
||||||
backends[backendName] = container
|
|
||||||
servers[backendName] = append(servers[backendName], container)
|
|
||||||
}
|
|
||||||
|
|
||||||
templateObjects := struct {
|
|
||||||
Containers []dockerData
|
|
||||||
Frontends map[string][]dockerData
|
|
||||||
Backends map[string]dockerData
|
|
||||||
Servers map[string][]dockerData
|
|
||||||
Domain string
|
|
||||||
}{
|
|
||||||
Containers: filteredContainers,
|
|
||||||
Frontends: frontends,
|
|
||||||
Backends: backends,
|
|
||||||
Servers: servers,
|
|
||||||
Domain: p.Domain,
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration, err := p.GetConfiguration("templates/docker-v1.tmpl", DockerFuncMap, templateObjects)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return configuration
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p Provider) containerFilterV1(container dockerData) bool {
|
|
||||||
if !label.IsEnabled(container.Labels, p.ExposedByDefault) {
|
|
||||||
log.Debugf("Filtering disabled container %s", container.Name)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
portLabel := "traefik.port label"
|
|
||||||
if hasServicesV1(container) {
|
|
||||||
portLabel = "traefik.<serviceName>.port or " + portLabel + "s"
|
|
||||||
err = checkServiceLabelPortV1(container)
|
|
||||||
} else {
|
|
||||||
_, err = strconv.Atoi(container.Labels[label.TraefikPort])
|
|
||||||
}
|
|
||||||
if len(container.NetworkSettings.Ports) == 0 && err != nil {
|
|
||||||
log.Debugf("Filtering container without port and no %s %s : %s", portLabel, container.Name, err.Error())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
constraintTags := label.SplitAndTrimString(container.Labels[label.TraefikTags], ",")
|
|
||||||
if ok, failingConstraint := p.MatchConstraints(constraintTags); !ok {
|
|
||||||
if failingConstraint != nil {
|
|
||||||
log.Debugf("Container %v pruned by '%v' constraint", container.Name, failingConstraint.String())
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if container.Health != "" && container.Health != "healthy" {
|
|
||||||
log.Debugf("Filtering unhealthy or starting container %s", container.Name)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(p.getFrontendRuleV1(container)) == 0 {
|
|
||||||
log.Debugf("Filtering container with empty frontend rule %s", container.Name)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Provider) getIPAddressV1(container dockerData) string {
|
|
||||||
if value := label.GetStringValue(container.Labels, labelDockerNetwork, p.Network); value != "" {
|
|
||||||
networkSettings := container.NetworkSettings
|
|
||||||
if networkSettings.Networks != nil {
|
|
||||||
network := networkSettings.Networks[value]
|
|
||||||
if network != nil {
|
|
||||||
return network.Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Warnf("Could not find network named '%s' for container '%s'! Maybe you're missing the project's prefix in the label? Defaulting to first available network.", value, container.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if container.NetworkSettings.NetworkMode.IsHost() {
|
|
||||||
if container.Node != nil {
|
|
||||||
if container.Node.IPAddress != "" {
|
|
||||||
return container.Node.IPAddress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "127.0.0.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
if container.NetworkSettings.NetworkMode.IsContainer() {
|
|
||||||
dockerClient, err := p.createClient()
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("Unable to get IP address for container %s, error: %s", container.Name, err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedContainer := container.NetworkSettings.NetworkMode.ConnectedContainer()
|
|
||||||
containerInspected, err := dockerClient.ContainerInspect(context.Background(), connectedContainer)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("Unable to get IP address for container %s : Failed to inspect container ID %s, error: %s", container.Name, connectedContainer, err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return p.getIPAddress(parseContainer(containerInspected))
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.UseBindPortIP {
|
|
||||||
port := getPortV1(container)
|
|
||||||
for netPort, portBindings := range container.NetworkSettings.Ports {
|
|
||||||
if strings.EqualFold(string(netPort), port+"/TCP") || strings.EqualFold(string(netPort), port+"/UDP") {
|
|
||||||
for _, p := range portBindings {
|
|
||||||
return p.HostIP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, network := range container.NetworkSettings.Networks {
|
|
||||||
return network.Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Warnf("Unable to find the IP address for the container %q.", container.Name)
|
|
||||||
return ""
|
|
||||||
}
|
|
|
@ -1,216 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/log"
|
|
||||||
"github.com/containous/traefik/provider"
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Specific functions
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p Provider) getFrontendNameV1(container dockerData, idx int) string {
|
|
||||||
return provider.Normalize(p.getFrontendRuleV1(container) + "-" + strconv.Itoa(idx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFrontendRule returns the frontend rule for the specified container, using
|
|
||||||
// it's label. It returns a default one (Host) if the label is not present.
|
|
||||||
// Deprecated
|
|
||||||
func (p Provider) getFrontendRuleV1(container dockerData) string {
|
|
||||||
if value := label.GetStringValue(container.Labels, label.TraefikFrontendRule, ""); len(value) != 0 {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
domain := label.GetStringValue(container.Labels, label.TraefikDomain, p.Domain)
|
|
||||||
|
|
||||||
if values, err := label.GetStringMultipleStrict(container.Labels, labelDockerComposeProject, labelDockerComposeService); err == nil {
|
|
||||||
return "Host:" + getSubDomain(values[labelDockerComposeService]+"."+values[labelDockerComposeProject]) + "." + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(domain) > 0 {
|
|
||||||
return "Host:" + getSubDomain(container.ServiceName) + "." + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getBackendNameV1(container dockerData) string {
|
|
||||||
if value := label.GetStringValue(container.Labels, label.TraefikBackend, ""); len(value) != 0 {
|
|
||||||
return provider.Normalize(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if values, err := label.GetStringMultipleStrict(container.Labels, labelDockerComposeProject, labelDockerComposeService); err == nil {
|
|
||||||
return provider.Normalize(values[labelDockerComposeService] + "_" + values[labelDockerComposeProject])
|
|
||||||
}
|
|
||||||
|
|
||||||
return provider.Normalize(container.ServiceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getPortV1(container dockerData) string {
|
|
||||||
if value := label.GetStringValue(container.Labels, label.TraefikPort, ""); len(value) != 0 {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// See iteration order in https://blog.golang.org/go-maps-in-action
|
|
||||||
var ports []nat.Port
|
|
||||||
for port := range container.NetworkSettings.Ports {
|
|
||||||
ports = append(ports, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
less := func(i, j nat.Port) bool {
|
|
||||||
return i.Int() < j.Int()
|
|
||||||
}
|
|
||||||
nat.Sort(ports, less)
|
|
||||||
|
|
||||||
if len(ports) > 0 {
|
|
||||||
min := ports[0]
|
|
||||||
return min.Port()
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// replaced by Stickiness
|
|
||||||
// Deprecated
|
|
||||||
func getStickyV1(container dockerData) bool {
|
|
||||||
if label.Has(container.Labels, label.TraefikBackendLoadBalancerSticky) {
|
|
||||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
|
||||||
}
|
|
||||||
|
|
||||||
return label.GetBoolValue(container.Labels, label.TraefikBackendLoadBalancerSticky, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasLoadBalancerLabelV1(container dockerData) bool {
|
|
||||||
method := label.Has(container.Labels, label.TraefikBackendLoadBalancerMethod)
|
|
||||||
sticky := label.Has(container.Labels, label.TraefikBackendLoadBalancerSticky)
|
|
||||||
stickiness := label.Has(container.Labels, label.TraefikBackendLoadBalancerStickiness)
|
|
||||||
cookieName := label.Has(container.Labels, label.TraefikBackendLoadBalancerStickinessCookieName)
|
|
||||||
return method || sticky || stickiness || cookieName
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasMaxConnLabelsV1(container dockerData) bool {
|
|
||||||
mca := label.Has(container.Labels, label.TraefikBackendMaxConnAmount)
|
|
||||||
mcef := label.Has(container.Labels, label.TraefikBackendMaxConnExtractorFunc)
|
|
||||||
return mca && mcef
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasRedirectV1(container dockerData) bool {
|
|
||||||
return hasLabelV1(label.TraefikFrontendRedirectEntryPoint)(container) ||
|
|
||||||
hasLabelV1(label.TraefikFrontendRedirectReplacement)(container) && hasLabelV1(label.TraefikFrontendRedirectRegex)(container)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasHeadersV1(container dockerData) bool {
|
|
||||||
for key := range container.Labels {
|
|
||||||
if strings.HasPrefix(key, label.Prefix+"frontend.headers.") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Label functions
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncStringLabelV1(labelName string, defaultValue string) func(container dockerData) string {
|
|
||||||
return func(container dockerData) string {
|
|
||||||
return label.GetStringValue(container.Labels, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncBoolLabelV1(labelName string, defaultValue bool) func(container dockerData) bool {
|
|
||||||
return func(container dockerData) bool {
|
|
||||||
return label.GetBoolValue(container.Labels, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncSliceStringLabelV1(labelName string) func(container dockerData) []string {
|
|
||||||
return func(container dockerData) []string {
|
|
||||||
return label.GetSliceStringValue(container.Labels, labelName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncIntLabelV1(labelName string, defaultValue int) func(container dockerData) int {
|
|
||||||
return func(container dockerData) int {
|
|
||||||
return label.GetIntValue(container.Labels, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncInt64LabelV1(labelName string, defaultValue int64) func(container dockerData) int64 {
|
|
||||||
return func(container dockerData) int64 {
|
|
||||||
return label.GetInt64Value(container.Labels, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasFuncV1(labelName string) func(container dockerData) bool {
|
|
||||||
return func(container dockerData) bool {
|
|
||||||
return label.Has(container.Labels, labelName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasLabelV1(label string) func(container dockerData) bool {
|
|
||||||
return func(container dockerData) bool {
|
|
||||||
lbl, err := getLabelV1(container, label)
|
|
||||||
return err == nil && len(lbl) > 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getLabelV1(container dockerData, label string) (string, error) {
|
|
||||||
if value, ok := container.Labels[label]; ok {
|
|
||||||
return value, nil
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("label not found: %s", label)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncMapLabelV1(labelName string) func(container dockerData) map[string]string {
|
|
||||||
return func(container dockerData) map[string]string {
|
|
||||||
return parseMapLabelV1(container, labelName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func parseMapLabelV1(container dockerData, labelName string) map[string]string {
|
|
||||||
if parts, err := getLabelV1(container, labelName); err == nil {
|
|
||||||
if len(parts) == 0 {
|
|
||||||
log.Errorf("Could not load %q", labelName)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
values := make(map[string]string)
|
|
||||||
for _, headers := range strings.Split(parts, "||") {
|
|
||||||
pair := strings.SplitN(headers, ":", 2)
|
|
||||||
if len(pair) != 2 {
|
|
||||||
log.Warnf("Could not load %q: %v, skipping...", labelName, pair)
|
|
||||||
} else {
|
|
||||||
values[http.CanonicalHeaderKey(strings.TrimSpace(pair[0]))] = strings.TrimSpace(pair[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(values) == 0 {
|
|
||||||
log.Errorf("Could not load %q", labelName)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,969 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
docker "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDockerBuildConfigurationV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
containers []docker.ContainerJSON
|
|
||||||
expectedFrontends map[string]*types.Frontend
|
|
||||||
expectedBackends map[string]*types.Backend
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "when no container",
|
|
||||||
containers: []docker.ContainerJSON{},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{},
|
|
||||||
expectedBackends: map[string]*types.Backend{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "when basic container configuration",
|
|
||||||
containers: []docker.ContainerJSON{
|
|
||||||
containerJSON(
|
|
||||||
name("test"),
|
|
||||||
ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
}),
|
|
||||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-Host-test-docker-localhost-0": {
|
|
||||||
Backend: "backend-test",
|
|
||||||
PassHostHeader: true,
|
|
||||||
EntryPoints: []string{},
|
|
||||||
BasicAuth: []string{},
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-frontend-Host-test-docker-localhost-0": {
|
|
||||||
Rule: "Host:test.docker.localhost",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-test": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-test": {
|
|
||||||
URL: "http://127.0.0.1:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "when container has label 'enable' to false",
|
|
||||||
containers: []docker.ContainerJSON{
|
|
||||||
containerJSON(
|
|
||||||
name("test"),
|
|
||||||
labels(map[string]string{
|
|
||||||
label.TraefikEnable: "false",
|
|
||||||
label.TraefikPort: "666",
|
|
||||||
label.TraefikProtocol: "https",
|
|
||||||
label.TraefikWeight: "12",
|
|
||||||
label.TraefikBackend: "foobar",
|
|
||||||
}),
|
|
||||||
ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
}),
|
|
||||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{},
|
|
||||||
expectedBackends: map[string]*types.Backend{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "when all labels are set",
|
|
||||||
containers: []docker.ContainerJSON{
|
|
||||||
containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
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.TraefikFrontendPassTLSCert: "true",
|
|
||||||
label.TraefikFrontendPriority: "666",
|
|
||||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
|
||||||
label.TraefikFrontendRedirectRegex: "nope",
|
|
||||||
label.TraefikFrontendRedirectReplacement: "nope",
|
|
||||||
label.TraefikFrontendRule: "Host:traefik.io",
|
|
||||||
label.TraefikFrontendWhitelistSourceRange: "10.10.10.10",
|
|
||||||
|
|
||||||
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.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",
|
|
||||||
}),
|
|
||||||
ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
}),
|
|
||||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-Host-traefik-io-0": {
|
|
||||||
EntryPoints: []string{
|
|
||||||
"http",
|
|
||||||
"https",
|
|
||||||
},
|
|
||||||
Backend: "backend-foobar",
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-frontend-Host-traefik-io-0": {
|
|
||||||
Rule: "Host:traefik.io",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PassHostHeader: true,
|
|
||||||
PassTLSCert: true,
|
|
||||||
Priority: 666,
|
|
||||||
BasicAuth: []string{
|
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
|
||||||
},
|
|
||||||
WhitelistSourceRange: []string{
|
|
||||||
"10.10.10.10",
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
ContentSecurityPolicy: "foo",
|
|
||||||
PublicKey: "foo",
|
|
||||||
ReferrerPolicy: "foo",
|
|
||||||
IsDevelopment: true,
|
|
||||||
},
|
|
||||||
Redirect: &types.Redirect{
|
|
||||||
EntryPoint: "https",
|
|
||||||
Regex: "nope",
|
|
||||||
Replacement: "nope",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-foobar": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-test1": {
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var dockerDataList []dockerData
|
|
||||||
for _, cont := range test.containers {
|
|
||||||
dData := parseContainer(cont)
|
|
||||||
dockerDataList = append(dockerDataList, dData)
|
|
||||||
}
|
|
||||||
|
|
||||||
provider := &Provider{
|
|
||||||
Domain: "docker.localhost",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
}
|
|
||||||
actualConfig := provider.buildConfigurationV1(dockerDataList)
|
|
||||||
require.NotNil(t, actualConfig, "actualConfig")
|
|
||||||
|
|
||||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
|
||||||
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerTraefikFilterV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expected bool
|
|
||||||
provider *Provider
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{},
|
|
||||||
},
|
|
||||||
expected: false,
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{
|
|
||||||
Labels: map[string]string{
|
|
||||||
label.TraefikEnable: "false",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{
|
|
||||||
Labels: map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Host:foo.bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container-multi-ports",
|
|
||||||
},
|
|
||||||
Config: &container.Config{},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
"443/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{
|
|
||||||
Labels: map[string]string{
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
"443/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{
|
|
||||||
Labels: map[string]string{
|
|
||||||
label.TraefikEnable: "true",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{
|
|
||||||
Labels: map[string]string{
|
|
||||||
label.TraefikEnable: "anything",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{
|
|
||||||
Labels: map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Host:foo.bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: false,
|
|
||||||
},
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{
|
|
||||||
Labels: map[string]string{
|
|
||||||
label.TraefikEnable: "true",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: false,
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{
|
|
||||||
Labels: map[string]string{
|
|
||||||
label.TraefikEnable: "true",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
ExposedByDefault: false,
|
|
||||||
},
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: docker.ContainerJSON{
|
|
||||||
ContainerJSONBase: &docker.ContainerJSONBase{
|
|
||||||
Name: "container",
|
|
||||||
},
|
|
||||||
Config: &container.Config{
|
|
||||||
Labels: map[string]string{
|
|
||||||
label.TraefikEnable: "true",
|
|
||||||
label.TraefikFrontendRule: "Host:i.love.this.host",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NetworkSettings: &docker.NetworkSettings{
|
|
||||||
NetworkSettingsBase: docker.NetworkSettingsBase{
|
|
||||||
Ports: nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
provider: &Provider{
|
|
||||||
ExposedByDefault: false,
|
|
||||||
},
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
actual := test.provider.containerFilterV1(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %v for %+v, got %+v", test.expected, test, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetFuncStringLabelV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
labelName string
|
|
||||||
defaultValue string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(),
|
|
||||||
labelName: label.TraefikProtocol,
|
|
||||||
defaultValue: label.DefaultProtocol,
|
|
||||||
expected: "http",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikProtocol: "https",
|
|
||||||
})),
|
|
||||||
labelName: label.TraefikProtocol,
|
|
||||||
defaultValue: label.DefaultProtocol,
|
|
||||||
expected: "https",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.labelName+strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
actual := getFuncStringLabelV1(test.labelName, test.defaultValue)(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("got %q, expected %q", actual, test.expected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetSliceStringLabelV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
container docker.ContainerJSON
|
|
||||||
labelName string
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "no whitelist-label",
|
|
||||||
container: containerJSON(),
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "whitelist-label with empty string",
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendWhitelistSourceRange: "",
|
|
||||||
})),
|
|
||||||
labelName: label.TraefikFrontendWhitelistSourceRange,
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "whitelist-label with IPv4 mask",
|
|
||||||
container: containerJSON(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",
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendWhitelistSourceRange: "fe80::/16",
|
|
||||||
})),
|
|
||||||
labelName: label.TraefikFrontendWhitelistSourceRange,
|
|
||||||
expected: []string{
|
|
||||||
"fe80::/16",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "whitelist-label with multiple masks",
|
|
||||||
container: containerJSON(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()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
actual := getFuncSliceStringLabelV1(test.labelName)(dData)
|
|
||||||
if !reflect.DeepEqual(actual, test.expected) {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetFrontendNameV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(name("foo")),
|
|
||||||
expected: "Host-foo-docker-localhost-0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Headers:User-Agent,bat/0.1.0",
|
|
||||||
})),
|
|
||||||
expected: "Headers-User-Agent-bat-0-1-0-0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
"com.docker.compose.project": "foo",
|
|
||||||
"com.docker.compose.service": "bar",
|
|
||||||
})),
|
|
||||||
expected: "Host-bar-foo-docker-localhost-0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Host:foo.bar",
|
|
||||||
})),
|
|
||||||
expected: "Host-foo-bar-0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Path:/test",
|
|
||||||
})),
|
|
||||||
expected: "Path-test-0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "PathPrefix:/test2",
|
|
||||||
})),
|
|
||||||
expected: "PathPrefix-test2-0",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
provider := &Provider{
|
|
||||||
Domain: "docker.localhost",
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := provider.getFrontendNameV1(dData, 0)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetFrontendRuleV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(name("foo")),
|
|
||||||
expected: "Host:foo.docker.localhost",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(name("foo"),
|
|
||||||
labels(map[string]string{
|
|
||||||
label.TraefikDomain: "traefik.localhost",
|
|
||||||
})),
|
|
||||||
expected: "Host:foo.traefik.localhost",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Host:foo.bar",
|
|
||||||
})),
|
|
||||||
expected: "Host:foo.bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
"com.docker.compose.project": "foo",
|
|
||||||
"com.docker.compose.service": "bar",
|
|
||||||
})),
|
|
||||||
expected: "Host:bar.foo.docker.localhost",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Path:/test",
|
|
||||||
})),
|
|
||||||
expected: "Path:/test",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
provider := &Provider{
|
|
||||||
Domain: "docker.localhost",
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := provider.getFrontendRuleV1(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetBackendNameV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(name("foo")),
|
|
||||||
expected: "foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(name("bar")),
|
|
||||||
expected: "bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikBackend: "foobar",
|
|
||||||
})),
|
|
||||||
expected: "foobar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
"com.docker.compose.project": "foo",
|
|
||||||
"com.docker.compose.service": "bar",
|
|
||||||
})),
|
|
||||||
expected: "bar-foo",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
actual := getBackendNameV1(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetIPAddressV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(withNetwork("testnet", ipv4("10.11.12.13"))),
|
|
||||||
expected: "10.11.12.13",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(
|
|
||||||
labels(map[string]string{
|
|
||||||
labelDockerNetwork: "testnet",
|
|
||||||
}),
|
|
||||||
withNetwork("testnet", ipv4("10.11.12.13")),
|
|
||||||
),
|
|
||||||
expected: "10.11.12.13",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(
|
|
||||||
labels(map[string]string{
|
|
||||||
labelDockerNetwork: "testnet2",
|
|
||||||
}),
|
|
||||||
withNetwork("testnet", ipv4("10.11.12.13")),
|
|
||||||
withNetwork("testnet2", ipv4("10.11.12.14")),
|
|
||||||
),
|
|
||||||
expected: "10.11.12.14",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(
|
|
||||||
networkMode("host"),
|
|
||||||
withNetwork("testnet", ipv4("10.11.12.13")),
|
|
||||||
withNetwork("testnet2", ipv4("10.11.12.14")),
|
|
||||||
),
|
|
||||||
expected: "127.0.0.1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(
|
|
||||||
networkMode("host"),
|
|
||||||
),
|
|
||||||
expected: "127.0.0.1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(
|
|
||||||
networkMode("host"),
|
|
||||||
nodeIP("10.0.0.5"),
|
|
||||||
),
|
|
||||||
expected: "10.0.0.5",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
provider := &Provider{}
|
|
||||||
actual := provider.getDeprecatedIPAddress(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetPortV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(name("foo")),
|
|
||||||
expected: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
})),
|
|
||||||
expected: "80",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
"443/tcp": {},
|
|
||||||
})),
|
|
||||||
expected: "80",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikPort: "8080",
|
|
||||||
})),
|
|
||||||
expected: "8080",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikPort: "8080",
|
|
||||||
}), ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
})),
|
|
||||||
expected: "8080",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikPort: "8080",
|
|
||||||
}), ports(nat.PortMap{
|
|
||||||
"8080/tcp": {},
|
|
||||||
"80/tcp": {},
|
|
||||||
})),
|
|
||||||
expected: "8080",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, e := range testCases {
|
|
||||||
e := e
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(e.container)
|
|
||||||
|
|
||||||
actual := getPortV1(dData)
|
|
||||||
if actual != e.expected {
|
|
||||||
t.Errorf("expected %q, got %q", e.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,709 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
docker "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/swarm"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSwarmBuildConfigurationV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
services []swarm.Service
|
|
||||||
expectedFrontends map[string]*types.Frontend
|
|
||||||
expectedBackends map[string]*types.Backend
|
|
||||||
networks map[string]*docker.NetworkResource
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "when no container",
|
|
||||||
services: []swarm.Service{},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{},
|
|
||||||
expectedBackends: map[string]*types.Backend{},
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "when basic container configuration",
|
|
||||||
services: []swarm.Service{
|
|
||||||
swarmService(
|
|
||||||
serviceName("test"),
|
|
||||||
serviceLabels(map[string]string{
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
}),
|
|
||||||
withEndpointSpec(modeVIP),
|
|
||||||
withEndpoint(virtualIP("1", "127.0.0.1/24")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-Host-test-docker-localhost-0": {
|
|
||||||
Backend: "backend-test",
|
|
||||||
PassHostHeader: true,
|
|
||||||
EntryPoints: []string{},
|
|
||||||
BasicAuth: []string{},
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-frontend-Host-test-docker-localhost-0": {
|
|
||||||
Rule: "Host:test.docker.localhost",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-test": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-test": {
|
|
||||||
URL: "http://127.0.0.1:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
networks: map[string]*docker.NetworkResource{
|
|
||||||
"1": {
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "when container has label 'enable' to false",
|
|
||||||
services: []swarm.Service{
|
|
||||||
swarmService(
|
|
||||||
serviceName("test1"),
|
|
||||||
serviceLabels(map[string]string{
|
|
||||||
label.TraefikEnable: "false",
|
|
||||||
label.TraefikPort: "666",
|
|
||||||
label.TraefikProtocol: "https",
|
|
||||||
label.TraefikWeight: "12",
|
|
||||||
label.TraefikBackend: "foobar",
|
|
||||||
}),
|
|
||||||
withEndpointSpec(modeVIP),
|
|
||||||
withEndpoint(virtualIP("1", "127.0.0.1/24")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{},
|
|
||||||
expectedBackends: map[string]*types.Backend{},
|
|
||||||
networks: map[string]*docker.NetworkResource{
|
|
||||||
"1": {
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "when all labels are set",
|
|
||||||
services: []swarm.Service{
|
|
||||||
swarmService(
|
|
||||||
serviceName("test1"),
|
|
||||||
serviceLabels(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.TraefikFrontendPassTLSCert: "true",
|
|
||||||
label.TraefikFrontendPriority: "666",
|
|
||||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
|
||||||
label.TraefikFrontendRedirectRegex: "nope",
|
|
||||||
label.TraefikFrontendRedirectReplacement: "nope",
|
|
||||||
label.TraefikFrontendRule: "Host:traefik.io",
|
|
||||||
label.TraefikFrontendWhitelistSourceRange: "10.10.10.10",
|
|
||||||
|
|
||||||
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.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",
|
|
||||||
}),
|
|
||||||
withEndpointSpec(modeVIP),
|
|
||||||
withEndpoint(virtualIP("1", "127.0.0.1/24")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-Host-traefik-io-0": {
|
|
||||||
EntryPoints: []string{
|
|
||||||
"http",
|
|
||||||
"https",
|
|
||||||
},
|
|
||||||
Backend: "backend-foobar",
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-frontend-Host-traefik-io-0": {
|
|
||||||
Rule: "Host:traefik.io",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PassHostHeader: true,
|
|
||||||
PassTLSCert: true,
|
|
||||||
Priority: 666,
|
|
||||||
BasicAuth: []string{
|
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
|
||||||
},
|
|
||||||
WhitelistSourceRange: []string{
|
|
||||||
"10.10.10.10",
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
ContentSecurityPolicy: "foo",
|
|
||||||
PublicKey: "foo",
|
|
||||||
ReferrerPolicy: "foo",
|
|
||||||
IsDevelopment: true,
|
|
||||||
},
|
|
||||||
Redirect: &types.Redirect{
|
|
||||||
EntryPoint: "https",
|
|
||||||
Regex: "nope",
|
|
||||||
Replacement: "nope",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-foobar": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-test1": {
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
networks: map[string]*docker.NetworkResource{
|
|
||||||
"1": {
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var dockerDataList []dockerData
|
|
||||||
for _, service := range test.services {
|
|
||||||
dData := parseService(service, test.networks)
|
|
||||||
dockerDataList = append(dockerDataList, dData)
|
|
||||||
}
|
|
||||||
|
|
||||||
provider := &Provider{
|
|
||||||
Domain: "docker.localhost",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
SwarmMode: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
actualConfig := provider.buildConfigurationV1(dockerDataList)
|
|
||||||
require.NotNil(t, actualConfig, "actualConfig")
|
|
||||||
|
|
||||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
|
||||||
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSwarmTraefikFilterV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
service swarm.Service
|
|
||||||
expected bool
|
|
||||||
networks map[string]*docker.NetworkResource
|
|
||||||
provider *Provider
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
service: swarmService(),
|
|
||||||
expected: false,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
provider: &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikEnable: "false",
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expected: false,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
provider: &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Host:foo.bar",
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expected: true,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
provider: &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expected: true,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
provider: &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikEnable: "true",
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expected: true,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
provider: &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikEnable: "anything",
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expected: true,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
provider: &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Host:foo.bar",
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expected: true,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
provider: &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expected: false,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
provider: &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikEnable: "true",
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expected: true,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
provider: &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
Domain: "test",
|
|
||||||
ExposedByDefault: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for serviceID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseService(test.service, test.networks)
|
|
||||||
|
|
||||||
actual := test.provider.containerFilterV1(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %v for %+v, got %+v", test.expected, test, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSwarmGetFuncStringLabelV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
service swarm.Service
|
|
||||||
labelName string
|
|
||||||
defaultValue string
|
|
||||||
networks map[string]*docker.NetworkResource
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
service: swarmService(),
|
|
||||||
labelName: label.TraefikProtocol,
|
|
||||||
defaultValue: label.DefaultProtocol,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
expected: "http",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikProtocol: "https",
|
|
||||||
})),
|
|
||||||
labelName: label.TraefikProtocol,
|
|
||||||
defaultValue: label.DefaultProtocol,
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
expected: "https",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for serviceID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.labelName+strconv.Itoa(serviceID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseService(test.service, test.networks)
|
|
||||||
|
|
||||||
actual := getFuncStringLabelV1(test.labelName, test.defaultValue)(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("got %q, expected %q", actual, test.expected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSwarmGetFrontendNameV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
service swarm.Service
|
|
||||||
expected string
|
|
||||||
networks map[string]*docker.NetworkResource
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
service: swarmService(serviceName("foo")),
|
|
||||||
expected: "Host-foo-docker-localhost-0",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Headers:User-Agent,bat/0.1.0",
|
|
||||||
})),
|
|
||||||
expected: "Headers-User-Agent-bat-0-1-0-0",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Host:foo.bar",
|
|
||||||
})),
|
|
||||||
expected: "Host-foo-bar-0",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Path:/test",
|
|
||||||
})),
|
|
||||||
expected: "Path-test-0",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(
|
|
||||||
serviceName("test"),
|
|
||||||
serviceLabels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "PathPrefix:/test2",
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
expected: "PathPrefix-test2-0",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for serviceID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseService(test.service, test.networks)
|
|
||||||
|
|
||||||
provider := &Provider{
|
|
||||||
Domain: "docker.localhost",
|
|
||||||
SwarmMode: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := provider.getFrontendNameV1(dData, 0)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSwarmGetFrontendRuleV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
service swarm.Service
|
|
||||||
expected string
|
|
||||||
networks map[string]*docker.NetworkResource
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
service: swarmService(serviceName("foo")),
|
|
||||||
expected: "Host:foo.docker.localhost",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceName("foo"),
|
|
||||||
serviceLabels(map[string]string{
|
|
||||||
label.TraefikDomain: "traefik.localhost",
|
|
||||||
})),
|
|
||||||
expected: "Host:foo.traefik.localhost",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Host:foo.bar",
|
|
||||||
})),
|
|
||||||
expected: "Host:foo.bar",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Path:/test",
|
|
||||||
})),
|
|
||||||
expected: "Path:/test",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for serviceID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseService(test.service, test.networks)
|
|
||||||
|
|
||||||
provider := &Provider{
|
|
||||||
Domain: "docker.localhost",
|
|
||||||
SwarmMode: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := provider.getFrontendRuleV1(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSwarmGetBackendNameV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
service swarm.Service
|
|
||||||
expected string
|
|
||||||
networks map[string]*docker.NetworkResource
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
service: swarmService(serviceName("foo")),
|
|
||||||
expected: "foo",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceName("bar")),
|
|
||||||
expected: "bar",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
|
||||||
label.TraefikBackend: "foobar",
|
|
||||||
})),
|
|
||||||
expected: "foobar",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for serviceID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseService(test.service, test.networks)
|
|
||||||
|
|
||||||
actual := getBackendNameV1(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSwarmGetIPAddressV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
service swarm.Service
|
|
||||||
expected string
|
|
||||||
networks map[string]*docker.NetworkResource
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
service: swarmService(withEndpointSpec(modeDNSSR)),
|
|
||||||
expected: "",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(
|
|
||||||
withEndpointSpec(modeVIP),
|
|
||||||
withEndpoint(virtualIP("1", "10.11.12.13/24")),
|
|
||||||
),
|
|
||||||
expected: "10.11.12.13",
|
|
||||||
networks: map[string]*docker.NetworkResource{
|
|
||||||
"1": {
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: swarmService(
|
|
||||||
serviceLabels(map[string]string{
|
|
||||||
labelDockerNetwork: "barnet",
|
|
||||||
}),
|
|
||||||
withEndpointSpec(modeVIP),
|
|
||||||
withEndpoint(
|
|
||||||
virtualIP("1", "10.11.12.13/24"),
|
|
||||||
virtualIP("2", "10.11.12.99/24"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
expected: "10.11.12.99",
|
|
||||||
networks: map[string]*docker.NetworkResource{
|
|
||||||
"1": {
|
|
||||||
Name: "foonet",
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
Name: "barnet",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for serviceID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseService(test.service, test.networks)
|
|
||||||
|
|
||||||
provider := &Provider{
|
|
||||||
SwarmMode: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := provider.getDeprecatedIPAddress(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSwarmGetPortV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
service swarm.Service
|
|
||||||
expected string
|
|
||||||
networks map[string]*docker.NetworkResource
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
service: swarmService(
|
|
||||||
serviceLabels(map[string]string{
|
|
||||||
label.TraefikPort: "8080",
|
|
||||||
}),
|
|
||||||
withEndpointSpec(modeDNSSR),
|
|
||||||
),
|
|
||||||
expected: "8080",
|
|
||||||
networks: map[string]*docker.NetworkResource{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for serviceID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseService(test.service, test.networks)
|
|
||||||
|
|
||||||
actual := getPortV1(dData)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/provider"
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Specific functions
|
|
||||||
|
|
||||||
// Extract rule from labels for a given service and a given docker container
|
|
||||||
// Deprecated
|
|
||||||
func (p Provider) getServiceFrontendRuleV1(container dockerData, serviceName string) string {
|
|
||||||
if value, ok := getServiceLabelsV1(container, serviceName)[label.SuffixFrontendRule]; ok {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return p.getFrontendRuleV1(container)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if for the given container, we find labels that are defining services
|
|
||||||
// Deprecated
|
|
||||||
func hasServicesV1(container dockerData) bool {
|
|
||||||
return len(label.ExtractServiceProperties(container.Labels)) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets array of service names for a given container
|
|
||||||
// Deprecated
|
|
||||||
func getServiceNamesV1(container dockerData) []string {
|
|
||||||
labelServiceProperties := label.ExtractServiceProperties(container.Labels)
|
|
||||||
keys := make([]string, 0, len(labelServiceProperties))
|
|
||||||
for k := range labelServiceProperties {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkServiceLabelPort checks if all service names have a port service label
|
|
||||||
// or if port container label exists for default value
|
|
||||||
// Deprecated
|
|
||||||
func checkServiceLabelPortV1(container dockerData) error {
|
|
||||||
// If port container label is present, there is a default values for all ports, use it for the check
|
|
||||||
_, err := strconv.Atoi(container.Labels[label.TraefikPort])
|
|
||||||
if err != nil {
|
|
||||||
serviceLabelPorts := make(map[string]struct{})
|
|
||||||
serviceLabels := make(map[string]struct{})
|
|
||||||
for lbl := range container.Labels {
|
|
||||||
// Get all port service labels
|
|
||||||
portLabel := extractServicePortV1(lbl)
|
|
||||||
if len(portLabel) > 0 {
|
|
||||||
serviceLabelPorts[portLabel[0]] = struct{}{}
|
|
||||||
}
|
|
||||||
// Get only one instance of all service names from service labels
|
|
||||||
servicesLabelNames := label.FindSegmentSubmatch(lbl)
|
|
||||||
|
|
||||||
if len(servicesLabelNames) > 0 {
|
|
||||||
serviceLabels[strings.Split(servicesLabelNames[0], ".")[1]] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the number of service labels is different than the number of port services label
|
|
||||||
// there is an error
|
|
||||||
if len(serviceLabels) == len(serviceLabelPorts) {
|
|
||||||
for labelPort := range serviceLabelPorts {
|
|
||||||
_, err = strconv.Atoi(container.Labels[labelPort])
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = errors.New("port service labels missing, please use traefik.port as default value or define all port service labels")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func extractServicePortV1(labelName string) []string {
|
|
||||||
if strings.HasPrefix(labelName, label.TraefikFrontend+".") ||
|
|
||||||
strings.HasPrefix(labelName, label.TraefikBackend+".") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return label.PortRegexp.FindStringSubmatch(labelName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract backend from labels for a given service and a given docker container
|
|
||||||
// Deprecated
|
|
||||||
func getServiceBackendNameV1(container dockerData, serviceName string) string {
|
|
||||||
if value, ok := getServiceLabelsV1(container, serviceName)[label.SuffixBackend]; ok {
|
|
||||||
return provider.Normalize(container.ServiceName + "-" + value)
|
|
||||||
}
|
|
||||||
return provider.Normalize(container.ServiceName + "-" + getBackendNameV1(container) + "-" + serviceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract port from labels for a given service and a given docker container
|
|
||||||
// Deprecated
|
|
||||||
func getServicePortV1(container dockerData, serviceName string) string {
|
|
||||||
if value, ok := getServiceLabelsV1(container, serviceName)[label.SuffixPort]; ok {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return getPortV1(container)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service label functions
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncServiceSliceStringLabelV1(labelSuffix string) func(container dockerData, serviceName string) []string {
|
|
||||||
return func(container dockerData, serviceName string) []string {
|
|
||||||
serviceLabels := getServiceLabelsV1(container, serviceName)
|
|
||||||
return getServiceSliceValueV1(container, serviceLabels, labelSuffix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncServiceStringLabelV1(labelSuffix string, defaultValue string) func(container dockerData, serviceName string) string {
|
|
||||||
return func(container dockerData, serviceName string) string {
|
|
||||||
serviceLabels := getServiceLabelsV1(container, serviceName)
|
|
||||||
return getServiceStringValueV1(container, serviceLabels, labelSuffix, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncServiceBoolLabelV1(labelSuffix string, defaultValue bool) func(container dockerData, serviceName string) bool {
|
|
||||||
return func(container dockerData, serviceName string) bool {
|
|
||||||
serviceLabels := getServiceLabelsV1(container, serviceName)
|
|
||||||
return getServiceBoolValueV1(container, serviceLabels, labelSuffix, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncServiceIntLabelV1(labelSuffix string, defaultValue int) func(container dockerData, serviceName string) int {
|
|
||||||
return func(container dockerData, serviceName string) int {
|
|
||||||
return getServiceIntLabelV1(container, serviceName, labelSuffix, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getServiceStringValueV1(container dockerData, serviceLabels map[string]string, labelSuffix string, defaultValue string) string {
|
|
||||||
if value, ok := serviceLabels[labelSuffix]; ok {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return label.GetStringValue(container.Labels, label.Prefix+labelSuffix, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getServiceSliceValueV1(container dockerData, serviceLabels map[string]string, labelSuffix string) []string {
|
|
||||||
if value, ok := serviceLabels[labelSuffix]; ok {
|
|
||||||
return label.SplitAndTrimString(value, ",")
|
|
||||||
}
|
|
||||||
return label.GetSliceStringValue(container.Labels, label.Prefix+labelSuffix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getServiceBoolValueV1(container dockerData, serviceLabels map[string]string, labelSuffix string, defaultValue bool) bool {
|
|
||||||
if rawValue, ok := serviceLabels[labelSuffix]; ok {
|
|
||||||
value, err := strconv.ParseBool(rawValue)
|
|
||||||
if err == nil {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return label.GetBoolValue(container.Labels, label.Prefix+labelSuffix, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getServiceIntLabelV1(container dockerData, serviceName string, labelSuffix string, defaultValue int) int {
|
|
||||||
if rawValue, ok := getServiceLabelsV1(container, serviceName)[labelSuffix]; ok {
|
|
||||||
value, err := strconv.Atoi(rawValue)
|
|
||||||
if err == nil {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return label.GetIntValue(container.Labels, label.Prefix+labelSuffix, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getServiceLabelsV1(container dockerData, serviceName string) label.SegmentPropertyValues {
|
|
||||||
return label.ExtractServiceProperties(container.Labels)[serviceName]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasServiceRedirectV1(container dockerData, serviceName string) bool {
|
|
||||||
serviceLabels, ok := label.ExtractServiceProperties(container.Labels)[serviceName]
|
|
||||||
if !ok || len(serviceLabels) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
value, ok := serviceLabels[label.SuffixFrontendRedirectEntryPoint]
|
|
||||||
frep := ok && len(value) > 0
|
|
||||||
value, ok = serviceLabels[label.SuffixFrontendRedirectRegex]
|
|
||||||
frrg := ok && len(value) > 0
|
|
||||||
value, ok = serviceLabels[label.SuffixFrontendRedirectReplacement]
|
|
||||||
frrp := ok && len(value) > 0
|
|
||||||
|
|
||||||
return frep || frrg && frrp
|
|
||||||
}
|
|
|
@ -1,695 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
docker "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDockerServiceBuildConfigurationV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
containers []docker.ContainerJSON
|
|
||||||
expectedFrontends map[string]*types.Frontend
|
|
||||||
expectedBackends map[string]*types.Backend
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "when no container",
|
|
||||||
containers: []docker.ContainerJSON{},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{},
|
|
||||||
expectedBackends: map[string]*types.Backend{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "simple configuration",
|
|
||||||
containers: []docker.ContainerJSON{
|
|
||||||
containerJSON(
|
|
||||||
name("foo"),
|
|
||||||
labels(map[string]string{
|
|
||||||
"traefik.service.port": "2503",
|
|
||||||
"traefik.service.frontend.entryPoints": "http,https",
|
|
||||||
}),
|
|
||||||
ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
}),
|
|
||||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-foo-foo-service": {
|
|
||||||
Backend: "backend-foo-foo-service",
|
|
||||||
PassHostHeader: true,
|
|
||||||
EntryPoints: []string{"http", "https"},
|
|
||||||
BasicAuth: []string{},
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"service-service": {
|
|
||||||
Rule: "Host:foo.docker.localhost",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-foo-foo-service": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"service-0": {
|
|
||||||
URL: "http://127.0.0.1:2503",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "when all labels are set",
|
|
||||||
containers: []docker.ContainerJSON{
|
|
||||||
containerJSON(
|
|
||||||
name("foo"),
|
|
||||||
labels(map[string]string{
|
|
||||||
label.Prefix + "service." + label.SuffixPort: "666",
|
|
||||||
label.Prefix + "service." + label.SuffixProtocol: "https",
|
|
||||||
label.Prefix + "service." + label.SuffixWeight: "12",
|
|
||||||
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendEntryPoints: "http,https",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendPassHostHeader: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendPassTLSCert: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendPriority: "666",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendRedirectEntryPoint: "https",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendRedirectRegex: "nope",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendRedirectReplacement: "nope",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendRedirectPermanent: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendWhitelistSourceRange: "10.10.10.10",
|
|
||||||
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersSSLProxyHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersAllowedHosts: "foo,bar,bor",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersHostsProxyHeaders: "foo,bar,bor",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersSSLHost: "foo",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersCustomFrameOptionsValue: "foo",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersContentSecurityPolicy: "foo",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersPublicKey: "foo",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersReferrerPolicy: "foo",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersCustomBrowserXSSValue: "foo",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersSTSSeconds: "666",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersSSLRedirect: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersSSLTemporaryRedirect: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersSTSIncludeSubdomains: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersSTSPreload: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersForceSTSHeader: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersFrameDeny: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersContentTypeNosniff: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersBrowserXSSFilter: "true",
|
|
||||||
label.Prefix + "service." + label.SuffixFrontendHeadersIsDevelopment: "true",
|
|
||||||
}),
|
|
||||||
ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
}),
|
|
||||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-foo-foo-service": {
|
|
||||||
Backend: "backend-foo-foo-service",
|
|
||||||
EntryPoints: []string{
|
|
||||||
"http",
|
|
||||||
"https",
|
|
||||||
},
|
|
||||||
PassHostHeader: true,
|
|
||||||
PassTLSCert: true,
|
|
||||||
Priority: 666,
|
|
||||||
BasicAuth: []string{
|
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
|
||||||
},
|
|
||||||
Redirect: &types.Redirect{
|
|
||||||
EntryPoint: "https",
|
|
||||||
Regex: "nope",
|
|
||||||
Replacement: "nope",
|
|
||||||
},
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"service-service": {
|
|
||||||
Rule: "Host:foo.docker.localhost",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-foo-foo-service": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"service-0": {
|
|
||||||
URL: "https://127.0.0.1:666",
|
|
||||||
Weight: 12,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "several containers",
|
|
||||||
containers: []docker.ContainerJSON{
|
|
||||||
containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
labels(map[string]string{
|
|
||||||
"traefik.service.port": "2503",
|
|
||||||
"traefik.service.protocol": "https",
|
|
||||||
"traefik.service.weight": "80",
|
|
||||||
"traefik.service.backend": "foobar",
|
|
||||||
"traefik.service.frontend.passHostHeader": "false",
|
|
||||||
"traefik.service.frontend.rule": "Path:/mypath",
|
|
||||||
"traefik.service.frontend.priority": "5000",
|
|
||||||
"traefik.service.frontend.entryPoints": "http,https,ws",
|
|
||||||
"traefik.service.frontend.auth.basic": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
|
||||||
"traefik.service.frontend.redirect.entryPoint": "https",
|
|
||||||
}),
|
|
||||||
ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
}),
|
|
||||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
|
||||||
),
|
|
||||||
containerJSON(
|
|
||||||
name("test2"),
|
|
||||||
labels(map[string]string{
|
|
||||||
"traefik.anotherservice.port": "8079",
|
|
||||||
"traefik.anotherservice.weight": "33",
|
|
||||||
"traefik.anotherservice.frontend.rule": "Path:/anotherpath",
|
|
||||||
}),
|
|
||||||
ports(nat.PortMap{
|
|
||||||
"80/tcp": {},
|
|
||||||
}),
|
|
||||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-test1-foobar": {
|
|
||||||
Backend: "backend-test1-foobar",
|
|
||||||
PassHostHeader: false,
|
|
||||||
Priority: 5000,
|
|
||||||
EntryPoints: []string{"http", "https", "ws"},
|
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
|
||||||
Redirect: &types.Redirect{
|
|
||||||
EntryPoint: "https",
|
|
||||||
},
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"service-service": {
|
|
||||||
Rule: "Path:/mypath",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"frontend-test2-test2-anotherservice": {
|
|
||||||
Backend: "backend-test2-test2-anotherservice",
|
|
||||||
PassHostHeader: true,
|
|
||||||
EntryPoints: []string{},
|
|
||||||
BasicAuth: []string{},
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"service-anotherservice": {
|
|
||||||
Rule: "Path:/anotherpath",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-test1-foobar": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"service-0": {
|
|
||||||
URL: "https://127.0.0.1:2503",
|
|
||||||
Weight: 80,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: nil,
|
|
||||||
},
|
|
||||||
"backend-test2-test2-anotherservice": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"service-0": {
|
|
||||||
URL: "http://127.0.0.1:8079",
|
|
||||||
Weight: 33,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
provider := &Provider{
|
|
||||||
Domain: "docker.localhost",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
var dockerDataList []dockerData
|
|
||||||
for _, container := range test.containers {
|
|
||||||
dData := parseContainer(container)
|
|
||||||
dockerDataList = append(dockerDataList, dData)
|
|
||||||
}
|
|
||||||
|
|
||||||
actualConfig := provider.buildConfigurationV1(dockerDataList)
|
|
||||||
require.NotNil(t, actualConfig, "actualConfig")
|
|
||||||
|
|
||||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
|
||||||
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetFuncServiceStringLabelV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
suffixLabel string
|
|
||||||
defaultValue string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(),
|
|
||||||
suffixLabel: label.SuffixProtocol,
|
|
||||||
defaultValue: label.DefaultProtocol,
|
|
||||||
expected: "http",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikProtocol: "https",
|
|
||||||
})),
|
|
||||||
suffixLabel: label.SuffixProtocol,
|
|
||||||
defaultValue: label.DefaultProtocol,
|
|
||||||
expected: "https",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.Prefix + "myservice." + label.SuffixProtocol: "https",
|
|
||||||
})),
|
|
||||||
suffixLabel: label.SuffixProtocol,
|
|
||||||
defaultValue: label.DefaultProtocol,
|
|
||||||
expected: "https",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.suffixLabel+strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
actual := getFuncServiceStringLabelV1(test.suffixLabel, test.defaultValue)(dData, "myservice")
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("got %q, expected %q", actual, test.expected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetFuncServiceSliceStringLabelV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
suffixLabel string
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(),
|
|
||||||
suffixLabel: label.SuffixFrontendEntryPoints,
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendEntryPoints: "http,https",
|
|
||||||
})),
|
|
||||||
suffixLabel: label.SuffixFrontendEntryPoints,
|
|
||||||
expected: []string{"http", "https"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
"traefik.myservice.frontend.entryPoints": "http,https",
|
|
||||||
})),
|
|
||||||
suffixLabel: label.SuffixFrontendEntryPoints,
|
|
||||||
expected: []string{"http", "https"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.suffixLabel+strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
actual := getFuncServiceSliceStringLabelV1(test.suffixLabel)(dData, "myservice")
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual, test.expected) {
|
|
||||||
t.Errorf("for container %q: got %q, expected %q", dData.Name, actual, test.expected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetServiceStringValueV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
container docker.ContainerJSON
|
|
||||||
serviceLabels map[string]string
|
|
||||||
labelSuffix string
|
|
||||||
defaultValue string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "should use service label when label exists in service labels",
|
|
||||||
container: containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
labels(map[string]string{
|
|
||||||
"traefik.foo": "bir",
|
|
||||||
})),
|
|
||||||
serviceLabels: map[string]string{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
labelSuffix: "foo",
|
|
||||||
defaultValue: "fail",
|
|
||||||
expected: "bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should use container label when label doesn't exist in service labels",
|
|
||||||
container: containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
labels(map[string]string{
|
|
||||||
"traefik.foo": "bir",
|
|
||||||
})),
|
|
||||||
serviceLabels: map[string]string{
|
|
||||||
"fo": "bar",
|
|
||||||
},
|
|
||||||
labelSuffix: "foo",
|
|
||||||
defaultValue: "fail",
|
|
||||||
expected: "bir",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
actual := getServiceStringValueV1(dData, test.serviceLabels, test.labelSuffix, test.defaultValue)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetServiceSliceValueV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
container docker.ContainerJSON
|
|
||||||
serviceLabels map[string]string
|
|
||||||
labelSuffix string
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "should return nil when no label",
|
|
||||||
container: containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
labels(map[string]string{})),
|
|
||||||
serviceLabels: map[string]string{},
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return a slice when label",
|
|
||||||
container: containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
labels(map[string]string{
|
|
||||||
"traefik.foo": "bor, byr, ber",
|
|
||||||
})),
|
|
||||||
serviceLabels: map[string]string{
|
|
||||||
"foo": "bar, bir, bur",
|
|
||||||
},
|
|
||||||
labelSuffix: "foo",
|
|
||||||
expected: []string{"bar", "bir", "bur"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return a slice when label (fallback to container labels)",
|
|
||||||
container: containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
labels(map[string]string{
|
|
||||||
"traefik.foo": "bor, byr, ber",
|
|
||||||
})),
|
|
||||||
serviceLabels: map[string]string{
|
|
||||||
"fo": "bar, bir, bur",
|
|
||||||
},
|
|
||||||
labelSuffix: "foo",
|
|
||||||
expected: []string{"bor", "byr", "ber"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
actual := getServiceSliceValueV1(dData, test.serviceLabels, test.labelSuffix)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetServiceBoolValueV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
container docker.ContainerJSON
|
|
||||||
serviceLabels map[string]string
|
|
||||||
labelSuffix string
|
|
||||||
defaultValue bool
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "should return default value when no label",
|
|
||||||
container: containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
labels(map[string]string{})),
|
|
||||||
serviceLabels: map[string]string{},
|
|
||||||
labelSuffix: "foo",
|
|
||||||
defaultValue: true,
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return a bool when label",
|
|
||||||
container: containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
labels(map[string]string{
|
|
||||||
"traefik.foo": "false",
|
|
||||||
})),
|
|
||||||
serviceLabels: map[string]string{
|
|
||||||
"foo": "true",
|
|
||||||
},
|
|
||||||
labelSuffix: "foo",
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return a bool when label (fallback to container labels)",
|
|
||||||
container: containerJSON(
|
|
||||||
name("test1"),
|
|
||||||
labels(map[string]string{
|
|
||||||
"traefik.foo": "true",
|
|
||||||
})),
|
|
||||||
serviceLabels: map[string]string{
|
|
||||||
"fo": "false",
|
|
||||||
},
|
|
||||||
labelSuffix: "foo",
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
|
|
||||||
actual := getServiceBoolValueV1(dData, test.serviceLabels, test.labelSuffix, test.defaultValue)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerCheckPortLabelsV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expectedError bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expectedError: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.Prefix + "servicename.protocol": "http",
|
|
||||||
label.Prefix + "servicename.port": "80",
|
|
||||||
})),
|
|
||||||
expectedError: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.Prefix + "servicename.protocol": "http",
|
|
||||||
label.TraefikPort: "80",
|
|
||||||
})),
|
|
||||||
expectedError: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.Prefix + "servicename.protocol": "http",
|
|
||||||
})),
|
|
||||||
expectedError: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
err := checkServiceLabelPortV1(dData)
|
|
||||||
|
|
||||||
if test.expectedError && err == nil {
|
|
||||||
t.Error("expected an error but got nil")
|
|
||||||
} else if !test.expectedError && err != nil {
|
|
||||||
t.Errorf("expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetServiceBackendNameV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(name("foo")),
|
|
||||||
expected: "foo-foo-myservice",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikBackend: "another-backend",
|
|
||||||
})),
|
|
||||||
expected: "fake-another-backend-myservice",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(name("foo.bar")),
|
|
||||||
expected: "foo-bar-foo-bar-myservice",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
"traefik.myservice.backend": "custom-backend",
|
|
||||||
})),
|
|
||||||
expected: "fake-custom-backend",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikBackend: "another.backend",
|
|
||||||
})),
|
|
||||||
expected: "fake-another-backend-myservice",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
actual := getServiceBackendNameV1(dData, "myservice")
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetServiceFrontendRuleV1(t *testing.T) {
|
|
||||||
provider := &Provider{}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(name("foo")),
|
|
||||||
expected: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikFrontendRule: "Path:/helloworld",
|
|
||||||
})),
|
|
||||||
expected: "Path:/helloworld",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
"traefik.myservice.frontend.rule": "Path:/mycustomservicepath",
|
|
||||||
})),
|
|
||||||
expected: "Path:/mycustomservicepath",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
actual := provider.getServiceFrontendRuleV1(dData, "myservice")
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGetServicePortV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
container docker.ContainerJSON
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
container: containerJSON(),
|
|
||||||
expected: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
label.TraefikPort: "2500",
|
|
||||||
})),
|
|
||||||
expected: "2500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
container: containerJSON(labels(map[string]string{
|
|
||||||
"traefik.myservice.port": "1234",
|
|
||||||
})),
|
|
||||||
expected: "1234",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for containerID, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
dData := parseContainer(test.container)
|
|
||||||
actual := getServicePortV1(dData, "myservice")
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// buildConfiguration fills the config template with the given instances
|
// buildConfiguration fills the config template with the given instances
|
||||||
func (p *Provider) buildConfigurationV2(instances []ecsInstance) (*types.Configuration, error) {
|
func (p *Provider) buildConfiguration(instances []ecsInstance) (*types.Configuration, error) {
|
||||||
services := make(map[string][]ecsInstance)
|
services := make(map[string][]ecsInstance)
|
||||||
for _, instance := range instances {
|
for _, instance := range instances {
|
||||||
backendName := getBackendName(instance)
|
backendName := getBackendName(instance)
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package ecs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Provider) buildConfiguration(instances []ecsInstance) (*types.Configuration, error) {
|
|
||||||
if p.TemplateVersion == 1 {
|
|
||||||
return p.buildConfigurationV1(instances)
|
|
||||||
}
|
|
||||||
return p.buildConfigurationV2(instances)
|
|
||||||
}
|
|
|
@ -1144,82 +1144,6 @@ func TestGetPort(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetFuncStringValue(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
expected string
|
|
||||||
instanceInfo ecsInstance
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "Protocol label is not set should return a string equals to http",
|
|
||||||
expected: "http",
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Protocol label is set to http should return a string equals to http",
|
|
||||||
expected: "http",
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{
|
|
||||||
label.TraefikProtocol: aws.String("http"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Protocol label is set to https should return a string equals to https",
|
|
||||||
expected: "https",
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{
|
|
||||||
label.TraefikProtocol: aws.String("https"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actual := getFuncStringValueV1(label.TraefikProtocol, label.DefaultProtocol)(test.instanceInfo)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetFuncSliceString(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
expected []string
|
|
||||||
instanceInfo ecsInstance
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "Frontend entrypoints label not set should return empty array",
|
|
||||||
expected: nil,
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Frontend entrypoints label set to http should return a string array of 1 element",
|
|
||||||
expected: []string{"http"},
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{
|
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Frontend entrypoints label set to http,https should return a string array of 2 elements",
|
|
||||||
expected: []string{"http", "https"},
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{
|
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actual := getFuncSliceStringV1(label.TraefikFrontendEntryPoints)(test.instanceInfo)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeEcsInstance(containerDef *ecs.ContainerDefinition) ecsInstance {
|
func makeEcsInstance(containerDef *ecs.ContainerDefinition) ecsInstance {
|
||||||
container := &ecs.Container{
|
container := &ecs.Container{
|
||||||
Name: containerDef.Name,
|
Name: containerDef.Name,
|
||||||
|
|
|
@ -1,260 +0,0 @@
|
||||||
package ecs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/BurntSushi/ty/fun"
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/containous/traefik/log"
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// buildConfiguration fills the config template with the given instances
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) buildConfigurationV1(instances []ecsInstance) (*types.Configuration, error) {
|
|
||||||
services := make(map[string][]ecsInstance)
|
|
||||||
for _, instance := range instances {
|
|
||||||
backendName := getBackendNameV1(instance)
|
|
||||||
if p.filterInstanceV1(instance) {
|
|
||||||
if serviceInstances, ok := services[backendName]; ok {
|
|
||||||
services[backendName] = append(serviceInstances, instance)
|
|
||||||
} else {
|
|
||||||
services[backendName] = []ecsInstance{instance}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ecsFuncMap = template.FuncMap{
|
|
||||||
// Backend functions
|
|
||||||
"getHost": getHost,
|
|
||||||
"getPort": getPort,
|
|
||||||
|
|
||||||
"getProtocol": getFuncStringValueV1(label.TraefikProtocol, label.DefaultProtocol),
|
|
||||||
"getWeight": getFuncIntValueV1(label.TraefikWeight, label.DefaultWeight),
|
|
||||||
"getLoadBalancerMethod": getFuncFirstStringValueV1(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod),
|
|
||||||
"getLoadBalancerSticky": getStickyV1,
|
|
||||||
"hasStickinessLabel": getFuncFirstBoolValueV1(label.TraefikBackendLoadBalancerStickiness, false),
|
|
||||||
"getStickinessCookieName": getFuncFirstStringValueV1(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
|
||||||
"hasHealthCheckLabels": hasFuncFirstV1(label.TraefikBackendHealthCheckPath),
|
|
||||||
"getHealthCheckPath": getFuncFirstStringValueV1(label.TraefikBackendHealthCheckPath, ""),
|
|
||||||
"getHealthCheckInterval": getFuncFirstStringValueV1(label.TraefikBackendHealthCheckInterval, ""),
|
|
||||||
|
|
||||||
// Frontend functions
|
|
||||||
"filterFrontends": filterFrontendsV1,
|
|
||||||
"getFrontendRule": p.getFrontendRule,
|
|
||||||
"getPassHostHeader": getFuncBoolValueV1(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
|
||||||
"getPassTLSCert": getFuncBoolValueV1(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
|
||||||
"getPriority": getFuncIntValueV1(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
|
||||||
"getBasicAuth": getFuncSliceStringV1(label.TraefikFrontendAuthBasic),
|
|
||||||
"getEntryPoints": getFuncSliceStringV1(label.TraefikFrontendEntryPoints),
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.GetConfiguration("templates/ecs-v1.tmpl", ecsFuncMap, struct {
|
|
||||||
Services map[string][]ecsInstance
|
|
||||||
}{
|
|
||||||
Services: services,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getBackendNameV1(i ecsInstance) string {
|
|
||||||
return getStringValueV1(i, label.TraefikBackend, i.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func filterFrontendsV1(instances []ecsInstance) []ecsInstance {
|
|
||||||
byName := make(map[string]struct{})
|
|
||||||
|
|
||||||
return fun.Filter(func(i ecsInstance) bool {
|
|
||||||
backendName := getBackendName(i)
|
|
||||||
_, found := byName[backendName]
|
|
||||||
if !found {
|
|
||||||
byName[backendName] = struct{}{}
|
|
||||||
}
|
|
||||||
return !found
|
|
||||||
}, instances).([]ecsInstance)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) filterInstanceV1(i ecsInstance) bool {
|
|
||||||
if i.machine == nil {
|
|
||||||
log.Debug("Filtering ecs instance with nil machine")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if labelPort := getStringValueV1(i, label.TraefikPort, ""); len(i.machine.ports) == 0 && labelPort == "" {
|
|
||||||
log.Debugf("Filtering ecs instance without port %s (%s)", i.Name, i.ID)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.ToLower(i.machine.state) != ec2.InstanceStateNameRunning {
|
|
||||||
log.Debugf("Filtering ecs instance in an incorrect state %s (%s) (state = %s)", i.Name, i.ID, i.machine.state)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(i.machine.privateIP) == 0 {
|
|
||||||
log.Debugf("Filtering ecs instance without an ip address %s (%s)", i.Name, i.ID)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isEnabled(i, p.ExposedByDefault) {
|
|
||||||
log.Debugf("Filtering disabled ecs instance %s (%s)", i.Name, i.ID)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Deprecated
|
|
||||||
// replaced by Stickiness
|
|
||||||
// Deprecated
|
|
||||||
func getStickyV1(instances []ecsInstance) bool {
|
|
||||||
if hasFirstV1(instances, label.TraefikBackendLoadBalancerSticky) {
|
|
||||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
|
||||||
}
|
|
||||||
return getFirstBoolValueV1(instances, label.TraefikBackendLoadBalancerSticky, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Label functions
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncStringValueV1(labelName string, defaultValue string) func(i ecsInstance) string {
|
|
||||||
return func(i ecsInstance) string {
|
|
||||||
return getStringValueV1(i, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncBoolValueV1(labelName string, defaultValue bool) func(i ecsInstance) bool {
|
|
||||||
return func(i ecsInstance) bool {
|
|
||||||
return getBoolValueV1(i, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncIntValueV1(labelName string, defaultValue int) func(i ecsInstance) int {
|
|
||||||
return func(i ecsInstance) int {
|
|
||||||
return getIntValueV1(i, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncSliceStringV1(labelName string) func(i ecsInstance) []string {
|
|
||||||
return func(i ecsInstance) []string {
|
|
||||||
return getSliceStringV1(i, labelName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasLabelV1(i ecsInstance, labelName string) bool {
|
|
||||||
value, ok := i.containerDefinition.DockerLabels[labelName]
|
|
||||||
return ok && value != nil && len(aws.StringValue(value)) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getStringValueV1(i ecsInstance, labelName string, defaultValue string) string {
|
|
||||||
if v, ok := i.containerDefinition.DockerLabels[labelName]; ok {
|
|
||||||
if v == nil {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
if len(aws.StringValue(v)) == 0 {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return aws.StringValue(v)
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getBoolValueV1(i ecsInstance, labelName string, defaultValue bool) bool {
|
|
||||||
rawValue, ok := i.containerDefinition.DockerLabels[labelName]
|
|
||||||
if ok {
|
|
||||||
if rawValue != nil {
|
|
||||||
v, err := strconv.ParseBool(aws.StringValue(rawValue))
|
|
||||||
if err == nil {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getIntValueV1(i ecsInstance, labelName string, defaultValue int) int {
|
|
||||||
rawValue, ok := i.containerDefinition.DockerLabels[labelName]
|
|
||||||
if ok {
|
|
||||||
if rawValue != nil {
|
|
||||||
v, err := strconv.Atoi(aws.StringValue(rawValue))
|
|
||||||
if err == nil {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getSliceStringV1(i ecsInstance, labelName string) []string {
|
|
||||||
if value, ok := i.containerDefinition.DockerLabels[labelName]; ok {
|
|
||||||
if value == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(aws.StringValue(value)) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return label.SplitAndTrimString(aws.StringValue(value), ",")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasFuncFirstV1(labelName string) func(instances []ecsInstance) bool {
|
|
||||||
return func(instances []ecsInstance) bool {
|
|
||||||
return hasFirstV1(instances, labelName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncFirstStringValueV1(labelName string, defaultValue string) func(instances []ecsInstance) string {
|
|
||||||
return func(instances []ecsInstance) string {
|
|
||||||
return getFirstStringValueV1(instances, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncFirstBoolValueV1(labelName string, defaultValue bool) func(instances []ecsInstance) bool {
|
|
||||||
return func(instances []ecsInstance) bool {
|
|
||||||
if len(instances) == 0 {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return getBoolValueV1(instances[0], labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func hasFirstV1(instances []ecsInstance, labelName string) bool {
|
|
||||||
if len(instances) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return hasLabelV1(instances[0], labelName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFirstStringValueV1(instances []ecsInstance, labelName string, defaultValue string) string {
|
|
||||||
if len(instances) == 0 {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return getStringValueV1(instances[0], labelName, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFirstBoolValueV1(instances []ecsInstance, labelName string, defaultValue bool) bool {
|
|
||||||
if len(instances) == 0 {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return getBoolValueV1(instances[0], labelName, defaultValue)
|
|
||||||
}
|
|
|
@ -1,395 +0,0 @@
|
||||||
package ecs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ecs"
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBuildConfigurationV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
instances []ecsInstance
|
|
||||||
expected *types.Configuration
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "config parsed successfully",
|
|
||||||
instances: []ecsInstance{
|
|
||||||
{
|
|
||||||
Name: "testing",
|
|
||||||
ID: "1",
|
|
||||||
containerDefinition: &ecs.ContainerDefinition{
|
|
||||||
DockerLabels: map[string]*string{},
|
|
||||||
},
|
|
||||||
machine: &machine{
|
|
||||||
state: ec2.InstanceStateNameRunning,
|
|
||||||
privateIP: "10.0.0.1",
|
|
||||||
ports: []portMapping{{hostPort: 1337}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &types.Configuration{
|
|
||||||
Backends: map[string]*types.Backend{
|
|
||||||
"backend-testing": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-testing1": {
|
|
||||||
URL: "http://10.0.0.1:1337",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
}},
|
|
||||||
LoadBalancer: &types.LoadBalancer{
|
|
||||||
Method: "wrr",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Frontends: map[string]*types.Frontend{
|
|
||||||
"frontend-testing": {
|
|
||||||
EntryPoints: []string{},
|
|
||||||
Backend: "backend-testing",
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-frontend-testing": {
|
|
||||||
Rule: "Host:testing.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PassHostHeader: true,
|
|
||||||
BasicAuth: []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "config parsed successfully with health check labels",
|
|
||||||
instances: []ecsInstance{
|
|
||||||
{
|
|
||||||
Name: "testing",
|
|
||||||
ID: "1",
|
|
||||||
containerDefinition: &ecs.ContainerDefinition{
|
|
||||||
DockerLabels: map[string]*string{
|
|
||||||
label.TraefikBackendHealthCheckPath: aws.String("/health"),
|
|
||||||
label.TraefikBackendHealthCheckInterval: aws.String("1s"),
|
|
||||||
}},
|
|
||||||
machine: &machine{
|
|
||||||
state: ec2.InstanceStateNameRunning,
|
|
||||||
privateIP: "10.0.0.1",
|
|
||||||
ports: []portMapping{{hostPort: 1337}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &types.Configuration{
|
|
||||||
Backends: map[string]*types.Backend{
|
|
||||||
"backend-testing": {
|
|
||||||
HealthCheck: &types.HealthCheck{
|
|
||||||
Path: "/health",
|
|
||||||
Interval: "1s",
|
|
||||||
},
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-testing1": {
|
|
||||||
URL: "http://10.0.0.1:1337",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
}},
|
|
||||||
LoadBalancer: &types.LoadBalancer{
|
|
||||||
Method: "wrr",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Frontends: map[string]*types.Frontend{
|
|
||||||
"frontend-testing": {
|
|
||||||
EntryPoints: []string{},
|
|
||||||
Backend: "backend-testing",
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-frontend-testing": {
|
|
||||||
Rule: "Host:testing.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PassHostHeader: true,
|
|
||||||
BasicAuth: []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "when all labels are set",
|
|
||||||
instances: []ecsInstance{
|
|
||||||
{
|
|
||||||
Name: "testing-instance",
|
|
||||||
ID: "6",
|
|
||||||
containerDefinition: &ecs.ContainerDefinition{
|
|
||||||
DockerLabels: map[string]*string{
|
|
||||||
label.TraefikPort: aws.String("666"),
|
|
||||||
label.TraefikProtocol: aws.String("https"),
|
|
||||||
label.TraefikWeight: aws.String("12"),
|
|
||||||
|
|
||||||
label.TraefikBackend: aws.String("foobar"),
|
|
||||||
|
|
||||||
label.TraefikBackendHealthCheckPath: aws.String("/health"),
|
|
||||||
label.TraefikBackendHealthCheckInterval: aws.String("6"),
|
|
||||||
label.TraefikBackendLoadBalancerMethod: aws.String("drr"),
|
|
||||||
label.TraefikBackendLoadBalancerSticky: aws.String("true"),
|
|
||||||
label.TraefikBackendLoadBalancerStickiness: aws.String("true"),
|
|
||||||
label.TraefikBackendLoadBalancerStickinessCookieName: aws.String("chocolate"),
|
|
||||||
|
|
||||||
label.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
|
||||||
label.TraefikFrontendPassHostHeader: aws.String("true"),
|
|
||||||
label.TraefikFrontendPriority: aws.String("666"),
|
|
||||||
label.TraefikFrontendRule: aws.String("Host:traefik.io"),
|
|
||||||
}},
|
|
||||||
machine: &machine{
|
|
||||||
state: ec2.InstanceStateNameRunning,
|
|
||||||
privateIP: "10.0.0.1",
|
|
||||||
ports: []portMapping{{hostPort: 1337}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &types.Configuration{
|
|
||||||
Backends: map[string]*types.Backend{
|
|
||||||
"backend-foobar": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-testing-instance6": {
|
|
||||||
URL: "https://10.0.0.1:666",
|
|
||||||
Weight: 12,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LoadBalancer: &types.LoadBalancer{
|
|
||||||
Method: "drr",
|
|
||||||
Sticky: true,
|
|
||||||
Stickiness: &types.Stickiness{
|
|
||||||
CookieName: "chocolate",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
HealthCheck: &types.HealthCheck{
|
|
||||||
Path: "/health",
|
|
||||||
Interval: "6",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Frontends: map[string]*types.Frontend{
|
|
||||||
"frontend-foobar": {
|
|
||||||
EntryPoints: []string{
|
|
||||||
"http",
|
|
||||||
"https",
|
|
||||||
},
|
|
||||||
Backend: "backend-foobar",
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-frontend-foobar": {
|
|
||||||
Rule: "Host:traefik.io",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PassHostHeader: true,
|
|
||||||
Priority: 666,
|
|
||||||
BasicAuth: []string{
|
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
desc: "Containers with same backend name",
|
|
||||||
instances: []ecsInstance{
|
|
||||||
{
|
|
||||||
Name: "testing-instance",
|
|
||||||
ID: "6",
|
|
||||||
containerDefinition: &ecs.ContainerDefinition{
|
|
||||||
DockerLabels: map[string]*string{
|
|
||||||
label.TraefikPort: aws.String("666"),
|
|
||||||
label.TraefikProtocol: aws.String("https"),
|
|
||||||
label.TraefikWeight: aws.String("12"),
|
|
||||||
|
|
||||||
label.TraefikBackend: aws.String("foobar"),
|
|
||||||
|
|
||||||
label.TraefikBackendHealthCheckPath: aws.String("/health"),
|
|
||||||
label.TraefikBackendHealthCheckInterval: aws.String("6"),
|
|
||||||
label.TraefikBackendLoadBalancerMethod: aws.String("drr"),
|
|
||||||
label.TraefikBackendLoadBalancerSticky: aws.String("true"),
|
|
||||||
label.TraefikBackendLoadBalancerStickiness: aws.String("true"),
|
|
||||||
label.TraefikBackendLoadBalancerStickinessCookieName: aws.String("chocolate"),
|
|
||||||
|
|
||||||
label.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
|
||||||
label.TraefikFrontendPassHostHeader: aws.String("true"),
|
|
||||||
label.TraefikFrontendPriority: aws.String("666"),
|
|
||||||
label.TraefikFrontendRule: aws.String("Host:traefik.io"),
|
|
||||||
}},
|
|
||||||
machine: &machine{
|
|
||||||
state: ec2.InstanceStateNameRunning,
|
|
||||||
privateIP: "10.0.0.1",
|
|
||||||
ports: []portMapping{{hostPort: 1337}},
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Name: "testing-instance-v2",
|
|
||||||
ID: "6",
|
|
||||||
containerDefinition: &ecs.ContainerDefinition{
|
|
||||||
DockerLabels: map[string]*string{
|
|
||||||
label.TraefikPort: aws.String("555"),
|
|
||||||
label.TraefikProtocol: aws.String("https"),
|
|
||||||
label.TraefikWeight: aws.String("12"),
|
|
||||||
|
|
||||||
label.TraefikBackend: aws.String("foobar"),
|
|
||||||
|
|
||||||
label.TraefikBackendHealthCheckPath: aws.String("/health"),
|
|
||||||
label.TraefikBackendHealthCheckInterval: aws.String("6"),
|
|
||||||
label.TraefikBackendLoadBalancerMethod: aws.String("drr"),
|
|
||||||
label.TraefikBackendLoadBalancerSticky: aws.String("true"),
|
|
||||||
label.TraefikBackendLoadBalancerStickiness: aws.String("true"),
|
|
||||||
label.TraefikBackendLoadBalancerStickinessCookieName: aws.String("chocolate"),
|
|
||||||
|
|
||||||
label.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
|
||||||
label.TraefikFrontendPassHostHeader: aws.String("true"),
|
|
||||||
label.TraefikFrontendPriority: aws.String("666"),
|
|
||||||
label.TraefikFrontendRule: aws.String("Host:traefik.io"),
|
|
||||||
}},
|
|
||||||
machine: &machine{
|
|
||||||
state: ec2.InstanceStateNameRunning,
|
|
||||||
privateIP: "10.0.0.2",
|
|
||||||
ports: []portMapping{{hostPort: 1337}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &types.Configuration{
|
|
||||||
Backends: map[string]*types.Backend{
|
|
||||||
"backend-foobar": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-testing-instance6": {
|
|
||||||
URL: "https://10.0.0.1:666",
|
|
||||||
Weight: 12,
|
|
||||||
},
|
|
||||||
"server-testing-instance-v26": {
|
|
||||||
URL: "https://10.0.0.2:555",
|
|
||||||
Weight: 12,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LoadBalancer: &types.LoadBalancer{
|
|
||||||
Method: "drr",
|
|
||||||
Sticky: true,
|
|
||||||
Stickiness: &types.Stickiness{
|
|
||||||
CookieName: "chocolate",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
HealthCheck: &types.HealthCheck{
|
|
||||||
Path: "/health",
|
|
||||||
Interval: "6",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Frontends: map[string]*types.Frontend{
|
|
||||||
"frontend-foobar": {
|
|
||||||
EntryPoints: []string{
|
|
||||||
"http",
|
|
||||||
"https",
|
|
||||||
},
|
|
||||||
Backend: "backend-foobar",
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-frontend-foobar": {
|
|
||||||
Rule: "Host:traefik.io",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PassHostHeader: true,
|
|
||||||
Priority: 666,
|
|
||||||
BasicAuth: []string{
|
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
provider := &Provider{ExposedByDefault: true}
|
|
||||||
|
|
||||||
instances := fakeLoadTraefikLabels(test.instances)
|
|
||||||
|
|
||||||
got, err := provider.buildConfigurationV1(instances)
|
|
||||||
assert.Equal(t, test.err, err) // , err.Error()
|
|
||||||
assert.Equal(t, test.expected, got, test.desc)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetFuncStringValueV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
expected string
|
|
||||||
instanceInfo ecsInstance
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "Protocol label is not set should return a string equals to http",
|
|
||||||
expected: "http",
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Protocol label is set to http should return a string equals to http",
|
|
||||||
expected: "http",
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{
|
|
||||||
label.TraefikProtocol: aws.String("http"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Protocol label is set to https should return a string equals to https",
|
|
||||||
expected: "https",
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{
|
|
||||||
label.TraefikProtocol: aws.String("https"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actual := getFuncStringValueV1(label.TraefikProtocol, label.DefaultProtocol)(test.instanceInfo)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetFuncSliceStringV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
expected []string
|
|
||||||
instanceInfo ecsInstance
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "Frontend entrypoints label not set should return empty array",
|
|
||||||
expected: nil,
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Frontend entrypoints label set to http should return a string array of 1 element",
|
|
||||||
expected: []string{"http"},
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{
|
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Frontend entrypoints label set to http,https should return a string array of 2 elements",
|
|
||||||
expected: []string{"http", "https"},
|
|
||||||
instanceInfo: simpleEcsInstance(map[string]*string{
|
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actual := getFuncSliceStringV1(label.TraefikFrontendEntryPoints)(test.instanceInfo)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,7 +25,7 @@ type appData struct {
|
||||||
LinkedApps []*appData
|
LinkedApps []*appData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) buildConfigurationV2(applications *marathon.Applications) *types.Configuration {
|
func (p *Provider) buildConfiguration(applications *marathon.Applications) *types.Configuration {
|
||||||
var MarathonFuncMap = template.FuncMap{
|
var MarathonFuncMap = template.FuncMap{
|
||||||
"getDomain": label.GetFuncString(label.TraefikDomain, p.Domain), // 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
|
"getSubDomain": p.getSubDomain, // see https://github.com/containous/traefik/pull/1693
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
|
@ -655,7 +655,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
ExposedByDefault: true,
|
ExposedByDefault: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
actualConfig := p.buildConfigurationV2(test.applications)
|
actualConfig := p.buildConfiguration(test.applications)
|
||||||
|
|
||||||
assert.NotNil(t, actualConfig)
|
assert.NotNil(t, actualConfig)
|
||||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
||||||
|
@ -988,7 +988,7 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||||
ExposedByDefault: true,
|
ExposedByDefault: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
actualConfig := p.buildConfigurationV2(test.applications)
|
actualConfig := p.buildConfiguration(test.applications)
|
||||||
|
|
||||||
assert.NotNil(t, actualConfig)
|
assert.NotNil(t, actualConfig)
|
||||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
||||||
|
|
|
@ -1,436 +0,0 @@
|
||||||
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.DefaultWeight),
|
|
||||||
"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.DefaultPassHostHeader),
|
|
||||||
"getPassTLSCert": getFuncBoolServiceV1(label.SuffixFrontendPassTLSCert, label.DefaultPassTLSCert),
|
|
||||||
"getPriority": getFuncIntServiceV1(label.SuffixFrontendPriority, label.DefaultFrontendPriority),
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
domain := label.GetStringValue(labels, label.SuffixDomain, p.Domain)
|
|
||||||
if len(serviceName) > 0 {
|
|
||||||
return "Host:" + strings.ToLower(provider.Normalize(serviceName)) + "." + p.getSubDomain(application.ID) + "." + domain
|
|
||||||
}
|
|
||||||
return "Host:" + p.getSubDomain(application.ID) + "." + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getBackendServerV1(task marathon.Task, application marathon.Application) string {
|
|
||||||
networks := application.Networks
|
|
||||||
var hostFlag bool
|
|
||||||
|
|
||||||
if networks == nil {
|
|
||||||
hostFlag = application.IPAddressPerTask == nil
|
|
||||||
} else {
|
|
||||||
hostFlag = (*networks)[0].Mode != marathon.ContainerNetworkMode
|
|
||||||
}
|
|
||||||
|
|
||||||
if hostFlag || 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.DefaultWeight),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,895 +0,0 @@
|
||||||
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: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "filtered task",
|
|
||||||
application: application(
|
|
||||||
appPorts(80),
|
|
||||||
withTasks(localhostTask(taskPorts(80), taskState(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: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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"),
|
|
||||||
withSegmentLabel(label.TraefikPort, "80", "web"),
|
|
||||||
withSegmentLabel(label.TraefikPort, "81", "admin"),
|
|
||||||
withLabel("traefik..port", "82"), // This should be ignored, as it fails to match the servicesPropertiesRegexp regex.
|
|
||||||
withSegmentLabel(label.TraefikFrontendRule, "Host:web.app.marathon.localhost", "web"),
|
|
||||||
withSegmentLabel(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: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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"),
|
|
||||||
|
|
||||||
withSegmentLabel(label.TraefikPort, "80", "containous"),
|
|
||||||
withSegmentLabel(label.TraefikProtocol, "https", "containous"),
|
|
||||||
withSegmentLabel(label.TraefikWeight, "12", "containous"),
|
|
||||||
|
|
||||||
withSegmentLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"),
|
|
||||||
withSegmentLabel(label.TraefikFrontendEntryPoints, "http,https", "containous"),
|
|
||||||
withSegmentLabel(label.TraefikFrontendPassHostHeader, "true", "containous"),
|
|
||||||
withSegmentLabel(label.TraefikFrontendPriority, "666", "containous"),
|
|
||||||
withSegmentLabel(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(withSegmentLabel(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(withSegmentLabel(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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetServersV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
application marathon.Application
|
|
||||||
segmentName string
|
|
||||||
expected map[string]types.Server
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "should return nil when no task",
|
|
||||||
application: application(ipAddrPerTask(80)),
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should return nil when all hosts are empty",
|
|
||||||
application: application(
|
|
||||||
withTasks(
|
|
||||||
task(ipAddresses("1.1.1.1"), withTaskID("A"), taskPorts(80)),
|
|
||||||
task(ipAddresses("1.1.1.2"), withTaskID("B"), taskPorts(80)),
|
|
||||||
task(ipAddresses("1.1.1.3"), withTaskID("C"), taskPorts(80))),
|
|
||||||
),
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "with 3 tasks and hosts set",
|
|
||||||
application: application(
|
|
||||||
withTasks(
|
|
||||||
task(ipAddresses("1.1.1.1"), host("2.2.2.2"), withTaskID("A"), taskPorts(80)),
|
|
||||||
task(ipAddresses("1.1.1.2"), host("2.2.2.2"), withTaskID("B"), taskPorts(81)),
|
|
||||||
task(ipAddresses("1.1.1.3"), host("2.2.2.2"), withTaskID("C"), taskPorts(82))),
|
|
||||||
),
|
|
||||||
expected: map[string]types.Server{
|
|
||||||
"server-A": {
|
|
||||||
URL: "http://2.2.2.2:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-B": {
|
|
||||||
URL: "http://2.2.2.2:81",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-C": {
|
|
||||||
URL: "http://2.2.2.2:82",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "with 3 tasks and ipAddrPerTask set",
|
|
||||||
application: application(
|
|
||||||
ipAddrPerTask(80),
|
|
||||||
withTasks(
|
|
||||||
task(ipAddresses("1.1.1.1"), withTaskID("A"), taskPorts(80)),
|
|
||||||
task(ipAddresses("1.1.1.2"), withTaskID("B"), taskPorts(80)),
|
|
||||||
task(ipAddresses("1.1.1.3"), withTaskID("C"), taskPorts(80))),
|
|
||||||
),
|
|
||||||
expected: map[string]types.Server{
|
|
||||||
"server-A": {
|
|
||||||
URL: "http://1.1.1.1:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-B": {
|
|
||||||
URL: "http://1.1.1.2:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-C": {
|
|
||||||
URL: "http://1.1.1.3:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "with 3 tasks and bridge network",
|
|
||||||
application: application(
|
|
||||||
bridgeNetwork(),
|
|
||||||
withTasks(
|
|
||||||
task(ipAddresses("1.1.1.1"), host("2.2.2.2"), withTaskID("A"), taskPorts(80)),
|
|
||||||
task(ipAddresses("1.1.1.2"), host("2.2.2.2"), withTaskID("B"), taskPorts(81)),
|
|
||||||
task(ipAddresses("1.1.1.3"), host("2.2.2.2"), withTaskID("C"), taskPorts(82))),
|
|
||||||
),
|
|
||||||
expected: map[string]types.Server{
|
|
||||||
"server-A": {
|
|
||||||
URL: "http://2.2.2.2:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-B": {
|
|
||||||
URL: "http://2.2.2.2:81",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-C": {
|
|
||||||
URL: "http://2.2.2.2:82",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "with 3 tasks and cni set",
|
|
||||||
application: application(
|
|
||||||
containerNetwork(),
|
|
||||||
withTasks(
|
|
||||||
task(ipAddresses("1.1.1.1"), withTaskID("A"), taskPorts(80)),
|
|
||||||
task(ipAddresses("1.1.1.2"), withTaskID("B"), taskPorts(80)),
|
|
||||||
task(ipAddresses("1.1.1.3"), withTaskID("C"), taskPorts(80))),
|
|
||||||
),
|
|
||||||
expected: map[string]types.Server{
|
|
||||||
"server-A": {
|
|
||||||
URL: "http://1.1.1.1:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-B": {
|
|
||||||
URL: "http://1.1.1.2:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-C": {
|
|
||||||
URL: "http://1.1.1.3:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
p := &Provider{}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actual := p.getServersV1(test.application, test.segmentName)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,7 +21,7 @@ type taskData struct {
|
||||||
SegmentName string
|
SegmentName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) buildConfigurationV2(tasks []state.Task) *types.Configuration {
|
func (p *Provider) buildConfiguration(tasks []state.Task) *types.Configuration {
|
||||||
var mesosFuncMap = template.FuncMap{
|
var mesosFuncMap = template.FuncMap{
|
||||||
"getDomain": label.GetFuncString(label.TraefikDomain, p.Domain),
|
"getDomain": label.GetFuncString(label.TraefikDomain, p.Domain),
|
||||||
"getSubDomain": p.getSubDomain,
|
"getSubDomain": p.getSubDomain,
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package mesos
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
"github.com/mesosphere/mesos-dns/records/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Provider) buildConfiguration(tasks []state.Task) *types.Configuration {
|
|
||||||
if p.TemplateVersion == 1 {
|
|
||||||
return p.buildConfigurationV1(tasks)
|
|
||||||
}
|
|
||||||
return p.buildConfigurationV2(tasks)
|
|
||||||
}
|
|
|
@ -555,7 +555,7 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
actualConfig := p.buildConfigurationV2(test.tasks)
|
actualConfig := p.buildConfiguration(test.tasks)
|
||||||
|
|
||||||
require.NotNil(t, actualConfig)
|
require.NotNil(t, actualConfig)
|
||||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
||||||
|
@ -909,7 +909,7 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
actualConfig := p.buildConfigurationV2(test.tasks)
|
actualConfig := p.buildConfiguration(test.tasks)
|
||||||
|
|
||||||
require.NotNil(t, actualConfig)
|
require.NotNil(t, actualConfig)
|
||||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
||||||
|
|
|
@ -1,329 +0,0 @@
|
||||||
package mesos
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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/mesosphere/mesos-dns/records/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Provider) buildConfigurationV1(tasks []state.Task) *types.Configuration {
|
|
||||||
var mesosFuncMap = template.FuncMap{
|
|
||||||
"getDomain": getFuncStringValueV1(label.TraefikDomain, p.Domain),
|
|
||||||
"getID": getIDV1,
|
|
||||||
|
|
||||||
// Backend functions
|
|
||||||
"getBackendName": getBackendNameV1,
|
|
||||||
"getHost": p.getHostV1,
|
|
||||||
"getProtocol": getFuncApplicationStringValueV1(label.TraefikProtocol, label.DefaultProtocol),
|
|
||||||
"getWeight": getFuncApplicationIntValueV1(label.TraefikWeight, label.DefaultWeight),
|
|
||||||
"getBackend": getBackendV1,
|
|
||||||
"getPort": p.getPort,
|
|
||||||
|
|
||||||
// Frontend functions
|
|
||||||
"getFrontendBackend": getBackendNameV1,
|
|
||||||
"getFrontEndName": getFrontendNameV1,
|
|
||||||
"getEntryPoints": getFuncSliceStringValueV1(label.TraefikFrontendEntryPoints),
|
|
||||||
"getBasicAuth": getFuncSliceStringValueV1(label.TraefikFrontendAuthBasic),
|
|
||||||
"getPriority": getFuncIntValueV1(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
|
||||||
"getPassHostHeader": getFuncBoolValueV1(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
|
||||||
"getFrontendRule": p.getFrontendRuleV1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter tasks
|
|
||||||
filteredTasks := fun.Filter(func(task state.Task) bool {
|
|
||||||
return taskFilterV1(task, p.ExposedByDefault)
|
|
||||||
}, tasks).([]state.Task)
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
var filteredApps []state.Task
|
|
||||||
uniqueApps := make(map[string]struct{})
|
|
||||||
for _, task := range filteredTasks {
|
|
||||||
if _, ok := uniqueApps[task.DiscoveryInfo.Name]; !ok {
|
|
||||||
uniqueApps[task.DiscoveryInfo.Name] = struct{}{}
|
|
||||||
filteredApps = append(filteredApps, task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appsTasks := make(map[string][]state.Task)
|
|
||||||
for _, task := range filteredTasks {
|
|
||||||
if _, ok := appsTasks[task.DiscoveryInfo.Name]; !ok {
|
|
||||||
appsTasks[task.DiscoveryInfo.Name] = []state.Task{task}
|
|
||||||
} else {
|
|
||||||
appsTasks[task.DiscoveryInfo.Name] = append(appsTasks[task.DiscoveryInfo.Name], task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
templateObjects := struct {
|
|
||||||
ApplicationsTasks map[string][]state.Task
|
|
||||||
Applications []state.Task // Deprecated
|
|
||||||
Tasks []state.Task // Deprecated
|
|
||||||
Domain string
|
|
||||||
}{
|
|
||||||
ApplicationsTasks: appsTasks,
|
|
||||||
Applications: filteredApps, // Deprecated
|
|
||||||
Tasks: filteredTasks, // Deprecated
|
|
||||||
Domain: p.Domain,
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration, err := p.GetConfiguration("templates/mesos-v1.tmpl", mesosFuncMap, templateObjects)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
return configuration
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func taskFilterV1(task state.Task, exposedByDefaultFlag bool) bool {
|
|
||||||
if len(task.DiscoveryInfo.Ports.DiscoveryPorts) == 0 {
|
|
||||||
log.Debugf("Filtering Mesos task without port %s", task.Name)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isEnabledV1(task, exposedByDefaultFlag) {
|
|
||||||
log.Debugf("Filtering disabled Mesos task %s", task.DiscoveryInfo.Name)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter indeterminable task port
|
|
||||||
portIndexLabel := getStringValueV1(task, label.TraefikPortIndex, "")
|
|
||||||
portValueLabel := getStringValueV1(task, label.TraefikPort, "")
|
|
||||||
if portIndexLabel != "" && portValueLabel != "" {
|
|
||||||
log.Debugf("Filtering Mesos task %s specifying both %q' and %q labels", task.Name, label.TraefikPortIndex, label.TraefikPort)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if portIndexLabel != "" {
|
|
||||||
index, err := strconv.Atoi(portIndexLabel)
|
|
||||||
if err != nil || index < 0 || index > len(task.DiscoveryInfo.Ports.DiscoveryPorts)-1 {
|
|
||||||
log.Debugf("Filtering Mesos task %s with unexpected value for %q label", task.Name, label.TraefikPortIndex)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if portValueLabel != "" {
|
|
||||||
port, err := strconv.Atoi(portValueLabel)
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("Filtering Mesos task %s with unexpected value for %q label", task.Name, label.TraefikPort)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var foundPort bool
|
|
||||||
for _, exposedPort := range task.DiscoveryInfo.Ports.DiscoveryPorts {
|
|
||||||
if port == exposedPort.Number {
|
|
||||||
foundPort = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !foundPort {
|
|
||||||
log.Debugf("Filtering Mesos task %s without a matching port for %q label", task.Name, label.TraefikPort)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter healthChecks
|
|
||||||
if task.Statuses != nil && len(task.Statuses) > 0 && task.Statuses[0].Healthy != nil && !*task.Statuses[0].Healthy {
|
|
||||||
log.Debugf("Filtering Mesos task %s with bad healthCheck", task.DiscoveryInfo.Name)
|
|
||||||
return false
|
|
||||||
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getIDV1(task state.Task) string {
|
|
||||||
return provider.Normalize(task.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getBackendV1(task state.Task, apps []state.Task) string {
|
|
||||||
_, err := getApplicationV1(task, apps)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return getBackendNameV1(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getBackendNameV1(task state.Task) string {
|
|
||||||
if value := getStringValueV1(task, label.TraefikBackend, ""); len(value) > 0 {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return provider.Normalize(task.DiscoveryInfo.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFrontendNameV1(task state.Task) string {
|
|
||||||
// TODO task.ID -> task.Name + task.ID
|
|
||||||
return provider.Normalize(task.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getPort(task state.Task, applications []state.Task) string {
|
|
||||||
_, err := getApplicationV1(task, applications)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
plv := getIntValueV1(task, label.TraefikPortIndex, math.MinInt32, len(task.DiscoveryInfo.Ports.DiscoveryPorts)-1)
|
|
||||||
if plv >= 0 {
|
|
||||||
return strconv.Itoa(task.DiscoveryInfo.Ports.DiscoveryPorts[plv].Number)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pv := getStringValueV1(task, label.TraefikPort, ""); len(pv) > 0 {
|
|
||||||
return pv
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, port := range task.DiscoveryInfo.Ports.DiscoveryPorts {
|
|
||||||
return strconv.Itoa(port.Number)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// getFrontendRuleV1 returns the frontend rule for the specified application, using
|
|
||||||
// it's label. It returns a default one (Host) if the label is not present.
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getFrontendRuleV1(task state.Task) string {
|
|
||||||
if v := getStringValueV1(task, label.TraefikFrontendRule, ""); len(v) > 0 {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
domain := getStringValueV1(task, label.TraefikDomain, p.Domain)
|
|
||||||
return "Host:" + strings.ToLower(strings.Replace(p.getSubDomain(task.DiscoveryInfo.Name), "_", "-", -1)) + "." + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getHostV1(task state.Task) string {
|
|
||||||
return task.IP(strings.Split(p.IPSources, ",")...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func isEnabledV1(task state.Task, exposedByDefault bool) bool {
|
|
||||||
return getBoolValueV1(task, label.TraefikEnable, exposedByDefault)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Label functions
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncApplicationStringValueV1(labelName string, defaultValue string) func(task state.Task, applications []state.Task) string {
|
|
||||||
return func(task state.Task, applications []state.Task) string {
|
|
||||||
_, err := getApplicationV1(task, applications)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return getStringValueV1(task, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncApplicationIntValueV1(labelName string, defaultValue int) func(task state.Task, applications []state.Task) int {
|
|
||||||
return func(task state.Task, applications []state.Task) int {
|
|
||||||
_, err := getApplicationV1(task, applications)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return getIntValueV1(task, labelName, defaultValue, math.MaxInt32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncStringValueV1(labelName string, defaultValue string) func(task state.Task) string {
|
|
||||||
return func(task state.Task) string {
|
|
||||||
return getStringValueV1(task, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncBoolValueV1(labelName string, defaultValue bool) func(task state.Task) bool {
|
|
||||||
return func(task state.Task) bool {
|
|
||||||
return getBoolValueV1(task, labelName, defaultValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncIntValueV1(labelName string, defaultValue int) func(task state.Task) int {
|
|
||||||
return func(task state.Task) int {
|
|
||||||
return getIntValueV1(task, labelName, defaultValue, math.MaxInt32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getFuncSliceStringValueV1(labelName string) func(task state.Task) []string {
|
|
||||||
return func(task state.Task) []string {
|
|
||||||
return getSliceStringValueV1(task, labelName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getStringValueV1(task state.Task, labelName string, defaultValue string) string {
|
|
||||||
for _, lbl := range task.Labels {
|
|
||||||
if lbl.Key == labelName && len(lbl.Value) > 0 {
|
|
||||||
return lbl.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getBoolValueV1(task state.Task, labelName string, defaultValue bool) bool {
|
|
||||||
for _, lbl := range task.Labels {
|
|
||||||
if lbl.Key == labelName {
|
|
||||||
v, err := strconv.ParseBool(lbl.Value)
|
|
||||||
if err == nil {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getIntValueV1(task state.Task, labelName string, defaultValue int, maxValue int) int {
|
|
||||||
for _, lbl := range task.Labels {
|
|
||||||
if lbl.Key == labelName {
|
|
||||||
value, err := strconv.Atoi(lbl.Value)
|
|
||||||
if err == nil {
|
|
||||||
if value <= maxValue {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
log.Warnf("The value %q for %q exceed the max authorized value %q, falling back to %v.", lbl.Value, labelName, maxValue, defaultValue)
|
|
||||||
} else {
|
|
||||||
log.Warnf("Unable to parse %q: %q, falling back to %v. %v", labelName, lbl.Value, defaultValue, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getSliceStringValueV1(task state.Task, labelName string) []string {
|
|
||||||
for _, lbl := range task.Labels {
|
|
||||||
if lbl.Key == labelName {
|
|
||||||
return label.SplitAndTrimString(lbl.Value, ",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func getApplicationV1(task state.Task, apps []state.Task) (state.Task, error) {
|
|
||||||
for _, app := range apps {
|
|
||||||
if app.DiscoveryInfo.Name == task.DiscoveryInfo.Name {
|
|
||||||
return app, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return state.Task{}, fmt.Errorf("unable to get Mesos application from task %s", task.DiscoveryInfo.Name)
|
|
||||||
}
|
|
|
@ -1,432 +0,0 @@
|
||||||
package mesos
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/provider/label"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
"github.com/mesosphere/mesos-dns/records/state"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBuildConfigurationV1(t *testing.T) {
|
|
||||||
p := &Provider{
|
|
||||||
Domain: "mesos.localhost",
|
|
||||||
ExposedByDefault: true,
|
|
||||||
IPSources: "host",
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
tasks []state.Task
|
|
||||||
expectedFrontends map[string]*types.Frontend
|
|
||||||
expectedBackends map[string]*types.Backend
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "when no tasks",
|
|
||||||
tasks: []state.Task{},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{},
|
|
||||||
expectedBackends: map[string]*types.Backend{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "2 applications with 2 tasks",
|
|
||||||
tasks: []state.Task{
|
|
||||||
// App 1
|
|
||||||
aTask("ID1",
|
|
||||||
withIP("10.10.10.10"),
|
|
||||||
withInfo("name1",
|
|
||||||
withPorts(withPort("TCP", 80, "WEB"))),
|
|
||||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
|
||||||
),
|
|
||||||
aTask("ID2",
|
|
||||||
withIP("10.10.10.11"),
|
|
||||||
withInfo("name1",
|
|
||||||
withPorts(withPort("TCP", 81, "WEB"))),
|
|
||||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
|
||||||
),
|
|
||||||
// App 2
|
|
||||||
aTask("ID3",
|
|
||||||
withIP("20.10.10.10"),
|
|
||||||
withInfo("name2",
|
|
||||||
withPorts(withPort("TCP", 80, "WEB"))),
|
|
||||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
|
||||||
),
|
|
||||||
aTask("ID4",
|
|
||||||
withIP("20.10.10.11"),
|
|
||||||
withInfo("name2",
|
|
||||||
withPorts(withPort("TCP", 81, "WEB"))),
|
|
||||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-ID1": {
|
|
||||||
Backend: "backend-name1",
|
|
||||||
EntryPoints: []string{},
|
|
||||||
PassHostHeader: true,
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-host-ID1": {
|
|
||||||
Rule: "Host:name1.mesos.localhost",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"frontend-ID3": {
|
|
||||||
Backend: "backend-name2",
|
|
||||||
EntryPoints: []string{},
|
|
||||||
PassHostHeader: true,
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-host-ID3": {
|
|
||||||
Rule: "Host:name2.mesos.localhost",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-name1": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-ID1": {
|
|
||||||
URL: "http://10.10.10.10:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-ID2": {
|
|
||||||
URL: "http://10.10.10.11:81",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"backend-name2": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-ID3": {
|
|
||||||
URL: "http://20.10.10.10:80",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"server-ID4": {
|
|
||||||
URL: "http://20.10.10.11:81",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "with all labels",
|
|
||||||
tasks: []state.Task{
|
|
||||||
aTask("ID1",
|
|
||||||
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.TraefikBackendHealthCheckPort, "880"),
|
|
||||||
withLabel(label.TraefikBackendHealthCheckInterval, "6"),
|
|
||||||
withLabel(label.TraefikBackendLoadBalancerMethod, "drr"),
|
|
||||||
withLabel(label.TraefikBackendLoadBalancerStickiness, "true"),
|
|
||||||
withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"),
|
|
||||||
withLabel(label.TraefikBackendMaxConnAmount, "666"),
|
|
||||||
withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"),
|
|
||||||
withLabel(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"),
|
|
||||||
withLabel(label.TraefikBackendBufferingMemResponseBodyBytes, "2097152"),
|
|
||||||
withLabel(label.TraefikBackendBufferingMaxRequestBodyBytes, "10485760"),
|
|
||||||
withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"),
|
|
||||||
withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"),
|
|
||||||
|
|
||||||
withLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
|
||||||
withLabel(label.TraefikFrontendEntryPoints, "http,https"),
|
|
||||||
withLabel(label.TraefikFrontendPassHostHeader, "true"),
|
|
||||||
withLabel(label.TraefikFrontendPassTLSCert, "true"),
|
|
||||||
withLabel(label.TraefikFrontendPriority, "666"),
|
|
||||||
withLabel(label.TraefikFrontendRedirectEntryPoint, "https"),
|
|
||||||
withLabel(label.TraefikFrontendRedirectRegex, "nope"),
|
|
||||||
withLabel(label.TraefikFrontendRedirectReplacement, "nope"),
|
|
||||||
withLabel(label.TraefikFrontendRedirectPermanent, "true"),
|
|
||||||
withLabel(label.TraefikFrontendRule, "Host:traefik.io"),
|
|
||||||
withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"),
|
|
||||||
withLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true"),
|
|
||||||
|
|
||||||
withLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type:application/json; charset=utf-8"),
|
|
||||||
withLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type:application/json; charset=utf-8"),
|
|
||||||
withLabel(label.TraefikFrontendSSLProxyHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type:application/json; charset=utf-8"),
|
|
||||||
withLabel(label.TraefikFrontendAllowedHosts, "foo,bar,bor"),
|
|
||||||
withLabel(label.TraefikFrontendHostsProxyHeaders, "foo,bar,bor"),
|
|
||||||
withLabel(label.TraefikFrontendSSLHost, "foo"),
|
|
||||||
withLabel(label.TraefikFrontendCustomFrameOptionsValue, "foo"),
|
|
||||||
withLabel(label.TraefikFrontendContentSecurityPolicy, "foo"),
|
|
||||||
withLabel(label.TraefikFrontendPublicKey, "foo"),
|
|
||||||
withLabel(label.TraefikFrontendReferrerPolicy, "foo"),
|
|
||||||
withLabel(label.TraefikFrontendCustomBrowserXSSValue, "foo"),
|
|
||||||
withLabel(label.TraefikFrontendSTSSeconds, "666"),
|
|
||||||
withLabel(label.TraefikFrontendSSLRedirect, "true"),
|
|
||||||
withLabel(label.TraefikFrontendSSLTemporaryRedirect, "true"),
|
|
||||||
withLabel(label.TraefikFrontendSTSIncludeSubdomains, "true"),
|
|
||||||
withLabel(label.TraefikFrontendSTSPreload, "true"),
|
|
||||||
withLabel(label.TraefikFrontendForceSTSHeader, "true"),
|
|
||||||
withLabel(label.TraefikFrontendFrameDeny, "true"),
|
|
||||||
withLabel(label.TraefikFrontendContentTypeNosniff, "true"),
|
|
||||||
withLabel(label.TraefikFrontendBrowserXSSFilter, "true"),
|
|
||||||
withLabel(label.TraefikFrontendIsDevelopment, "true"),
|
|
||||||
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageStatus, "404"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageBackend, "foobar"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageQuery, "foo_query"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageStatus, "500,600"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageBackend, "foobar"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageQuery, "bar_query"),
|
|
||||||
|
|
||||||
withLabel(label.TraefikFrontendRateLimitExtractorFunc, "client.ip"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitPeriod, "6"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitAverage, "12"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitBurst, "18"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitPeriod, "3"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitAverage, "6"),
|
|
||||||
withLabel(label.Prefix+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitBurst, "9"),
|
|
||||||
withIP("10.10.10.10"),
|
|
||||||
withInfo("name1", withPorts(
|
|
||||||
withPortTCP(80, "n"),
|
|
||||||
withPortTCP(666, "n"))),
|
|
||||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
|
||||||
"frontend-ID1": {
|
|
||||||
EntryPoints: []string{
|
|
||||||
"http",
|
|
||||||
"https",
|
|
||||||
},
|
|
||||||
Backend: "backend-foobar",
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"route-host-ID1": {
|
|
||||||
Rule: "Host:traefik.io",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PassHostHeader: true,
|
|
||||||
Priority: 666,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedBackends: map[string]*types.Backend{
|
|
||||||
"backend-foobar": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"server-ID1": {
|
|
||||||
URL: "https://10.10.10.10:666",
|
|
||||||
Weight: 12,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actualConfig := p.buildConfigurationV1(test.tasks)
|
|
||||||
|
|
||||||
require.NotNil(t, actualConfig)
|
|
||||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
|
||||||
assert.Equal(t, test.expectedFrontends, actualConfig.Frontends)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTaskFilterV1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
mesosTask state.Task
|
|
||||||
exposedByDefault bool
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "no task",
|
|
||||||
mesosTask: state.Task{},
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "task not healthy",
|
|
||||||
mesosTask: aTask("test", withStatus(withState("TASK_RUNNING"))),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "exposedByDefault false and traefik.enable false",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "false"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: false,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "traefik.enable = true",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: false,
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "exposedByDefault true and traefik.enable true",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "exposedByDefault true and traefik.enable false",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "false"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "traefik.portIndex and traefik.port both set",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withLabel(label.TraefikPortIndex, "1"),
|
|
||||||
withLabel(label.TraefikEnable, "80"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "valid traefik.portIndex",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withLabel(label.TraefikPortIndex, "1"),
|
|
||||||
withInfo("test", withPorts(
|
|
||||||
withPortTCP(80, "WEB"),
|
|
||||||
withPortTCP(443, "WEB HTTPS"),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "default to first port index",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withInfo("test", withPorts(
|
|
||||||
withPortTCP(80, "WEB"),
|
|
||||||
withPortTCP(443, "WEB HTTPS"),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "traefik.portIndex and discoveryPorts don't correspond",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withLabel(label.TraefikPortIndex, "1"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "traefik.portIndex and discoveryPorts correspond",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withLabel(label.TraefikPortIndex, "0"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "traefik.port is not an integer",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withLabel(label.TraefikPort, "TRAEFIK"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "traefik.port is not the same as discovery.port",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withLabel(label.TraefikPort, "443"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "traefik.port is the same as discovery.port",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withDefaultStatus(),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withLabel(label.TraefikPort, "80"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "healthy nil",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withStatus(
|
|
||||||
withState("TASK_RUNNING"),
|
|
||||||
),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withLabel(label.TraefikPort, "80"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "healthy false",
|
|
||||||
mesosTask: aTask("test",
|
|
||||||
withStatus(
|
|
||||||
withState("TASK_RUNNING"),
|
|
||||||
withHealthy(false),
|
|
||||||
),
|
|
||||||
withLabel(label.TraefikEnable, "true"),
|
|
||||||
withLabel(label.TraefikPort, "80"),
|
|
||||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
|
||||||
),
|
|
||||||
exposedByDefault: true,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actual := taskFilterV1(test.mesosTask, test.exposedByDefault)
|
|
||||||
ok := assert.Equal(t, test.expected, actual)
|
|
||||||
if !ok {
|
|
||||||
t.Logf("Statuses : %v", test.mesosTask.Statuses)
|
|
||||||
t.Logf("Label : %v", test.mesosTask.Labels)
|
|
||||||
t.Logf("DiscoveryInfo : %v", test.mesosTask.DiscoveryInfo)
|
|
||||||
t.Fatalf("Expected %v, got %v", test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *Provider) buildConfigurationV2(services []rancherData) *types.Configuration {
|
func (p *Provider) buildConfiguration(services []rancherData) *types.Configuration {
|
||||||
var RancherFuncMap = template.FuncMap{
|
var RancherFuncMap = template.FuncMap{
|
||||||
"getLabelValue": label.GetStringValue,
|
"getLabelValue": label.GetStringValue,
|
||||||
"getDomain": label.GetFuncString(label.TraefikDomain, p.Domain),
|
"getDomain": label.GetFuncString(label.TraefikDomain, p.Domain),
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,233 +0,0 @@
|
||||||
package rancher
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
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.DefaultWeight),
|
|
||||||
"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.getFrontendRuleV1,
|
|
||||||
"getPriority": getFuncIntV1(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
|
||||||
"getPassHostHeader": getFuncBoolV1(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
|
||||||
"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) getFrontendRuleV1(service rancherData) string {
|
|
||||||
defaultRule := "Host:" + strings.ToLower(strings.Replace(service.Name, "/", ".", -1)) + "." + p.Domain
|
|
||||||
return label.GetStringValue(service.Labels, label.TraefikFrontendRule, defaultRule)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func (p *Provider) getFrontendNameV1(service rancherData) string {
|
|
||||||
return provider.Normalize(p.getFrontendRuleV1(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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,503 +0,0 @@
|
||||||
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: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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.Name, test.service.Labels)
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
[backends]
|
|
||||||
{{range $index, $node := .Nodes }}
|
|
||||||
[backends."backend-{{ getBackend $node }}".servers."{{ getBackendName $node $index }}"]
|
|
||||||
url = "{{ getAttribute "protocol" $node.Service.Tags "http" }}://{{ getBackendAddress $node }}:{{ $node.Service.Port }}"
|
|
||||||
{{ $weight := getAttribute "backend.weight" $node.Service.Tags "0" }}
|
|
||||||
{{with $weight }}
|
|
||||||
weight = {{ $weight }}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .Services }}
|
|
||||||
{{ $service := .ServiceName }}
|
|
||||||
|
|
||||||
{{ $circuitBreaker := getAttribute "backend.circuitbreaker" .Attributes "" }}
|
|
||||||
{{with $circuitBreaker }}
|
|
||||||
[backends."backend-{{ $service }}".circuitbreaker]
|
|
||||||
expression = "{{ $circuitBreaker }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[backends."backend-{{ $service }}".loadbalancer]
|
|
||||||
method = "{{ getAttribute "backend.loadbalancer" .Attributes "wrr" }}"
|
|
||||||
sticky = {{ getSticky .Attributes }}
|
|
||||||
{{if hasStickinessLabel .Attributes }}
|
|
||||||
[backends."backend-{{ $service }}".loadbalancer.stickiness]
|
|
||||||
cookieName = "{{ getStickinessCookieName .Attributes }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if hasMaxconnAttributes .Attributes }}
|
|
||||||
[backends."backend-{{ $service }}".maxconn]
|
|
||||||
amount = {{ getAttribute "backend.maxconn.amount" .Attributes "" }}
|
|
||||||
extractorfunc = "{{ getAttribute "backend.maxconn.extractorfunc" .Attributes "" }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends]
|
|
||||||
{{range .Services }}
|
|
||||||
[frontends."frontend-{{ .ServiceName }}"]
|
|
||||||
backend = "backend-{{ .ServiceName }}"
|
|
||||||
passHostHeader = {{ getAttribute "frontend.passHostHeader" .Attributes "true" }}
|
|
||||||
priority = {{ getAttribute "frontend.priority" .Attributes "0" }}
|
|
||||||
|
|
||||||
{{ $entryPoints := getAttribute "frontend.entrypoints" .Attributes "" }}
|
|
||||||
{{with $entryPoints }}
|
|
||||||
entrypoints = [{{range getEntryPoints $entryPoints }}
|
|
||||||
"{{ . }}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth .Attributes }}
|
|
||||||
"{{ . }}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
[frontends."frontend-{{ .ServiceName }}".routes."route-host-{{ .ServiceName }}"]
|
|
||||||
rule = "{{ getFrontendRule . }}"
|
|
||||||
{{end}}
|
|
|
@ -1,192 +0,0 @@
|
||||||
{{$backendServers := .Servers}}
|
|
||||||
|
|
||||||
[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}}
|
|
||||||
|
|
||||||
{{ $servers := index $backendServers $backendName }}
|
|
||||||
{{range $serverName, $server := $servers }}
|
|
||||||
{{if hasServices $server }}
|
|
||||||
{{$services := getServiceNames $server }}
|
|
||||||
{{range $serviceIndex, $serviceName := $services }}
|
|
||||||
[backends."backend-{{ getServiceBackend $server $serviceName }}".servers."service-{{ $serverName }}"]
|
|
||||||
url = "{{ getServiceProtocol $server $serviceName }}://{{ getIPAddress $server }}:{{ getServicePort $server $serviceName }}"
|
|
||||||
weight = {{ getServiceWeight $server $serviceName }}
|
|
||||||
{{end}}
|
|
||||||
{{else}}
|
|
||||||
[backends."backend-{{ $backendName }}".servers."server-{{$server.Name | replace "/" "" | replace "." "-"}}"]
|
|
||||||
url = "{{ getProtocol $server }}://{{ getIPAddress $server }}:{{ getPort $server }}"
|
|
||||||
weight = {{ getWeight $server }}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends]
|
|
||||||
{{range $frontend, $containers := .Frontends}}
|
|
||||||
{{$container := index $containers 0}}
|
|
||||||
|
|
||||||
{{if hasServices $container }}
|
|
||||||
{{ $services := getServiceNames $container }}
|
|
||||||
{{range $serviceIndex, $serviceName := $services }}
|
|
||||||
[frontends."frontend-{{ getServiceBackend $container $serviceName }}"]
|
|
||||||
backend = "backend-{{ getServiceBackend $container $serviceName }}"
|
|
||||||
passHostHeader = {{ getServicePassHostHeader $container $serviceName }}
|
|
||||||
passTLSCert = {{ getServicePassTLSCert $container $serviceName }}
|
|
||||||
|
|
||||||
{{if getWhitelistSourceRange $container }}
|
|
||||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
priority = {{ getServicePriority $container $serviceName }}
|
|
||||||
|
|
||||||
entryPoints = [{{range getServiceEntryPoints $container $serviceName }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
basicAuth = [{{range getServiceBasicAuth $container $serviceName }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
{{if hasServiceRedirect $container $serviceName }}
|
|
||||||
[frontends."frontend-{{ getServiceBackend $container $serviceName }}".redirect]
|
|
||||||
entryPoint = "{{ getServiceRedirectEntryPoint $container $serviceName }}"
|
|
||||||
regex = "{{ getServiceRedirectRegex $container $serviceName }}"
|
|
||||||
replacement = "{{ getServiceRedirectReplacement $container $serviceName }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends."frontend-{{ getServiceBackend $container $serviceName }}".routes."service-{{ $serviceName | replace "/" "" | replace "." "-" }}"]
|
|
||||||
rule = "{{ getServiceFrontendRule $container $serviceName }}"
|
|
||||||
{{end}}
|
|
||||||
{{else}}
|
|
||||||
[frontends."frontend-{{ $frontend }}"]
|
|
||||||
backend = "backend-{{ getBackend $container }}"
|
|
||||||
passHostHeader = {{ getPassHostHeader $container}}
|
|
||||||
passTLSCert = {{ getPassTLSCert $container }}
|
|
||||||
priority = {{ getPriority $container }}
|
|
||||||
|
|
||||||
{{if getWhitelistSourceRange $container}}
|
|
||||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
entryPoints = [{{range getEntryPoints $container }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $container }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
{{if hasRedirect $container}}
|
|
||||||
[frontends."frontend-{{$frontend}}".redirect]
|
|
||||||
entryPoint = "{{getRedirectEntryPoint $container}}"
|
|
||||||
regex = "{{getRedirectRegex $container}}"
|
|
||||||
replacement = "{{getRedirectReplacement $container}}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if hasHeaders $container }}
|
|
||||||
[frontends."frontend-{{ $frontend }}".headers]
|
|
||||||
{{if hasSSLRedirectHeaders $container}}
|
|
||||||
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSSLTemporaryRedirectHeaders $container}}
|
|
||||||
SSLTemporaryRedirect = {{getSSLTemporaryRedirectHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSSLHostHeaders $container}}
|
|
||||||
SSLHost = "{{getSSLHostHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasSTSSecondsHeaders $container}}
|
|
||||||
STSSeconds = {{getSTSSecondsHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSTSIncludeSubdomainsHeaders $container}}
|
|
||||||
STSIncludeSubdomains = {{getSTSIncludeSubdomainsHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSTSPreloadHeaders $container}}
|
|
||||||
STSPreload = {{getSTSPreloadHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasForceSTSHeaderHeaders $container}}
|
|
||||||
ForceSTSHeader = {{getForceSTSHeaderHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasFrameDenyHeaders $container}}
|
|
||||||
FrameDeny = {{getFrameDenyHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasCustomFrameOptionsValueHeaders $container}}
|
|
||||||
CustomFrameOptionsValue = "{{getCustomFrameOptionsValueHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasContentTypeNosniffHeaders $container}}
|
|
||||||
ContentTypeNosniff = {{getContentTypeNosniffHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasBrowserXSSFilterHeaders $container}}
|
|
||||||
BrowserXSSFilter = {{getBrowserXSSFilterHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasContentSecurityPolicyHeaders $container}}
|
|
||||||
ContentSecurityPolicy = "{{getContentSecurityPolicyHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasPublicKeyHeaders $container}}
|
|
||||||
PublicKey = "{{getPublicKeyHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasReferrerPolicyHeaders $container}}
|
|
||||||
ReferrerPolicy = "{{getReferrerPolicyHeaders $container}}"
|
|
||||||
{{end}}
|
|
||||||
{{if hasIsDevelopmentHeaders $container}}
|
|
||||||
IsDevelopment = {{getIsDevelopmentHeaders $container}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasAllowedHostsHeaders $container}}
|
|
||||||
AllowedHosts = [{{range getAllowedHostsHeaders $container}}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
{{if hasHostsProxyHeaders $container}}
|
|
||||||
HostsProxyHeaders = [{{range getHostsProxyHeaders $container}}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
{{if hasRequestHeaders $container}}
|
|
||||||
[frontends."frontend-{{$frontend}}".headers.customrequestheaders]
|
|
||||||
{{range $k, $v := getRequestHeaders $container}}
|
|
||||||
{{$k}} = "{{$v}}"
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasResponseHeaders $container}}
|
|
||||||
[frontends."frontend-{{$frontend}}".headers.customresponseheaders]
|
|
||||||
{{range $k, $v := getResponseHeaders $container}}
|
|
||||||
{{$k}} = "{{$v}}"
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
{{if hasSSLProxyHeaders $container}}
|
|
||||||
[frontends."frontend-{{$frontend}}".headers.SSLProxyHeaders]
|
|
||||||
{{range $k, $v := getSSLProxyHeaders $container}}
|
|
||||||
{{$k}} = "{{$v}}"
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends."frontend-{{$frontend}}".routes."route-frontend-{{$frontend}}"]
|
|
||||||
rule = "{{getFrontendRule $container}}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{end}}
|
|
|
@ -1,44 +0,0 @@
|
||||||
[backends]
|
|
||||||
{{range $serviceName, $instances := .Services }}
|
|
||||||
[backends."backend-{{ $serviceName }}".loadBalancer]
|
|
||||||
method = "{{ getLoadBalancerMethod $instances }}"
|
|
||||||
sticky = {{ getLoadBalancerSticky $instances }}
|
|
||||||
|
|
||||||
{{if hasStickinessLabel $instances }}
|
|
||||||
[backends."backend-{{ $serviceName }}".loadBalancer.stickiness]
|
|
||||||
cookieName = "{{ getStickinessCookieName $instances }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{ if hasHealthCheckLabels $instances }}
|
|
||||||
[backends."backend-{{ $serviceName }}".healthCheck]
|
|
||||||
path = "{{ getHealthCheckPath $instances }}"
|
|
||||||
interval = "{{ getHealthCheckInterval $instances }}"
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range $index, $i := $instances }}
|
|
||||||
[backends."backend-{{ $serviceName }}".servers."server-{{ $i.Name }}{{ $i.ID }}"]
|
|
||||||
url = "{{ getProtocol $i }}://{{ getHost $i }}:{{ getPort $i }}"
|
|
||||||
weight = {{ getWeight $i }}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends]
|
|
||||||
{{range $serviceName, $instances := .Services}}
|
|
||||||
{{range filterFrontends $instances }}
|
|
||||||
[frontends."frontend-{{ $serviceName }}"]
|
|
||||||
backend = "backend-{{ $serviceName }}"
|
|
||||||
passHostHeader = {{ getPassHostHeader . }}
|
|
||||||
priority = {{ getPriority . }}
|
|
||||||
|
|
||||||
entryPoints = [{{range getEntryPoints . }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth . }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
[frontends."frontend-{{ $serviceName }}".routes."route-frontend-{{ $serviceName }}"]
|
|
||||||
rule = "{{getFrontendRule .}}"
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
|
@ -1,68 +0,0 @@
|
||||||
{{$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}}
|
|
|
@ -1,27 +0,0 @@
|
||||||
{{$apps := .Applications}}
|
|
||||||
|
|
||||||
[backends]
|
|
||||||
{{range .Tasks}}
|
|
||||||
|
|
||||||
[backends."backend-{{ getBackend . $apps }}".servers."server-{{ getID . }}"]
|
|
||||||
url = "{{ getProtocol . $apps }}://{{ getHost . }}:{{ getPort . $apps }}"
|
|
||||||
weight = {{ getWeight . $apps }}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
[frontends]
|
|
||||||
{{range .Applications}}
|
|
||||||
|
|
||||||
[frontends."frontend-{{getFrontEndName . }}"]
|
|
||||||
backend = "backend-{{ getFrontendBackend . }}"
|
|
||||||
passHostHeader = {{ getPassHostHeader . }}
|
|
||||||
priority = {{ getPriority . }}
|
|
||||||
|
|
||||||
entryPoints = [{{range getEntryPoints . }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
|
|
||||||
[frontends."frontend-{{ getFrontEndName . }}".routes."route-host-{{ getFrontEndName . }}"]
|
|
||||||
rule = "{{ getFrontendRule . }}"
|
|
||||||
|
|
||||||
{{end}}
|
|
|
@ -1,58 +0,0 @@
|
||||||
{{$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}}
|
|
Loading…
Reference in a new issue