Skip to content

Commit

Permalink
Stickiness cookie name.
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez authored and traefiker committed Oct 12, 2017
1 parent cba0898 commit 8cb3f08
Show file tree
Hide file tree
Showing 21 changed files with 524 additions and 147 deletions.
2 changes: 1 addition & 1 deletion provider/consul/consul_catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ func (p *CatalogProvider) hasStickinessLabel(tags []string) bool {

stickyTag := p.getTag(types.LabelBackendLoadbalancerSticky, tags, "")
if len(stickyTag) > 0 {
log.Warn("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness)
log.Warnf("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness)
}

stickiness := len(stickinessTag) > 0 && strings.EqualFold(strings.TrimSpace(stickinessTag), "true")
Expand Down
67 changes: 67 additions & 0 deletions provider/consul/consul_catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/BurntSushi/ty/fun"
"github.com/containous/traefik/types"
"github.com/hashicorp/consul/api"
"github.com/stretchr/testify/assert"
)

func TestConsulCatalogGetFrontendRule(t *testing.T) {
Expand Down Expand Up @@ -891,3 +892,69 @@ func TestConsulCatalogGetBasicAuth(t *testing.T) {
})
}
}

func TestConsulCatalogHasStickinessLabel(t *testing.T) {
testCases := []struct {
desc string
tags []string
expected bool
}{
{
desc: "label missing",
tags: []string{},
expected: false,
},
{
desc: "sticky=true",
tags: []string{
"traefik.backend.loadbalancer.sticky=true",
},
expected: true,
},
{
desc: "stickiness=true",
tags: []string{
"traefik.backend.loadbalancer.stickiness=true",
},
expected: true,
},
{
desc: "sticky=true and stickiness=true",
tags: []string{
"traefik.backend.loadbalancer.sticky=true",
"traefik.backend.loadbalancer.stickiness=true",
},
expected: true,
},
{
desc: "sticky=false and stickiness=true",
tags: []string{
"traefik.backend.loadbalancer.sticky=true",
"traefik.backend.loadbalancer.stickiness=false",
},
expected: true,
},
{
desc: "sticky=true and stickiness=false",
tags: []string{
"traefik.backend.loadbalancer.sticky=true",
"traefik.backend.loadbalancer.stickiness=false",
},
expected: true,
},
}

provider := &CatalogProvider{
Prefix: "traefik",
}

for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

actual := provider.hasStickinessLabel(test.tags)
assert.Equal(t, actual, test.expected)
})
}
}
12 changes: 7 additions & 5 deletions provider/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -645,14 +645,16 @@ func (p *Provider) getWeight(container dockerData) string {
}

func (p *Provider) hasStickinessLabel(container dockerData) bool {
_, errStickiness := getLabel(container, types.LabelBackendLoadbalancerStickiness)
labelStickiness, 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)
labelSticky, errSticky := getLabel(container, types.LabelBackendLoadbalancerSticky)
if len(labelSticky) > 0 {
log.Warnf("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness)
}

return errStickiness == nil || (errSticky == nil && strings.EqualFold(strings.TrimSpace(label), "true"))
stickiness := errStickiness == nil && len(labelStickiness) > 0 && strings.EqualFold(strings.TrimSpace(labelStickiness), "true")
sticky := errSticky == nil && len(labelSticky) > 0 && strings.EqualFold(strings.TrimSpace(labelSticky), "true")
return stickiness || sticky
}

func (p *Provider) getStickinessCookieName(container dockerData) string {
Expand Down
78 changes: 78 additions & 0 deletions provider/docker/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
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"
)

func TestDockerGetFrontendName(t *testing.T) {
Expand Down Expand Up @@ -1051,3 +1052,80 @@ func TestDockerLoadDockerConfig(t *testing.T) {
})
}
}

