feat(docker): add error pages labels.
This commit is contained in:
parent
50757b5e99
commit
c30ebe5f90
9 changed files with 413 additions and 18 deletions
|
@ -49,6 +49,8 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
||||||
"getRedirectEntryPoint": getFuncStringLabel(label.TraefikFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint),
|
"getRedirectEntryPoint": getFuncStringLabel(label.TraefikFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint),
|
||||||
"getRedirectRegex": getFuncStringLabel(label.TraefikFrontendRedirectRegex, ""),
|
"getRedirectRegex": getFuncStringLabel(label.TraefikFrontendRedirectRegex, ""),
|
||||||
"getRedirectReplacement": getFuncStringLabel(label.TraefikFrontendRedirectReplacement, ""),
|
"getRedirectReplacement": getFuncStringLabel(label.TraefikFrontendRedirectReplacement, ""),
|
||||||
|
"hasErrorPages": hasErrorPages,
|
||||||
|
"getErrorPages": getErrorPages,
|
||||||
// Headers
|
// Headers
|
||||||
"hasRequestHeaders": hasFunc(label.TraefikFrontendRequestHeaders),
|
"hasRequestHeaders": hasFunc(label.TraefikFrontendRequestHeaders),
|
||||||
"getRequestHeaders": getFuncMapLabel(label.TraefikFrontendRequestHeaders),
|
"getRequestHeaders": getFuncMapLabel(label.TraefikFrontendRequestHeaders),
|
||||||
|
@ -114,6 +116,8 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
||||||
"getServiceRequestHeaders": getFuncServiceMapLabel(label.SuffixFrontendRequestHeaders),
|
"getServiceRequestHeaders": getFuncServiceMapLabel(label.SuffixFrontendRequestHeaders),
|
||||||
"hasServiceResponseHeaders": hasFuncServiceLabel(label.SuffixFrontendResponseHeaders),
|
"hasServiceResponseHeaders": hasFuncServiceLabel(label.SuffixFrontendResponseHeaders),
|
||||||
"getServiceResponseHeaders": getFuncServiceMapLabel(label.SuffixFrontendResponseHeaders),
|
"getServiceResponseHeaders": getFuncServiceMapLabel(label.SuffixFrontendResponseHeaders),
|
||||||
|
"hasServiceErrorPages": hasServiceErrorPages,
|
||||||
|
"getServiceErrorPages": getServiceErrorPages,
|
||||||
}
|
}
|
||||||
// filter containers
|
// filter containers
|
||||||
filteredContainers := fun.Filter(func(container dockerData) bool {
|
filteredContainers := fun.Filter(func(container dockerData) bool {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/provider"
|
"github.com/containous/traefik/provider"
|
||||||
"github.com/containous/traefik/provider/label"
|
"github.com/containous/traefik/provider/label"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -172,6 +173,15 @@ func hasRedirect(container dockerData) bool {
|
||||||
label.Has(container.Labels, label.TraefikFrontendRedirectReplacement) && label.Has(container.Labels, label.TraefikFrontendRedirectRegex)
|
label.Has(container.Labels, label.TraefikFrontendRedirectReplacement) && label.Has(container.Labels, label.TraefikFrontendRedirectRegex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasErrorPages(container dockerData) bool {
|
||||||
|
return label.HasPrefix(container.Labels, label.Prefix+label.BaseFrontendErrorPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getErrorPages(container dockerData) map[string]*types.ErrorPage {
|
||||||
|
prefix := label.Prefix + label.BaseFrontendErrorPage
|
||||||
|
return label.ParseErrorPages(container.Labels, prefix, label.RegexpFrontendErrorPage)
|
||||||
|
}
|
||||||
|
|
||||||
// Label functions
|
// Label functions
|
||||||
|
|
||||||
func getFuncInt64Label(labelName string, defaultValue int64) func(container dockerData) int64 {
|
func getFuncInt64Label(labelName string, defaultValue int64) func(container dockerData) int64 {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDockerLoadDockerConfig(t *testing.T) {
|
func TestDockerBuildConfiguration(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
containers []docker.ContainerJSON
|
containers []docker.ContainerJSON
|
||||||
expectedFrontends map[string]*types.Frontend
|
expectedFrontends map[string]*types.Frontend
|
||||||
|
@ -181,6 +181,61 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
containers: []docker.ContainerJSON{
|
||||||
|
containerJSON(
|
||||||
|
name("test1"),
|
||||||
|
labels(map[string]string{
|
||||||
|
label.TraefikBackend: "foobar",
|
||||||
|
label.Prefix + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageStatus: "404",
|
||||||
|
label.Prefix + label.BaseFrontendErrorPage + "foo." + label.SuffixErrorPageBackend: "foobar",
|
||||||
|
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: "foobar",
|
||||||
|
label.Prefix + label.BaseFrontendErrorPage + "bar." + label.SuffixErrorPageQuery: "bar_query",
|
||||||
|
}),
|
||||||
|
ports(nat.PortMap{
|
||||||
|
"80/tcp": {},
|
||||||
|
}),
|
||||||
|
withNetwork("bridge", ipv4("127.0.0.1")),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
expectedFrontends: map[string]*types.Frontend{
|
||||||
|
"frontend-Host-test1-docker-localhost-0": {
|
||||||
|
EntryPoints: []string{},
|
||||||
|
BasicAuth: []string{},
|
||||||
|
PassHostHeader: true,
|
||||||
|
Backend: "backend-foobar",
|
||||||
|
Routes: map[string]types.Route{
|
||||||
|
"route-frontend-Host-test1-docker-localhost-0": {
|
||||||
|
Rule: "Host:test1.docker.localhost",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Errors: map[string]types.ErrorPage{
|
||||||
|
"foo": {
|
||||||
|
Status: []string{"404"},
|
||||||
|
Query: "foo_query",
|
||||||
|
Backend: "foobar",
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
Status: []string{"500", "600"},
|
||||||
|
Query: "bar_query",
|
||||||
|
Backend: "foobar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedBackends: map[string]*types.Backend{
|
||||||
|
"backend-foobar": {
|
||||||
|
Servers: map[string]types.Server{
|
||||||
|
"server-test1": {
|
||||||
|
URL: "http://127.0.0.1:80",
|
||||||
|
Weight: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for caseID, test := range testCases {
|
for caseID, test := range testCases {
|
||||||
|
@ -897,3 +952,59 @@ func TestDockerGetPort(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/containous/traefik/provider"
|
"github.com/containous/traefik/provider"
|
||||||
"github.com/containous/traefik/provider/label"
|
"github.com/containous/traefik/provider/label"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Specific functions
|
// Specific functions
|
||||||
|
@ -50,7 +51,8 @@ func checkServiceLabelPort(container dockerData) error {
|
||||||
}
|
}
|
||||||
// Get only one instance of all service names from service labels
|
// Get only one instance of all service names from service labels
|
||||||
servicesLabelNames := label.ServicesPropertiesRegexp.FindStringSubmatch(lbl)
|
servicesLabelNames := label.ServicesPropertiesRegexp.FindStringSubmatch(lbl)
|
||||||
if len(servicesLabelNames) > 0 {
|
|
||||||
|
if len(servicesLabelNames) > 0 && !strings.HasPrefix(lbl, label.TraefikFrontend) {
|
||||||
serviceLabels[strings.Split(servicesLabelNames[0], ".")[1]] = struct{}{}
|
serviceLabels[strings.Split(servicesLabelNames[0], ".")[1]] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,6 +98,16 @@ func hasServiceRedirect(container dockerData, serviceName string) bool {
|
||||||
label.Has(serviceLabels, label.SuffixFrontendRedirectRegex) && label.Has(serviceLabels, label.SuffixFrontendRedirectReplacement)
|
label.Has(serviceLabels, label.SuffixFrontendRedirectRegex) && label.Has(serviceLabels, label.SuffixFrontendRedirectReplacement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasServiceErrorPages(container dockerData, serviceName string) bool {
|
||||||
|
serviceLabels := getServiceLabels(container, serviceName)
|
||||||
|
return label.HasPrefix(serviceLabels, label.BaseFrontendErrorPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServiceErrorPages(container dockerData, serviceName string) map[string]*types.ErrorPage {
|
||||||
|
serviceLabels := getServiceLabels(container, serviceName)
|
||||||
|
return label.ParseErrorPages(serviceLabels, label.BaseFrontendErrorPage, label.RegexpBaseFrontendErrorPage)
|
||||||
|
}
|
||||||
|
|
||||||
// Service label functions
|
// Service label functions
|
||||||
|
|
||||||
func getFuncServiceMapLabel(labelSuffix string) func(container dockerData, serviceName string) map[string]string {
|
func getFuncServiceMapLabel(labelSuffix string) func(container dockerData, serviceName string) map[string]string {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/traefik/provider/label"
|
"github.com/containous/traefik/provider/label"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
docker "github.com/docker/docker/api/types"
|
docker "github.com/docker/docker/api/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -219,3 +220,60 @@ func TestDockerCheckPortLabels(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetServiceErrorPages(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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -30,12 +31,20 @@ const (
|
||||||
DefaultBackendHealthCheckPort = 0
|
DefaultBackendHealthCheckPort = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServicesPropertiesRegexp used to extract the name of the service and the name of the property for this service
|
var (
|
||||||
// All properties are under the format traefik.<servicename>.frontend.*= except the port/portIndex/weight/protocol/backend directly after traefik.<servicename>.
|
// ServicesPropertiesRegexp used to extract the name of the service and the name of the property for this service
|
||||||
var ServicesPropertiesRegexp = regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.(?P<property_name>port|portIndex|weight|protocol|backend|frontend\.(.+))$`)
|
// 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 used to extract the port label of the service
|
||||||
var PortRegexp = regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.port$`)
|
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>[^ .]+)$`)
|
||||||
|
|
||||||
|
// RegexpFrontendErrorPage used to extract error pages from label
|
||||||
|
RegexpFrontendErrorPage = regexp.MustCompile(`^traefik\.frontend\.errors\.(?P<name>[^ .]+)\.(?P<field>[^ .]+)$`)
|
||||||
|
)
|
||||||
|
|
||||||
// ServicePropertyValues is a map of services properties
|
// ServicePropertyValues is a map of services properties
|
||||||
// an example value is: weight=42
|
// an example value is: weight=42
|
||||||
|
@ -200,13 +209,23 @@ func HasP(labels *map[string]string, labelName string) bool {
|
||||||
return Has(*labels, labelName)
|
return Has(*labels, labelName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasPrefix Check if a value is associated to a less one label with a prefix
|
||||||
|
func HasPrefix(labels map[string]string, prefix string) bool {
|
||||||
|
for name, value := range labels {
|
||||||
|
if strings.HasPrefix(name, prefix) && len(value) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractServiceProperties Extract services labels
|
// ExtractServiceProperties Extract services labels
|
||||||
func ExtractServiceProperties(labels map[string]string) ServiceProperties {
|
func ExtractServiceProperties(labels map[string]string) ServiceProperties {
|
||||||
v := make(ServiceProperties)
|
v := make(ServiceProperties)
|
||||||
|
|
||||||
for name, value := range labels {
|
for name, value := range labels {
|
||||||
matches := ServicesPropertiesRegexp.FindStringSubmatch(name)
|
matches := ServicesPropertiesRegexp.FindStringSubmatch(name)
|
||||||
if matches == nil {
|
if matches == nil || strings.HasPrefix(name, TraefikFrontend) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +258,43 @@ func ExtractServicePropertiesP(labels *map[string]string) ServiceProperties {
|
||||||
return ExtractServiceProperties(*labels)
|
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 {
|
||||||
|
errorPages := make(map[string]*types.ErrorPage)
|
||||||
|
|
||||||
|
for lblName, value := range labels {
|
||||||
|
if strings.HasPrefix(lblName, labelPrefix) {
|
||||||
|
submatch := labelRegex.FindStringSubmatch(lblName)
|
||||||
|
if len(submatch) != 3 {
|
||||||
|
log.Errorf("Invalid page error label: %s, sub-match: %v", lblName, submatch)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pageName := submatch[1]
|
||||||
|
|
||||||
|
ep, ok := errorPages[pageName]
|
||||||
|
if !ok {
|
||||||
|
ep = &types.ErrorPage{}
|
||||||
|
errorPages[pageName] = ep
|
||||||
|
}
|
||||||
|
|
||||||
|
switch submatch[2] {
|
||||||
|
case SuffixErrorPageStatus:
|
||||||
|
ep.Status = SplitAndTrimString(value, ",")
|
||||||
|
case SuffixErrorPageQuery:
|
||||||
|
ep.Query = value
|
||||||
|
case SuffixErrorPageBackend:
|
||||||
|
ep.Backend = value
|
||||||
|
default:
|
||||||
|
log.Errorf("Invalid page error label: %s", lblName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorPages
|
||||||
|
}
|
||||||
|
|
||||||
// IsEnabled Check if a container is enabled in Træfik
|
// IsEnabled Check if a container is enabled in Træfik
|
||||||
func IsEnabled(labels map[string]string, exposedByDefault bool) bool {
|
func IsEnabled(labels map[string]string, exposedByDefault bool) bool {
|
||||||
return GetBoolValue(labels, TraefikEnable, exposedByDefault)
|
return GetBoolValue(labels, TraefikEnable, exposedByDefault)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package label
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -739,22 +740,24 @@ func TestExtractServiceProperties(t *testing.T) {
|
||||||
labels: map[string]string{
|
labels: map[string]string{
|
||||||
"traefik.foo.port": "bar",
|
"traefik.foo.port": "bar",
|
||||||
"traefik.foo.frontend.bar": "1bar",
|
"traefik.foo.frontend.bar": "1bar",
|
||||||
"traefik.foo.frontend.": "2bar",
|
"traefik.foo.backend": "3bar",
|
||||||
},
|
},
|
||||||
expected: ServiceProperties{
|
expected: ServiceProperties{
|
||||||
"foo": ServicePropertyValues{
|
"foo": ServicePropertyValues{
|
||||||
"port": "bar",
|
"port": "bar",
|
||||||
"frontend.bar": "1bar",
|
"frontend.bar": "1bar",
|
||||||
|
"backend": "3bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "invalid label names",
|
desc: "invalid label names",
|
||||||
labels: map[string]string{
|
labels: map[string]string{
|
||||||
"foo.frontend.bar": "1bar",
|
"foo.frontend.bar": "1bar",
|
||||||
"traefik.foo.frontend.": "2bar",
|
"traefik.foo.frontend.": "2bar",
|
||||||
"traefik.foo.port.bar": "barbar",
|
"traefik.foo.port.bar": "barbar",
|
||||||
"traefik.foo.frontend": "0bar",
|
"traefik.foo.frontend": "0bar",
|
||||||
|
"traefik.frontend.foo.backend": "0bar",
|
||||||
},
|
},
|
||||||
expected: ServiceProperties{},
|
expected: ServiceProperties{},
|
||||||
},
|
},
|
||||||
|
@ -785,22 +788,24 @@ func TestExtractServicePropertiesP(t *testing.T) {
|
||||||
labels: &map[string]string{
|
labels: &map[string]string{
|
||||||
"traefik.foo.port": "bar",
|
"traefik.foo.port": "bar",
|
||||||
"traefik.foo.frontend.bar": "1bar",
|
"traefik.foo.frontend.bar": "1bar",
|
||||||
"traefik.foo.frontend.": "2bar",
|
"traefik.foo.backend": "3bar",
|
||||||
},
|
},
|
||||||
expected: ServiceProperties{
|
expected: ServiceProperties{
|
||||||
"foo": ServicePropertyValues{
|
"foo": ServicePropertyValues{
|
||||||
"port": "bar",
|
"port": "bar",
|
||||||
"frontend.bar": "1bar",
|
"frontend.bar": "1bar",
|
||||||
|
"backend": "3bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "invalid label names",
|
desc: "invalid label names",
|
||||||
labels: &map[string]string{
|
labels: &map[string]string{
|
||||||
"foo.frontend.bar": "1bar",
|
"foo.frontend.bar": "1bar",
|
||||||
"traefik.foo.frontend.": "2bar",
|
"traefik.foo.frontend.": "2bar",
|
||||||
"traefik.foo.port.bar": "barbar",
|
"traefik.foo.port.bar": "barbar",
|
||||||
"traefik.foo.frontend": "0bar",
|
"traefik.foo.frontend": "0bar",
|
||||||
|
"traefik.frontend.foo.backend": "0bar",
|
||||||
},
|
},
|
||||||
expected: ServiceProperties{},
|
expected: ServiceProperties{},
|
||||||
},
|
},
|
||||||
|
@ -967,3 +972,112 @@ func TestGetServiceLabel(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHasPrefix(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
prefix string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "nil labels map",
|
||||||
|
prefix: "foo",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "nonexistent prefix",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo.carotte": "bar",
|
||||||
|
},
|
||||||
|
prefix: "fii",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existent prefix",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo.carotte": "bar",
|
||||||
|
},
|
||||||
|
prefix: "foo",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "existent prefix with empty value",
|
||||||
|
labels: map[string]string{
|
||||||
|
"foo.carotte": "",
|
||||||
|
},
|
||||||
|
prefix: "foo",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := HasPrefix(test.labels, test.prefix)
|
||||||
|
assert.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseErrorPages(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
expected map[string]*types.ErrorPage
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "2 errors pages",
|
||||||
|
labels: map[string]string{
|
||||||
|
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageStatus: "404",
|
||||||
|
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageBackend: "foo_backend",
|
||||||
|
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageQuery: "foo_query",
|
||||||
|
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageStatus: "500,600",
|
||||||
|
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageBackend: "bar_backend",
|
||||||
|
Prefix + BaseFrontendErrorPage + "bar." + SuffixErrorPageQuery: "bar_query",
|
||||||
|
},
|
||||||
|
expected: map[string]*types.ErrorPage{
|
||||||
|
"foo": {
|
||||||
|
Status: []string{"404"},
|
||||||
|
Query: "foo_query",
|
||||||
|
Backend: "foo_backend",
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
Status: []string{"500", "600"},
|
||||||
|
Query: "bar_query",
|
||||||
|
Backend: "bar_backend",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "only status field",
|
||||||
|
labels: map[string]string{
|
||||||
|
Prefix + BaseFrontendErrorPage + "foo." + SuffixErrorPageStatus: "404",
|
||||||
|
},
|
||||||
|
expected: map[string]*types.ErrorPage{
|
||||||
|
"foo": {
|
||||||
|
Status: []string{"404"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid field",
|
||||||
|
labels: map[string]string{
|
||||||
|
Prefix + BaseFrontendErrorPage + "foo." + "courgette": "404",
|
||||||
|
},
|
||||||
|
expected: map[string]*types.ErrorPage{"foo": {}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
pages := ParseErrorPages(test.labels, Prefix+BaseFrontendErrorPage, RegexpFrontendErrorPage)
|
||||||
|
|
||||||
|
assert.EqualValues(t, test.expected, pages)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ const (
|
||||||
SuffixBackendLoadBalancerStickinessCookieName = "backend.loadbalancer.stickiness.cookieName"
|
SuffixBackendLoadBalancerStickinessCookieName = "backend.loadbalancer.stickiness.cookieName"
|
||||||
SuffixBackendMaxConnAmount = "backend.maxconn.amount"
|
SuffixBackendMaxConnAmount = "backend.maxconn.amount"
|
||||||
SuffixBackendMaxConnExtractorFunc = "backend.maxconn.extractorfunc"
|
SuffixBackendMaxConnExtractorFunc = "backend.maxconn.extractorfunc"
|
||||||
|
SuffixFrontend = "frontend"
|
||||||
SuffixFrontendAuthBasic = "frontend.auth.basic"
|
SuffixFrontendAuthBasic = "frontend.auth.basic"
|
||||||
SuffixFrontendBackend = "frontend.backend"
|
SuffixFrontendBackend = "frontend.backend"
|
||||||
SuffixFrontendEntryPoints = "frontend.entryPoints"
|
SuffixFrontendEntryPoints = "frontend.entryPoints"
|
||||||
|
@ -76,6 +77,7 @@ const (
|
||||||
TraefikBackendLoadBalancerStickinessCookieName = Prefix + SuffixBackendLoadBalancerStickinessCookieName
|
TraefikBackendLoadBalancerStickinessCookieName = Prefix + SuffixBackendLoadBalancerStickinessCookieName
|
||||||
TraefikBackendMaxConnAmount = Prefix + SuffixBackendMaxConnAmount
|
TraefikBackendMaxConnAmount = Prefix + SuffixBackendMaxConnAmount
|
||||||
TraefikBackendMaxConnExtractorFunc = Prefix + SuffixBackendMaxConnExtractorFunc
|
TraefikBackendMaxConnExtractorFunc = Prefix + SuffixBackendMaxConnExtractorFunc
|
||||||
|
TraefikFrontend = Prefix + SuffixFrontend
|
||||||
TraefikFrontendAuthBasic = Prefix + SuffixFrontendAuthBasic
|
TraefikFrontendAuthBasic = Prefix + SuffixFrontendAuthBasic
|
||||||
TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints
|
TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints
|
||||||
TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader
|
TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader
|
||||||
|
@ -108,4 +110,8 @@ const (
|
||||||
TraefikFrontendPublicKey = Prefix + SuffixFrontendHeadersPublicKey
|
TraefikFrontendPublicKey = Prefix + SuffixFrontendHeadersPublicKey
|
||||||
TraefikFrontendReferrerPolicy = Prefix + SuffixFrontendHeadersReferrerPolicy
|
TraefikFrontendReferrerPolicy = Prefix + SuffixFrontendHeadersReferrerPolicy
|
||||||
TraefikFrontendIsDevelopment = Prefix + SuffixFrontendHeadersIsDevelopment
|
TraefikFrontendIsDevelopment = Prefix + SuffixFrontendHeadersIsDevelopment
|
||||||
|
BaseFrontendErrorPage = "frontend.errors."
|
||||||
|
SuffixErrorPageBackend = "backend"
|
||||||
|
SuffixErrorPageQuery = "query"
|
||||||
|
SuffixErrorPageStatus = "status"
|
||||||
)
|
)
|
||||||
|
|
|
@ -83,6 +83,18 @@
|
||||||
replacement = "{{getServiceRedirectReplacement $container $serviceName}}"
|
replacement = "{{getServiceRedirectReplacement $container $serviceName}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{ if hasServiceErrorPages $container $serviceName }}
|
||||||
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".errors]
|
||||||
|
{{ range $pageName, $page := getServiceErrorPages $container $serviceName }}
|
||||||
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".errors.{{$pageName}}]
|
||||||
|
status = [{{range $page.Status}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
backend = "{{$page.Backend}}"
|
||||||
|
query = "{{$page.Query}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
||||||
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
||||||
|
|
||||||
|
@ -131,6 +143,18 @@
|
||||||
replacement = "{{getRedirectReplacement $container}}"
|
replacement = "{{getRedirectReplacement $container}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{ if hasErrorPages $container }}
|
||||||
|
[frontends."frontend-{{$frontend}}".errors]
|
||||||
|
{{ range $pageName, $page := getErrorPages $container }}
|
||||||
|
[frontends."frontend-{{$frontend}}".errors.{{ $pageName }}]
|
||||||
|
status = [{{range $page.Status}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
backend = "{{$page.Backend}}"
|
||||||
|
query = "{{$page.Query}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
[frontends."frontend-{{$frontend}}".headers]
|
[frontends."frontend-{{$frontend}}".headers]
|
||||||
{{if hasSSLRedirectHeaders $container}}
|
{{if hasSSLRedirectHeaders $container}}
|
||||||
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
||||||
|
|
Loading…
Reference in a new issue