From a9d4b09bdb330356ce26ab48bdba85ffe34fd363 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 10 Oct 2017 11:10:02 +0200 Subject: [PATCH] Stickiness cookie name --- provider/consul/consul_catalog.go | 47 ++++++++++----- provider/docker/docker.go | 26 +++++--- provider/dynamodb/dynamodb.go | 6 +- provider/ecs/ecs.go | 42 ++++++++----- provider/kubernetes/kubernetes.go | 12 +++- provider/kubernetes/kubernetes_test.go | 27 +-------- provider/kv/kv.go | 26 ++++++-- provider/marathon/marathon.go | 22 +++++-- provider/marathon/marathon_test.go | 27 +++++++-- provider/provider.go | 2 +- provider/rancher/rancher.go | 22 +++++-- server/cookie/cookie.go | 57 ++++++++++++++++++ server/cookie/cookie_test.go | 83 ++++++++++++++++++++++++++ server/server.go | 67 +++++++++------------ server/server_test.go | 52 +++++++--------- templates/consul_catalog.tmpl | 6 +- templates/docker.tmpl | 5 +- templates/ecs.tmpl | 5 +- templates/kubernetes.tmpl | 5 +- templates/kv.tmpl | 18 +++--- templates/marathon.tmpl | 5 +- templates/rancher.tmpl | 5 +- types/common_label.go | 81 +++++++++---------------- types/types.go | 10 +++- 24 files changed, 434 insertions(+), 224 deletions(-) create mode 100644 server/cookie/cookie.go create mode 100644 server/cookie/cookie_test.go diff --git a/provider/consul/consul_catalog.go b/provider/consul/consul_catalog.go index 0d7876358..dd2c75156 100644 --- a/provider/consul/consul_catalog.go +++ b/provider/consul/consul_catalog.go @@ -385,10 +385,6 @@ func (p *CatalogProvider) getBackendName(node *api.ServiceEntry, index int) stri return serviceName } -func (p *CatalogProvider) getAttribute(name string, tags []string, defaultValue string) string { - return p.getTag(p.getPrefixedName(name), tags, defaultValue) -} - func (p *CatalogProvider) getBasicAuth(tags []string) []string { list := p.getAttribute("frontend.auth.basic", tags, "") if list != "" { @@ -397,6 +393,27 @@ func (p *CatalogProvider) getBasicAuth(tags []string) []string { return []string{} } +func (p *CatalogProvider) hasStickinessLabel(tags []string) bool { + stickinessTag := p.getTag(types.LabelBackendLoadbalancerStickiness, tags, "") + + stickyTag := p.getTag(types.LabelBackendLoadbalancerSticky, tags, "") + if len(stickyTag) > 0 { + log.Warn("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness) + } + + stickiness := len(stickinessTag) > 0 && strings.EqualFold(strings.TrimSpace(stickinessTag), "true") + sticky := len(stickyTag) > 0 && strings.EqualFold(strings.TrimSpace(stickyTag), "true") + return stickiness || sticky +} + +func (p *CatalogProvider) getStickinessCookieName(tags []string) string { + return p.getTag(types.LabelBackendLoadbalancerStickinessCookieName, tags, "") +} + +func (p *CatalogProvider) getAttribute(name string, tags []string, defaultValue string) string { + return p.getTag(p.getPrefixedName(name), tags, defaultValue) +} + func (p *CatalogProvider) hasTag(name string, tags []string) bool { // Very-very unlikely that a Consul tag would ever start with '=!=' tag := p.getTag(name, tags, "=!=") @@ -439,16 +456,18 @@ func (p *CatalogProvider) getConstraintTags(tags []string) []string { func (p *CatalogProvider) buildConfig(catalog []catalogUpdate) *types.Configuration { var FuncMap = template.FuncMap{ - "getBackend": p.getBackend, - "getFrontendRule": p.getFrontendRule, - "getBackendName": p.getBackendName, - "getBackendAddress": p.getBackendAddress, - "getAttribute": p.getAttribute, - "getBasicAuth": p.getBasicAuth, - "getTag": p.getTag, - "hasTag": p.hasTag, - "getEntryPoints": p.getEntryPoints, - "hasMaxconnAttributes": p.hasMaxconnAttributes, + "getBackend": p.getBackend, + "getFrontendRule": p.getFrontendRule, + "getBackendName": p.getBackendName, + "getBackendAddress": p.getBackendAddress, + "getBasicAuth": p.getBasicAuth, + "hasStickinessLabel": p.hasStickinessLabel, + "getStickinessCookieName": p.getStickinessCookieName, + "getAttribute": p.getAttribute, + "getTag": p.getTag, + "hasTag": p.hasTag, + "getEntryPoints": p.getEntryPoints, + "hasMaxconnAttributes": p.hasMaxconnAttributes, } allNodes := []*api.ServiceEntry{} diff --git a/provider/docker/docker.go b/provider/docker/docker.go index 1dec90b4b..ec0b7f707 100644 --- a/provider/docker/docker.go +++ b/provider/docker/docker.go @@ -275,7 +275,8 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con "hasMaxConnLabels": p.hasMaxConnLabels, "getMaxConnAmount": p.getMaxConnAmount, "getMaxConnExtractorFunc": p.getMaxConnExtractorFunc, - "getSticky": p.getSticky, + "getStickinessCookieName": p.getStickinessCookieName, + "hasStickinessLabel": p.hasStickinessLabel, "getIsBackendLBSwarm": p.getIsBackendLBSwarm, "hasServices": p.hasServices, "getServiceNames": p.getServiceNames, @@ -328,10 +329,8 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con } func (p *Provider) hasCircuitBreakerLabel(container dockerData) bool { - if _, err := getLabel(container, types.LabelBackendCircuitbreakerExpression); err != nil { - return false - } - return true + _, err := getLabel(container, types.LabelBackendCircuitbreakerExpression) + return err == nil } // Regexp used to extract the name of the service and the name of the property for this service @@ -645,11 +644,22 @@ func (p *Provider) getWeight(container dockerData) string { return "0" } -func (p *Provider) getSticky(container dockerData) string { - if label, err := getLabel(container, types.LabelBackendLoadbalancerSticky); err == nil { +func (p *Provider) hasStickinessLabel(container dockerData) bool { + _, errStickiness := getLabel(container, types.LabelBackendLoadbalancerStickiness) + + label, errSticky := getLabel(container, types.LabelBackendLoadbalancerSticky) + if len(label) > 0 { + log.Warn("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness) + } + + return errStickiness == nil || (errSticky == nil && strings.EqualFold(strings.TrimSpace(label), "true")) +} + +func (p *Provider) getStickinessCookieName(container dockerData) string { + if label, err := getLabel(container, types.LabelBackendLoadbalancerStickinessCookieName); err == nil { return label } - return "false" + return "" } func (p *Provider) getIsBackendLBSwarm(container dockerData) string { diff --git a/provider/dynamodb/dynamodb.go b/provider/dynamodb/dynamodb.go index 62070aa81..c021b598b 100644 --- a/provider/dynamodb/dynamodb.go +++ b/provider/dynamodb/dynamodb.go @@ -162,12 +162,12 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s }) operation := func() error { - aws, err := p.createClient() + awsClient, err := p.createClient() if err != nil { return handleCanceled(ctx, err) } - configuration, err := p.loadDynamoConfig(aws) + configuration, err := p.loadDynamoConfig(awsClient) if err != nil { return handleCanceled(ctx, err) } @@ -184,7 +184,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s log.Debug("Watching Provider...") select { case <-reload.C: - configuration, err := p.loadDynamoConfig(aws) + configuration, err := p.loadDynamoConfig(awsClient) if err != nil { return handleCanceled(ctx, err) } diff --git a/provider/ecs/ecs.go b/provider/ecs/ecs.go index 3c543d9b9..a8bb03365 100644 --- a/provider/ecs/ecs.go +++ b/provider/ecs/ecs.go @@ -122,12 +122,12 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s }) operation := func() error { - aws, err := p.createClient() + awsClient, err := p.createClient() if err != nil { return err } - configuration, err := p.loadECSConfig(ctx, aws) + configuration, err := p.loadECSConfig(ctx, awsClient) if err != nil { return handleCanceled(ctx, err) } @@ -143,7 +143,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s for { select { case <-reload.C: - configuration, err := p.loadECSConfig(ctx, aws) + configuration, err := p.loadECSConfig(ctx, awsClient) if err != nil { return handleCanceled(ctx, err) } @@ -180,11 +180,12 @@ func wrapAws(ctx context.Context, req *request.Request) error { func (p *Provider) loadECSConfig(ctx context.Context, client *awsClient) (*types.Configuration, error) { var ecsFuncMap = template.FuncMap{ - "filterFrontends": p.filterFrontends, - "getFrontendRule": p.getFrontendRule, - "getBasicAuth": p.getBasicAuth, - "getLoadBalancerSticky": p.getLoadBalancerSticky, - "getLoadBalancerMethod": p.getLoadBalancerMethod, + "filterFrontends": p.filterFrontends, + "getFrontendRule": p.getFrontendRule, + "getBasicAuth": p.getBasicAuth, + "getLoadBalancerMethod": p.getLoadBalancerMethod, + "hasStickinessLabel": p.hasStickinessLabel, + "getStickinessCookieName": p.getStickinessCookieName, } instances, err := p.listInstances(ctx, client) @@ -477,14 +478,27 @@ func (p *Provider) getBasicAuth(i ecsInstance) []string { return []string{} } -func (p *Provider) getLoadBalancerSticky(instances []ecsInstance) string { +func getFirstInstanceLabel(instances []ecsInstance, labelName string) string { if len(instances) > 0 { - label := instances[0].label(types.LabelBackendLoadbalancerSticky) - if label != "" { - return label - } + return instances[0].label(labelName) } - return "false" + return "" +} + +func (p *Provider) hasStickinessLabel(instances []ecsInstance) bool { + stickinessLabel := getFirstInstanceLabel(instances, types.LabelBackendLoadbalancerStickiness) + + stickyLabel := getFirstInstanceLabel(instances, types.LabelBackendLoadbalancerSticky) + if len(stickyLabel) > 0 { + log.Warn("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness) + } + stickiness := len(stickinessLabel) > 0 && strings.EqualFold(strings.TrimSpace(stickinessLabel), "true") + sticky := len(stickyLabel) > 0 && strings.EqualFold(strings.TrimSpace(stickyLabel), "true") + return stickiness || sticky +} + +func (p *Provider) getStickinessCookieName(instances []ecsInstance) string { + return getFirstInstanceLabel(instances, types.LabelBackendLoadbalancerStickinessCookieName) } func (p *Provider) getLoadBalancerMethod(instances []ecsInstance) string { diff --git a/provider/kubernetes/kubernetes.go b/provider/kubernetes/kubernetes.go index d6cdd1782..e5f5a65b5 100644 --- a/provider/kubernetes/kubernetes.go +++ b/provider/kubernetes/kubernetes.go @@ -18,6 +18,7 @@ import ( "github.com/containous/traefik/log" "github.com/containous/traefik/provider" "github.com/containous/traefik/safe" + "github.com/containous/traefik/server/cookie" "github.com/containous/traefik/types" "k8s.io/client-go/pkg/api/v1" "k8s.io/client-go/pkg/apis/extensions/v1beta1" @@ -160,7 +161,6 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) templateObjects.Backends[r.Host+pa.Path] = &types.Backend{ Servers: make(map[string]types.Server), LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, } @@ -247,8 +247,14 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) templateObjects.Backends[r.Host+pa.Path].LoadBalancer.Method = "drr" } - if service.Annotations[types.LabelBackendLoadbalancerSticky] == "true" { - templateObjects.Backends[r.Host+pa.Path].LoadBalancer.Sticky = true + if len(service.Annotations[types.LabelBackendLoadbalancerSticky]) > 0 { + log.Warn("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness) + } + + if service.Annotations[types.LabelBackendLoadbalancerSticky] == "true" || service.Annotations[types.LabelBackendLoadbalancerStickiness] == "true" { + templateObjects.Backends[r.Host+pa.Path].LoadBalancer.Stickiness = &types.Stickiness{ + CookieName: cookie.GenerateName(r.Host + pa.Path), + } } protocol := "http" diff --git a/provider/kubernetes/kubernetes_test.go b/provider/kubernetes/kubernetes_test.go index 823f6df47..e8df03ea1 100644 --- a/provider/kubernetes/kubernetes_test.go +++ b/provider/kubernetes/kubernetes_test.go @@ -243,7 +243,6 @@ func TestLoadIngresses(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -256,7 +255,6 @@ func TestLoadIngresses(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -273,7 +271,6 @@ func TestLoadIngresses(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -489,7 +486,6 @@ func TestGetPassHostHeader(t *testing.T) { Servers: map[string]types.Server{}, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -591,7 +587,6 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) { Servers: map[string]types.Server{}, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -771,7 +766,6 @@ func TestLoadNamespacedIngresses(t *testing.T) { Servers: map[string]types.Server{}, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -779,7 +773,6 @@ func TestLoadNamespacedIngresses(t *testing.T) { Servers: map[string]types.Server{}, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -996,7 +989,6 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { Servers: map[string]types.Server{}, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1004,7 +996,6 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { Servers: map[string]types.Server{}, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1012,7 +1003,6 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { Servers: map[string]types.Server{}, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1118,7 +1108,6 @@ func TestHostlessIngress(t *testing.T) { Servers: map[string]types.Server{}, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1320,7 +1309,6 @@ func TestServiceAnnotations(t *testing.T) { }, LoadBalancer: &types.LoadBalancer{ Method: "drr", - Sticky: false, }, }, "bar": { @@ -1337,7 +1325,9 @@ func TestServiceAnnotations(t *testing.T) { CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ Method: "wrr", - Sticky: true, + Stickiness: &types.Stickiness{ + CookieName: "_4155f", + }, }, }, }, @@ -1601,7 +1591,6 @@ func TestIngressAnnotations(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1614,7 +1603,6 @@ func TestIngressAnnotations(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1627,7 +1615,6 @@ func TestIngressAnnotations(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1640,7 +1627,6 @@ func TestIngressAnnotations(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1653,7 +1639,6 @@ func TestIngressAnnotations(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1807,7 +1792,6 @@ func TestPriorityHeaderValue(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -1909,7 +1893,6 @@ func TestInvalidPassHostHeaderValue(t *testing.T) { }, CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ - Sticky: false, Method: "wrr", }, }, @@ -2192,14 +2175,12 @@ func TestMissingResources(t *testing.T) { CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ Method: "wrr", - Sticky: false, }, }, "missing_service": { Servers: map[string]types.Server{}, LoadBalancer: &types.LoadBalancer{ Method: "wrr", - Sticky: false, }, }, "missing_endpoints": { @@ -2207,7 +2188,6 @@ func TestMissingResources(t *testing.T) { CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ Method: "wrr", - Sticky: false, }, }, "missing_endpoint_subsets": { @@ -2215,7 +2195,6 @@ func TestMissingResources(t *testing.T) { CircuitBreaker: nil, LoadBalancer: &types.LoadBalancer{ Method: "wrr", - Sticky: false, }, }, }, diff --git a/provider/kv/kv.go b/provider/kv/kv.go index e61ea5be9..36beb8650 100644 --- a/provider/kv/kv.go +++ b/provider/kv/kv.go @@ -139,11 +139,13 @@ func (p *Provider) loadConfig() *types.Configuration { } var KvFuncMap = template.FuncMap{ - "List": p.list, - "ListServers": p.listServers, - "Get": p.get, - "SplitGet": p.splitGet, - "Last": p.last, + "List": p.list, + "ListServers": p.listServers, + "Get": p.get, + "SplitGet": p.splitGet, + "Last": p.last, + "hasStickinessLabel": p.hasStickinessLabel, + "getStickinessCookieName": p.getStickinessCookieName, } configuration, err := p.GetConfiguration("templates/kv.tmpl", KvFuncMap, templateObjects) @@ -239,3 +241,17 @@ func (p *Provider) checkConstraints(keys ...string) bool { } return true } + +func (p *Provider) hasStickinessLabel(rootPath string) bool { + stickiness, err := p.kvclient.Exists(rootPath + "/loadbalancer/stickiness") + if err != nil { + log.Debugf("Error occurs when check stickiness: %v", err) + } + sticky := p.get("false", rootPath, "/loadbalancer", "/sticky") + + return stickiness || (len(sticky) != 0 && strings.EqualFold(strings.TrimSpace(sticky), "true")) +} + +func (p *Provider) getStickinessCookieName(rootPath string) string { + return p.get("", rootPath, "/loadbalancer", "/stickiness", "/cookiename") +} diff --git a/provider/marathon/marathon.go b/provider/marathon/marathon.go index 0ea7627be..d8639b13c 100644 --- a/provider/marathon/marathon.go +++ b/provider/marathon/marathon.go @@ -188,7 +188,8 @@ func (p *Provider) loadMarathonConfig() *types.Configuration { "getMaxConnAmount": p.getMaxConnAmount, "getLoadBalancerMethod": p.getLoadBalancerMethod, "getCircuitBreakerExpression": p.getCircuitBreakerExpression, - "getSticky": p.getSticky, + "getStickinessCookieName": p.getStickinessCookieName, + "hasStickinessLabel": p.hasStickinessLabel, "hasHealthCheckLabels": p.hasHealthCheckLabels, "getHealthCheckPath": p.getHealthCheckPath, "getHealthCheckInterval": p.getHealthCheckInterval, @@ -428,11 +429,22 @@ func (p *Provider) getProtocol(application marathon.Application, serviceName str return "http" } -func (p *Provider) getSticky(application marathon.Application) string { - if sticky, ok := p.getAppLabel(application, types.LabelBackendLoadbalancerSticky); ok { - return sticky +func (p *Provider) hasStickinessLabel(application marathon.Application) bool { + _, okStickiness := p.getAppLabel(application, types.LabelBackendLoadbalancerStickiness) + + label, okSticky := p.getAppLabel(application, types.LabelBackendLoadbalancerSticky) + if len(label) > 0 { + log.Warn("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness) } - return "false" + + return okStickiness || (okSticky && strings.EqualFold(strings.TrimSpace(label), "true")) +} + +func (p *Provider) getStickinessCookieName(application marathon.Application) string { + if label, ok := p.getAppLabel(application, types.LabelBackendLoadbalancerStickinessCookieName); ok { + return label + } + return "" } func (p *Provider) getPassHostHeader(application marathon.Application, serviceName string) string { diff --git a/provider/marathon/marathon_test.go b/provider/marathon/marathon_test.go index e5cc01e93..e78fd3f0b 100644 --- a/provider/marathon/marathon_test.go +++ b/provider/marathon/marathon_test.go @@ -855,21 +855,36 @@ func TestMarathonGetProtocol(t *testing.T) { } } -func TestMarathonGetSticky(t *testing.T) { +func TestMarathonHasStickinessLabel(t *testing.T) { cases := []struct { desc string application marathon.Application - expected string + expected bool }{ { desc: "label missing", application: application(), - expected: "false", + expected: false, }, { - desc: "label existing", + desc: "label existing and value equals true (deprecated)", application: application(label(types.LabelBackendLoadbalancerSticky, "true")), - expected: "true", + expected: true, + }, + { + desc: "label existing and value equals false (deprecated)", + application: application(label(types.LabelBackendLoadbalancerSticky, "false")), + expected: false, + }, + { + desc: "label existing and value equals true", + application: application(label(types.LabelBackendLoadbalancerStickiness, "true")), + expected: true, + }, + { + desc: "label existing and value equals false ", + application: application(label(types.LabelBackendLoadbalancerStickiness, "true")), + expected: true, }, } @@ -878,7 +893,7 @@ func TestMarathonGetSticky(t *testing.T) { t.Run(c.desc, func(t *testing.T) { t.Parallel() provider := &Provider{} - actual := provider.getSticky(c.application) + actual := provider.hasStickinessLabel(c.application) if actual != c.expected { t.Errorf("actual %q, expected %q", actual, c.expected) } diff --git a/provider/provider.go b/provider/provider.go index cc1e12edf..be727db80 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -34,7 +34,7 @@ type BaseProvider struct { // MatchConstraints must match with EVERY single contraint // returns first constraint that do not match or nil func (p *BaseProvider) MatchConstraints(tags []string) (bool, *types.Constraint) { - // if there is no tags and no contraints, filtering is disabled + // if there is no tags and no constraints, filtering is disabled if len(tags) == 0 && len(p.Constraints) == 0 { return true, nil } diff --git a/provider/rancher/rancher.go b/provider/rancher/rancher.go index 6fcb8fc1c..6327f460b 100644 --- a/provider/rancher/rancher.go +++ b/provider/rancher/rancher.go @@ -112,11 +112,22 @@ func (p *Provider) getCircuitBreakerExpression(service rancherData) string { return "NetworkErrorRatio() > 1" } -func (p *Provider) getSticky(service rancherData) string { - if _, err := getServiceLabel(service, types.LabelBackendLoadbalancerSticky); err == nil { - return "true" +func (p *Provider) hasStickinessLabel(service rancherData) bool { + _, errStickiness := getServiceLabel(service, types.LabelBackendLoadbalancerStickiness) + + label, errSticky := getServiceLabel(service, types.LabelBackendLoadbalancerSticky) + if len(label) > 0 { + log.Warn("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness) } - return "false" + + return errStickiness == nil || (errSticky == nil && strings.EqualFold(strings.TrimSpace(label), "true")) +} + +func (p *Provider) getStickinessCookieName(service rancherData, backendName string) string { + if label, err := getServiceLabel(service, types.LabelBackendLoadbalancerStickinessCookieName); err == nil { + return label + } + return "" } func (p *Provider) getBackend(service rancherData) string { @@ -222,7 +233,8 @@ func (p *Provider) loadRancherConfig(services []rancherData) *types.Configuratio "hasMaxConnLabels": p.hasMaxConnLabels, "getMaxConnAmount": p.getMaxConnAmount, "getMaxConnExtractorFunc": p.getMaxConnExtractorFunc, - "getSticky": p.getSticky, + "hasStickinessLabel": p.hasStickinessLabel, + "getStickinessCookieName": p.getStickinessCookieName, } // filter services diff --git a/server/cookie/cookie.go b/server/cookie/cookie.go new file mode 100644 index 000000000..780661963 --- /dev/null +++ b/server/cookie/cookie.go @@ -0,0 +1,57 @@ +package cookie + +import ( + "crypto/sha1" + "fmt" + "strings" + + "github.com/containous/traefik/log" +) + +const cookieNameLength = 6 + +// GetName of a cookie +func GetName(cookieName string, backendName string) string { + if len(cookieName) != 0 { + return sanitizeName(cookieName) + } + + return GenerateName(backendName) +} + +// GenerateName Generate a hashed name +func GenerateName(backendName string) string { + data := []byte("_TRAEFIK_BACKEND_" + backendName) + + hash := sha1.New() + _, err := hash.Write(data) + if err != nil { + // Impossible case + log.Errorf("Fail to create cookie name: %v", err) + } + + return fmt.Sprintf("_%x", hash.Sum(nil))[:cookieNameLength] +} + +// sanitizeName According to [RFC 2616](https://www.ietf.org/rfc/rfc2616.txt) section 2.2 +func sanitizeName(backend string) string { + sanitizer := func(r rune) rune { + switch r { + case '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '`', '|', '~': + return r + } + + switch { + case 'a' <= r && r <= 'z': + fallthrough + case 'A' <= r && r <= 'Z': + fallthrough + case '0' <= r && r <= '9': + return r + default: + return '_' + } + } + + return strings.Map(sanitizer, backend) +} diff --git a/server/cookie/cookie_test.go b/server/cookie/cookie_test.go new file mode 100644 index 000000000..799b906f3 --- /dev/null +++ b/server/cookie/cookie_test.go @@ -0,0 +1,83 @@ +package cookie + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetName(t *testing.T) { + testCases := []struct { + desc string + cookieName string + backendName string + expectedCookieName string + }{ + { + desc: "with backend name, without cookie name", + cookieName: "", + backendName: "/my/BACKEND-v1.0~rc1", + expectedCookieName: "_5f7bc", + }, + { + desc: "without backend name, with cookie name", + cookieName: "/my/BACKEND-v1.0~rc1", + backendName: "", + expectedCookieName: "_my_BACKEND-v1.0~rc1", + }, + { + desc: "with backend name, with cookie name", + cookieName: "containous", + backendName: "treafik", + expectedCookieName: "containous", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + cookieName := GetName(test.cookieName, test.backendName) + + assert.Equal(t, test.expectedCookieName, cookieName) + }) + } +} + +func Test_sanitizeName(t *testing.T) { + testCases := []struct { + desc string + srcName string + expectedName string + }{ + { + desc: "with /", + srcName: "/my/BACKEND-v1.0~rc1", + expectedName: "_my_BACKEND-v1.0~rc1", + }, + { + desc: "some chars", + srcName: "!#$%&'()*+-./:<=>?@[]^_`{|}~", + expectedName: "!#$%&'__*+-._________^_`_|_~", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + cookieName := sanitizeName(test.srcName) + + assert.Equal(t, test.expectedName, cookieName, "Cookie name") + }) + } +} + +func TestGenerateName(t *testing.T) { + cookieName := GenerateName("containous") + + assert.Len(t, "_8a7bc", 6) + assert.Equal(t, "_8a7bc", cookieName) +} diff --git a/server/server.go b/server/server.go index 370c5c79d..a25283e33 100644 --- a/server/server.go +++ b/server/server.go @@ -15,7 +15,6 @@ import ( "reflect" "regexp" "sort" - "strings" "sync" "time" @@ -31,6 +30,7 @@ import ( mauth "github.com/containous/traefik/middlewares/auth" "github.com/containous/traefik/provider" "github.com/containous/traefik/safe" + "github.com/containous/traefik/server/cookie" "github.com/containous/traefik/types" "github.com/streamrail/concurrent-map" thoas_stats "github.com/thoas/stats" @@ -335,11 +335,11 @@ func (server *Server) listenProviders(stop chan bool) { lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time) providersThrottleDuration := time.Duration(server.globalConfiguration.ProvidersThrottleDuration) if time.Now().After(lastReceivedConfigurationValue.Add(providersThrottleDuration)) { - log.Debugf("Last %s config received more than %s, OK", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration.String()) + log.Debugf("Last %s config received more than %s, OK", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration) // last config received more than n s ago server.configurationValidatedChan <- configMsg } else { - log.Debugf("Last %s config received less than %s, waiting...", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration.String()) + log.Debugf("Last %s config received less than %s, waiting...", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration) safe.Go(func() { <-time.After(providersThrottleDuration) lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time) @@ -826,11 +826,10 @@ func (server *Server) loadConfig(configurations types.Configurations, globalConf continue frontend } - stickySession := config.Backends[frontend.Backend].LoadBalancer.Sticky - cookieName := getCookieName(frontend.Backend) var sticky *roundrobin.StickySession - - if stickySession { + var cookieName string + if stickiness := config.Backends[frontend.Backend].LoadBalancer.Stickiness; stickiness != nil { + cookieName = cookie.GetName(stickiness.CookieName, frontend.Backend) sticky = roundrobin.NewStickySession(cookieName) } @@ -839,7 +838,7 @@ func (server *Server) loadConfig(configurations types.Configurations, globalConf case types.Drr: log.Debugf("Creating load-balancer drr") rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) - if stickySession { + if sticky != nil { log.Debugf("Sticky session with cookie %v", cookieName) rebalancer, _ = roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger), roundrobin.RebalancerStickySession(sticky)) } @@ -856,7 +855,7 @@ func (server *Server) loadConfig(configurations types.Configurations, globalConf lb = middlewares.NewEmptyBackendHandler(rebalancer, lb) case types.Wrr: log.Debugf("Creating load-balancer wrr") - if stickySession { + if sticky != nil { log.Debugf("Sticky session with cookie %v", cookieName) if server.accessLoggerMiddleware != nil { rr, _ = roundrobin.New(saveFrontend, roundrobin.EnableStickySession(sticky)) @@ -1150,16 +1149,31 @@ func (server *Server) configureFrontends(frontends map[string]*types.Frontend) { func (*Server) configureBackends(backends map[string]*types.Backend) { for backendName, backend := range backends { + if backend.LoadBalancer != nil && backend.LoadBalancer.Sticky { + log.Warn("Deprecated configuration found: %s. Please use %s.", "backend.LoadBalancer.Sticky", "backend.LoadBalancer.Stickiness") + } + _, err := types.NewLoadBalancerMethod(backend.LoadBalancer) - if err != nil { + if err == nil { + if backend.LoadBalancer != nil && backend.LoadBalancer.Stickiness == nil && backend.LoadBalancer.Sticky { + backend.LoadBalancer.Stickiness = &types.Stickiness{} + } + } else { log.Debugf("Validation of load balancer method for backend %s failed: %s. Using default method wrr.", backendName, err) - var sticky bool + + var stickiness *types.Stickiness if backend.LoadBalancer != nil { - sticky = backend.LoadBalancer.Sticky + if backend.LoadBalancer.Stickiness != nil { + stickiness = backend.LoadBalancer.Stickiness + } else if backend.LoadBalancer.Sticky { + if backend.LoadBalancer.Stickiness == nil { + stickiness = &types.Stickiness{} + } + } } backend.LoadBalancer = &types.LoadBalancer{ - Method: "wrr", - Sticky: sticky, + Method: "wrr", + Stickiness: stickiness, } } } @@ -1209,28 +1223,3 @@ func (server *Server) buildRetryMiddleware(handler http.Handler, globalConfig co return middlewares.NewRetry(retryAttempts, handler, retryListeners) } - -// getCookieName returns a cookie name from the given backend, sanitizing -// characters that do not satisfy the requirements of RFC 2616. -func getCookieName(backend string) string { - const cookiePrefix = "_TRAEFIK_BACKEND_" - sanitizer := func(r rune) rune { - switch r { - case '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '`', '|', '~': - return r - } - - switch { - case r >= 'a' && r <= 'z': - fallthrough - case r >= 'A' && r <= 'Z': - fallthrough - case r >= '0' && r <= '9': - return r - default: - return '_' - } - } - - return cookiePrefix + strings.Map(sanitizer, backend) -} diff --git a/server/server_test.go b/server/server_test.go index bbac7b5d2..9fc83bae2 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -422,52 +422,49 @@ func TestConfigureBackends(t *testing.T) { defaultMethod := "wrr" tests := []struct { - desc string - lb *types.LoadBalancer - wantMethod string - wantSticky bool + desc string + lb *types.LoadBalancer + wantMethod string + wantStickiness *types.Stickiness }{ { desc: "valid load balancer method with sticky enabled", lb: &types.LoadBalancer{ - Method: validMethod, - Sticky: true, + Method: validMethod, + Stickiness: &types.Stickiness{}, }, - wantMethod: validMethod, - wantSticky: true, + wantMethod: validMethod, + wantStickiness: &types.Stickiness{}, }, { desc: "valid load balancer method with sticky disabled", lb: &types.LoadBalancer{ - Method: validMethod, - Sticky: false, + Method: validMethod, + Stickiness: nil, }, wantMethod: validMethod, - wantSticky: false, }, { desc: "invalid load balancer method with sticky enabled", lb: &types.LoadBalancer{ - Method: "Invalid", - Sticky: true, + Method: "Invalid", + Stickiness: &types.Stickiness{}, }, - wantMethod: defaultMethod, - wantSticky: true, + wantMethod: defaultMethod, + wantStickiness: &types.Stickiness{}, }, { desc: "invalid load balancer method with sticky disabled", lb: &types.LoadBalancer{ - Method: "Invalid", - Sticky: false, + Method: "Invalid", + Stickiness: nil, }, wantMethod: defaultMethod, - wantSticky: false, }, { desc: "missing load balancer", lb: nil, wantMethod: defaultMethod, - wantSticky: false, }, } @@ -485,8 +482,8 @@ func TestConfigureBackends(t *testing.T) { }) wantLB := types.LoadBalancer{ - Method: test.wantMethod, - Sticky: test.wantSticky, + Method: test.wantMethod, + Stickiness: test.wantStickiness, } if !reflect.DeepEqual(*backend.LoadBalancer, wantLB) { t.Errorf("got backend load-balancer\n%v\nwant\n%v\n", spew.Sdump(backend.LoadBalancer), spew.Sdump(wantLB)) @@ -659,13 +656,6 @@ func TestServerResponseEmptyBackend(t *testing.T) { } } -func TestGetCookieName(t *testing.T) { - want := "_TRAEFIK_BACKEND__my_BACKEND-v1.0~rc1" - if got := getCookieName("/my/BACKEND-v1.0~rc1"); got != want { - t.Errorf("got sticky cookie name %q, want %q", got, want) - } -} - func buildDynamicConfig(dynamicConfigBuilders ...func(*types.Configuration)) *types.Configuration { config := &types.Configuration{ Frontends: make(map[string]*types.Frontend), @@ -726,6 +716,10 @@ func withServer(name, url string) func(backend *types.Backend) { func withLoadBalancer(method string, sticky bool) func(*types.Backend) { return func(be *types.Backend) { - be.LoadBalancer = &types.LoadBalancer{Method: method, Sticky: sticky} + if sticky { + be.LoadBalancer = &types.LoadBalancer{Method: method, Stickiness: &types.Stickiness{CookieName: "test"}} + } else { + be.LoadBalancer = &types.LoadBalancer{Method: method} + } } } diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index edcf9a7a1..b055a4aa4 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -17,8 +17,12 @@ {{end}} [backends."backend-{{$service}}".loadbalancer] - sticky = {{getAttribute "backend.loadbalancer.sticky" .Attributes "false"}} method = "{{getAttribute "backend.loadbalancer" .Attributes "wrr"}}" + sticky = {{getAttribute "backend.loadbalancer.sticky" .Attributes "false"}} + {{if hasStickinessLabel .Attributes}} + [Backends."backend-{{$service}}".LoadBalancer.Stickiness] + cookieName = {{getStickinessCookieName .Attributes}} + {{end}} {{if hasMaxconnAttributes .Attributes}} [backends."backend-{{$service}}".maxconn] diff --git a/templates/docker.tmpl b/templates/docker.tmpl index 50dafc423..6e18a9904 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -8,7 +8,10 @@ {{if hasLoadBalancerLabel $backend}} [backends.backend-{{$backendName}}.loadbalancer] method = "{{getLoadBalancerMethod $backend}}" - sticky = {{getSticky $backend}} + {{if hasStickinessLabel $backend}} + [Backends."{{$backendName}}".LoadBalancer.Stickiness] + cookieName = {{getStickinessCookieName $backend}} + {{end}} {{end}} {{if hasMaxConnLabels $backend}} diff --git a/templates/ecs.tmpl b/templates/ecs.tmpl index 36e9e29b4..a325f7539 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -1,7 +1,10 @@ [backends]{{range $serviceName, $instances := .Services}} [backends.backend-{{ $serviceName }}.loadbalancer] - sticky = {{ getLoadBalancerSticky $instances}} method = "{{ getLoadBalancerMethod $instances}}" + {{if hasStickinessLabel $instances}} + [Backends.backend-{{ $serviceName }}.LoadBalancer.Stickiness] + cookieName = {{getStickinessCookieName $instances}} + {{end}} {{range $index, $i := $instances}} [backends.backend-{{ $i.Name }}.servers.server-{{ $i.Name }}{{ $i.ID }}] diff --git a/templates/kubernetes.tmpl b/templates/kubernetes.tmpl index be70e9a80..47fec081c 100644 --- a/templates/kubernetes.tmpl +++ b/templates/kubernetes.tmpl @@ -6,8 +6,9 @@ {{end}} [backends."{{$backendName}}".loadbalancer] method = "{{$backend.LoadBalancer.Method}}" - {{if $backend.LoadBalancer.Sticky}} - sticky = true + {{if $backend.LoadBalancer.Stickiness}} + [Backends."{{$backendName}}".LoadBalancer.Stickiness] + cookieName = {{$backend.LoadBalancer.Stickiness.CookieName}} {{end}} {{range $serverName, $server := $backend.Servers}} [backends."{{$backendName}}".servers."{{$serverName}}"] diff --git a/templates/kv.tmpl b/templates/kv.tmpl index 88a17f114..ebbbb39c2 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -3,25 +3,29 @@ [backends]{{range $backends}} {{$backend := .}} +{{$backendName := Last $backend}} {{$servers := ListServers $backend }} {{$circuitBreaker := Get "" . "/circuitbreaker/" "expression"}} {{with $circuitBreaker}} -[backends."{{Last $backend}}".circuitBreaker] +[backends."{{$backendName}}".circuitBreaker] expression = "{{$circuitBreaker}}" {{end}} {{$loadBalancer := Get "" . "/loadbalancer/" "method"}} -{{$sticky := Get "false" . "/loadbalancer/" "sticky"}} {{with $loadBalancer}} -[backends."{{Last $backend}}".loadBalancer] +[backends."{{$backendName}}".loadBalancer] method = "{{$loadBalancer}}" - sticky = {{$sticky}} + sticky = {{ Get "false" . "/loadbalancer/" "sticky" }} + {{if hasStickinessLabel $backend}} + [Backends."{{$backendName}}".LoadBalancer.Stickiness] + cookieName = {{getStickinessCookieName $backend}} + {{end}} {{end}} {{$healthCheck := Get "" . "/healthcheck/" "path"}} {{with $healthCheck}} -[backends."{{Last $backend}}".healthCheck] +[backends."{{$backendName}}".healthCheck] path = "{{$healthCheck}}" interval = "{{ Get "30s" $backend "/healthcheck/" "interval" }}" {{end}} @@ -30,14 +34,14 @@ {{$maxConnExtractorFunc := Get "" . "/maxconn/" "extractorfunc"}} {{with $maxConnAmt}} {{with $maxConnExtractorFunc}} -[backends."{{Last $backend}}".maxConn] +[backends."{{$backendName}}".maxConn] amount = {{$maxConnAmt}} extractorFunc = "{{$maxConnExtractorFunc}}" {{end}} {{end}} {{range $servers}} -[backends."{{Last $backend}}".servers."{{Last .}}"] +[backends."{{$backendName}}".servers."{{Last .}}"] url = "{{Get "" . "/url"}}" weight = {{Get "0" . "/weight"}} {{end}} diff --git a/templates/marathon.tmpl b/templates/marathon.tmpl index 5292ebb37..e5c802a63 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -20,7 +20,10 @@ {{ if hasLoadBalancerLabels $app }} [backends."backend{{getBackend $app $serviceName }}".loadbalancer] method = "{{getLoadBalancerMethod $app }}" - sticky = {{getSticky $app}} + {{if hasStickinessLabel $app}} + [Backends."backend{{getBackend $app $serviceName }}".LoadBalancer.Stickiness] + cookieName = {{getStickinessCookieName $app}} + {{end}} {{end}} {{ if hasCircuitBreakerLabels $app }} [backends."backend{{getBackend $app $serviceName }}".circuitbreaker] diff --git a/templates/rancher.tmpl b/templates/rancher.tmpl index f98bc3289..90a10ac56 100644 --- a/templates/rancher.tmpl +++ b/templates/rancher.tmpl @@ -8,7 +8,10 @@ {{if hasLoadBalancerLabel $backend}} [backends.backend-{{$backendName}}.loadbalancer] method = "{{getLoadBalancerMethod $backend}}" - sticky = {{getSticky $backend}} + {{if hasStickinessLabel $backend}} + [Backends."{{$backendName}}".LoadBalancer.Stickiness] + cookieName = {{getStickinessCookieName $backend}} + {{end}} {{end}} {{if hasMaxConnLabels $backend}} diff --git a/types/common_label.go b/types/common_label.go index e22f53c62..1f974615a 100644 --- a/types/common_label.go +++ b/types/common_label.go @@ -2,59 +2,36 @@ package types import "strings" +// Traefik labels const ( - // LabelPrefix Traefik label - LabelPrefix = "traefik." - // LabelDomain Traefik label - LabelDomain = LabelPrefix + "domain" - // LabelEnable Traefik label - LabelEnable = LabelPrefix + "enable" - // LabelPort Traefik label - LabelPort = LabelPrefix + "port" - // LabelPortIndex Traefik label - LabelPortIndex = LabelPrefix + "portIndex" - // LabelProtocol Traefik label - LabelProtocol = LabelPrefix + "protocol" - // LabelTags Traefik label - LabelTags = LabelPrefix + "tags" - // LabelWeight Traefik label - LabelWeight = LabelPrefix + "weight" - // LabelFrontendAuthBasic Traefik label - LabelFrontendAuthBasic = LabelPrefix + "frontend.auth.basic" - // LabelFrontendEntryPoints Traefik label - LabelFrontendEntryPoints = LabelPrefix + "frontend.entryPoints" - // LabelFrontendPassHostHeader Traefik label - LabelFrontendPassHostHeader = LabelPrefix + "frontend.passHostHeader" - // LabelFrontendPriority Traefik label - LabelFrontendPriority = LabelPrefix + "frontend.priority" - // LabelFrontendRule Traefik label - LabelFrontendRule = LabelPrefix + "frontend.rule" - // LabelFrontendRuleType Traefik label - LabelFrontendRuleType = LabelPrefix + "frontend.rule.type" - // LabelTraefikFrontendValue Traefik label - LabelTraefikFrontendValue = LabelPrefix + "frontend.value" - // LabelTraefikFrontendWhitelistSourceRange Traefik label - LabelTraefikFrontendWhitelistSourceRange = LabelPrefix + "frontend.whitelistSourceRange" - // LabelBackend Traefik label - LabelBackend = LabelPrefix + "backend" - // LabelBackendID Traefik label - LabelBackendID = LabelPrefix + "backend.id" - // LabelTraefikBackendCircuitbreaker Traefik label - LabelTraefikBackendCircuitbreaker = LabelPrefix + "backend.circuitbreaker" - // LabelBackendCircuitbreakerExpression Traefik label - LabelBackendCircuitbreakerExpression = LabelPrefix + "backend.circuitbreaker.expression" - // LabelBackendHealthcheckPath Traefik label - LabelBackendHealthcheckPath = LabelPrefix + "backend.healthcheck.path" - // LabelBackendHealthcheckInterval Traefik label - LabelBackendHealthcheckInterval = LabelPrefix + "backend.healthcheck.interval" - // LabelBackendLoadbalancerMethod Traefik label - LabelBackendLoadbalancerMethod = LabelPrefix + "backend.loadbalancer.method" - // LabelBackendLoadbalancerSticky Traefik label - LabelBackendLoadbalancerSticky = LabelPrefix + "backend.loadbalancer.sticky" - // LabelBackendMaxconnAmount Traefik label - LabelBackendMaxconnAmount = LabelPrefix + "backend.maxconn.amount" - // LabelBackendMaxconnExtractorfunc Traefik label - LabelBackendMaxconnExtractorfunc = LabelPrefix + "backend.maxconn.extractorfunc" + LabelPrefix = "traefik." + LabelDomain = LabelPrefix + "domain" + LabelEnable = LabelPrefix + "enable" + LabelPort = LabelPrefix + "port" + LabelPortIndex = LabelPrefix + "portIndex" + LabelProtocol = LabelPrefix + "protocol" + LabelTags = LabelPrefix + "tags" + LabelWeight = LabelPrefix + "weight" + LabelFrontendAuthBasic = LabelPrefix + "frontend.auth.basic" + LabelFrontendEntryPoints = LabelPrefix + "frontend.entryPoints" + LabelFrontendPassHostHeader = LabelPrefix + "frontend.passHostHeader" + LabelFrontendPriority = LabelPrefix + "frontend.priority" + LabelFrontendRule = LabelPrefix + "frontend.rule" + LabelFrontendRuleType = LabelPrefix + "frontend.rule.type" + LabelTraefikFrontendValue = LabelPrefix + "frontend.value" + LabelTraefikFrontendWhitelistSourceRange = LabelPrefix + "frontend.whitelistSourceRange" + LabelBackend = LabelPrefix + "backend" + LabelBackendID = LabelPrefix + "backend.id" + LabelTraefikBackendCircuitbreaker = LabelPrefix + "backend.circuitbreaker" + LabelBackendCircuitbreakerExpression = LabelPrefix + "backend.circuitbreaker.expression" + LabelBackendHealthcheckPath = LabelPrefix + "backend.healthcheck.path" + LabelBackendHealthcheckInterval = LabelPrefix + "backend.healthcheck.interval" + LabelBackendLoadbalancerMethod = LabelPrefix + "backend.loadbalancer.method" + LabelBackendLoadbalancerSticky = LabelPrefix + "backend.loadbalancer.sticky" + LabelBackendLoadbalancerStickiness = LabelPrefix + "backend.loadbalancer.stickiness" + LabelBackendLoadbalancerStickinessCookieName = LabelPrefix + "backend.loadbalancer.stickiness.cookieName" + LabelBackendMaxconnAmount = LabelPrefix + "backend.maxconn.amount" + LabelBackendMaxconnExtractorfunc = LabelPrefix + "backend.maxconn.extractorfunc" ) //ServiceLabel converts a key value of Label*, given a serviceName, into a pattern .. diff --git a/types/types.go b/types/types.go index 2bf2b9cf7..fb67af6ad 100644 --- a/types/types.go +++ b/types/types.go @@ -33,8 +33,14 @@ type MaxConn struct { // LoadBalancer holds load balancing configuration. type LoadBalancer struct { - Method string `json:"method,omitempty"` - Sticky bool `json:"sticky,omitempty"` + Method string `json:"method,omitempty"` + Sticky bool `json:"sticky,omitempty"` // Deprecated: use Stickiness instead + Stickiness *Stickiness `json:"stickiness,omitempty"` +} + +// Stickiness holds sticky session configuration. +type Stickiness struct { + CookieName string `json:"cookieName,omitempty"` } // CircuitBreaker holds circuit breaker configuration.