Segment labels: Docker
This commit is contained in:
parent
c762b9bb2e
commit
4802484729
28 changed files with 4095 additions and 2464 deletions
2
Makefile
2
Makefile
|
@ -73,7 +73,7 @@ test-integration: build ## run the integration tests
|
|||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary test-integration
|
||||
TEST_HOST=1 ./script/make.sh test-integration
|
||||
|
||||
validate: build ## validate gofmt, golint and go vet
|
||||
validate: build ## validate code, vendor and autogen
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-gofmt validate-govet validate-golint validate-misspell validate-vendor validate-autogen
|
||||
|
||||
build: dist
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Code generated by go-bindata.
|
||||
// sources:
|
||||
// templates/consul_catalog.tmpl
|
||||
// templates/docker-v1.tmpl
|
||||
// templates/docker.tmpl
|
||||
// templates/ecs.tmpl
|
||||
// templates/eureka.tmpl
|
||||
|
@ -244,17 +245,227 @@ func templatesConsul_catalogTmpl() (*asset, error) {
|
|||
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}}
|
||||
[backends]
|
||||
{{range $backendName, $backend := .Backends}}
|
||||
{{range $backendName, $servers := .Servers}}
|
||||
{{ $backend := index $servers 0 }}
|
||||
|
||||
{{ $circuitBreaker := getCircuitBreaker $backend }}
|
||||
{{ $circuitBreaker := getCircuitBreaker $backend.SegmentLabels }}
|
||||
{{if $circuitBreaker }}
|
||||
[backends."backend-{{ $backendName }}".circuitBreaker]
|
||||
expression = "{{ $circuitBreaker.Expression }}"
|
||||
{{end}}
|
||||
|
||||
{{ $loadBalancer := getLoadBalancer $backend }}
|
||||
{{ $loadBalancer := getLoadBalancer $backend.SegmentLabels }}
|
||||
{{if $loadBalancer }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer]
|
||||
method = "{{ $loadBalancer.Method }}"
|
||||
|
@ -265,14 +476,14 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $maxConn := getMaxConn $backend }}
|
||||
{{ $maxConn := getMaxConn $backend.SegmentLabels }}
|
||||
{{if $maxConn }}
|
||||
[backends."backend-{{ $backendName }}".maxConn]
|
||||
extractorFunc = "{{ $maxConn.ExtractorFunc }}"
|
||||
amount = {{ $maxConn.Amount }}
|
||||
{{end}}
|
||||
|
||||
{{ $healthCheck := getHealthCheck $backend }}
|
||||
{{ $healthCheck := getHealthCheck $backend.SegmentLabels }}
|
||||
{{if $healthCheck }}
|
||||
[backends."backend-{{ $backendName }}".healthCheck]
|
||||
path = "{{ $healthCheck.Path }}"
|
||||
|
@ -280,7 +491,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
|||
interval = "{{ $healthCheck.Interval }}"
|
||||
{{end}}
|
||||
|
||||
{{ $buffering := getBuffering $backend }}
|
||||
{{ $buffering := getBuffering $backend.SegmentLabels }}
|
||||
{{if $buffering }}
|
||||
[backends."backend-{{ $backendName }}".buffering]
|
||||
maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }}
|
||||
|
@ -290,173 +501,40 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
|||
retryExpression = "{{ $buffering.RetryExpression }}"
|
||||
{{end}}
|
||||
|
||||
{{ $servers := index $backendServers $backendName }}
|
||||
{{range $serverName, $server := $servers }}
|
||||
{{if hasServices $server }}
|
||||
{{ $services := getServiceNames $server }}
|
||||
{{range $serviceIndex, $serviceName := $services }}
|
||||
[backends."backend-{{ getServiceBackendName $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}}
|
||||
{{range $serverName, $server := getServers $servers }}
|
||||
[backends."backend-{{ $backendName }}".servers."{{ $serverName }}"]
|
||||
url = "{{ $server.URL }}"
|
||||
weight = {{ $server.Weight }}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
[frontends]
|
||||
{{range $frontendName, $containers := .Frontends }}
|
||||
{{$container := index $containers 0}}
|
||||
|
||||
{{if hasServices $container }}
|
||||
{{ $services := getServiceNames $container }}
|
||||
|
||||
{{range $serviceIndex, $serviceName := $services }}
|
||||
{{ $ServiceFrontendName := getServiceBackendName $container $serviceName }}
|
||||
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}"]
|
||||
backend = "backend-{{ $ServiceFrontendName }}"
|
||||
priority = {{ getServicePriority $container $serviceName }}
|
||||
passHostHeader = {{ getServicePassHostHeader $container $serviceName }}
|
||||
passTLSCert = {{ getServicePassTLSCert $container $serviceName }}
|
||||
|
||||
entryPoints = [{{range getServiceEntryPoints $container $serviceName }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $whitelistSourceRange := getServiceWhitelistSourceRange $container $serviceName }}
|
||||
{{if $whitelistSourceRange }}
|
||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
basicAuth = [{{range getServiceBasicAuth $container $serviceName }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $redirect := getServiceRedirect $container $serviceName }}
|
||||
{{if $redirect }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".redirect]
|
||||
entryPoint = "{{ $redirect.EntryPoint }}"
|
||||
regex = "{{ $redirect.Regex }}"
|
||||
replacement = "{{ $redirect.Replacement }}"
|
||||
permanent = {{ $redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{ $errorPages := getServiceErrorPages $container $serviceName }}
|
||||
{{if $errorPages }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".errors]
|
||||
{{ range $pageName, $page := $errorPages }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".errors."{{ $pageName }}"]
|
||||
status = [{{range $page.Status }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
backend = "{{ $page.Backend }}"
|
||||
query = "{{ $page.Query }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $rateLimit := getServiceRateLimit $container $serviceName }}
|
||||
{{if $rateLimit }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".rateLimit]
|
||||
extractorFunc = "{{ $rateLimit.ExtractorFunc }}"
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".rateLimit.rateSet]
|
||||
{{range $limitName, $limit := $rateLimit.RateSet }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".rateLimit.rateSet."{{ $limitName }}"]
|
||||
period = "{{ $limit.Period }}"
|
||||
average = {{ $limit.Average }}
|
||||
burst = {{ $limit.Burst }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $headers := getServiceHeaders $container $serviceName }}
|
||||
{{if $headers }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".headers]
|
||||
SSLRedirect = {{ $headers.SSLRedirect }}
|
||||
SSLTemporaryRedirect = {{ $headers.SSLTemporaryRedirect }}
|
||||
SSLHost = "{{ $headers.SSLHost }}"
|
||||
STSSeconds = {{ $headers.STSSeconds }}
|
||||
STSIncludeSubdomains = {{ $headers.STSIncludeSubdomains }}
|
||||
STSPreload = {{ $headers.STSPreload }}
|
||||
ForceSTSHeader = {{ $headers.ForceSTSHeader }}
|
||||
FrameDeny = {{ $headers.FrameDeny }}
|
||||
CustomFrameOptionsValue = "{{ $headers.CustomFrameOptionsValue }}"
|
||||
ContentTypeNosniff = {{ $headers.ContentTypeNosniff }}
|
||||
BrowserXSSFilter = {{ $headers.BrowserXSSFilter }}
|
||||
CustomBrowserXSSValue = "{{ $headers.CustomBrowserXSSValue }}"
|
||||
ContentSecurityPolicy = "{{ $headers.ContentSecurityPolicy }}"
|
||||
PublicKey = "{{ $headers.PublicKey }}"
|
||||
ReferrerPolicy = "{{ $headers.ReferrerPolicy }}"
|
||||
IsDevelopment = {{ $headers.IsDevelopment }}
|
||||
|
||||
{{if $headers.AllowedHosts }}
|
||||
AllowedHosts = [{{range $headers.AllowedHosts }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $headers.HostsProxyHeaders }}
|
||||
HostsProxyHeaders = [{{range $headers.HostsProxyHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $headers.CustomRequestHeaders }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".headers.customRequestHeaders]
|
||||
{{range $k, $v := $headers.CustomRequestHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $headers.CustomResponseHeaders }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".headers.customResponseHeaders]
|
||||
{{range $k, $v := $headers.CustomResponseHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $headers.SSLProxyHeaders }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".headers.SSLProxyHeaders]
|
||||
{{range $k, $v := $headers.SSLProxyHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".routes."service-{{ $serviceName | replace "/" "" | replace "." "-" }}"]
|
||||
rule = "{{ getServiceFrontendRule $container $serviceName }}"
|
||||
|
||||
{{end}} ## end range services
|
||||
|
||||
{{else}}
|
||||
{{ $container := index $containers 0 }}
|
||||
|
||||
[frontends."frontend-{{ $frontendName }}"]
|
||||
backend = "backend-{{ getBackendName $container }}"
|
||||
priority = {{ getPriority $container }}
|
||||
passHostHeader = {{ getPassHostHeader $container }}
|
||||
passTLSCert = {{ getPassTLSCert $container }}
|
||||
priority = {{ getPriority $container.SegmentLabels }}
|
||||
passHostHeader = {{ getPassHostHeader $container.SegmentLabels }}
|
||||
passTLSCert = {{ getPassTLSCert $container.SegmentLabels }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $container }}
|
||||
entryPoints = [{{range getEntryPoints $container.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $whitelistSourceRange := getWhitelistSourceRange $container}}
|
||||
{{ $whitelistSourceRange := getWhitelistSourceRange $container.SegmentLabels }}
|
||||
{{if $whitelistSourceRange }}
|
||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
basicAuth = [{{range getBasicAuth $container }}
|
||||
basicAuth = [{{range getBasicAuth $container.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $redirect := getRedirect $container }}
|
||||
{{ $redirect := getRedirect $container.SegmentLabels }}
|
||||
{{if $redirect }}
|
||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||
entryPoint = "{{ $redirect.EntryPoint }}"
|
||||
|
@ -465,7 +543,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
|||
permanent = {{ $redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{ $errorPages := getErrorPages $container }}
|
||||
{{ $errorPages := getErrorPages $container.SegmentLabels }}
|
||||
{{if $errorPages }}
|
||||
[frontends."frontend-{{ $frontendName }}".errors]
|
||||
{{range $pageName, $page := $errorPages }}
|
||||
|
@ -478,12 +556,12 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $rateLimit := getRateLimit $container }}
|
||||
{{ $rateLimit := getRateLimit $container.SegmentLabels }}
|
||||
{{if $rateLimit }}
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit]
|
||||
extractorFunc = "{{ $rateLimit.ExtractorFunc }}"
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit.rateSet]
|
||||
{{range $limitName, $limit := $rateLimit.RateSet }}
|
||||
{{ range $limitName, $limit := $rateLimit.RateSet }}
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit.rateSet."{{ $limitName }}"]
|
||||
period = "{{ $limit.Period }}"
|
||||
average = {{ $limit.Average }}
|
||||
|
@ -491,7 +569,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $headers := getHeaders $container }}
|
||||
{{ $headers := getHeaders $container.SegmentLabels }}
|
||||
{{if $headers }}
|
||||
[frontends."frontend-{{ $frontendName }}".headers]
|
||||
SSLRedirect = {{ $headers.SSLRedirect }}
|
||||
|
@ -505,8 +583,8 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
|||
CustomFrameOptionsValue = "{{ $headers.CustomFrameOptionsValue }}"
|
||||
ContentTypeNosniff = {{ $headers.ContentTypeNosniff }}
|
||||
BrowserXSSFilter = {{ $headers.BrowserXSSFilter }}
|
||||
CustomBrowserXSSValue = "{{ $headers.CustomBrowserXSSValue }}"
|
||||
ContentSecurityPolicy = "{{ $headers.ContentSecurityPolicy }}"
|
||||
CustomBrowserXSSValue = "{{ $headers.CustomBrowserXSSValue }}"
|
||||
PublicKey = "{{ $headers.PublicKey }}"
|
||||
ReferrerPolicy = "{{ $headers.ReferrerPolicy }}"
|
||||
IsDevelopment = {{ $headers.IsDevelopment }}
|
||||
|
@ -543,13 +621,12 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
|||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
[frontends."frontend-{{ $frontendName }}".routes."route-frontend-{{ $frontendName }}"]
|
||||
rule = "{{ getFrontendRule $container }}"
|
||||
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
|
@ -1833,6 +1910,7 @@ func AssetNames() []string {
|
|||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"templates/consul_catalog.tmpl": templatesConsul_catalogTmpl,
|
||||
"templates/docker-v1.tmpl": templatesDockerV1Tmpl,
|
||||
"templates/docker.tmpl": templatesDockerTmpl,
|
||||
"templates/ecs.tmpl": templatesEcsTmpl,
|
||||
"templates/eureka.tmpl": templatesEurekaTmpl,
|
||||
|
@ -1887,6 +1965,7 @@ type bintree struct {
|
|||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"templates": {nil, map[string]*bintree{
|
||||
"consul_catalog.tmpl": {templatesConsul_catalogTmpl, map[string]*bintree{}},
|
||||
"docker-v1.tmpl": {templatesDockerV1Tmpl, map[string]*bintree{}},
|
||||
"docker.tmpl": {templatesDockerTmpl, map[string]*bintree{}},
|
||||
"ecs.tmpl": {templatesEcsTmpl, map[string]*bintree{}},
|
||||
"eureka.tmpl": {templatesEurekaTmpl, map[string]*bintree{}},
|
||||
|
|
|
@ -39,7 +39,7 @@ type TraefikConfiguration struct {
|
|||
|
||||
// NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values
|
||||
func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
//default Docker
|
||||
// default Docker
|
||||
var defaultDocker docker.Provider
|
||||
defaultDocker.Watch = true
|
||||
defaultDocker.ExposedByDefault = true
|
||||
|
@ -49,7 +49,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
|||
// default File
|
||||
var defaultFile file.Provider
|
||||
defaultFile.Watch = true
|
||||
defaultFile.Filename = "" //needs equivalent to viper.ConfigFileUsed()
|
||||
defaultFile.Filename = "" // needs equivalent to viper.ConfigFileUsed()
|
||||
|
||||
// default Rest
|
||||
var defaultRest rest.Provider
|
||||
|
@ -113,21 +113,21 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
|||
defaultEtcd.Prefix = "/traefik"
|
||||
defaultEtcd.Constraints = types.Constraints{}
|
||||
|
||||
//default Zookeeper
|
||||
// default Zookeeper
|
||||
var defaultZookeeper zk.Provider
|
||||
defaultZookeeper.Watch = true
|
||||
defaultZookeeper.Endpoint = "127.0.0.1:2181"
|
||||
defaultZookeeper.Prefix = "traefik"
|
||||
defaultZookeeper.Constraints = types.Constraints{}
|
||||
|
||||
//default Boltdb
|
||||
// default Boltdb
|
||||
var defaultBoltDb boltdb.Provider
|
||||
defaultBoltDb.Watch = true
|
||||
defaultBoltDb.Endpoint = "127.0.0.1:4001"
|
||||
defaultBoltDb.Prefix = "/traefik"
|
||||
defaultBoltDb.Constraints = types.Constraints{}
|
||||
|
||||
//default Kubernetes
|
||||
// default Kubernetes
|
||||
var defaultKubernetes kubernetes.Provider
|
||||
defaultKubernetes.Watch = true
|
||||
defaultKubernetes.Constraints = types.Constraints{}
|
||||
|
@ -142,7 +142,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
|||
defaultMesos.ZkDetectionTimeout = 30
|
||||
defaultMesos.StateTimeoutSecond = 30
|
||||
|
||||
//default ECS
|
||||
// default ECS
|
||||
var defaultECS ecs.Provider
|
||||
defaultECS.Watch = true
|
||||
defaultECS.ExposedByDefault = true
|
||||
|
@ -151,7 +151,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
|||
defaultECS.RefreshSeconds = 15
|
||||
defaultECS.Constraints = types.Constraints{}
|
||||
|
||||
//default Rancher
|
||||
// default Rancher
|
||||
var defaultRancher rancher.Provider
|
||||
defaultRancher.Watch = true
|
||||
defaultRancher.ExposedByDefault = true
|
||||
|
|
|
@ -201,6 +201,14 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
|||
gc.LifeCycle.GraceTimeOut = gc.GraceTimeOut
|
||||
}
|
||||
|
||||
if gc.Docker != nil {
|
||||
if len(gc.Docker.Filename) != 0 && gc.Docker.TemplateVersion != 2 {
|
||||
gc.Docker.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Docker.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Eureka != nil {
|
||||
if gc.Eureka.Delay != 0 {
|
||||
log.Warn("Delay has been deprecated -- please use RefreshSeconds")
|
||||
|
|
|
@ -39,6 +39,15 @@ watch = true
|
|||
#
|
||||
# filename = "docker.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = "2"
|
||||
|
||||
# Expose containers by default in Traefik.
|
||||
# If set to false, containers that don't have `traefik.enable=true` will be ignored.
|
||||
#
|
||||
|
@ -123,6 +132,15 @@ swarmmode = true
|
|||
#
|
||||
# filename = "docker.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = "2"
|
||||
|
||||
# Expose services by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
|
@ -254,70 +272,74 @@ Or if your service references external network use it's name instead.
|
|||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
|
||||
### On Service
|
||||
### On containers with Multiple Ports (segment labels)
|
||||
|
||||
Services labels can be used for overriding default behaviour
|
||||
Segment labels are used to define routes to a container exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by a container.
|
||||
You can define as many segments as ports exposed in a container.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
|
||||
| `traefik.<service-name>.protocol` | Overrides `traefik.protocol`. |
|
||||
| `traefik.<service-name>.weight` | Assign this service weight. Overrides `traefik.weight`. |
|
||||
| `traefik.<service-name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
|
||||
| `traefik.<service-name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
|
||||
| `traefik.<service-name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
|
||||
| `traefik.<service-name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<service-name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<service-name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<service-name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
|
||||
| `traefik.<service-name>.frontend.passTLSCert` | Overrides `traefik.frontend.passTLSCert`. |
|
||||
| `traefik.<service-name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
||||
| `traefik.<service-name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<service-name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<service-name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<service-name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<service-name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
|
||||
| `traefik.<service-name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.<service-name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.<service-name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.<service-name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
||||
| `traefik.<service-name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
|
||||
| `traefik.<segment_name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the segment labels could be used. |
|
||||
| `traefik.<segment_name>.protocol` | Overrides `traefik.protocol`. |
|
||||
| `traefik.<segment_name>.weight` | Assign this segment weight. Overrides `traefik.weight`. |
|
||||
| `traefik.<segment_name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
|
||||
| `traefik.<segment_name>.frontend.backend=BACKEND` | Assign this segment frontend to `BACKEND`. Default is to assign to the segment backend. |
|
||||
| `traefik.<segment_name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert` | Overrides `traefik.frontend.passTLSCert`. |
|
||||
| `traefik.<segment_name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.<segment_name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
||||
| `traefik.<segment_name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<service-name>.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.<service-name>.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.<service-name>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.<service-name>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.<service-name>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.<service-name>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<service-name>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.<service-name>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.<service-name>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
| `traefik.<service-name>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.<service-name>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.<service-name>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.<service-name>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.<service-name>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.<service-name>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.<service-name>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.<service-name>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.<service-name>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.<service-name>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
|
||||
!!! note
|
||||
If a label is defined both as a `container label` and a `service label` (for example `traefik.<service-name>.port=PORT` and `traefik.port=PORT` ), the `service label` is used to defined the `<service-name>` property (`port` in the example).
|
||||
If a label is defined both as a `container label` and a `segment label` (for example `traefik.<segment_name>.port=PORT` and `traefik.port=PORT` ), the `segment label` is used to defined the `<segment_name>` property (`port` in the example).
|
||||
|
||||
It's possible to mix `container labels` and `service labels`, in this case `container labels` are used as default value for missing `service labels` but no frontends are going to be created with the `container labels`.
|
||||
It's possible to mix `container labels` and `segment labels`, in this case `container labels` are used as default value for missing `segment labels` but no frontends are going to be created with the `container labels`.
|
||||
|
||||
More details in this [example](/user-guide/docker-and-lets-encrypt/#labels).
|
||||
|
||||
|
|
|
@ -150,15 +150,15 @@ domain = "marathon.localhost"
|
|||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
|
||||
## Labels: overriding default behaviour
|
||||
## Labels: overriding default behavior
|
||||
|
||||
Marathon labels may be used to dynamically change the routing and forwarding behaviour.
|
||||
Marathon labels may be used to dynamically change the routing and forwarding behavior.
|
||||
|
||||
They may be specified on one of two levels: Application or service.
|
||||
|
||||
### Application Level
|
||||
|
||||
The following labels can be defined on Marathon applications. They adjust the behaviour for the entire application.
|
||||
The following labels can be defined on Marathon applications. They adjust the behavior for the entire application.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
@ -234,63 +234,65 @@ The following labels can be defined on Marathon applications. They adjust the be
|
|||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
|
||||
### Service Level
|
||||
### Applications with Multiple Ports (segment labels)
|
||||
|
||||
For applications that expose multiple ports, specific labels can be used to extract one frontend/backend configuration pair per port. Each such pair is called a _service_. The (freely choosable) name of the service is an integral part of the service label name.
|
||||
Segment labels are used to define routes to an application exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by an application.
|
||||
You can define as many segments as ports exposed in an application.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.portIndex=1` | Create a service binding with frontend/backend using this port index. Overrides `traefik.portIndex`. |
|
||||
| `traefik.<service-name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
|
||||
| `traefik.<service-name>.protocol=http` | Overrides `traefik.protocol`. |
|
||||
| `traefik.<service-name>.weight=10` | Assign this service weight. Overrides `traefik.weight`. |
|
||||
| `traefik.<service-name>.frontend.auth.basic=EXPR` | Sets a Basic Auth for that frontend |
|
||||
| `traefik.<service-name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
|
||||
| `traefik.<service-name>.frontend.entryPoints=https` | Overrides `traefik.frontend.entrypoints` |
|
||||
| `traefik.<service-name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<service-name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<service-name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<service-name>.frontend.passHostHeader=true` | Overrides `traefik.frontend.passHostHeader`. |
|
||||
| `traefik.<service-name>.frontend.passTLSCert=true` | Overrides `traefik.frontend.passTLSCert`. |
|
||||
| `traefik.<service-name>.frontend.priority=10` | Overrides `traefik.frontend.priority`. |
|
||||
| `traefik.<service-name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<service-name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<service-name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<service-name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<service-name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
|
||||
| `traefik.<service-name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.<service-name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.<service-name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.<service-name>.frontend.rule=EXP` | Overrides `traefik.frontend.rule`. Default: `{service_name}.{sub_domain}.{domain}` |
|
||||
| `traefik.<service-name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
|
||||
| `traefik.<segment_name>.portIndex=1` | Create a service binding with frontend/backend using this port index. Overrides `traefik.portIndex`. |
|
||||
| `traefik.<segment_name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
|
||||
| `traefik.<segment_name>.protocol=http` | Overrides `traefik.protocol`. |
|
||||
| `traefik.<segment_name>.weight=10` | Assign this service weight. Overrides `traefik.weight`. |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Sets a Basic Auth for that frontend |
|
||||
| `traefik.<segment_name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Overrides `traefik.frontend.entrypoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Overrides `traefik.frontend.passHostHeader`. |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Overrides `traefik.frontend.passTLSCert`. |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Overrides `traefik.frontend.priority`. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Overrides `traefik.frontend.rule`. Default: `{service_name}.{sub_domain}.{domain}` |
|
||||
| `traefik.<segment_name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<service-name>.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.<service-name>.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.<service-name>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.<service-name>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.<service-name>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.<service-name>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<service-name>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.<service-name>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.<service-name>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
| `traefik.<service-name>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.<service-name>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.<service-name>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.<service-name>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.<service-name>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.<service-name>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.<service-name>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.<service-name>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.<service-name>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.<service-name>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
|
|
|
@ -1,56 +1,45 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.Configuration {
|
||||
var DockerFuncMap = template.FuncMap{
|
||||
"getDomain": getFuncStringLabel(label.TraefikDomain, p.Domain),
|
||||
const (
|
||||
labelDockerNetwork = "traefik.docker.network"
|
||||
labelBackendLoadBalancerSwarm = "traefik.backend.loadbalancer.swarm"
|
||||
labelDockerComposeProject = "com.docker.compose.project"
|
||||
labelDockerComposeService = "com.docker.compose.service"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfigurationV2(containersInspected []dockerData) *types.Configuration {
|
||||
dockerFuncMap := template.FuncMap{
|
||||
"getLabelValue": label.GetStringValue,
|
||||
"getSubDomain": getSubDomain,
|
||||
"isBackendLBSwarm": isBackendLBSwarm, // FIXME dead ?
|
||||
"isBackendLBSwarm": isBackendLBSwarm,
|
||||
"getDomain": getFuncStringLabel(label.TraefikDomain, p.Domain),
|
||||
|
||||
// Backend functions
|
||||
"getIPAddress": p.getIPAddress,
|
||||
"getPort": getPort,
|
||||
"getWeight": getFuncIntLabel(label.TraefikWeight, label.DefaultWeightInt),
|
||||
"getProtocol": getFuncStringLabel(label.TraefikProtocol, label.DefaultProtocol),
|
||||
"getServers": p.getServers,
|
||||
"getMaxConn": getMaxConn,
|
||||
"getHealthCheck": getHealthCheck,
|
||||
"getBuffering": getBuffering,
|
||||
"getCircuitBreaker": getCircuitBreaker,
|
||||
"getLoadBalancer": getLoadBalancer,
|
||||
|
||||
// TODO Deprecated [breaking]
|
||||
"hasCircuitBreakerLabel": hasFunc(label.TraefikBackendCircuitBreakerExpression),
|
||||
// TODO Deprecated [breaking]
|
||||
"getCircuitBreakerExpression": getFuncStringLabel(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
|
||||
// TODO Deprecated [breaking]
|
||||
"hasLoadBalancerLabel": hasLoadBalancerLabel,
|
||||
// TODO Deprecated [breaking]
|
||||
"getLoadBalancerMethod": getFuncStringLabel(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod),
|
||||
// TODO Deprecated [breaking]
|
||||
"hasMaxConnLabels": hasMaxConnLabels,
|
||||
// TODO Deprecated [breaking]
|
||||
"getMaxConnAmount": getFuncInt64Label(label.TraefikBackendMaxConnAmount, math.MaxInt64),
|
||||
// TODO Deprecated [breaking]
|
||||
"getMaxConnExtractorFunc": getFuncStringLabel(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc),
|
||||
// TODO Deprecated [breaking]
|
||||
"getSticky": getSticky,
|
||||
// TODO Deprecated [breaking]
|
||||
"hasStickinessLabel": hasFunc(label.TraefikBackendLoadBalancerStickiness),
|
||||
// TODO Deprecated [breaking]
|
||||
"getStickinessCookieName": getFuncStringLabel(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
||||
|
||||
// Frontend functions
|
||||
"getBackend": getBackendName, // TODO Deprecated [breaking] replaced by getBackendName
|
||||
"getBackendName": getBackendName,
|
||||
"getPriority": getFuncIntLabel(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"getPassHostHeader": getFuncBoolLabel(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
|
@ -59,72 +48,56 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
|||
"getBasicAuth": getFuncSliceStringLabel(label.TraefikFrontendAuthBasic),
|
||||
"getWhitelistSourceRange": getFuncSliceStringLabel(label.TraefikFrontendWhitelistSourceRange),
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
|
||||
"getRedirect": getRedirect,
|
||||
"getErrorPages": getErrorPages,
|
||||
"getRateLimit": getRateLimit,
|
||||
"getHeaders": getHeaders,
|
||||
|
||||
// Services
|
||||
"hasServices": hasServices,
|
||||
"getServiceNames": getServiceNames,
|
||||
"getServiceBackend": getServiceBackendName, // TODO Deprecated [breaking] replaced by getServiceBackendName
|
||||
"getServiceBackendName": getServiceBackendName,
|
||||
// Services - Backend server functions
|
||||
"getServicePort": getServicePort,
|
||||
"getServiceProtocol": getFuncServiceStringLabel(label.SuffixProtocol, label.DefaultProtocol),
|
||||
"getServiceWeight": getFuncServiceStringLabel(label.SuffixWeight, label.DefaultWeight),
|
||||
// Services - Frontend functions
|
||||
"getServiceEntryPoints": getFuncServiceSliceStringLabel(label.SuffixFrontendEntryPoints),
|
||||
"getServiceWhitelistSourceRange": getFuncServiceSliceStringLabel(label.SuffixFrontendWhitelistSourceRange),
|
||||
"getServiceBasicAuth": getFuncServiceSliceStringLabel(label.SuffixFrontendAuthBasic),
|
||||
"getServiceFrontendRule": p.getServiceFrontendRule,
|
||||
"getServicePassHostHeader": getFuncServiceBoolLabel(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"getServicePassTLSCert": getFuncServiceBoolLabel(label.SuffixFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getServicePriority": getFuncServiceIntLabel(label.SuffixFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
|
||||
"getServiceRedirect": getServiceRedirect,
|
||||
"getServiceErrorPages": getServiceErrorPages,
|
||||
"getServiceRateLimit": getServiceRateLimit,
|
||||
"getServiceHeaders": getServiceHeaders,
|
||||
}
|
||||
|
||||
// filter containers
|
||||
filteredContainers := fun.Filter(func(container dockerData) bool {
|
||||
return p.containerFilter(container)
|
||||
}, containersInspected).([]dockerData)
|
||||
filteredContainers := fun.Filter(p.containerFilter, containersInspected).([]dockerData)
|
||||
|
||||
frontends := map[string][]dockerData{}
|
||||
backends := map[string]dockerData{}
|
||||
servers := map[string][]dockerData{}
|
||||
|
||||
serviceNames := make(map[string]struct{})
|
||||
|
||||
for idx, container := range filteredContainers {
|
||||
if _, exists := serviceNames[container.ServiceName]; !exists {
|
||||
segmentProperties := label.ExtractTraefikLabels(container.Labels)
|
||||
for segmentName, labels := range segmentProperties {
|
||||
container.SegmentLabels = labels
|
||||
container.SegmentName = segmentName
|
||||
|
||||
// Frontends
|
||||
if _, exists := serviceNames[container.ServiceName+segmentName]; !exists {
|
||||
frontendName := p.getFrontendName(container, idx)
|
||||
frontends[frontendName] = append(frontends[frontendName], container)
|
||||
if len(container.ServiceName) > 0 {
|
||||
serviceNames[container.ServiceName] = struct{}{}
|
||||
if len(container.ServiceName+segmentName) > 0 {
|
||||
serviceNames[container.ServiceName+segmentName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Backends
|
||||
backendName := getBackendName(container)
|
||||
backends[backendName] = container
|
||||
|
||||
// Servers
|
||||
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.tmpl", DockerFuncMap, templateObjects)
|
||||
configuration, err := p.GetConfiguration("templates/docker.tmpl", dockerFuncMap, templateObjects)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
@ -132,29 +105,33 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
|||
return configuration
|
||||
}
|
||||
|
||||
func (p Provider) containerFilter(container dockerData) bool {
|
||||
func (p *Provider) containerFilter(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 hasServices(container) {
|
||||
portLabel = "traefik.<serviceName>.port or " + portLabel + "s"
|
||||
err = checkServiceLabelPort(container)
|
||||
} else {
|
||||
_, err = strconv.Atoi(container.Labels[label.TraefikPort])
|
||||
segmentProperties := label.ExtractTraefikLabels(container.Labels)
|
||||
|
||||
var errPort error
|
||||
for segmentName, labels := range segmentProperties {
|
||||
errPort = checkSegmentPort(labels, segmentName)
|
||||
|
||||
if len(p.getFrontendRule(container)) == 0 {
|
||||
log.Debugf("Filtering container with empty frontend rule %s %s", container.Name, segmentName)
|
||||
return false
|
||||
}
|
||||
if len(container.NetworkSettings.Ports) == 0 && err != nil {
|
||||
log.Debugf("Filtering container without port and no %s %s : %s", portLabel, container.Name, err.Error())
|
||||
}
|
||||
|
||||
if len(container.NetworkSettings.Ports) == 0 && errPort != nil {
|
||||
log.Debugf("Filtering container without port, %s: %v", container.Name, errPort)
|
||||
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())
|
||||
log.Debugf("Container %s pruned by %q constraint", container.Name, failingConstraint.String())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -164,10 +141,368 @@ func (p Provider) containerFilter(container dockerData) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if len(p.getFrontendRule(container)) == 0 {
|
||||
log.Debugf("Filtering container with empty frontend rule %s", container.Name)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func checkSegmentPort(labels map[string]string, segmentName string) error {
|
||||
if port, ok := labels[label.TraefikPort]; ok {
|
||||
_, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid port value %q for the segment %q: %v", port, segmentName, err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("port label is missing, please use %s as default value or define port label for all segments ('traefik.<segment_name>.port')", label.TraefikPort)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) getFrontendName(container dockerData, idx int) string {
|
||||
var name string
|
||||
if len(container.SegmentName) > 0 {
|
||||
name = getBackendName(container)
|
||||
} else {
|
||||
name = p.getFrontendRule(container) + "-" + strconv.Itoa(idx)
|
||||
}
|
||||
|
||||
return provider.Normalize(name)
|
||||
}
|
||||
|
||||
func (p *Provider) getFrontendRule(container dockerData) string {
|
||||
if value := label.GetStringValue(container.SegmentLabels, label.TraefikFrontendRule, ""); len(value) != 0 {
|
||||
return value
|
||||
}
|
||||
|
||||
if values, err := label.GetStringMultipleStrict(container.Labels, labelDockerComposeProject, labelDockerComposeService); err == nil {
|
||||
return "Host:" + getSubDomain(values[labelDockerComposeService]+"."+values[labelDockerComposeProject]) + "." + p.Domain
|
||||
}
|
||||
|
||||
if len(p.Domain) > 0 {
|
||||
return "Host:" + getSubDomain(container.ServiceName) + "." + p.Domain
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p Provider) getIPAddress(container dockerData) string {
|
||||
|
||||
if value := label.GetStringValue(container.Labels, labelDockerNetwork, ""); 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 string(netPort) == port+"/TCP" || string(netPort) == port+"/UDP" {
|
||||
for _, p := range portBindings {
|
||||
return p.HostIP
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, network := range container.NetworkSettings.Networks {
|
||||
return network.Addr
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Escape beginning slash "/", convert all others to dash "-", and convert underscores "_" to dash "-"
|
||||
func getSubDomain(name string) string {
|
||||
return strings.Replace(strings.Replace(strings.TrimPrefix(name, "/"), "/", "-", -1), "_", "-", -1)
|
||||
}
|
||||
|
||||
func isBackendLBSwarm(container dockerData) bool {
|
||||
return label.GetBoolValue(container.Labels, labelBackendLoadBalancerSwarm, false)
|
||||
}
|
||||
|
||||
func getSegmentBackendName(container dockerData) string {
|
||||
if value := label.GetStringValue(container.SegmentLabels, label.TraefikFrontendBackend, ""); len(value) > 0 {
|
||||
return provider.Normalize(container.ServiceName + "-" + value)
|
||||
}
|
||||
|
||||
return provider.Normalize(container.ServiceName + "-" + getDefaultBackendName(container) + "-" + container.SegmentName)
|
||||
}
|
||||
|
||||
func getDefaultBackendName(container dockerData) string {
|
||||
if value := label.GetStringValue(container.SegmentLabels, 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)
|
||||
}
|
||||
|
||||
func getBackendName(container dockerData) string {
|
||||
if len(container.SegmentName) > 0 {
|
||||
return getSegmentBackendName(container)
|
||||
}
|
||||
|
||||
return getDefaultBackendName(container)
|
||||
}
|
||||
|
||||
func getRedirect(labels map[string]string) *types.Redirect {
|
||||
permanent := label.GetBoolValue(labels, label.TraefikFrontendRedirectPermanent, false)
|
||||
|
||||
if label.Has(labels, label.TraefikFrontendRedirectEntryPoint) {
|
||||
return &types.Redirect{
|
||||
EntryPoint: label.GetStringValue(labels, label.TraefikFrontendRedirectEntryPoint, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
if label.Has(labels, label.TraefikFrontendRedirectRegex) &&
|
||||
label.Has(labels, label.TraefikFrontendRedirectReplacement) {
|
||||
return &types.Redirect{
|
||||
Regex: label.GetStringValue(labels, label.TraefikFrontendRedirectRegex, ""),
|
||||
Replacement: label.GetStringValue(labels, label.TraefikFrontendRedirectReplacement, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getErrorPages(labels map[string]string) map[string]*types.ErrorPage {
|
||||
prefix := label.Prefix + label.BaseFrontendErrorPage
|
||||
return label.ParseErrorPages(labels, prefix, label.RegexpFrontendErrorPage)
|
||||
}
|
||||
|
||||
func getRateLimit(labels map[string]string) *types.RateLimit {
|
||||
extractorFunc := label.GetStringValue(labels, label.TraefikFrontendRateLimitExtractorFunc, "")
|
||||
if len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
prefix := label.Prefix + label.BaseFrontendRateLimit
|
||||
limits := label.ParseRateSets(labels, prefix, label.RegexpFrontendRateLimit)
|
||||
|
||||
return &types.RateLimit{
|
||||
ExtractorFunc: extractorFunc,
|
||||
RateSet: limits,
|
||||
}
|
||||
}
|
||||
|
||||
func getHeaders(labels map[string]string) *types.Headers {
|
||||
headers := &types.Headers{
|
||||
CustomRequestHeaders: label.GetMapValue(labels, label.TraefikFrontendRequestHeaders),
|
||||
CustomResponseHeaders: label.GetMapValue(labels, label.TraefikFrontendResponseHeaders),
|
||||
SSLProxyHeaders: label.GetMapValue(labels, label.TraefikFrontendSSLProxyHeaders),
|
||||
AllowedHosts: label.GetSliceStringValue(labels, label.TraefikFrontendAllowedHosts),
|
||||
HostsProxyHeaders: label.GetSliceStringValue(labels, label.TraefikFrontendHostsProxyHeaders),
|
||||
STSSeconds: label.GetInt64Value(labels, label.TraefikFrontendSTSSeconds, 0),
|
||||
SSLRedirect: label.GetBoolValue(labels, label.TraefikFrontendSSLRedirect, false),
|
||||
SSLTemporaryRedirect: label.GetBoolValue(labels, label.TraefikFrontendSSLTemporaryRedirect, false),
|
||||
STSIncludeSubdomains: label.GetBoolValue(labels, label.TraefikFrontendSTSIncludeSubdomains, false),
|
||||
STSPreload: label.GetBoolValue(labels, label.TraefikFrontendSTSPreload, false),
|
||||
ForceSTSHeader: label.GetBoolValue(labels, label.TraefikFrontendForceSTSHeader, false),
|
||||
FrameDeny: label.GetBoolValue(labels, label.TraefikFrontendFrameDeny, false),
|
||||
ContentTypeNosniff: label.GetBoolValue(labels, label.TraefikFrontendContentTypeNosniff, false),
|
||||
BrowserXSSFilter: label.GetBoolValue(labels, label.TraefikFrontendBrowserXSSFilter, false),
|
||||
IsDevelopment: label.GetBoolValue(labels, label.TraefikFrontendIsDevelopment, false),
|
||||
SSLHost: label.GetStringValue(labels, label.TraefikFrontendSSLHost, ""),
|
||||
CustomFrameOptionsValue: label.GetStringValue(labels, label.TraefikFrontendCustomFrameOptionsValue, ""),
|
||||
ContentSecurityPolicy: label.GetStringValue(labels, label.TraefikFrontendContentSecurityPolicy, ""),
|
||||
PublicKey: label.GetStringValue(labels, label.TraefikFrontendPublicKey, ""),
|
||||
ReferrerPolicy: label.GetStringValue(labels, label.TraefikFrontendReferrerPolicy, ""),
|
||||
CustomBrowserXSSValue: label.GetStringValue(labels, label.TraefikFrontendCustomBrowserXSSValue, ""),
|
||||
}
|
||||
|
||||
if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func getPort(container dockerData) string {
|
||||
if value := label.GetStringValue(container.SegmentLabels, label.TraefikPort, ""); len(value) != 0 {
|
||||
return value
|
||||
}
|
||||
|
||||
// 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 ""
|
||||
}
|
||||
|
||||
func getMaxConn(labels map[string]string) *types.MaxConn {
|
||||
amount := label.GetInt64Value(labels, label.TraefikBackendMaxConnAmount, math.MinInt64)
|
||||
extractorFunc := label.GetStringValue(labels, label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc)
|
||||
|
||||
if amount == math.MinInt64 || len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.MaxConn{
|
||||
Amount: amount,
|
||||
ExtractorFunc: extractorFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func getHealthCheck(labels map[string]string) *types.HealthCheck {
|
||||
path := label.GetStringValue(labels, label.TraefikBackendHealthCheckPath, "")
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
port := label.GetIntValue(labels, label.TraefikBackendHealthCheckPort, label.DefaultBackendHealthCheckPort)
|
||||
interval := label.GetStringValue(labels, label.TraefikBackendHealthCheckInterval, "")
|
||||
|
||||
return &types.HealthCheck{
|
||||
Path: path,
|
||||
Port: port,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
func getBuffering(labels map[string]string) *types.Buffering {
|
||||
if !label.HasPrefix(labels, label.TraefikBackendBuffering) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.Buffering{
|
||||
MaxRequestBodyBytes: label.GetInt64Value(labels, label.TraefikBackendBufferingMaxRequestBodyBytes, 0),
|
||||
MaxResponseBodyBytes: label.GetInt64Value(labels, label.TraefikBackendBufferingMaxResponseBodyBytes, 0),
|
||||
MemRequestBodyBytes: label.GetInt64Value(labels, label.TraefikBackendBufferingMemRequestBodyBytes, 0),
|
||||
MemResponseBodyBytes: label.GetInt64Value(labels, label.TraefikBackendBufferingMemResponseBodyBytes, 0),
|
||||
RetryExpression: label.GetStringValue(labels, label.TraefikBackendBufferingRetryExpression, ""),
|
||||
}
|
||||
}
|
||||
|
||||
func getCircuitBreaker(labels map[string]string) *types.CircuitBreaker {
|
||||
circuitBreaker := label.GetStringValue(labels, label.TraefikBackendCircuitBreakerExpression, "")
|
||||
if len(circuitBreaker) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &types.CircuitBreaker{Expression: circuitBreaker}
|
||||
}
|
||||
|
||||
func getLoadBalancer(labels map[string]string) *types.LoadBalancer {
|
||||
if !label.HasPrefix(labels, label.TraefikBackendLoadBalancer) {
|
||||
return nil
|
||||
}
|
||||
|
||||
method := label.GetStringValue(labels, label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod)
|
||||
|
||||
lb := &types.LoadBalancer{
|
||||
Method: method,
|
||||
Sticky: getSticky(labels),
|
||||
}
|
||||
|
||||
if label.GetBoolValue(labels, label.TraefikBackendLoadBalancerStickiness, false) {
|
||||
cookieName := label.GetStringValue(labels, label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName)
|
||||
lb.Stickiness = &types.Stickiness{CookieName: cookieName}
|
||||
}
|
||||
|
||||
return lb
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
// replaced by Stickiness
|
||||
// Deprecated
|
||||
func getSticky(labels map[string]string) bool {
|
||||
if label.Has(labels, label.TraefikBackendLoadBalancerSticky) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
||||
}
|
||||
|
||||
return label.GetBoolValue(labels, label.TraefikBackendLoadBalancerSticky, false)
|
||||
}
|
||||
|
||||
func (p *Provider) getServers(containers []dockerData) map[string]types.Server {
|
||||
var servers map[string]types.Server
|
||||
|
||||
for i, container := range containers {
|
||||
if servers == nil {
|
||||
servers = make(map[string]types.Server)
|
||||
}
|
||||
|
||||
protocol := label.GetStringValue(container.SegmentLabels, label.TraefikProtocol, label.DefaultProtocol)
|
||||
ip := p.getIPAddress(container)
|
||||
port := getPort(container)
|
||||
|
||||
serverName := "server-" + container.SegmentName + "-" + container.Name
|
||||
if len(container.SegmentName) > 0 {
|
||||
serverName += "-" + strconv.Itoa(i)
|
||||
}
|
||||
|
||||
servers[provider.Normalize(serverName)] = types.Server{
|
||||
URL: fmt.Sprintf("%s://%s:%s", protocol, ip, port),
|
||||
Weight: label.GetIntValue(container.SegmentLabels, label.TraefikWeight, label.DefaultWeightInt),
|
||||
}
|
||||
}
|
||||
|
||||
return servers
|
||||
}
|
||||
|
||||
func getFuncStringLabel(labelName string, defaultValue string) func(map[string]string) string {
|
||||
return func(labels map[string]string) string {
|
||||
return label.GetStringValue(labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncIntLabel(labelName string, defaultValue int) func(map[string]string) int {
|
||||
return func(labels map[string]string) int {
|
||||
return label.GetIntValue(labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncBoolLabel(labelName string, defaultValue bool) func(map[string]string) bool {
|
||||
return func(labels map[string]string) bool {
|
||||
return label.GetBoolValue(labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncSliceStringLabel(labelName string) func(map[string]string) []string {
|
||||
return func(labels map[string]string) []string {
|
||||
return label.GetSliceStringValue(labels, labelName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,358 +0,0 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
const (
|
||||
labelDockerNetwork = "traefik.docker.network"
|
||||
labelBackendLoadBalancerSwarm = "traefik.backend.loadbalancer.swarm"
|
||||
labelDockerComposeProject = "com.docker.compose.project"
|
||||
labelDockerComposeService = "com.docker.compose.service"
|
||||
)
|
||||
|
||||
// Specific functions
|
||||
|
||||
func (p Provider) getFrontendName(container dockerData, idx int) string {
|
||||
return provider.Normalize(p.getFrontendRule(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.
|
||||
func (p Provider) getFrontendRule(container dockerData) string {
|
||||
if value := label.GetStringValue(container.Labels, label.TraefikFrontendRule, ""); len(value) != 0 {
|
||||
return value
|
||||
}
|
||||
|
||||
if values, err := label.GetStringMultipleStrict(container.Labels, labelDockerComposeProject, labelDockerComposeService); err == nil {
|
||||
return "Host:" + getSubDomain(values[labelDockerComposeService]+"."+values[labelDockerComposeProject]) + "." + p.Domain
|
||||
}
|
||||
|
||||
if len(p.Domain) > 0 {
|
||||
return "Host:" + getSubDomain(container.ServiceName) + "." + p.Domain
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p Provider) getIPAddress(container dockerData) string {
|
||||
|
||||
if value := label.GetStringValue(container.Labels, labelDockerNetwork, ""); 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 := getPort(container)
|
||||
for netPort, portBindings := range container.NetworkSettings.Ports {
|
||||
if string(netPort) == port+"/TCP" || string(netPort) == port+"/UDP" {
|
||||
for _, p := range portBindings {
|
||||
return p.HostIP
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, network := range container.NetworkSettings.Networks {
|
||||
return network.Addr
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getBackendName(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)
|
||||
}
|
||||
|
||||
func getPort(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 ""
|
||||
}
|
||||
|
||||
// Escape beginning slash "/", convert all others to dash "-", and convert underscores "_" to dash "-"
|
||||
func getSubDomain(name string) string {
|
||||
return strings.Replace(strings.Replace(strings.TrimPrefix(name, "/"), "/", "-", -1), "_", "-", -1)
|
||||
}
|
||||
|
||||
func isBackendLBSwarm(container dockerData) bool {
|
||||
return label.GetBoolValue(container.Labels, labelBackendLoadBalancerSwarm, false)
|
||||
}
|
||||
|
||||
func getMaxConn(container dockerData) *types.MaxConn {
|
||||
amount := label.GetInt64Value(container.Labels, label.TraefikBackendMaxConnAmount, math.MinInt64)
|
||||
extractorFunc := label.GetStringValue(container.Labels, label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc)
|
||||
|
||||
if amount == math.MinInt64 || len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.MaxConn{
|
||||
Amount: amount,
|
||||
ExtractorFunc: extractorFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func getLoadBalancer(container dockerData) *types.LoadBalancer {
|
||||
if !label.HasPrefix(container.Labels, label.TraefikBackendLoadBalancer) {
|
||||
return nil
|
||||
}
|
||||
|
||||
method := label.GetStringValue(container.Labels, label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod)
|
||||
|
||||
lb := &types.LoadBalancer{
|
||||
Method: method,
|
||||
Sticky: getSticky(container),
|
||||
}
|
||||
|
||||
if label.GetBoolValue(container.Labels, label.TraefikBackendLoadBalancerStickiness, false) {
|
||||
cookieName := label.GetStringValue(container.Labels, label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName)
|
||||
lb.Stickiness = &types.Stickiness{CookieName: cookieName}
|
||||
}
|
||||
|
||||
return lb
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
// replaced by Stickiness
|
||||
// Deprecated
|
||||
func getSticky(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)
|
||||
}
|
||||
|
||||
func getCircuitBreaker(container dockerData) *types.CircuitBreaker {
|
||||
circuitBreaker := label.GetStringValue(container.Labels, label.TraefikBackendCircuitBreakerExpression, "")
|
||||
if len(circuitBreaker) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &types.CircuitBreaker{Expression: circuitBreaker}
|
||||
}
|
||||
|
||||
func getHealthCheck(container dockerData) *types.HealthCheck {
|
||||
path := label.GetStringValue(container.Labels, label.TraefikBackendHealthCheckPath, "")
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
port := label.GetIntValue(container.Labels, label.TraefikBackendHealthCheckPort, label.DefaultBackendHealthCheckPort)
|
||||
interval := label.GetStringValue(container.Labels, label.TraefikBackendHealthCheckInterval, "")
|
||||
|
||||
return &types.HealthCheck{
|
||||
Path: path,
|
||||
Port: port,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
func getBuffering(container dockerData) *types.Buffering {
|
||||
if !label.HasPrefix(container.Labels, label.TraefikBackendBuffering) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.Buffering{
|
||||
MaxRequestBodyBytes: label.GetInt64Value(container.Labels, label.TraefikBackendBufferingMaxRequestBodyBytes, 0),
|
||||
MaxResponseBodyBytes: label.GetInt64Value(container.Labels, label.TraefikBackendBufferingMaxResponseBodyBytes, 0),
|
||||
MemRequestBodyBytes: label.GetInt64Value(container.Labels, label.TraefikBackendBufferingMemRequestBodyBytes, 0),
|
||||
MemResponseBodyBytes: label.GetInt64Value(container.Labels, label.TraefikBackendBufferingMemResponseBodyBytes, 0),
|
||||
RetryExpression: label.GetStringValue(container.Labels, label.TraefikBackendBufferingRetryExpression, ""),
|
||||
}
|
||||
}
|
||||
|
||||
func getRedirect(container dockerData) *types.Redirect {
|
||||
permanent := label.GetBoolValue(container.Labels, label.TraefikFrontendRedirectPermanent, false)
|
||||
|
||||
if label.Has(container.Labels, label.TraefikFrontendRedirectEntryPoint) {
|
||||
return &types.Redirect{
|
||||
EntryPoint: label.GetStringValue(container.Labels, label.TraefikFrontendRedirectEntryPoint, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
if label.Has(container.Labels, label.TraefikFrontendRedirectRegex) &&
|
||||
label.Has(container.Labels, label.TraefikFrontendRedirectReplacement) {
|
||||
return &types.Redirect{
|
||||
Regex: label.GetStringValue(container.Labels, label.TraefikFrontendRedirectRegex, ""),
|
||||
Replacement: label.GetStringValue(container.Labels, label.TraefikFrontendRedirectReplacement, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getErrorPages(container dockerData) map[string]*types.ErrorPage {
|
||||
prefix := label.Prefix + label.BaseFrontendErrorPage
|
||||
return label.ParseErrorPages(container.Labels, prefix, label.RegexpFrontendErrorPage)
|
||||
}
|
||||
|
||||
func getRateLimit(container dockerData) *types.RateLimit {
|
||||
extractorFunc := label.GetStringValue(container.Labels, label.TraefikFrontendRateLimitExtractorFunc, "")
|
||||
if len(extractorFunc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
prefix := label.Prefix + label.BaseFrontendRateLimit
|
||||
limits := label.ParseRateSets(container.Labels, prefix, label.RegexpFrontendRateLimit)
|
||||
|
||||
return &types.RateLimit{
|
||||
ExtractorFunc: extractorFunc,
|
||||
RateSet: limits,
|
||||
}
|
||||
}
|
||||
|
||||
func getHeaders(container dockerData) *types.Headers {
|
||||
headers := &types.Headers{
|
||||
CustomRequestHeaders: label.GetMapValue(container.Labels, label.TraefikFrontendRequestHeaders),
|
||||
CustomResponseHeaders: label.GetMapValue(container.Labels, label.TraefikFrontendResponseHeaders),
|
||||
SSLProxyHeaders: label.GetMapValue(container.Labels, label.TraefikFrontendSSLProxyHeaders),
|
||||
AllowedHosts: label.GetSliceStringValue(container.Labels, label.TraefikFrontendAllowedHosts),
|
||||
HostsProxyHeaders: label.GetSliceStringValue(container.Labels, label.TraefikFrontendHostsProxyHeaders),
|
||||
STSSeconds: label.GetInt64Value(container.Labels, label.TraefikFrontendSTSSeconds, 0),
|
||||
SSLRedirect: label.GetBoolValue(container.Labels, label.TraefikFrontendSSLRedirect, false),
|
||||
SSLTemporaryRedirect: label.GetBoolValue(container.Labels, label.TraefikFrontendSSLTemporaryRedirect, false),
|
||||
STSIncludeSubdomains: label.GetBoolValue(container.Labels, label.TraefikFrontendSTSIncludeSubdomains, false),
|
||||
STSPreload: label.GetBoolValue(container.Labels, label.TraefikFrontendSTSPreload, false),
|
||||
ForceSTSHeader: label.GetBoolValue(container.Labels, label.TraefikFrontendForceSTSHeader, false),
|
||||
FrameDeny: label.GetBoolValue(container.Labels, label.TraefikFrontendFrameDeny, false),
|
||||
ContentTypeNosniff: label.GetBoolValue(container.Labels, label.TraefikFrontendContentTypeNosniff, false),
|
||||
BrowserXSSFilter: label.GetBoolValue(container.Labels, label.TraefikFrontendBrowserXSSFilter, false),
|
||||
IsDevelopment: label.GetBoolValue(container.Labels, label.TraefikFrontendIsDevelopment, false),
|
||||
SSLHost: label.GetStringValue(container.Labels, label.TraefikFrontendSSLHost, ""),
|
||||
CustomFrameOptionsValue: label.GetStringValue(container.Labels, label.TraefikFrontendCustomFrameOptionsValue, ""),
|
||||
ContentSecurityPolicy: label.GetStringValue(container.Labels, label.TraefikFrontendContentSecurityPolicy, ""),
|
||||
PublicKey: label.GetStringValue(container.Labels, label.TraefikFrontendPublicKey, ""),
|
||||
ReferrerPolicy: label.GetStringValue(container.Labels, label.TraefikFrontendReferrerPolicy, ""),
|
||||
CustomBrowserXSSValue: label.GetStringValue(container.Labels, label.TraefikFrontendCustomBrowserXSSValue, ""),
|
||||
}
|
||||
|
||||
if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasLoadBalancerLabel(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 hasMaxConnLabels(container dockerData) bool {
|
||||
mca := label.Has(container.Labels, label.TraefikBackendMaxConnAmount)
|
||||
mcef := label.Has(container.Labels, label.TraefikBackendMaxConnExtractorFunc)
|
||||
return mca && mcef
|
||||
}
|
||||
|
||||
// Label functions
|
||||
|
||||
func getFuncStringLabel(labelName string, defaultValue string) func(container dockerData) string {
|
||||
return func(container dockerData) string {
|
||||
return label.GetStringValue(container.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncBoolLabel(labelName string, defaultValue bool) func(container dockerData) bool {
|
||||
return func(container dockerData) bool {
|
||||
return label.GetBoolValue(container.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncSliceStringLabel(labelName string) func(container dockerData) []string {
|
||||
return func(container dockerData) []string {
|
||||
return label.GetSliceStringValue(container.Labels, labelName)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncIntLabel(labelName string, defaultValue int) func(container dockerData) int {
|
||||
return func(container dockerData) int {
|
||||
return label.GetIntValue(container.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncInt64Label(labelName string, defaultValue int64) func(container dockerData) int64 {
|
||||
return func(container dockerData) int64 {
|
||||
return label.GetInt64Value(container.Labels, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func hasFunc(labelName string) func(container dockerData) bool {
|
||||
return func(container dockerData) bool {
|
||||
return label.Has(container.Labels, labelName)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -319,7 +318,7 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
|||
Domain: "docker.localhost",
|
||||
ExposedByDefault: true,
|
||||
}
|
||||
actualConfig := provider.buildConfiguration(dockerDataList)
|
||||
actualConfig := provider.buildConfigurationV2(dockerDataList)
|
||||
require.NotNil(t, actualConfig, "actualConfig")
|
||||
|
||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||
|
@ -631,7 +630,11 @@ func TestDockerTraefikFilter(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
actual := test.provider.containerFilter(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %v for %+v, got %+v", test.expected, test, actual)
|
||||
|
@ -642,21 +645,21 @@ func TestDockerTraefikFilter(t *testing.T) {
|
|||
|
||||
func TestDockerGetFuncStringLabel(t *testing.T) {
|
||||
testCases := []struct {
|
||||
container docker.ContainerJSON
|
||||
labels map[string]string
|
||||
labelName string
|
||||
defaultValue string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
container: containerJSON(),
|
||||
labels: nil,
|
||||
labelName: label.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
expected: "0",
|
||||
},
|
||||
{
|
||||
container: containerJSON(labels(map[string]string{
|
||||
labels: map[string]string{
|
||||
label.TraefikWeight: "10",
|
||||
})),
|
||||
},
|
||||
labelName: label.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
expected: "10",
|
||||
|
@ -668,13 +671,8 @@ func TestDockerGetFuncStringLabel(t *testing.T) {
|
|||
t.Run(test.labelName+strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getFuncStringLabel(test.labelName, test.defaultValue)(dData)
|
||||
|
||||
if actual != test.expected {
|
||||
t.Errorf("got %q, expected %q", actual, test.expected)
|
||||
}
|
||||
actual := getFuncStringLabel(test.labelName, test.defaultValue)(test.labels)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -682,28 +680,28 @@ func TestDockerGetFuncStringLabel(t *testing.T) {
|
|||
func TestDockerGetSliceStringLabel(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
labels map[string]string
|
||||
labelName string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
desc: "no whitelist-label",
|
||||
container: containerJSON(),
|
||||
labels: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with empty string",
|
||||
container: containerJSON(labels(map[string]string{
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhitelistSourceRange: "",
|
||||
})),
|
||||
},
|
||||
labelName: label.TraefikFrontendWhitelistSourceRange,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "whitelist-label with IPv4 mask",
|
||||
container: containerJSON(labels(map[string]string{
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhitelistSourceRange: "1.2.3.4/16",
|
||||
})),
|
||||
},
|
||||
labelName: label.TraefikFrontendWhitelistSourceRange,
|
||||
expected: []string{
|
||||
"1.2.3.4/16",
|
||||
|
@ -711,9 +709,9 @@ func TestDockerGetSliceStringLabel(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "whitelist-label with IPv6 mask",
|
||||
container: containerJSON(labels(map[string]string{
|
||||
labels: map[string]string{
|
||||
label.TraefikFrontendWhitelistSourceRange: "fe80::/16",
|
||||
})),
|
||||
},
|
||||
labelName: label.TraefikFrontendWhitelistSourceRange,
|
||||
expected: []string{
|
||||
"fe80::/16",
|
||||
|
@ -721,9 +719,9 @@ func TestDockerGetSliceStringLabel(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "whitelist-label with multiple masks",
|
||||
container: containerJSON(labels(map[string]string{
|
||||
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",
|
||||
|
@ -736,13 +734,9 @@ func TestDockerGetSliceStringLabel(t *testing.T) {
|
|||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getFuncSliceStringLabel(test.labelName)(dData)
|
||||
|
||||
if !reflect.DeepEqual(actual, test.expected) {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
actual := getFuncSliceStringLabel(test.labelName)(test.labels)
|
||||
assert.EqualValues(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -793,14 +787,17 @@ func TestDockerGetFrontendName(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
provider := &Provider{
|
||||
Domain: "docker.localhost",
|
||||
}
|
||||
|
||||
actual := provider.getFrontendName(dData, 0)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -842,14 +839,16 @@ func TestDockerGetFrontendRule(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
provider := &Provider{
|
||||
Domain: "docker.localhost",
|
||||
}
|
||||
actual := provider.getFrontendRule(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -886,11 +885,13 @@ func TestDockerGetBackendName(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
actual := getBackendName(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -950,12 +951,15 @@ func TestDockerGetIPAddress(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
provider := &Provider{}
|
||||
|
||||
actual := provider.getIPAddress(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1007,599 +1011,16 @@ func TestDockerGetPort(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for containerID, e := range testCases {
|
||||
e := e
|
||||
for containerID, test := range testCases {
|
||||
test := test
|
||||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dData := parseContainer(e.container)
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
actual := getPort(dData)
|
||||
if actual != e.expected {
|
||||
t.Errorf("expected %q, got %q", e.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetMaxConn(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.MaxConn
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no max conn labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return nil when no amount label",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return default when no empty extractorFunc label",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikBackendMaxConnExtractorFunc: "",
|
||||
label.TraefikBackendMaxConnAmount: "666",
|
||||
})),
|
||||
expected: &types.MaxConn{
|
||||
ExtractorFunc: "request.host",
|
||||
Amount: 666,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when max conn labels are set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
label.TraefikBackendMaxConnAmount: "666",
|
||||
})),
|
||||
expected: &types.MaxConn{
|
||||
ExtractorFunc: "client.ip",
|
||||
Amount: 666,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getMaxConn(dData)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetCircuitBreaker(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.CircuitBreaker
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no CB labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct CB when CB labels are set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikBackendCircuitBreakerExpression: "NetworkErrorRatio() > 0.5",
|
||||
})),
|
||||
expected: &types.CircuitBreaker{
|
||||
Expression: "NetworkErrorRatio() > 0.5",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getCircuitBreaker(dData)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetLoadBalancer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.LoadBalancer
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no LB labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when labels are set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikBackendLoadBalancerMethod: "drr",
|
||||
label.TraefikBackendLoadBalancerSticky: "true",
|
||||
label.TraefikBackendLoadBalancerStickiness: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: "foo",
|
||||
})),
|
||||
expected: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a nil Stickiness when Stickiness is not set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikBackendLoadBalancerMethod: "drr",
|
||||
label.TraefikBackendLoadBalancerSticky: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: "foo",
|
||||
})),
|
||||
expected: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getLoadBalancer(dData)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetRedirect(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.Redirect
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no redirect labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should use only entry point tag when mix regex redirect and entry point redirect",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||
label.TraefikFrontendRedirectRegex: "(.*)",
|
||||
label.TraefikFrontendRedirectReplacement: "$1",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when entry point redirect label",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when entry point redirect label (permanent)",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||
label.TraefikFrontendRedirectPermanent: "true",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Permanent: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when regex redirect labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRedirectRegex: "(.*)",
|
||||
label.TraefikFrontendRedirectReplacement: "$1",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
Regex: "(.*)",
|
||||
Replacement: "$1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when regex redirect tags (permanent)",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRedirectRegex: "(.*)",
|
||||
label.TraefikFrontendRedirectReplacement: "$1",
|
||||
label.TraefikFrontendRedirectPermanent: "true",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
Regex: "(.*)",
|
||||
Replacement: "$1",
|
||||
Permanent: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getRedirect(dData)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetRateLimit(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.RateLimit
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no rate limit labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when rate limit labels are defined",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRateLimitExtractorFunc: "client.ip",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
})),
|
||||
expected: &types.RateLimit{
|
||||
ExtractorFunc: "client.ip",
|
||||
RateSet: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return nil when ExtractorFunc is missing",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
})),
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getRateLimit(dData)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetErrorPages(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
data dockerData
|
||||
expected map[string]*types.ErrorPage
|
||||
}{
|
||||
{
|
||||
desc: "2 errors pages",
|
||||
data: parseContainer(containerJSON(
|
||||
labels(map[string]string{
|
||||
label.Prefix + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageStatus: "404",
|
||||
label.Prefix + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageBackend: "foo_backend",
|
||||
label.Prefix + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageQuery: "foo_query",
|
||||
label.Prefix + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageStatus: "500,600",
|
||||
label.Prefix + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageBackend: "bar_backend",
|
||||
label.Prefix + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageQuery: "bar_query",
|
||||
}))),
|
||||
expected: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
Query: "foo_query",
|
||||
Backend: "foo_backend",
|
||||
},
|
||||
"bar": {
|
||||
Status: []string{"500", "600"},
|
||||
Query: "bar_query",
|
||||
Backend: "bar_backend",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "only status field",
|
||||
data: parseContainer(containerJSON(
|
||||
labels(map[string]string{
|
||||
label.Prefix + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageStatus: "404",
|
||||
}))),
|
||||
expected: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pages := getErrorPages(test.data)
|
||||
|
||||
assert.EqualValues(t, test.expected, pages)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetHealthCheck(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.HealthCheck
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no health check labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return nil when no health check Path label",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikBackendHealthCheckPort: "80",
|
||||
label.TraefikBackendHealthCheckInterval: "6",
|
||||
})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when health check labels are set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikBackendHealthCheckPath: "/health",
|
||||
label.TraefikBackendHealthCheckPort: "80",
|
||||
label.TraefikBackendHealthCheckInterval: "6",
|
||||
})),
|
||||
expected: &types.HealthCheck{
|
||||
Path: "/health",
|
||||
Port: 80,
|
||||
Interval: "6",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getHealthCheck(dData)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetBuffering(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.Buffering
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no health check labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when buffering labels are set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikBackendBufferingMaxResponseBodyBytes: "10485760",
|
||||
label.TraefikBackendBufferingMemResponseBodyBytes: "2097152",
|
||||
label.TraefikBackendBufferingMaxRequestBodyBytes: "10485760",
|
||||
label.TraefikBackendBufferingMemRequestBodyBytes: "2097152",
|
||||
label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2",
|
||||
})),
|
||||
expected: &types.Buffering{
|
||||
MaxResponseBodyBytes: 10485760,
|
||||
MemResponseBodyBytes: 2097152,
|
||||
MaxRequestBodyBytes: 10485760,
|
||||
MemRequestBodyBytes: 2097152,
|
||||
RetryExpression: "IsNetworkError() && Attempts() <= 2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getBuffering(dData)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetHeaders(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.Headers
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no custom headers options are set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when all custom headers options are set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.TraefikFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.TraefikFrontendSSLProxyHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.TraefikFrontendAllowedHosts: "foo,bar,bor",
|
||||
label.TraefikFrontendHostsProxyHeaders: "foo,bar,bor",
|
||||
label.TraefikFrontendSSLHost: "foo",
|
||||
label.TraefikFrontendCustomFrameOptionsValue: "foo",
|
||||
label.TraefikFrontendContentSecurityPolicy: "foo",
|
||||
label.TraefikFrontendPublicKey: "foo",
|
||||
label.TraefikFrontendReferrerPolicy: "foo",
|
||||
label.TraefikFrontendCustomBrowserXSSValue: "foo",
|
||||
label.TraefikFrontendSTSSeconds: "666",
|
||||
label.TraefikFrontendSSLRedirect: "true",
|
||||
label.TraefikFrontendSSLTemporaryRedirect: "true",
|
||||
label.TraefikFrontendSTSIncludeSubdomains: "true",
|
||||
label.TraefikFrontendSTSPreload: "true",
|
||||
label.TraefikFrontendForceSTSHeader: "true",
|
||||
label.TraefikFrontendFrameDeny: "true",
|
||||
label.TraefikFrontendContentTypeNosniff: "true",
|
||||
label.TraefikFrontendBrowserXSSFilter: "true",
|
||||
label.TraefikFrontendIsDevelopment: "true",
|
||||
}),
|
||||
),
|
||||
expected: &types.Headers{
|
||||
CustomRequestHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
CustomResponseHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
SSLProxyHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
AllowedHosts: []string{"foo", "bar", "bor"},
|
||||
HostsProxyHeaders: []string{"foo", "bar", "bor"},
|
||||
SSLHost: "foo",
|
||||
CustomFrameOptionsValue: "foo",
|
||||
ContentSecurityPolicy: "foo",
|
||||
PublicKey: "foo",
|
||||
ReferrerPolicy: "foo",
|
||||
CustomBrowserXSSValue: "foo",
|
||||
STSSeconds: 666,
|
||||
SSLRedirect: true,
|
||||
SSLTemporaryRedirect: true,
|
||||
STSIncludeSubdomains: true,
|
||||
STSPreload: true,
|
||||
ForceSTSHeader: true,
|
||||
FrameDeny: true,
|
||||
ContentTypeNosniff: true,
|
||||
BrowserXSSFilter: true,
|
||||
IsDevelopment: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getHeaders(dData)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -331,7 +331,7 @@ func TestSwarmBuildConfiguration(t *testing.T) {
|
|||
SwarmMode: true,
|
||||
}
|
||||
|
||||
actualConfig := provider.buildConfiguration(dockerDataList)
|
||||
actualConfig := provider.buildConfigurationV2(dockerDataList)
|
||||
require.NotNil(t, actualConfig, "actualConfig")
|
||||
|
||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||
|
@ -465,7 +465,11 @@ func TestSwarmTraefikFilter(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseService(test.service, test.networks)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
actual := test.provider.containerFilter(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %v for %+v, got %+v", test.expected, test, actual)
|
||||
|
@ -474,47 +478,6 @@ func TestSwarmTraefikFilter(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSwarmGetFuncStringLabel(t *testing.T) {
|
||||
testCases := []struct {
|
||||
service swarm.Service
|
||||
labelName string
|
||||
defaultValue string
|
||||
networks map[string]*docker.NetworkResource
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
service: swarmService(),
|
||||
labelName: label.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
networks: map[string]*docker.NetworkResource{},
|
||||
expected: "0",
|
||||
},
|
||||
{
|
||||
service: swarmService(serviceLabels(map[string]string{
|
||||
label.TraefikWeight: "10",
|
||||
})),
|
||||
labelName: label.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
networks: map[string]*docker.NetworkResource{},
|
||||
expected: "10",
|
||||
},
|
||||
}
|
||||
|
||||
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 := getFuncStringLabel(test.labelName, test.defaultValue)(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("got %q, expected %q", actual, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwarmGetFrontendName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
service swarm.Service
|
||||
|
@ -563,15 +526,18 @@ func TestSwarmGetFrontendName(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseService(test.service, test.networks)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
provider := &Provider{
|
||||
Domain: "docker.localhost",
|
||||
SwarmMode: true,
|
||||
}
|
||||
|
||||
actual := provider.getFrontendName(dData, 0)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -612,15 +578,18 @@ func TestSwarmGetFrontendRule(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseService(test.service, test.networks)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
provider := &Provider{
|
||||
Domain: "docker.localhost",
|
||||
SwarmMode: true,
|
||||
}
|
||||
|
||||
actual := provider.getFrontendRule(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -654,11 +623,13 @@ func TestSwarmGetBackendName(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseService(test.service, test.networks)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
actual := getBackendName(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -713,14 +684,17 @@ func TestSwarmGetIPAddress(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dData := parseService(test.service, test.networks)
|
||||
|
||||
provider := &Provider{
|
||||
SwarmMode: true,
|
||||
}
|
||||
|
||||
dData := parseService(test.service, test.networks)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
actual := provider.getIPAddress(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -747,11 +721,13 @@ func TestSwarmGetPort(t *testing.T) {
|
|||
test := test
|
||||
t.Run(strconv.Itoa(serviceID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseService(test.service, test.networks)
|
||||
segmentProperties := label.ExtractTraefikLabels(dData.Labels)
|
||||
dData.SegmentLabels = segmentProperties[""]
|
||||
|
||||
actual := getPort(dData)
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
12
provider/docker/config_root.go
Normal file
12
provider/docker/config_root.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
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)
|
||||
}
|
351
provider/docker/config_segment_test.go
Normal file
351
provider/docker/config_segment_test.go
Normal file
|
@ -0,0 +1,351 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"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 TestSegmentBuildConfiguration(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.sauternes.port": "2503",
|
||||
"traefik.sauternes.frontend.entryPoints": "http,https",
|
||||
}),
|
||||
ports(nat.PortMap{
|
||||
"80/tcp": {},
|
||||
}),
|
||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
||||
),
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-foo-foo-sauternes": {
|
||||
Backend: "backend-foo-foo-sauternes",
|
||||
PassHostHeader: true,
|
||||
EntryPoints: []string{"http", "https"},
|
||||
BasicAuth: []string{},
|
||||
Routes: map[string]types.Route{
|
||||
"route-frontend-foo-foo-sauternes": {
|
||||
Rule: "Host:foo.docker.localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-foo-foo-sauternes": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-sauternes-foo-0": {
|
||||
URL: "http://127.0.0.1:2503",
|
||||
Weight: 0,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "when all labels are set",
|
||||
containers: []docker.ContainerJSON{
|
||||
containerJSON(
|
||||
name("foo"),
|
||||
labels(map[string]string{
|
||||
label.Prefix + "sauternes." + label.SuffixPort: "666",
|
||||
label.Prefix + "sauternes." + label.SuffixProtocol: "https",
|
||||
label.Prefix + "sauternes." + label.SuffixWeight: "12",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendWhitelistSourceRange: "10.10.10.10",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSSLProxyHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersAllowedHosts: "foo,bar,bor",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersHostsProxyHeaders: "foo,bar,bor",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSSLHost: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersCustomFrameOptionsValue: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersContentSecurityPolicy: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersPublicKey: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersReferrerPolicy: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersCustomBrowserXSSValue: "foo",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSTSSeconds: "666",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSSLRedirect: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSSLTemporaryRedirect: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSTSIncludeSubdomains: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersSTSPreload: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersForceSTSHeader: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersFrameDeny: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersContentTypeNosniff: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersBrowserXSSFilter: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendHeadersIsDevelopment: "true",
|
||||
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageStatus: "404",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageBackend: "foobar",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageQuery: "foo_query",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageStatus: "500,600",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageBackend: "foobar",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageQuery: "bar_query",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRateLimitExtractorFunc: "client.ip",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + "sauternes." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
}),
|
||||
ports(nat.PortMap{
|
||||
"80/tcp": {},
|
||||
}),
|
||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
||||
),
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-foo-foo-sauternes": {
|
||||
Backend: "backend-foo-foo-sauternes",
|
||||
EntryPoints: []string{
|
||||
"http",
|
||||
"https",
|
||||
},
|
||||
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,
|
||||
CustomBrowserXSSValue: "foo",
|
||||
ContentSecurityPolicy: "foo",
|
||||
PublicKey: "foo",
|
||||
ReferrerPolicy: "foo",
|
||||
IsDevelopment: true,
|
||||
},
|
||||
Errors: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
Query: "foo_query",
|
||||
Backend: "foobar",
|
||||
},
|
||||
"bar": {
|
||||
Status: []string{"500", "600"},
|
||||
Query: "bar_query",
|
||||
Backend: "foobar",
|
||||
},
|
||||
},
|
||||
RateLimit: &types.RateLimit{
|
||||
ExtractorFunc: "client.ip",
|
||||
RateSet: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Regex: "",
|
||||
Replacement: "",
|
||||
Permanent: true,
|
||||
},
|
||||
|
||||
Routes: map[string]types.Route{
|
||||
"route-frontend-foo-foo-sauternes": {
|
||||
Rule: "Host:foo.docker.localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-foo-foo-sauternes": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-sauternes-foo-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.sauternes.port": "2503",
|
||||
"traefik.sauternes.protocol": "https",
|
||||
"traefik.sauternes.weight": "80",
|
||||
"traefik.sauternes.frontend.backend": "foobar",
|
||||
"traefik.sauternes.frontend.passHostHeader": "false",
|
||||
"traefik.sauternes.frontend.rule": "Path:/mypath",
|
||||
"traefik.sauternes.frontend.priority": "5000",
|
||||
"traefik.sauternes.frontend.entryPoints": "http,https,ws",
|
||||
"traefik.sauternes.frontend.auth.basic": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
"traefik.sauternes.frontend.redirect.entryPoint": "https",
|
||||
}),
|
||||
ports(nat.PortMap{
|
||||
"80/tcp": {},
|
||||
}),
|
||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
||||
),
|
||||
containerJSON(
|
||||
name("test2"),
|
||||
labels(map[string]string{
|
||||
"traefik.anothersauternes.port": "8079",
|
||||
"traefik.anothersauternes.weight": "33",
|
||||
"traefik.anothersauternes.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{
|
||||
"route-frontend-test1-foobar": {
|
||||
Rule: "Path:/mypath",
|
||||
},
|
||||
},
|
||||
},
|
||||
"frontend-test2-test2-anothersauternes": {
|
||||
Backend: "backend-test2-test2-anothersauternes",
|
||||
PassHostHeader: true,
|
||||
EntryPoints: []string{},
|
||||
BasicAuth: []string{},
|
||||
Routes: map[string]types.Route{
|
||||
"route-frontend-test2-test2-anothersauternes": {
|
||||
Rule: "Path:/anotherpath",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-test1-foobar": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-sauternes-test1-0": {
|
||||
URL: "https://127.0.0.1:2503",
|
||||
Weight: 80,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: nil,
|
||||
},
|
||||
"backend-test2-test2-anothersauternes": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-anothersauternes-test2-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.buildConfigurationV2(dockerDataList)
|
||||
require.NotNil(t, actualConfig, "actualConfig")
|
||||
|
||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,277 +0,0 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
// Specific functions
|
||||
|
||||
// Extract rule from labels for a given service and a given docker container
|
||||
func (p Provider) getServiceFrontendRule(container dockerData, serviceName string) string {
|
||||
if value, ok := getServiceLabels(container, serviceName)[label.SuffixFrontendRule]; ok {
|
||||
return value
|
||||
}
|
||||
return p.getFrontendRule(container)
|
||||
}
|
||||
|
||||
// Check if for the given container, we find labels that are defining services
|
||||
func hasServices(container dockerData) bool {
|
||||
return len(label.ExtractServiceProperties(container.Labels)) > 0
|
||||
}
|
||||
|
||||
// Gets array of service names for a given container
|
||||
func getServiceNames(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
|
||||
func checkServiceLabelPort(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 := extractServicePort(lbl)
|
||||
if len(portLabel) > 0 {
|
||||
serviceLabelPorts[portLabel[0]] = struct{}{}
|
||||
}
|
||||
// Get only one instance of all service names from service labels
|
||||
servicesLabelNames := label.FindServiceSubmatch(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
|
||||
}
|
||||
|
||||
func extractServicePort(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
|
||||
func getServiceBackendName(container dockerData, serviceName string) string {
|
||||
if value, ok := getServiceLabels(container, serviceName)[label.SuffixFrontendBackend]; ok {
|
||||
return provider.Normalize(container.ServiceName + "-" + value)
|
||||
}
|
||||
return provider.Normalize(container.ServiceName + "-" + getBackendName(container) + "-" + serviceName)
|
||||
}
|
||||
|
||||
// Extract port from labels for a given service and a given docker container
|
||||
func getServicePort(container dockerData, serviceName string) string {
|
||||
if value, ok := getServiceLabels(container, serviceName)[label.SuffixPort]; ok {
|
||||
return value
|
||||
}
|
||||
return getPort(container)
|
||||
}
|
||||
|
||||
func getServiceRedirect(container dockerData, serviceName string) *types.Redirect {
|
||||
serviceLabels := getServiceLabels(container, serviceName)
|
||||
|
||||
permanent := getServiceBoolValue(container, serviceLabels, label.SuffixFrontendRedirectPermanent, false)
|
||||
|
||||
if hasStrictServiceLabel(serviceLabels, label.SuffixFrontendRedirectEntryPoint) {
|
||||
return &types.Redirect{
|
||||
EntryPoint: getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
if hasStrictServiceLabel(serviceLabels, label.SuffixFrontendRedirectRegex) &&
|
||||
hasStrictServiceLabel(serviceLabels, label.SuffixFrontendRedirectReplacement) {
|
||||
return &types.Redirect{
|
||||
Regex: getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRedirectRegex, ""),
|
||||
Replacement: getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRedirectReplacement, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
}
|
||||
|
||||
return getRedirect(container)
|
||||
}
|
||||
|
||||
func getServiceErrorPages(container dockerData, serviceName string) map[string]*types.ErrorPage {
|
||||
serviceLabels := getServiceLabels(container, serviceName)
|
||||
|
||||
if label.HasPrefix(serviceLabels, label.BaseFrontendErrorPage) {
|
||||
return label.ParseErrorPages(serviceLabels, label.BaseFrontendErrorPage, label.RegexpBaseFrontendErrorPage)
|
||||
}
|
||||
|
||||
return getErrorPages(container)
|
||||
}
|
||||
|
||||
func getServiceRateLimit(container dockerData, serviceName string) *types.RateLimit {
|
||||
serviceLabels := getServiceLabels(container, serviceName)
|
||||
|
||||
if hasStrictServiceLabel(serviceLabels, label.SuffixFrontendRateLimitExtractorFunc) {
|
||||
extractorFunc := getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRateLimitExtractorFunc, label.DefaultBackendMaxconnExtractorFunc)
|
||||
return &types.RateLimit{
|
||||
ExtractorFunc: extractorFunc,
|
||||
RateSet: label.ParseRateSets(serviceLabels, label.BaseFrontendRateLimit, label.RegexpBaseFrontendRateLimit),
|
||||
}
|
||||
}
|
||||
|
||||
return getRateLimit(container)
|
||||
}
|
||||
|
||||
func getServiceHeaders(container dockerData, serviceName string) *types.Headers {
|
||||
serviceLabels := getServiceLabels(container, serviceName)
|
||||
|
||||
headers := &types.Headers{
|
||||
CustomRequestHeaders: getServiceMapValue(container, serviceLabels, serviceName, label.SuffixFrontendRequestHeaders),
|
||||
CustomResponseHeaders: getServiceMapValue(container, serviceLabels, serviceName, label.SuffixFrontendResponseHeaders),
|
||||
SSLProxyHeaders: getServiceMapValue(container, serviceLabels, serviceName, label.SuffixFrontendHeadersSSLProxyHeaders),
|
||||
AllowedHosts: getServiceSliceValue(container, serviceLabels, label.SuffixFrontendHeadersAllowedHosts),
|
||||
HostsProxyHeaders: getServiceSliceValue(container, serviceLabels, label.SuffixFrontendHeadersHostsProxyHeaders),
|
||||
STSSeconds: getServiceInt64Value(container, serviceLabels, label.SuffixFrontendHeadersSTSSeconds, 0),
|
||||
SSLRedirect: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersSSLRedirect, false),
|
||||
SSLTemporaryRedirect: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersSSLTemporaryRedirect, false),
|
||||
STSIncludeSubdomains: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersSTSIncludeSubdomains, false),
|
||||
STSPreload: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersSTSPreload, false),
|
||||
ForceSTSHeader: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersForceSTSHeader, false),
|
||||
FrameDeny: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersFrameDeny, false),
|
||||
ContentTypeNosniff: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersContentTypeNosniff, false),
|
||||
BrowserXSSFilter: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersBrowserXSSFilter, false),
|
||||
IsDevelopment: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersIsDevelopment, false),
|
||||
SSLHost: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersSSLHost, ""),
|
||||
CustomFrameOptionsValue: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersCustomFrameOptionsValue, ""),
|
||||
ContentSecurityPolicy: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersContentSecurityPolicy, ""),
|
||||
PublicKey: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersPublicKey, ""),
|
||||
ReferrerPolicy: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersReferrerPolicy, ""),
|
||||
CustomBrowserXSSValue: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersCustomBrowserXSSValue, ""),
|
||||
}
|
||||
|
||||
if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
// Service label functions
|
||||
|
||||
func getFuncServiceSliceStringLabel(labelSuffix string) func(container dockerData, serviceName string) []string {
|
||||
return func(container dockerData, serviceName string) []string {
|
||||
serviceLabels := getServiceLabels(container, serviceName)
|
||||
return getServiceSliceValue(container, serviceLabels, labelSuffix)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncServiceStringLabel(labelSuffix string, defaultValue string) func(container dockerData, serviceName string) string {
|
||||
return func(container dockerData, serviceName string) string {
|
||||
serviceLabels := getServiceLabels(container, serviceName)
|
||||
return getServiceStringValue(container, serviceLabels, labelSuffix, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncServiceBoolLabel(labelSuffix string, defaultValue bool) func(container dockerData, serviceName string) bool {
|
||||
return func(container dockerData, serviceName string) bool {
|
||||
serviceLabels := getServiceLabels(container, serviceName)
|
||||
return getServiceBoolValue(container, serviceLabels, labelSuffix, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncServiceIntLabel(labelSuffix string, defaultValue int) func(container dockerData, serviceName string) int {
|
||||
return func(container dockerData, serviceName string) int {
|
||||
return getServiceIntLabel(container, serviceName, labelSuffix, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func hasStrictServiceLabel(serviceLabels map[string]string, labelSuffix string) bool {
|
||||
value, ok := serviceLabels[labelSuffix]
|
||||
return ok && len(value) > 0
|
||||
}
|
||||
|
||||
func getServiceStringValue(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)
|
||||
}
|
||||
|
||||
func getStrictServiceStringValue(serviceLabels map[string]string, labelSuffix string, defaultValue string) string {
|
||||
if value, ok := serviceLabels[labelSuffix]; ok {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getServiceMapValue(container dockerData, serviceLabels map[string]string, serviceName string, labelSuffix string) map[string]string {
|
||||
if value, ok := serviceLabels[labelSuffix]; ok {
|
||||
lblName := label.GetServiceLabel(labelSuffix, serviceName)
|
||||
return label.ParseMapValue(lblName, value)
|
||||
}
|
||||
return label.GetMapValue(container.Labels, label.Prefix+labelSuffix)
|
||||
}
|
||||
|
||||
func getServiceSliceValue(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)
|
||||
}
|
||||
|
||||
func getServiceBoolValue(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)
|
||||
}
|
||||
|
||||
func getServiceIntLabel(container dockerData, serviceName string, labelSuffix string, defaultValue int) int {
|
||||
if rawValue, ok := getServiceLabels(container, serviceName)[labelSuffix]; ok {
|
||||
value, err := strconv.Atoi(rawValue)
|
||||
if err == nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return label.GetIntValue(container.Labels, label.Prefix+labelSuffix, defaultValue)
|
||||
}
|
||||
|
||||
func getServiceInt64Value(container dockerData, serviceLabels map[string]string, labelSuffix string, defaultValue int64) int64 {
|
||||
if rawValue, ok := serviceLabels[labelSuffix]; ok {
|
||||
value, err := strconv.ParseInt(rawValue, 10, 64)
|
||||
if err == nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return label.GetInt64Value(container.Labels, label.Prefix+labelSuffix, defaultValue)
|
||||
}
|
||||
|
||||
func getServiceLabels(container dockerData, serviceName string) label.ServicePropertyValues {
|
||||
return label.ExtractServiceProperties(container.Labels)[serviceName]
|
||||
}
|
201
provider/docker/deprecated_config.go
Normal file
201
provider/docker/deprecated_config.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"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.getIPAddress,
|
||||
"getPort": getPortV1,
|
||||
"getWeight": getFuncIntLabelV1(label.TraefikWeight, label.DefaultWeightInt),
|
||||
"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.DefaultFrontendPriorityInt),
|
||||
"getPassHostHeader": getFuncBoolLabelV1(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||
"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": getFuncServiceStringLabelV1(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.DefaultPassHostHeaderBool),
|
||||
"getServicePassTLSCert": getFuncServiceBoolLabelV1(label.SuffixFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getServicePriority": getFuncServiceIntLabelV1(label.SuffixFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||
"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 {
|
||||
if _, exists := serviceNames[container.ServiceName]; !exists {
|
||||
frontendName := p.getFrontendNameV1(container, idx)
|
||||
frontends[frontendName] = append(frontends[frontendName], container)
|
||||
if len(container.ServiceName) > 0 {
|
||||
serviceNames[container.ServiceName] = 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
|
||||
}
|
214
provider/docker/deprecated_container.go
Normal file
214
provider/docker/deprecated_container.go
Normal file
|
@ -0,0 +1,214 @@
|
|||
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
|
||||
}
|
||||
|
||||
if values, err := label.GetStringMultipleStrict(container.Labels, labelDockerComposeProject, labelDockerComposeService); err == nil {
|
||||
return "Host:" + getSubDomain(values[labelDockerComposeService]+"."+values[labelDockerComposeProject]) + "." + p.Domain
|
||||
}
|
||||
|
||||
if len(p.Domain) > 0 {
|
||||
return "Host:" + getSubDomain(container.ServiceName) + "." + p.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
|
||||
}
|
965
provider/docker/deprecated_container_docker_test.go
Normal file
965
provider/docker/deprecated_container_docker_test.go
Normal file
|
@ -0,0 +1,965 @@
|
|||
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: 0,
|
||||
},
|
||||
},
|
||||
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.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
expected: "0",
|
||||
},
|
||||
{
|
||||
container: containerJSON(labels(map[string]string{
|
||||
label.TraefikWeight: "10",
|
||||
})),
|
||||
labelName: label.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
expected: "10",
|
||||
},
|
||||
}
|
||||
|
||||
for containerID, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.labelName+strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
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("bar")),
|
||||
expected: "Host:bar.docker.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.getIPAddress(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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
706
provider/docker/deprecated_container_swarm_test.go
Normal file
706
provider/docker/deprecated_container_swarm_test.go
Normal file
|
@ -0,0 +1,706 @@
|
|||
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: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
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.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
networks: map[string]*docker.NetworkResource{},
|
||||
expected: "0",
|
||||
},
|
||||
{
|
||||
service: swarmService(serviceLabels(map[string]string{
|
||||
label.TraefikWeight: "10",
|
||||
})),
|
||||
labelName: label.TraefikWeight,
|
||||
defaultValue: label.DefaultWeight,
|
||||
networks: map[string]*docker.NetworkResource{},
|
||||
expected: "10",
|
||||
},
|
||||
}
|
||||
|
||||
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("bar")),
|
||||
expected: "Host:bar.docker.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.getIPAddress(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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
231
provider/docker/deprecated_service.go
Normal file
231
provider/docker/deprecated_service.go
Normal file
|
@ -0,0 +1,231 @@
|
|||
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.SuffixFrontendBackend]; 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 hasStrictServiceLabelV1(serviceLabels map[string]string, labelSuffix string) bool {
|
||||
value, ok := serviceLabels[labelSuffix]
|
||||
return ok && len(value) > 0
|
||||
}
|
||||
|
||||
// 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 getStrictServiceStringValueV1(serviceLabels map[string]string, labelSuffix string, defaultValue string) string {
|
||||
if value, ok := serviceLabels[labelSuffix]; ok {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func getServiceMapValueV1(container dockerData, serviceLabels map[string]string, serviceName string, labelSuffix string) map[string]string {
|
||||
if value, ok := serviceLabels[labelSuffix]; ok {
|
||||
lblName := label.GetServiceLabel(labelSuffix, serviceName)
|
||||
return label.ParseMapValue(lblName, value)
|
||||
}
|
||||
return label.GetMapValue(container.Labels, label.Prefix+labelSuffix)
|
||||
}
|
||||
|
||||
// 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 getServiceInt64ValueV1(container dockerData, serviceLabels map[string]string, labelSuffix string, defaultValue int64) int64 {
|
||||
if rawValue, ok := serviceLabels[labelSuffix]; ok {
|
||||
value, err := strconv.ParseInt(rawValue, 10, 64)
|
||||
if err == nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return label.GetInt64Value(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
|
||||
}
|
|
@ -4,9 +4,7 @@ import (
|
|||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
docker "github.com/docker/docker/api/types"
|
||||
|
@ -15,7 +13,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDockerServiceBuildConfiguration(t *testing.T) {
|
||||
func TestDockerServiceBuildConfigurationV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
containers []docker.ContainerJSON
|
||||
|
@ -110,21 +108,6 @@ func TestDockerServiceBuildConfiguration(t *testing.T) {
|
|||
label.Prefix + "service." + label.SuffixFrontendHeadersContentTypeNosniff: "true",
|
||||
label.Prefix + "service." + label.SuffixFrontendHeadersBrowserXSSFilter: "true",
|
||||
label.Prefix + "service." + label.SuffixFrontendHeadersIsDevelopment: "true",
|
||||
|
||||
label.Prefix + "service." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageStatus: "404",
|
||||
label.Prefix + "service." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageBackend: "foobar",
|
||||
label.Prefix + "service." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageQuery: "foo_query",
|
||||
label.Prefix + "service." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageStatus: "500,600",
|
||||
label.Prefix + "service." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageBackend: "foobar",
|
||||
label.Prefix + "service." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageQuery: "bar_query",
|
||||
|
||||
label.Prefix + "service." + label.SuffixFrontendRateLimitExtractorFunc: "client.ip",
|
||||
label.Prefix + "service." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + "service." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + "service." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + "service." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + "service." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + "service." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
}),
|
||||
ports(nat.PortMap{
|
||||
"80/tcp": {},
|
||||
|
@ -146,84 +129,11 @@ func TestDockerServiceBuildConfiguration(t *testing.T) {
|
|||
"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,
|
||||
CustomBrowserXSSValue: "foo",
|
||||
ContentSecurityPolicy: "foo",
|
||||
PublicKey: "foo",
|
||||
ReferrerPolicy: "foo",
|
||||
IsDevelopment: true,
|
||||
},
|
||||
|
||||
Errors: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
Query: "foo_query",
|
||||
Backend: "foobar",
|
||||
},
|
||||
"bar": {
|
||||
Status: []string{"500", "600"},
|
||||
Query: "bar_query",
|
||||
Backend: "foobar",
|
||||
},
|
||||
},
|
||||
RateLimit: &types.RateLimit{
|
||||
ExtractorFunc: "client.ip",
|
||||
RateSet: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Regex: "",
|
||||
Replacement: "",
|
||||
Permanent: true,
|
||||
Regex: "nope",
|
||||
Replacement: "nope",
|
||||
},
|
||||
|
||||
Routes: map[string]types.Route{
|
||||
"service-service": {
|
||||
Rule: "Host:foo.docker.localhost",
|
||||
|
@ -344,7 +254,7 @@ func TestDockerServiceBuildConfiguration(t *testing.T) {
|
|||
dockerDataList = append(dockerDataList, dData)
|
||||
}
|
||||
|
||||
actualConfig := provider.buildConfiguration(dockerDataList)
|
||||
actualConfig := provider.buildConfigurationV1(dockerDataList)
|
||||
require.NotNil(t, actualConfig, "actualConfig")
|
||||
|
||||
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
|
||||
|
@ -353,7 +263,7 @@ func TestDockerServiceBuildConfiguration(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDockerGetFuncServiceStringLabel(t *testing.T) {
|
||||
func TestDockerGetFuncServiceStringLabelV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
container docker.ContainerJSON
|
||||
suffixLabel string
|
||||
|
@ -391,7 +301,7 @@ func TestDockerGetFuncServiceStringLabel(t *testing.T) {
|
|||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getFuncServiceStringLabel(test.suffixLabel, test.defaultValue)(dData, "myservice")
|
||||
actual := getFuncServiceStringLabelV1(test.suffixLabel, test.defaultValue)(dData, "myservice")
|
||||
if actual != test.expected {
|
||||
t.Errorf("got %q, expected %q", actual, test.expected)
|
||||
}
|
||||
|
@ -399,7 +309,7 @@ func TestDockerGetFuncServiceStringLabel(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDockerGetFuncServiceSliceStringLabel(t *testing.T) {
|
||||
func TestDockerGetFuncServiceSliceStringLabelV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
container docker.ContainerJSON
|
||||
suffixLabel string
|
||||
|
@ -433,7 +343,7 @@ func TestDockerGetFuncServiceSliceStringLabel(t *testing.T) {
|
|||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getFuncServiceSliceStringLabel(test.suffixLabel)(dData, "myservice")
|
||||
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)
|
||||
|
@ -442,7 +352,7 @@ func TestDockerGetFuncServiceSliceStringLabel(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceStringValue(t *testing.T) {
|
||||
func TestDockerGetServiceStringValueV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
|
@ -488,14 +398,14 @@ func TestDockerGetServiceStringValue(t *testing.T) {
|
|||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getServiceStringValue(dData, test.serviceLabels, test.labelSuffix, test.defaultValue)
|
||||
actual := getServiceStringValueV1(dData, test.serviceLabels, test.labelSuffix, test.defaultValue)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerHasStrictServiceLabel(t *testing.T) {
|
||||
func TestDockerHasStrictServiceLabelV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
serviceLabels map[string]string
|
||||
|
@ -523,14 +433,14 @@ func TestDockerHasStrictServiceLabel(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := hasStrictServiceLabel(test.serviceLabels, test.labelSuffix)
|
||||
actual := hasStrictServiceLabelV1(test.serviceLabels, test.labelSuffix)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetStrictServiceStringValue(t *testing.T) {
|
||||
func TestDockerGetStrictServiceStringValueV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
serviceLabels map[string]string
|
||||
|
@ -569,14 +479,14 @@ func TestDockerGetStrictServiceStringValue(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := getStrictServiceStringValue(test.serviceLabels, test.labelSuffix, test.defaultValue)
|
||||
actual := getStrictServiceStringValueV1(test.serviceLabels, test.labelSuffix, test.defaultValue)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceMapValue(t *testing.T) {
|
||||
func TestDockerGetServiceMapValueV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
|
@ -636,14 +546,14 @@ func TestDockerGetServiceMapValue(t *testing.T) {
|
|||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getServiceMapValue(dData, test.serviceLabels, test.serviceName, test.labelSuffix)
|
||||
actual := getServiceMapValueV1(dData, test.serviceLabels, test.serviceName, test.labelSuffix)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceSliceValue(t *testing.T) {
|
||||
func TestDockerGetServiceSliceValueV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
|
@ -694,14 +604,14 @@ func TestDockerGetServiceSliceValue(t *testing.T) {
|
|||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getServiceSliceValue(dData, test.serviceLabels, test.labelSuffix)
|
||||
actual := getServiceSliceValueV1(dData, test.serviceLabels, test.labelSuffix)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceBoolValue(t *testing.T) {
|
||||
func TestDockerGetServiceBoolValueV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
|
@ -755,14 +665,14 @@ func TestDockerGetServiceBoolValue(t *testing.T) {
|
|||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getServiceBoolValue(dData, test.serviceLabels, test.labelSuffix, test.defaultValue)
|
||||
actual := getServiceBoolValueV1(dData, test.serviceLabels, test.labelSuffix, test.defaultValue)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceInt64Value(t *testing.T) {
|
||||
func TestDockerGetServiceInt64ValueV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
|
@ -816,14 +726,14 @@ func TestDockerGetServiceInt64Value(t *testing.T) {
|
|||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getServiceInt64Value(dData, test.serviceLabels, test.labelSuffix, test.defaultValue)
|
||||
actual := getServiceInt64ValueV1(dData, test.serviceLabels, test.labelSuffix, test.defaultValue)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerCheckPortLabels(t *testing.T) {
|
||||
func TestDockerCheckPortLabelsV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
container docker.ContainerJSON
|
||||
expectedError bool
|
||||
|
@ -862,7 +772,7 @@ func TestDockerCheckPortLabels(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
err := checkServiceLabelPort(dData)
|
||||
err := checkServiceLabelPortV1(dData)
|
||||
|
||||
if test.expectedError && err == nil {
|
||||
t.Error("expected an error but got nil")
|
||||
|
@ -873,7 +783,7 @@ func TestDockerCheckPortLabels(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceBackendName(t *testing.T) {
|
||||
func TestDockerGetServiceBackendNameV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
|
@ -911,7 +821,7 @@ func TestDockerGetServiceBackendName(t *testing.T) {
|
|||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dData := parseContainer(test.container)
|
||||
actual := getServiceBackendName(dData, "myservice")
|
||||
actual := getServiceBackendNameV1(dData, "myservice")
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
|
@ -919,7 +829,7 @@ func TestDockerGetServiceBackendName(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceFrontendRule(t *testing.T) {
|
||||
func TestDockerGetServiceFrontendRuleV1(t *testing.T) {
|
||||
provider := &Provider{}
|
||||
|
||||
testCases := []struct {
|
||||
|
@ -949,7 +859,7 @@ func TestDockerGetServiceFrontendRule(t *testing.T) {
|
|||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dData := parseContainer(test.container)
|
||||
actual := provider.getServiceFrontendRule(dData, "myservice")
|
||||
actual := provider.getServiceFrontendRuleV1(dData, "myservice")
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
|
@ -957,7 +867,7 @@ func TestDockerGetServiceFrontendRule(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServicePort(t *testing.T) {
|
||||
func TestDockerGetServicePortV1(t *testing.T) {
|
||||
testCases := []struct {
|
||||
container docker.ContainerJSON
|
||||
expected string
|
||||
|
@ -985,420 +895,10 @@ func TestDockerGetServicePort(t *testing.T) {
|
|||
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dData := parseContainer(test.container)
|
||||
actual := getServicePort(dData, "myservice")
|
||||
actual := getServicePortV1(dData, "myservice")
|
||||
if actual != test.expected {
|
||||
t.Errorf("expected %q, got %q", test.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceRedirect(t *testing.T) {
|
||||
service := "rubiks"
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.Redirect
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no redirect labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should use only entry point tag when mix regex redirect and entry point redirect",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.Prefix + service + "." + label.SuffixFrontendRedirectEntryPoint: "https",
|
||||
label.Prefix + service + "." + label.SuffixFrontendRedirectRegex: "(.*)",
|
||||
label.Prefix + service + "." + label.SuffixFrontendRedirectReplacement: "$1",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when entry point redirect label",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.Prefix + service + "." + label.SuffixFrontendRedirectEntryPoint: "https",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when entry point redirect label (fallback to container labels)",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when regex redirect labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.Prefix + service + "." + label.SuffixFrontendRedirectRegex: "(.*)",
|
||||
label.Prefix + service + "." + label.SuffixFrontendRedirectReplacement: "$1",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
Regex: "(.*)",
|
||||
Replacement: "$1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when regex redirect labels (fallback to container labels)",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRedirectRegex: "(.*)",
|
||||
label.TraefikFrontendRedirectReplacement: "$1",
|
||||
}),
|
||||
),
|
||||
expected: &types.Redirect{
|
||||
Regex: "(.*)",
|
||||
Replacement: "$1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getServiceRedirect(dData, service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceHeaders(t *testing.T) {
|
||||
service := "rubiks"
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.Headers
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no custom headers options are set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when all custom headers options are set",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
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",
|
||||
}),
|
||||
),
|
||||
expected: &types.Headers{
|
||||
CustomRequestHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
CustomResponseHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
SSLProxyHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
AllowedHosts: []string{"foo", "bar", "bor"},
|
||||
HostsProxyHeaders: []string{"foo", "bar", "bor"},
|
||||
SSLHost: "foo",
|
||||
CustomFrameOptionsValue: "foo",
|
||||
ContentSecurityPolicy: "foo",
|
||||
PublicKey: "foo",
|
||||
ReferrerPolicy: "foo",
|
||||
CustomBrowserXSSValue: "foo",
|
||||
STSSeconds: 666,
|
||||
SSLRedirect: true,
|
||||
SSLTemporaryRedirect: true,
|
||||
STSIncludeSubdomains: true,
|
||||
STSPreload: true,
|
||||
ForceSTSHeader: true,
|
||||
FrameDeny: true,
|
||||
ContentTypeNosniff: true,
|
||||
BrowserXSSFilter: true,
|
||||
IsDevelopment: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when all custom headers options are set (fallback to container labels)",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.TraefikFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.TraefikFrontendSSLProxyHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
||||
label.TraefikFrontendAllowedHosts: "foo,bar,bor",
|
||||
label.TraefikFrontendHostsProxyHeaders: "foo,bar,bor",
|
||||
label.TraefikFrontendSSLHost: "foo",
|
||||
label.TraefikFrontendCustomFrameOptionsValue: "foo",
|
||||
label.TraefikFrontendContentSecurityPolicy: "foo",
|
||||
label.TraefikFrontendPublicKey: "foo",
|
||||
label.TraefikFrontendReferrerPolicy: "foo",
|
||||
label.TraefikFrontendCustomBrowserXSSValue: "foo",
|
||||
label.TraefikFrontendSTSSeconds: "666",
|
||||
label.TraefikFrontendSSLRedirect: "true",
|
||||
label.TraefikFrontendSSLTemporaryRedirect: "true",
|
||||
label.TraefikFrontendSTSIncludeSubdomains: "true",
|
||||
label.TraefikFrontendSTSPreload: "true",
|
||||
label.TraefikFrontendForceSTSHeader: "true",
|
||||
label.TraefikFrontendFrameDeny: "true",
|
||||
label.TraefikFrontendContentTypeNosniff: "true",
|
||||
label.TraefikFrontendBrowserXSSFilter: "true",
|
||||
label.TraefikFrontendIsDevelopment: "true",
|
||||
}),
|
||||
),
|
||||
expected: &types.Headers{
|
||||
CustomRequestHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
CustomResponseHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
SSLProxyHeaders: map[string]string{
|
||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
AllowedHosts: []string{"foo", "bar", "bor"},
|
||||
HostsProxyHeaders: []string{"foo", "bar", "bor"},
|
||||
SSLHost: "foo",
|
||||
CustomFrameOptionsValue: "foo",
|
||||
ContentSecurityPolicy: "foo",
|
||||
PublicKey: "foo",
|
||||
ReferrerPolicy: "foo",
|
||||
CustomBrowserXSSValue: "foo",
|
||||
STSSeconds: 666,
|
||||
SSLRedirect: true,
|
||||
SSLTemporaryRedirect: true,
|
||||
STSIncludeSubdomains: true,
|
||||
STSPreload: true,
|
||||
ForceSTSHeader: true,
|
||||
FrameDeny: true,
|
||||
ContentTypeNosniff: true,
|
||||
BrowserXSSFilter: true,
|
||||
IsDevelopment: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getServiceHeaders(dData, service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceRateLimit(t *testing.T) {
|
||||
service := "rubiks"
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
container docker.ContainerJSON
|
||||
expected *types.RateLimit
|
||||
}{
|
||||
{
|
||||
desc: "should return nil when no rate limit labels",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when rate limit labels are defined",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.Prefix + service + "." + label.SuffixFrontendRateLimitExtractorFunc: "client.ip",
|
||||
label.Prefix + service + "." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + service + "." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + service + "." + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + service + "." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + service + "." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + service + "." + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
})),
|
||||
expected: &types.RateLimit{
|
||||
ExtractorFunc: "client.ip",
|
||||
RateSet: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should return nil when ExtractorFunc is missing",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
})),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return a struct when rate limit labels are defined (fallback to container labels)",
|
||||
container: containerJSON(
|
||||
name("test1"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendRateLimitExtractorFunc: "client.ip",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitPeriod: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitAverage: "12",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "foo." + label.SuffixRateLimitBurst: "18",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitPeriod: "3",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitAverage: "6",
|
||||
label.Prefix + label.BaseFrontendRateLimit + "bar." + label.SuffixRateLimitBurst: "9",
|
||||
})),
|
||||
expected: &types.RateLimit{
|
||||
ExtractorFunc: "client.ip",
|
||||
RateSet: map[string]*types.Rate{
|
||||
"foo": {
|
||||
Period: flaeg.Duration(6 * time.Second),
|
||||
Average: 12,
|
||||
Burst: 18,
|
||||
},
|
||||
"bar": {
|
||||
Period: flaeg.Duration(3 * time.Second),
|
||||
Average: 6,
|
||||
Burst: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dData := parseContainer(test.container)
|
||||
|
||||
actual := getServiceRateLimit(dData, service)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerGetServiceErrorPages(t *testing.T) {
|
||||
service := "courgette"
|
||||
testCases := []struct {
|
||||
desc string
|
||||
data dockerData
|
||||
expected map[string]*types.ErrorPage
|
||||
}{
|
||||
{
|
||||
desc: "2 errors pages",
|
||||
data: parseContainer(containerJSON(
|
||||
labels(map[string]string{
|
||||
label.Prefix + service + "." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageStatus: "404",
|
||||
label.Prefix + service + "." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageBackend: "foo_backend",
|
||||
label.Prefix + service + "." + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageQuery: "foo_query",
|
||||
label.Prefix + service + "." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageStatus: "500,600",
|
||||
label.Prefix + service + "." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageBackend: "bar_backend",
|
||||
label.Prefix + service + "." + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageQuery: "bar_query",
|
||||
}))),
|
||||
expected: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
Query: "foo_query",
|
||||
Backend: "foo_backend",
|
||||
},
|
||||
"bar": {
|
||||
Status: []string{"500", "600"},
|
||||
Query: "bar_query",
|
||||
Backend: "bar_backend",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "only status field",
|
||||
data: parseContainer(containerJSON(
|
||||
labels(map[string]string{
|
||||
label.Prefix + service + ".frontend.errors.foo.status": "404",
|
||||
}))),
|
||||
expected: map[string]*types.ErrorPage{
|
||||
"foo": {
|
||||
Status: []string{"404"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pages := getServiceErrorPages(test.data, service)
|
||||
|
||||
assert.EqualValues(t, test.expected, pages)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -55,6 +55,8 @@ type dockerData struct {
|
|||
NetworkSettings networkSettings
|
||||
Health string
|
||||
Node *dockertypes.ContainerNode
|
||||
SegmentLabels map[string]string
|
||||
SegmentName string
|
||||
}
|
||||
|
||||
// NetworkSettings holds the networks data to the Provider p
|
||||
|
@ -84,12 +86,12 @@ func (p *Provider) createClient() (client.APIClient, error) {
|
|||
tr := &http.Transport{
|
||||
TLSClientConfig: config,
|
||||
}
|
||||
proto, addr, _, err := client.ParseHost(p.Endpoint)
|
||||
|
||||
hostURL, err := client.ParseHostURL(p.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sockets.ConfigureTransport(tr, proto, addr)
|
||||
sockets.ConfigureTransport(tr, hostURL.Scheme, hostURL.Host)
|
||||
|
||||
httpClient = &http.Client{
|
||||
Transport: tr,
|
||||
|
@ -290,7 +292,7 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
|
|||
|
||||
if container.ContainerJSONBase != nil {
|
||||
dData.Name = container.ContainerJSONBase.Name
|
||||
dData.ServiceName = dData.Name //Default ServiceName to be the container's Name.
|
||||
dData.ServiceName = dData.Name // Default ServiceName to be the container's Name.
|
||||
dData.Node = container.ContainerJSONBase.Node
|
||||
|
||||
if container.ContainerJSONBase.HostConfig != nil {
|
||||
|
|
|
@ -36,13 +36,6 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
// ServicesPropertiesRegexp used to extract the name of the service and the name of the property for this service
|
||||
// All properties are under the format traefik.<servicename>.frontend.*= except the port/portIndex/weight/protocol/backend directly after traefik.<servicename>.
|
||||
ServicesPropertiesRegexp = regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.(?P<property_name>port|portIndex|weight|protocol|backend|frontend\.(.+))$`)
|
||||
|
||||
// PortRegexp used to extract the port label of the service
|
||||
PortRegexp = regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.port$`)
|
||||
|
||||
// RegexpBaseFrontendErrorPage used to extract error pages from service's label
|
||||
RegexpBaseFrontendErrorPage = regexp.MustCompile(`^frontend\.errors\.(?P<name>[^ .]+)\.(?P<field>[^ .]+)$`)
|
||||
|
||||
|
@ -56,15 +49,6 @@ var (
|
|||
RegexpFrontendRateLimit = regexp.MustCompile(`^traefik\.frontend\.rateLimit\.rateSet\.(?P<name>[^ .]+)\.(?P<field>[^ .]+)$`)
|
||||
)
|
||||
|
||||
// ServicePropertyValues is a map of services properties
|
||||
// an example value is: weight=42
|
||||
type ServicePropertyValues map[string]string
|
||||
|
||||
// ServiceProperties is a map of service properties per service,
|
||||
// which we can get with label[serviceName][propertyName].
|
||||
// It yields a property value.
|
||||
type ServiceProperties map[string]ServicePropertyValues
|
||||
|
||||
// GetStringValue get string value associated to a label
|
||||
func GetStringValue(labels map[string]string, labelName string, defaultValue string) string {
|
||||
if value, ok := labels[labelName]; ok && len(value) > 0 {
|
||||
|
@ -245,56 +229,6 @@ func HasPrefixP(labels *map[string]string, prefix string) bool {
|
|||
return HasPrefix(*labels, prefix)
|
||||
}
|
||||
|
||||
// FindServiceSubmatch split service label
|
||||
func FindServiceSubmatch(name string) []string {
|
||||
matches := ServicesPropertiesRegexp.FindStringSubmatch(name)
|
||||
if matches == nil ||
|
||||
strings.HasPrefix(name, TraefikFrontend+".") ||
|
||||
strings.HasPrefix(name, TraefikBackend+".") {
|
||||
return nil
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
// ExtractServiceProperties Extract services labels
|
||||
func ExtractServiceProperties(labels map[string]string) ServiceProperties {
|
||||
v := make(ServiceProperties)
|
||||
|
||||
for name, value := range labels {
|
||||
matches := FindServiceSubmatch(name)
|
||||
if matches == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var serviceName string
|
||||
var propertyName string
|
||||
for i, name := range ServicesPropertiesRegexp.SubexpNames() {
|
||||
if i != 0 {
|
||||
if name == "service_name" {
|
||||
serviceName = matches[i]
|
||||
} else if name == "property_name" {
|
||||
propertyName = matches[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := v[serviceName]; !ok {
|
||||
v[serviceName] = make(ServicePropertyValues)
|
||||
}
|
||||
v[serviceName][propertyName] = value
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// ExtractServicePropertiesP Extract services labels
|
||||
func ExtractServicePropertiesP(labels *map[string]string) ServiceProperties {
|
||||
if labels == nil {
|
||||
return make(ServiceProperties)
|
||||
}
|
||||
return ExtractServiceProperties(*labels)
|
||||
}
|
||||
|
||||
// ParseErrorPages parse error pages to create ErrorPage struct
|
||||
func ParseErrorPages(labels map[string]string, labelPrefix string, labelRegex *regexp.Regexp) map[string]*types.ErrorPage {
|
||||
var errorPages map[string]*types.ErrorPage
|
||||
|
@ -420,14 +354,3 @@ func SplitAndTrimString(base string, sep string) []string {
|
|||
|
||||
return trimmedStrings
|
||||
}
|
||||
|
||||
// GetServiceLabel converts a key value of Label*, given a serviceName,
|
||||
// into a pattern <LabelPrefix>.<serviceName>.<property>
|
||||
// i.e. For LabelFrontendRule and serviceName=app it will return "traefik.app.frontend.rule"
|
||||
func GetServiceLabel(labelName, serviceName string) string {
|
||||
if len(serviceName) > 0 {
|
||||
property := strings.TrimPrefix(labelName, Prefix)
|
||||
return Prefix + serviceName + "." + property
|
||||
}
|
||||
return labelName
|
||||
}
|
||||
|
|
|
@ -731,11 +731,11 @@ func TestExtractServiceProperties(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
expected ServiceProperties
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "empty labels map",
|
||||
expected: ServiceProperties{},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
{
|
||||
desc: "valid label names",
|
||||
|
@ -744,8 +744,8 @@ func TestExtractServiceProperties(t *testing.T) {
|
|||
"traefik.foo.frontend.bar": "1bar",
|
||||
"traefik.foo.backend": "3bar",
|
||||
},
|
||||
expected: ServiceProperties{
|
||||
"foo": ServicePropertyValues{
|
||||
expected: SegmentProperties{
|
||||
"foo": SegmentPropertyValues{
|
||||
"port": "bar",
|
||||
"frontend.bar": "1bar",
|
||||
"backend": "3bar",
|
||||
|
@ -761,7 +761,7 @@ func TestExtractServiceProperties(t *testing.T) {
|
|||
"traefik.foo.frontend": "0bar",
|
||||
"traefik.frontend.foo.backend": "0bar",
|
||||
},
|
||||
expected: ServiceProperties{},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
|
@ -779,11 +779,11 @@ func TestExtractServicePropertiesP(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
labels *map[string]string
|
||||
expected ServiceProperties
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
expected: ServiceProperties{},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
{
|
||||
desc: "valid label names",
|
||||
|
@ -792,8 +792,8 @@ func TestExtractServicePropertiesP(t *testing.T) {
|
|||
"traefik.foo.frontend.bar": "1bar",
|
||||
"traefik.foo.backend": "3bar",
|
||||
},
|
||||
expected: ServiceProperties{
|
||||
"foo": ServicePropertyValues{
|
||||
expected: SegmentProperties{
|
||||
"foo": SegmentPropertyValues{
|
||||
"port": "bar",
|
||||
"frontend.bar": "1bar",
|
||||
"backend": "3bar",
|
||||
|
@ -809,7 +809,7 @@ func TestExtractServicePropertiesP(t *testing.T) {
|
|||
"traefik.foo.frontend": "0bar",
|
||||
"traefik.frontend.foo.backend": "0bar",
|
||||
},
|
||||
expected: ServiceProperties{},
|
||||
expected: SegmentProperties{},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
|
@ -1137,3 +1137,91 @@ func TestParseRateSets(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractTraefikLabels(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
prefix string
|
||||
originLabels map[string]string
|
||||
expected SegmentProperties
|
||||
}{
|
||||
{
|
||||
desc: "nil labels map",
|
||||
prefix: "traefik",
|
||||
originLabels: nil,
|
||||
expected: SegmentProperties{"": {}},
|
||||
},
|
||||
{
|
||||
desc: "container labels",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"frontend.priority": "foo", // missing prefix: skip
|
||||
"traefik.port": "bar",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"": {
|
||||
"traefik.port": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: only segment no default",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.goo.frontend.priority": "A",
|
||||
"traefik.goo.port": "D",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"goo": {
|
||||
"traefik.frontend.priority": "A",
|
||||
"traefik.port": "D",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: use default",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.guu.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"guu": {
|
||||
"traefik.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "segment labels: several segments",
|
||||
prefix: "traefik",
|
||||
originLabels: map[string]string{
|
||||
"traefik.goo.frontend.priority": "A",
|
||||
"traefik.goo.port": "D",
|
||||
"traefik.guu.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
expected: SegmentProperties{
|
||||
"goo": {
|
||||
"traefik.frontend.priority": "A",
|
||||
"traefik.port": "D",
|
||||
},
|
||||
"guu": {
|
||||
"traefik.frontend.priority": "B",
|
||||
"traefik.port": "C",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := ExtractTraefikLabels(test.originLabels)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,6 @@ const (
|
|||
SuffixFrontendRedirectReplacement = "frontend.redirect.replacement"
|
||||
SuffixFrontendRedirectPermanent = "frontend.redirect.permanent"
|
||||
SuffixFrontendRule = "frontend.rule"
|
||||
SuffixFrontendRuleType = "frontend.rule.type"
|
||||
SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange"
|
||||
TraefikDomain = Prefix + SuffixDomain
|
||||
TraefikEnable = Prefix + SuffixEnable
|
||||
|
@ -96,6 +95,7 @@ const (
|
|||
TraefikBackendBufferingRetryExpression = Prefix + SuffixBackendBufferingRetryExpression
|
||||
TraefikFrontend = Prefix + SuffixFrontend
|
||||
TraefikFrontendAuthBasic = Prefix + SuffixFrontendAuthBasic
|
||||
TraefikFrontendBackend = Prefix + SuffixFrontendBackend
|
||||
TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints
|
||||
TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader
|
||||
TraefikFrontendPassTLSCert = Prefix + SuffixFrontendPassTLSCert
|
||||
|
@ -106,9 +106,7 @@ const (
|
|||
TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement
|
||||
TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent
|
||||
TraefikFrontendRule = Prefix + SuffixFrontendRule
|
||||
TraefikFrontendRuleType = Prefix + SuffixFrontendRuleType // k8s only
|
||||
TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange
|
||||
TraefikFrontendHeaders = Prefix + SuffixFrontendHeaders
|
||||
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
|
||||
TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders
|
||||
TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts
|
||||
|
|
167
provider/label/segment.go
Normal file
167
provider/label/segment.go
Normal file
|
@ -0,0 +1,167 @@
|
|||
package label
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// SegmentPropertiesRegexp used to extract the name of the segment and the name of the property for this segment
|
||||
// All properties are under the format traefik.<segment_name>.frontend.*= except the port/portIndex/weight/protocol/backend directly after traefik.<segment_name>.
|
||||
SegmentPropertiesRegexp = regexp.MustCompile(`^traefik\.(?P<segment_name>.+?)\.(?P<property_name>port|portIndex|weight|protocol|backend|frontend\.(.+))$`)
|
||||
|
||||
// PortRegexp used to extract the port label of the segment
|
||||
PortRegexp = regexp.MustCompile(`^traefik\.(?P<segment_name>.+?)\.port$`)
|
||||
)
|
||||
|
||||
// SegmentPropertyValues is a map of segment properties
|
||||
// an example value is: weight=42
|
||||
type SegmentPropertyValues map[string]string
|
||||
|
||||
// SegmentProperties is a map of segment properties per segment,
|
||||
// which we can get with label[segmentName][propertyName].
|
||||
// It yields a property value.
|
||||
type SegmentProperties map[string]SegmentPropertyValues
|
||||
|
||||
// FindSegmentSubmatch split segment labels
|
||||
func FindSegmentSubmatch(name string) []string {
|
||||
matches := SegmentPropertiesRegexp.FindStringSubmatch(name)
|
||||
if matches == nil ||
|
||||
strings.HasPrefix(name, TraefikFrontend+".") ||
|
||||
strings.HasPrefix(name, TraefikBackend+".") {
|
||||
return nil
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
// ExtractServicePropertiesP Extract services labels
|
||||
// Deprecated
|
||||
func ExtractServicePropertiesP(labels *map[string]string) SegmentProperties {
|
||||
if labels == nil {
|
||||
return make(SegmentProperties)
|
||||
}
|
||||
return ExtractServiceProperties(*labels)
|
||||
}
|
||||
|
||||
// ExtractServiceProperties Extract services labels
|
||||
// Deprecated
|
||||
func ExtractServiceProperties(labels map[string]string) SegmentProperties {
|
||||
v := make(SegmentProperties)
|
||||
|
||||
for name, value := range labels {
|
||||
matches := FindSegmentSubmatch(name)
|
||||
if matches == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var segmentName string
|
||||
var propertyName string
|
||||
for i, name := range SegmentPropertiesRegexp.SubexpNames() {
|
||||
// the group 0 is anonymous because it's always the root expression
|
||||
if i != 0 {
|
||||
if name == "segment_name" {
|
||||
segmentName = matches[i]
|
||||
} else if name == "property_name" {
|
||||
propertyName = matches[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := v[segmentName]; !ok {
|
||||
v[segmentName] = make(SegmentPropertyValues)
|
||||
}
|
||||
v[segmentName][propertyName] = value
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// GetServiceLabel converts a key value of Label*, given a serviceName,
|
||||
// into a pattern <LabelPrefix>.<serviceName>.<property>
|
||||
// i.e. For LabelFrontendRule and serviceName=app it will return "traefik.app.frontend.rule"
|
||||
// Deprecated
|
||||
func GetServiceLabel(labelName, serviceName string) string {
|
||||
if len(serviceName) > 0 {
|
||||
property := strings.TrimPrefix(labelName, Prefix)
|
||||
return Prefix + serviceName + "." + property
|
||||
}
|
||||
return labelName
|
||||
}
|
||||
|
||||
// ExtractTraefikLabels transform labels to segment labels
|
||||
func ExtractTraefikLabels(originLabels map[string]string) SegmentProperties {
|
||||
allLabels := make(SegmentProperties)
|
||||
|
||||
if _, ok := allLabels[""]; !ok {
|
||||
allLabels[""] = make(SegmentPropertyValues)
|
||||
}
|
||||
|
||||
for name, value := range originLabels {
|
||||
if !strings.HasPrefix(name, Prefix) {
|
||||
continue
|
||||
}
|
||||
|
||||
matches := FindSegmentSubmatch(name)
|
||||
if matches == nil {
|
||||
// Classic labels
|
||||
allLabels[""][name] = value
|
||||
} else {
|
||||
// segments labels
|
||||
var segmentName string
|
||||
var propertyName string
|
||||
for i, name := range SegmentPropertiesRegexp.SubexpNames() {
|
||||
// the group 0 is anonymous because it's always the root expression
|
||||
if i != 0 {
|
||||
if name == "segment_name" {
|
||||
segmentName = matches[i]
|
||||
} else if name == "property_name" {
|
||||
propertyName = matches[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := allLabels[segmentName]; !ok {
|
||||
allLabels[segmentName] = make(SegmentPropertyValues)
|
||||
}
|
||||
allLabels[segmentName][Prefix+propertyName] = value
|
||||
}
|
||||
}
|
||||
log.Debug(originLabels, allLabels)
|
||||
|
||||
allLabels.mergeDefault()
|
||||
|
||||
return allLabels
|
||||
}
|
||||
|
||||
func (s SegmentProperties) mergeDefault() {
|
||||
// if SegmentProperties contains the default segment, merge each segments with the default segment
|
||||
if defaultLabels, okDefault := s[""]; okDefault {
|
||||
|
||||
segmentsNames := s.GetSegmentNames()
|
||||
if len(defaultLabels) > 0 {
|
||||
for _, name := range segmentsNames {
|
||||
segmentLabels := s[name]
|
||||
for key, value := range defaultLabels {
|
||||
if _, ok := segmentLabels[key]; !ok {
|
||||
segmentLabels[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(segmentsNames) > 1 {
|
||||
delete(s, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetSegmentNames get all segment names
|
||||
func (s SegmentProperties) GetSegmentNames() []string {
|
||||
var names []string
|
||||
for name := range s {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
|
@ -28,6 +28,7 @@ type BaseProvider struct {
|
|||
Filename string `description:"Override default configuration template. For advanced users :)" export:"true"`
|
||||
Constraints types.Constraints `description:"Filter services by constraint, matching with Traefik tags." export:"true"`
|
||||
Trace bool `description:"Display additional provider logs (if available)." export:"true"`
|
||||
TemplateVersion int `description:"Template version." export:"true"`
|
||||
DebugLogGeneratedTemplate bool `description:"Enable debug logging of generated configuration template." export:"true"`
|
||||
}
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ func (r *Rules) parseRules(expression string, onRule func(functionName string, f
|
|||
}
|
||||
|
||||
if len(expression) == 0 {
|
||||
return errors.New("Empty rule")
|
||||
return errors.New("empty rule")
|
||||
}
|
||||
|
||||
f := func(c rune) bool {
|
||||
|
@ -208,16 +208,18 @@ func (r *Rules) parseRules(expression string, onRule func(functionName string, f
|
|||
if len(parsedFunctions) == 0 {
|
||||
return fmt.Errorf("error parsing rule: '%s'", rule)
|
||||
}
|
||||
|
||||
functionName := strings.TrimSpace(parsedFunctions[0])
|
||||
parsedFunction, ok := functions[functionName]
|
||||
if !ok {
|
||||
return fmt.Errorf("error parsing rule: '%s'. Unknown function: '%s'", rule, parsedFunctions[0])
|
||||
}
|
||||
parsedFunctions = append(parsedFunctions[:0], parsedFunctions[1:]...)
|
||||
|
||||
// get function
|
||||
fargs := func(c rune) bool {
|
||||
return c == ','
|
||||
}
|
||||
// get function
|
||||
parsedArgs := strings.FieldsFunc(strings.Join(parsedFunctions, ":"), fargs)
|
||||
if len(parsedArgs) == 0 {
|
||||
return fmt.Errorf("error parsing args from rule: '%s'", rule)
|
||||
|
@ -229,7 +231,7 @@ func (r *Rules) parseRules(expression string, onRule func(functionName string, f
|
|||
|
||||
err := onRule(functionName, parsedFunction, parsedArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Parsing error on rule: %v", err)
|
||||
return fmt.Errorf("parsing error on rule: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -238,6 +240,7 @@ func (r *Rules) parseRules(expression string, onRule func(functionName string, f
|
|||
// Parse parses rules expressions
|
||||
func (r *Rules) Parse(expression string) (*mux.Route, error) {
|
||||
var resultRoute *mux.Route
|
||||
|
||||
err := r.parseRules(expression, func(functionName string, function interface{}, arguments []string) error {
|
||||
inputs := make([]reflect.Value, len(arguments))
|
||||
for i := range arguments {
|
||||
|
@ -252,21 +255,22 @@ func (r *Rules) Parse(expression string) (*mux.Route, error) {
|
|||
if resultRoute.GetError() != nil {
|
||||
return resultRoute.GetError()
|
||||
}
|
||||
|
||||
} else {
|
||||
return fmt.Errorf("Method not found: '%s'", functionName)
|
||||
return fmt.Errorf("method not found: '%s'", functionName)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing rule: %v", err)
|
||||
}
|
||||
|
||||
return resultRoute, nil
|
||||
}
|
||||
|
||||
// ParseDomains parses rules expressions and returns domains
|
||||
func (r *Rules) ParseDomains(expression string) ([]string, error) {
|
||||
domains := []string{}
|
||||
var domains []string
|
||||
|
||||
err := r.parseRules(expression, func(functionName string, function interface{}, arguments []string) error {
|
||||
if functionName == "Host" {
|
||||
domains = append(domains, arguments...)
|
||||
|
@ -276,5 +280,6 @@ func (r *Rules) ParseDomains(expression string) ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing domains: %v", err)
|
||||
}
|
||||
|
||||
return fun.Map(types.CanonicalDomain, domains).([]string), nil
|
||||
}
|
||||
|
|
192
templates/docker-v1.tmpl
Normal file
192
templates/docker-v1.tmpl
Normal file
|
@ -0,0 +1,192 @@
|
|||
{{$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,14 +1,15 @@
|
|||
{{$backendServers := .Servers}}
|
||||
[backends]
|
||||
{{range $backendName, $backend := .Backends}}
|
||||
{{range $backendName, $servers := .Servers}}
|
||||
{{ $backend := index $servers 0 }}
|
||||
|
||||
{{ $circuitBreaker := getCircuitBreaker $backend }}
|
||||
{{ $circuitBreaker := getCircuitBreaker $backend.SegmentLabels }}
|
||||
{{if $circuitBreaker }}
|
||||
[backends."backend-{{ $backendName }}".circuitBreaker]
|
||||
expression = "{{ $circuitBreaker.Expression }}"
|
||||
{{end}}
|
||||
|
||||
{{ $loadBalancer := getLoadBalancer $backend }}
|
||||
{{ $loadBalancer := getLoadBalancer $backend.SegmentLabels }}
|
||||
{{if $loadBalancer }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer]
|
||||
method = "{{ $loadBalancer.Method }}"
|
||||
|
@ -19,14 +20,14 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $maxConn := getMaxConn $backend }}
|
||||
{{ $maxConn := getMaxConn $backend.SegmentLabels }}
|
||||
{{if $maxConn }}
|
||||
[backends."backend-{{ $backendName }}".maxConn]
|
||||
extractorFunc = "{{ $maxConn.ExtractorFunc }}"
|
||||
amount = {{ $maxConn.Amount }}
|
||||
{{end}}
|
||||
|
||||
{{ $healthCheck := getHealthCheck $backend }}
|
||||
{{ $healthCheck := getHealthCheck $backend.SegmentLabels }}
|
||||
{{if $healthCheck }}
|
||||
[backends."backend-{{ $backendName }}".healthCheck]
|
||||
path = "{{ $healthCheck.Path }}"
|
||||
|
@ -34,7 +35,7 @@
|
|||
interval = "{{ $healthCheck.Interval }}"
|
||||
{{end}}
|
||||
|
||||
{{ $buffering := getBuffering $backend }}
|
||||
{{ $buffering := getBuffering $backend.SegmentLabels }}
|
||||
{{if $buffering }}
|
||||
[backends."backend-{{ $backendName }}".buffering]
|
||||
maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }}
|
||||
|
@ -44,173 +45,40 @@
|
|||
retryExpression = "{{ $buffering.RetryExpression }}"
|
||||
{{end}}
|
||||
|
||||
{{ $servers := index $backendServers $backendName }}
|
||||
{{range $serverName, $server := $servers }}
|
||||
{{if hasServices $server }}
|
||||
{{ $services := getServiceNames $server }}
|
||||
{{range $serviceIndex, $serviceName := $services }}
|
||||
[backends."backend-{{ getServiceBackendName $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}}
|
||||
{{range $serverName, $server := getServers $servers }}
|
||||
[backends."backend-{{ $backendName }}".servers."{{ $serverName }}"]
|
||||
url = "{{ $server.URL }}"
|
||||
weight = {{ $server.Weight }}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
[frontends]
|
||||
{{range $frontendName, $containers := .Frontends }}
|
||||
{{$container := index $containers 0}}
|
||||
|
||||
{{if hasServices $container }}
|
||||
{{ $services := getServiceNames $container }}
|
||||
|
||||
{{range $serviceIndex, $serviceName := $services }}
|
||||
{{ $ServiceFrontendName := getServiceBackendName $container $serviceName }}
|
||||
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}"]
|
||||
backend = "backend-{{ $ServiceFrontendName }}"
|
||||
priority = {{ getServicePriority $container $serviceName }}
|
||||
passHostHeader = {{ getServicePassHostHeader $container $serviceName }}
|
||||
passTLSCert = {{ getServicePassTLSCert $container $serviceName }}
|
||||
|
||||
entryPoints = [{{range getServiceEntryPoints $container $serviceName }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $whitelistSourceRange := getServiceWhitelistSourceRange $container $serviceName }}
|
||||
{{if $whitelistSourceRange }}
|
||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
basicAuth = [{{range getServiceBasicAuth $container $serviceName }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $redirect := getServiceRedirect $container $serviceName }}
|
||||
{{if $redirect }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".redirect]
|
||||
entryPoint = "{{ $redirect.EntryPoint }}"
|
||||
regex = "{{ $redirect.Regex }}"
|
||||
replacement = "{{ $redirect.Replacement }}"
|
||||
permanent = {{ $redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{ $errorPages := getServiceErrorPages $container $serviceName }}
|
||||
{{if $errorPages }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".errors]
|
||||
{{ range $pageName, $page := $errorPages }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".errors."{{ $pageName }}"]
|
||||
status = [{{range $page.Status }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
backend = "{{ $page.Backend }}"
|
||||
query = "{{ $page.Query }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $rateLimit := getServiceRateLimit $container $serviceName }}
|
||||
{{if $rateLimit }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".rateLimit]
|
||||
extractorFunc = "{{ $rateLimit.ExtractorFunc }}"
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".rateLimit.rateSet]
|
||||
{{range $limitName, $limit := $rateLimit.RateSet }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".rateLimit.rateSet."{{ $limitName }}"]
|
||||
period = "{{ $limit.Period }}"
|
||||
average = {{ $limit.Average }}
|
||||
burst = {{ $limit.Burst }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $headers := getServiceHeaders $container $serviceName }}
|
||||
{{if $headers }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".headers]
|
||||
SSLRedirect = {{ $headers.SSLRedirect }}
|
||||
SSLTemporaryRedirect = {{ $headers.SSLTemporaryRedirect }}
|
||||
SSLHost = "{{ $headers.SSLHost }}"
|
||||
STSSeconds = {{ $headers.STSSeconds }}
|
||||
STSIncludeSubdomains = {{ $headers.STSIncludeSubdomains }}
|
||||
STSPreload = {{ $headers.STSPreload }}
|
||||
ForceSTSHeader = {{ $headers.ForceSTSHeader }}
|
||||
FrameDeny = {{ $headers.FrameDeny }}
|
||||
CustomFrameOptionsValue = "{{ $headers.CustomFrameOptionsValue }}"
|
||||
ContentTypeNosniff = {{ $headers.ContentTypeNosniff }}
|
||||
BrowserXSSFilter = {{ $headers.BrowserXSSFilter }}
|
||||
CustomBrowserXSSValue = "{{ $headers.CustomBrowserXSSValue }}"
|
||||
ContentSecurityPolicy = "{{ $headers.ContentSecurityPolicy }}"
|
||||
PublicKey = "{{ $headers.PublicKey }}"
|
||||
ReferrerPolicy = "{{ $headers.ReferrerPolicy }}"
|
||||
IsDevelopment = {{ $headers.IsDevelopment }}
|
||||
|
||||
{{if $headers.AllowedHosts }}
|
||||
AllowedHosts = [{{range $headers.AllowedHosts }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $headers.HostsProxyHeaders }}
|
||||
HostsProxyHeaders = [{{range $headers.HostsProxyHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $headers.CustomRequestHeaders }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".headers.customRequestHeaders]
|
||||
{{range $k, $v := $headers.CustomRequestHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $headers.CustomResponseHeaders }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".headers.customResponseHeaders]
|
||||
{{range $k, $v := $headers.CustomResponseHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $headers.SSLProxyHeaders }}
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".headers.SSLProxyHeaders]
|
||||
{{range $k, $v := $headers.SSLProxyHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends."frontend-{{ $ServiceFrontendName }}".routes."service-{{ $serviceName | replace "/" "" | replace "." "-" }}"]
|
||||
rule = "{{ getServiceFrontendRule $container $serviceName }}"
|
||||
|
||||
{{end}} ## end range services
|
||||
|
||||
{{else}}
|
||||
{{ $container := index $containers 0 }}
|
||||
|
||||
[frontends."frontend-{{ $frontendName }}"]
|
||||
backend = "backend-{{ getBackendName $container }}"
|
||||
priority = {{ getPriority $container }}
|
||||
passHostHeader = {{ getPassHostHeader $container }}
|
||||
passTLSCert = {{ getPassTLSCert $container }}
|
||||
priority = {{ getPriority $container.SegmentLabels }}
|
||||
passHostHeader = {{ getPassHostHeader $container.SegmentLabels }}
|
||||
passTLSCert = {{ getPassTLSCert $container.SegmentLabels }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $container }}
|
||||
entryPoints = [{{range getEntryPoints $container.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $whitelistSourceRange := getWhitelistSourceRange $container}}
|
||||
{{ $whitelistSourceRange := getWhitelistSourceRange $container.SegmentLabels }}
|
||||
{{if $whitelistSourceRange }}
|
||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
basicAuth = [{{range getBasicAuth $container }}
|
||||
basicAuth = [{{range getBasicAuth $container.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $redirect := getRedirect $container }}
|
||||
{{ $redirect := getRedirect $container.SegmentLabels }}
|
||||
{{if $redirect }}
|
||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||
entryPoint = "{{ $redirect.EntryPoint }}"
|
||||
|
@ -219,7 +87,7 @@
|
|||
permanent = {{ $redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{ $errorPages := getErrorPages $container }}
|
||||
{{ $errorPages := getErrorPages $container.SegmentLabels }}
|
||||
{{if $errorPages }}
|
||||
[frontends."frontend-{{ $frontendName }}".errors]
|
||||
{{range $pageName, $page := $errorPages }}
|
||||
|
@ -232,12 +100,12 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $rateLimit := getRateLimit $container }}
|
||||
{{ $rateLimit := getRateLimit $container.SegmentLabels }}
|
||||
{{if $rateLimit }}
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit]
|
||||
extractorFunc = "{{ $rateLimit.ExtractorFunc }}"
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit.rateSet]
|
||||
{{range $limitName, $limit := $rateLimit.RateSet }}
|
||||
{{ range $limitName, $limit := $rateLimit.RateSet }}
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit.rateSet."{{ $limitName }}"]
|
||||
period = "{{ $limit.Period }}"
|
||||
average = {{ $limit.Average }}
|
||||
|
@ -245,7 +113,7 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $headers := getHeaders $container }}
|
||||
{{ $headers := getHeaders $container.SegmentLabels }}
|
||||
{{if $headers }}
|
||||
[frontends."frontend-{{ $frontendName }}".headers]
|
||||
SSLRedirect = {{ $headers.SSLRedirect }}
|
||||
|
@ -259,8 +127,8 @@
|
|||
CustomFrameOptionsValue = "{{ $headers.CustomFrameOptionsValue }}"
|
||||
ContentTypeNosniff = {{ $headers.ContentTypeNosniff }}
|
||||
BrowserXSSFilter = {{ $headers.BrowserXSSFilter }}
|
||||
CustomBrowserXSSValue = "{{ $headers.CustomBrowserXSSValue }}"
|
||||
ContentSecurityPolicy = "{{ $headers.ContentSecurityPolicy }}"
|
||||
CustomBrowserXSSValue = "{{ $headers.CustomBrowserXSSValue }}"
|
||||
PublicKey = "{{ $headers.PublicKey }}"
|
||||
ReferrerPolicy = "{{ $headers.ReferrerPolicy }}"
|
||||
IsDevelopment = {{ $headers.IsDevelopment }}
|
||||
|
@ -297,11 +165,10 @@
|
|||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
[frontends."frontend-{{ $frontendName }}".routes."route-frontend-{{ $frontendName }}"]
|
||||
rule = "{{ getFrontendRule $container }}"
|
||||
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
|
Loading…
Reference in a new issue