func TestDockerHasStickinessLabel(t *testing.T) {
testCases := []struct {
desc string
container docker.ContainerJSON
expected bool
}{
{
desc: "no sticky/stickiness-label",
container: containerJSON(),
expected: false,
},
{
desc: "sticky true",
container: containerJSON(labels(map[string]string{
types.LabelBackendLoadbalancerSticky: "true",
})),
expected: true,
},
{
desc: "sticky false",
container: containerJSON(labels(map[string]string{
types.LabelBackendLoadbalancerSticky: "false",
})),
expected: false,
},
{
desc: "stickiness true",
container: containerJSON(labels(map[string]string{
types.LabelBackendLoadbalancerStickiness: "true",
})),
expected: true,
},
{
desc: "stickiness false",
container: containerJSON(labels(map[string]string{
types.LabelBackendLoadbalancerStickiness: "false",
})),
expected: false,
},
{
desc: "sticky true + stickiness false",
container: containerJSON(labels(map[string]string{
types.LabelBackendLoadbalancerSticky: "true",
types.LabelBackendLoadbalancerStickiness: "false",
})),
expected: true,
},
{
desc: "sticky false + stickiness true",
container: containerJSON(labels(map[string]string{
types.LabelBackendLoadbalancerSticky: "false",
types.LabelBackendLoadbalancerStickiness: "true",
})),
expected: true,
},
{
desc: "sticky false + stickiness false",
container: containerJSON(labels(map[string]string{
types.LabelBackendLoadbalancerSticky: "false",
types.LabelBackendLoadbalancerStickiness: "false",
})),
expected: false,
},
}

for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
dockerData := parseContainer(test.container)
provider := &Provider{}
actual := provider.hasStickinessLabel(dockerData)
assert.Equal(t, actual, test.expected)
})
}
}
2 changes: 1 addition & 1 deletion provider/ecs/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ func (p *Provider) hasStickinessLabel(instances []ecsInstance) bool {

stickyLabel := getFirstInstanceLabel(instances, types.LabelBackendLoadbalancerSticky)
if len(stickyLabel) > 0 {
log.Warn("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness)
log.Warnf("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")
Expand Down
9 changes: 4 additions & 5 deletions provider/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ 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"
Expand Down Expand Up @@ -185,8 +184,8 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
continue
}

witelistSourceRangeAnnotation := i.Annotations[annotationKubernetesWhitelistSourceRange]
whitelistSourceRange := provider.SplitAndTrimString(witelistSourceRangeAnnotation)
whitelistSourceRangeAnnotation := i.Annotations[annotationKubernetesWhitelistSourceRange]
whitelistSourceRange := provider.SplitAndTrimString(whitelistSourceRangeAnnotation)

if _, exists := templateObjects.Frontends[r.Host+pa.Path]; !exists {
basicAuthCreds, err := handleBasicAuthConfig(i, k8sClient)
Expand Down Expand Up @@ -250,12 +249,12 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
}

if len(service.Annotations[types.LabelBackendLoadbalancerSticky]) > 0 {
log.Warn("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness)
log.Warnf("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),
CookieName: r.Host + pa.Path,
}
}

Expand Down
2 changes: 1 addition & 1 deletion provider/kubernetes/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,7 @@ func TestServiceAnnotations(t *testing.T) {
LoadBalancer: &types.LoadBalancer{
Method: "wrr",
Stickiness: &types.Stickiness{
CookieName: "_4155f",
CookieName: "bar",
},
},
},
Expand Down
14 changes: 9 additions & 5 deletions provider/kv/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,17 @@ func (p *Provider) checkConstraints(keys ...string) bool {
}

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)
stickyValue := p.get("false", rootPath, "/loadbalancer", "/sticky")

sticky := len(stickyValue) != 0 && strings.EqualFold(strings.TrimSpace(stickyValue), "true")
if sticky {
log.Warnf("Deprecated configuration found: %s. Please use %s.", "loadbalancer/sticky", "loadbalancer/stickiness")
}
sticky := p.get("false", rootPath, "/loadbalancer", "/sticky")

return stickiness || (len(sticky) != 0 && strings.EqualFold(strings.TrimSpace(sticky), "true"))
stickinessValue := p.get("false", rootPath, "/loadbalancer", "/stickiness")
stickiness := len(stickinessValue) > 0 && strings.EqualFold(strings.TrimSpace(stickinessValue), "true")

return stickiness || sticky
}

func (p *Provider) getStickinessCookieName(rootPath string) string {
Expand Down
110 changes: 110 additions & 0 deletions provider/kv/kv_mock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package kv

import (
"errors"
"strings"

"github.com/containous/traefik/types"
"github.com/docker/libkv/store"
)

type KvMock struct {
Provider
}

func (provider *KvMock) loadConfig() *types.Configuration {
return nil
}

// Override Get/List to return a error
type KvError struct {
Get error
List error
}

// Extremely limited mock store so we can test initialization
type Mock struct {
Error KvError
KVPairs []*store.KVPair
WatchTreeMethod func() <-chan []*store.KVPair
}

func (s *Mock) Put(key string, value []byte, opts *store.WriteOptions) error {
return errors.New("Put not supported")
}

func (s *Mock) Get(key string) (*store.KVPair, error) {
if err := s.Error.Get; err != nil {
return nil, err
}
for _, kvPair := range s.KVPairs {
if kvPair.Key == key {
return kvPair, nil
}
}
return nil, store.ErrKeyNotFound
}

func (s *Mock) Delete(key string) error {
return errors.New("Delete not supported")
}

// Exists mock
func (s *Mock) Exists(key string) (bool, error) {
if err := s.Error.Get; err != nil {
return false, err
}
for _, kvPair := range s.KVPairs {
if strings.HasPrefix(kvPair.Key, key) {
return true, nil
}
}
return false, store.ErrKeyNotFound
}

// Watch mock
func (s *Mock) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) {
return nil, errors.New("Watch not supported")
}

// WatchTree mock
func (s *Mock) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) {
return s.WatchTreeMethod(), nil
}

// NewLock mock
func (s *Mock) NewLock(key string, options *store.LockOptions) (store.Locker, error) {
return nil, errors.New("NewLock not supported")
}

// List mock
func (s *Mock) List(prefix string) ([]*store.KVPair, error) {
if err := s.Error.List; err != nil {
return nil, err
}
kv := []*store.KVPair{}
for _, kvPair := range s.KVPairs {
if strings.HasPrefix(kvPair.Key, prefix) && !strings.ContainsAny(strings.TrimPrefix(kvPair.Key, prefix), "/") {
kv = append(kv, kvPair)
}
}
return kv, nil
}

// DeleteTree mock
func (s *Mock) DeleteTree(prefix string) error {
return errors.New("DeleteTree not supported")
}

// AtomicPut mock
func (s *Mock) AtomicPut(key string, value []byte, previous *store.KVPair, opts *store.WriteOptions) (bool, *store.KVPair, error) {
return false, nil, errors.New("AtomicPut not supported")
}

// AtomicDelete mock
func (s *Mock) AtomicDelete(key string, previous *store.KVPair) (bool, error) {
return false, errors.New("AtomicDelete not supported")
}

// Close mock
func (s *Mock) Close() {}

0 comments on commit 8cb3f08

Please sign in to comment.