refactor(rancher): rewrite configuration system.

This commit is contained in:
Fernandez Ludovic 2017-12-02 19:29:09 +01:00 committed by Traefiker
parent 5f71a43758
commit ca680710a2
5 changed files with 229 additions and 650 deletions

View file

@ -78,7 +78,7 @@ func (p *Provider) apiProvide(configurationChan chan<- types.ConfigMessage, pool
var rancherData = parseAPISourcedRancherData(stacks, services, container)
configuration := p.loadRancherConfig(rancherData)
configuration := p.buildConfiguration(rancherData)
configurationChan <- types.ConfigMessage{
ProviderName: "rancher",
Configuration: configuration,
@ -99,7 +99,7 @@ func (p *Provider) apiProvide(configurationChan chan<- types.ConfigMessage, pool
rancherData := parseAPISourcedRancherData(stacks, services, container)
configuration := p.loadRancherConfig(rancherData)
configuration := p.buildConfiguration(rancherData)
if configuration != nil {
configurationChan <- types.ConfigMessage{
ProviderName: "rancher",
@ -131,7 +131,7 @@ func (p *Provider) apiProvide(configurationChan chan<- types.ConfigMessage, pool
func listRancherStacks(client *rancher.RancherClient) []*rancher.Stack {
var stackList = []*rancher.Stack{}
var stackList []*rancher.Stack
stacks, err := client.Stack.List(withoutPagination)
@ -148,7 +148,7 @@ func listRancherStacks(client *rancher.RancherClient) []*rancher.Stack {
func listRancherServices(client *rancher.RancherClient) []*rancher.Service {
var servicesList = []*rancher.Service{}
var servicesList []*rancher.Service
services, err := client.Service.List(withoutPagination)
@ -165,7 +165,7 @@ func listRancherServices(client *rancher.RancherClient) []*rancher.Service {
func listRancherContainer(client *rancher.RancherClient) []*rancher.Container {
containerList := []*rancher.Container{}
var containerList []*rancher.Container
container, err := client.Container.List(withoutPagination)
@ -205,7 +205,7 @@ func parseAPISourcedRancherData(stacks []*rancher.Stack, services []*rancher.Ser
continue
}
rancherData := rancherData{
rData := rancherData{
Name: service.Name + "/" + stack.Name,
Health: service.HealthState,
State: service.State,
@ -217,7 +217,7 @@ func parseAPISourcedRancherData(stacks []*rancher.Stack, services []*rancher.Ser
log.Warnf("Rancher Service Labels are missing. Stack: %s, service: %s", stack.Name, service.Name)
} else {
for key, value := range service.LaunchConfig.Labels {
rancherData.Labels[key] = value.(string)
rData.Labels[key] = value.(string)
}
}
@ -235,14 +235,14 @@ func parseAPISourcedRancherData(stacks []*rancher.Stack, services []*rancher.Ser
}
if len(endpoints) > 0 {
rancherData.Containers = append(rancherData.Containers, endpoints[0].IpAddress)
rData.Containers = append(rData.Containers, endpoints[0].IpAddress)
}
} else {
rancherData.Containers = append(rancherData.Containers, container.PrimaryIpAddress)
rData.Containers = append(rData.Containers, container.PrimaryIpAddress)
}
}
}
rancherDataList = append(rancherDataList, rancherData)
rancherDataList = append(rancherDataList, rData)
}
}

170
provider/rancher/config.go Normal file
View file

@ -0,0 +1,170 @@
package rancher
import (
"math"
"strings"
"text/template"
"github.com/BurntSushi/ty/fun"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/provider/label"
"github.com/containous/traefik/types"
)
func (p *Provider) buildConfiguration(services []rancherData) *types.Configuration {
var RancherFuncMap = template.FuncMap{
"getPort": getFuncString(label.TraefikPort, ""),
"getBackend": getBackend,
"getWeight": getFuncString(label.TraefikWeight, label.DefaultWeight),
"getDomain": getFuncString(label.TraefikDomain, p.Domain),
"getProtocol": getFuncString(label.TraefikProtocol, label.DefaultProtocol),
"getPassHostHeader": getFuncString(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
"getPriority": getFuncString(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
"getEntryPoints": getFuncSliceString(label.TraefikFrontendEntryPoints),
"getBasicAuth": getFuncSliceString(label.TraefikFrontendAuthBasic),
"getFrontendRule": p.getFrontendRule,
"hasCircuitBreakerLabel": hasFunc(label.TraefikBackendCircuitBreakerExpression),
"getCircuitBreakerExpression": getFuncString(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression),
"hasLoadBalancerLabel": hasLoadBalancerLabel, // OK
"getLoadBalancerMethod": getFuncString(label.TraefikFrontendRedirect, label.DefaultBackendLoadBalancerMethod),
"hasMaxConnLabels": hasMaxConnLabels, // OK
"getMaxConnAmount": getFuncInt64(label.TraefikBackendMaxConnAmount, math.MaxInt64),
"getMaxConnExtractorFunc": getFuncString(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc),
"getSticky": getSticky, // deprecated
"hasStickinessLabel": hasFunc(label.TraefikBackendLoadBalancerStickiness),
"getStickinessCookieName": getFuncString(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
"getRedirect": getFuncString(label.TraefikFrontendRedirect, label.DefaultFrontendRedirect),
}
// filter services
filteredServices := fun.Filter(p.serviceFilter, services).([]rancherData)
frontends := map[string]rancherData{}
backends := map[string]rancherData{}
for _, service := range filteredServices {
frontendName := p.getFrontendName(service)
frontends[frontendName] = service
backendName := getBackend(service)
backends[backendName] = service
}
templateObjects := struct {
Frontends map[string]rancherData
Backends map[string]rancherData
Domain string
}{
frontends,
backends,
p.Domain,
}
configuration, err := p.GetConfiguration("templates/rancher.tmpl", RancherFuncMap, templateObjects)
if err != nil {
log.Error(err)
}
return configuration
}
func (p *Provider) serviceFilter(service rancherData) bool {
if service.Labels[label.TraefikPort] == "" {
log.Debugf("Filtering service %s without traefik.port label", service.Name)
return false
}
if !label.IsEnabled(service.Labels, p.ExposedByDefault) {
log.Debugf("Filtering disabled service %s", service.Name)
return false
}
constraintTags := label.GetSliceStringValue(service.Labels, label.TraefikTags)
if ok, failingConstraint := p.MatchConstraints(constraintTags); !ok {
if failingConstraint != nil {
log.Debugf("Filtering service %s with constraint %s", service.Name, failingConstraint.String())
}
return false
}
// Only filter services by Health (HealthState) and State if EnableServiceHealthFilter is true
if p.EnableServiceHealthFilter {
if service.Health != "" && service.Health != healthy && service.Health != updatingHealthy {
log.Debugf("Filtering service %s with healthState of %s", service.Name, service.Health)
return false
}
if service.State != "" && service.State != active && service.State != updatingActive && service.State != upgraded {
log.Debugf("Filtering service %s with state of %s", service.Name, service.State)
return false
}
}
return true
}
func (p *Provider) getFrontendRule(service rancherData) string {
defaultRule := "Host:" + strings.ToLower(strings.Replace(service.Name, "/", ".", -1)) + "." + p.Domain
return label.GetStringValue(service.Labels, label.TraefikFrontendRule, defaultRule)
}
func (p *Provider) getFrontendName(service rancherData) string {
return provider.Normalize(p.getFrontendRule(service))
}
// TODO: Deprecated
// Deprecated replaced by Stickiness
func getSticky(service rancherData) string {
if label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky) {
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
}
return label.GetStringValue(service.Labels, label.TraefikBackendLoadBalancerSticky, "false")
}
func hasLoadBalancerLabel(service rancherData) bool {
method := label.Has(service.Labels, label.TraefikBackendLoadBalancerMethod)
sticky := label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky)
stickiness := label.Has(service.Labels, label.TraefikBackendLoadBalancerStickiness)
cookieName := label.Has(service.Labels, label.TraefikBackendLoadBalancerStickinessCookieName)
return method || sticky || stickiness || cookieName
}
func hasMaxConnLabels(service rancherData) bool {
mca := label.Has(service.Labels, label.TraefikBackendMaxConnAmount)
mcef := label.Has(service.Labels, label.TraefikBackendMaxConnExtractorFunc)
return mca && mcef
}
func getBackend(service rancherData) string {
backend := label.GetStringValue(service.Labels, label.TraefikBackend, service.Name)
return provider.Normalize(backend)
}
// Label functions
func getFuncString(labelName string, defaultValue string) func(service rancherData) string {
return func(service rancherData) string {
return label.GetStringValue(service.Labels, labelName, defaultValue)
}
}
func getFuncInt64(labelName string, defaultValue int64) func(service rancherData) int64 {
return func(service rancherData) int64 {
return label.GetInt64Value(service.Labels, labelName, defaultValue)
}
}
func getFuncSliceString(labelName string) func(service rancherData) []string {
return func(service rancherData) []string {
return label.GetSliceStringValue(service.Labels, labelName)
}
}
func hasFunc(labelName string) func(service rancherData) bool {
return func(service rancherData) bool {
return label.Has(service.Labels, labelName)
}
}

View file

@ -1,9 +1,9 @@
package rancher
import (
"strings"
"testing"
"github.com/containous/traefik/provider/label"
"github.com/containous/traefik/types"
"github.com/stretchr/testify/assert"
)
@ -26,7 +26,7 @@ func TestProviderServiceFilter(t *testing.T) {
desc: "missing Port labels, don't respect constraint",
service: rancherData{
Labels: map[string]string{
types.LabelEnable: "true",
label.TraefikEnable: "true",
},
Health: "healthy",
State: "active",
@ -37,8 +37,8 @@ func TestProviderServiceFilter(t *testing.T) {
desc: "don't respect constraint",
service: rancherData{
Labels: map[string]string{
types.LabelPort: "80",
types.LabelEnable: "false",
label.TraefikPort: "80",
label.TraefikEnable: "false",
},
Health: "healthy",
State: "active",
@ -49,9 +49,9 @@ func TestProviderServiceFilter(t *testing.T) {
desc: "unhealthy",
service: rancherData{
Labels: map[string]string{
types.LabelTags: "cheese",
types.LabelPort: "80",
types.LabelEnable: "true",
label.TraefikTags: "cheese",
label.TraefikPort: "80",
label.TraefikEnable: "true",
},
Health: "unhealthy",
State: "active",
@ -62,9 +62,9 @@ func TestProviderServiceFilter(t *testing.T) {
desc: "inactive",
service: rancherData{
Labels: map[string]string{
types.LabelTags: "not-cheesy",
types.LabelPort: "80",
types.LabelEnable: "true",
label.TraefikTags: "not-cheesy",
label.TraefikPort: "80",
label.TraefikEnable: "true",
},
Health: "healthy",
State: "inactive",
@ -75,9 +75,9 @@ func TestProviderServiceFilter(t *testing.T) {
desc: "healthy & active, tag: cheese",
service: rancherData{
Labels: map[string]string{
types.LabelTags: "cheese",
types.LabelPort: "80",
types.LabelEnable: "true",
label.TraefikTags: "cheese",
label.TraefikPort: "80",
label.TraefikEnable: "true",
},
Health: "healthy",
State: "active",
@ -88,9 +88,9 @@ func TestProviderServiceFilter(t *testing.T) {
desc: "healthy & active, tag: chose",
service: rancherData{
Labels: map[string]string{
types.LabelTags: "chose",
types.LabelPort: "80",
types.LabelEnable: "true",
label.TraefikTags: "chose",
label.TraefikPort: "80",
label.TraefikEnable: "true",
},
Health: "healthy",
State: "active",
@ -101,9 +101,9 @@ func TestProviderServiceFilter(t *testing.T) {
desc: "healthy & upgraded",
service: rancherData{
Labels: map[string]string{
types.LabelTags: "cheeeeese",
types.LabelPort: "80",
types.LabelEnable: "true",
label.TraefikTags: "cheeeeese",
label.TraefikPort: "80",
label.TraefikEnable: "true",
},
Health: "healthy",
State: "upgraded",
@ -187,7 +187,7 @@ func TestProviderGetFrontendName(t *testing.T) {
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelFrontendRule: "Headers:User-Agent,bat/0.1.0",
label.TraefikFrontendRule: "Headers:User-Agent,bat/0.1.0",
},
},
expected: "Headers-User-Agent-bat-0-1-0",
@ -197,7 +197,7 @@ func TestProviderGetFrontendName(t *testing.T) {
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelFrontendRule: "Host:foo.bar",
label.TraefikFrontendRule: "Host:foo.bar",
},
},
expected: "Host-foo-bar",
@ -207,7 +207,7 @@ func TestProviderGetFrontendName(t *testing.T) {
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelFrontendRule: "Path:/test",
label.TraefikFrontendRule: "Path:/test",
},
},
expected: "Path-test",
@ -217,7 +217,7 @@ func TestProviderGetFrontendName(t *testing.T) {
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelFrontendRule: "PathPrefix:/test2",
label.TraefikFrontendRule: "PathPrefix:/test2",
},
},
expected: "PathPrefix-test2",
@ -262,7 +262,7 @@ func TestProviderGetFrontendRule(t *testing.T) {
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelFrontendRule: "Host:foo.bar.com",
label.TraefikFrontendRule: "Host:foo.bar.com",
},
},
expected: "Host:foo.bar.com",
@ -272,7 +272,7 @@ func TestProviderGetFrontendRule(t *testing.T) {
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelFrontendRule: "Path:/test",
label.TraefikFrontendRule: "Path:/test",
},
},
expected: "Path:/test",
@ -282,7 +282,7 @@ func TestProviderGetFrontendRule(t *testing.T) {
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelFrontendRule: "PathPrefix:/test2",
label.TraefikFrontendRule: "PathPrefix:/test2",
},
},
expected: "PathPrefix:/test2",
@ -300,9 +300,7 @@ func TestProviderGetFrontendRule(t *testing.T) {
}
}
func TestProviderGetBackend(t *testing.T) {
provider := &Provider{Domain: "rancher.localhost"}
func TestGetBackend(t *testing.T) {
testCases := []struct {
desc string
service rancherData
@ -320,7 +318,7 @@ func TestProviderGetBackend(t *testing.T) {
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelBackend: "foobar",
label.TraefikBackend: "foobar",
},
},
@ -333,283 +331,12 @@ func TestProviderGetBackend(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := provider.getBackend(test.service)
actual := getBackend(test.service)
assert.Equal(t, test.expected, actual)
})
}
}
func TestProviderGetWeight(t *testing.T) {
provider := &Provider{Domain: "rancher.localhost"}
testCases := []struct {
desc string
service rancherData
expected string
}{
{
desc: "without label",
service: rancherData{
Name: "test-service",
},
expected: "0",
},
{
desc: "with label",
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelWeight: "5",
},
},
expected: "5",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := provider.getWeight(test.service)
assert.Equal(t, test.expected, actual)
})
}
}
func TestProviderGetPort(t *testing.T) {
provider := &Provider{Domain: "rancher.localhost"}
testCases := []struct {
desc string
service rancherData
expected string
}{
{
desc: "without label",
service: rancherData{
Name: "test-service",
},
expected: "",
},
{
desc: "with label",
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelPort: "1337",
},
},
expected: "1337",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := provider.getPort(test.service)
assert.Equal(t, test.expected, actual)
})
}
}
func TestProviderGetDomain(t *testing.T) {
provider := &Provider{Domain: "rancher.localhost"}
testCases := []struct {
desc string
service rancherData
expected string
}{
{
desc: "without label",
service: rancherData{
Name: "test-service",
},
expected: "rancher.localhost",
},
{
desc: "with label",
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelDomain: "foo.bar",
},
},
expected: "foo.bar",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := provider.getDomain(test.service)
assert.Equal(t, test.expected, actual)
})
}
}
func TestProviderGetProtocol(t *testing.T) {
provider := &Provider{Domain: "rancher.localhost"}
testCases := []struct {
desc string
service rancherData
expected string
}{
{
desc: "without label",
service: rancherData{
Name: "test-service",
},
expected: "http",
},
{
desc: "with label",
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelProtocol: "https",
},
},
expected: "https",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := provider.getProtocol(test.service)
assert.Equal(t, test.expected, actual)
})
}
}
func TestProviderGetPassHostHeader(t *testing.T) {
provider := &Provider{Domain: "rancher.localhost"}
testCases := []struct {
desc string
service rancherData
expected string
}{
{
desc: "without label",
service: rancherData{
Name: "test-service",
},
expected: "true",
},
{
desc: "with label",
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelFrontendPassHostHeader: "false",
},
},
expected: "false",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := provider.getPassHostHeader(test.service)
assert.Equal(t, test.expected, actual)
})
}
}
func TestProviderGetRedirect(t *testing.T) {
provider := &Provider{Domain: "rancher.localhost"}
testCases := []struct {
desc string
service rancherData
expected string
}{
{
desc: "without label",
service: rancherData{
Name: "test-service",
},
expected: "",
},
{
desc: "with label",
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelFrontendRedirect: "https",
},
},
expected: "https",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := provider.getRedirect(test.service)
assert.Equal(t, test.expected, actual)
})
}
}
func TestProviderGetLabel(t *testing.T) {
testCases := []struct {
desc string
service rancherData
expected string
}{
{
desc: "without label",
service: rancherData{
Name: "test-service",
},
expected: "label not found",
},
{
desc: "with label",
service: rancherData{
Name: "test-service",
Labels: map[string]string{
"foo": "bar",
},
},
expected: "",
},
}
for _, test := range testCases {
test := test
t.Run("", func(t *testing.T) {
t.Parallel()
label, err := getServiceLabel(test.service, "foo")
if test.expected != "" {
if err == nil || !strings.Contains(err.Error(), test.expected) {
t.Fatalf("expected an error with %q, got %v", test.expected, err)
}
} else {
assert.Equal(t, "bar", label)
}
})
}
}
func TestProviderLoadRancherConfig(t *testing.T) {
provider := &Provider{
Domain: "rancher.localhost",
@ -634,9 +361,9 @@ func TestProviderLoadRancherConfig(t *testing.T) {
{
Name: "test/service",
Labels: map[string]string{
types.LabelPort: "80",
types.LabelFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
types.LabelFrontendRedirect: "https",
label.TraefikPort: "80",
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
label.TraefikFrontendRedirect: "https",
},
Health: "healthy",
Containers: []string{"127.0.0.1"},
@ -677,58 +404,10 @@ func TestProviderLoadRancherConfig(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actualConfig := provider.loadRancherConfig(test.services)
actualConfig := provider.buildConfiguration(test.services)
assert.EqualValues(t, test.expectedBackends, actualConfig.Backends)
assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends)
})
}
}
func TestProviderHasStickinessLabel(t *testing.T) {
provider := &Provider{Domain: "rancher.localhost"}
testCases := []struct {
desc string
service rancherData
expected bool
}{
{
desc: "no labels",
service: rancherData{
Name: "test-service",
},
expected: false,
},
{
desc: "stickiness=true",
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelBackendLoadbalancerStickiness: "true",
},
},
expected: true,
},
{
desc: "stickiness=true",
service: rancherData{
Name: "test-service",
Labels: map[string]string{
types.LabelBackendLoadbalancerStickiness: "false",
},
},
expected: false,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := provider.hasStickinessLabel(test.service)
assert.Equal(t, actual, test.expected)
})
}
}

View file

@ -45,7 +45,7 @@ func (p *Provider) metadataProvide(configurationChan chan<- types.ConfigMessage,
}
rancherData := parseMetadataSourcedRancherData(stacks)
configuration := p.loadRancherConfig(rancherData)
configuration := p.buildConfiguration(rancherData)
configurationChan <- types.ConfigMessage{
ProviderName: "rancher",
Configuration: configuration,

View file

@ -2,18 +2,26 @@ package rancher
import (
"fmt"
"math"
"strconv"
"strings"
"text/template"
"github.com/BurntSushi/ty/fun"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
)
const (
// Health
healthy = "healthy"
updatingHealthy = "updating-healthy"
// State
active = "active"
running = "running"
upgraded = "upgraded"
updatingActive = "updating-active"
updatingRunning = "updating-running"
)
var _ provider.Provider = (*Provider)(nil)
// Provider holds configurations of the provider.
@ -40,180 +48,6 @@ func (r rancherData) String() string {
return fmt.Sprintf("{name:%s, labels:%v, containers: %v, health: %s, state: %s}", r.Name, r.Labels, r.Containers, r.Health, r.State)
}
// Frontend Labels
func (p *Provider) getPassHostHeader(service rancherData) string {
if passHostHeader, err := getServiceLabel(service, types.LabelFrontendPassHostHeader); err == nil {
return passHostHeader
}
return "true"
}
func (p *Provider) getPriority(service rancherData) string {
if priority, err := getServiceLabel(service, types.LabelFrontendPriority); err == nil {
return priority
}
return "0"
}
func (p *Provider) getEntryPoints(service rancherData) []string {
if entryPoints, err := getServiceLabel(service, types.LabelFrontendEntryPoints); err == nil {
return strings.Split(entryPoints, ",")
}
return []string{}
}
func (p *Provider) getFrontendRule(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelFrontendRule); err == nil {
return label
}
return "Host:" + strings.ToLower(strings.Replace(service.Name, "/", ".", -1)) + "." + p.Domain
}
func (p *Provider) getBasicAuth(service rancherData) []string {
if basicAuth, err := getServiceLabel(service, types.LabelFrontendAuthBasic); err == nil {
return strings.Split(basicAuth, ",")
}
return []string{}
}
func (p *Provider) getRedirect(service rancherData) string {
if redirect, err := getServiceLabel(service, types.LabelFrontendRedirect); err == nil {
return redirect
}
return ""
}
func (p *Provider) getFrontendName(service rancherData) string {
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
return provider.Normalize(p.getFrontendRule(service))
}
// Backend Labels
func (p *Provider) getLoadBalancerMethod(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelBackendLoadbalancerMethod); err == nil {
return label
}
return "wrr"
}
func (p *Provider) hasLoadBalancerLabel(service rancherData) bool {
_, errMethod := getServiceLabel(service, types.LabelBackendLoadbalancerMethod)
_, errSticky := getServiceLabel(service, types.LabelBackendLoadbalancerSticky)
_, errStickiness := getServiceLabel(service, types.LabelBackendLoadbalancerStickiness)
_, errCookieName := getServiceLabel(service, types.LabelBackendLoadbalancerStickinessCookieName)
return errMethod == nil || errSticky == nil || errStickiness == nil || errCookieName == nil
}
func (p *Provider) hasCircuitBreakerLabel(service rancherData) bool {
if _, err := getServiceLabel(service, types.LabelBackendCircuitbreakerExpression); err != nil {
return false
}
return true
}
func (p *Provider) getCircuitBreakerExpression(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelBackendCircuitbreakerExpression); err == nil {
return label
}
return "NetworkErrorRatio() > 1"
}
func (p *Provider) getSticky(service rancherData) string {
if _, err := getServiceLabel(service, types.LabelBackendLoadbalancerSticky); err == nil {
log.Warnf("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness)
return "true"
}
return "false"
}
func (p *Provider) hasStickinessLabel(service rancherData) bool {
labelStickiness, errStickiness := getServiceLabel(service, types.LabelBackendLoadbalancerStickiness)
return errStickiness == nil && len(labelStickiness) > 0 && strings.EqualFold(strings.TrimSpace(labelStickiness), "true")
}
func (p *Provider) getStickinessCookieName(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelBackendLoadbalancerStickinessCookieName); err == nil {
return label
}
return ""
}
func (p *Provider) getBackend(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelBackend); err == nil {
return provider.Normalize(label)
}
return provider.Normalize(service.Name)
}
// General Application Stuff
func (p *Provider) getPort(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelPort); err == nil {
return label
}
return ""
}
func (p *Provider) getProtocol(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelProtocol); err == nil {
return label
}
return "http"
}
func (p *Provider) getWeight(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelWeight); err == nil {
return label
}
return "0"
}
func (p *Provider) getDomain(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelDomain); err == nil {
return label
}
return p.Domain
}
func (p *Provider) hasMaxConnLabels(service rancherData) bool {
if _, err := getServiceLabel(service, types.LabelBackendMaxconnAmount); err != nil {
return false
}
if _, err := getServiceLabel(service, types.LabelBackendMaxconnExtractorfunc); err != nil {
return false
}
return true
}
func (p *Provider) getMaxConnAmount(service rancherData) int64 {
if label, err := getServiceLabel(service, types.LabelBackendMaxconnAmount); err == nil {
i, errConv := strconv.ParseInt(label, 10, 64)
if errConv != nil {
log.Errorf("Unable to parse %s %s", types.LabelBackendMaxconnAmount, label)
return math.MaxInt64
}
return i
}
return math.MaxInt64
}
func (p *Provider) getMaxConnExtractorFunc(service rancherData) string {
if label, err := getServiceLabel(service, types.LabelBackendMaxconnExtractorfunc); err == nil {
return label
}
return "request.host"
}
func getServiceLabel(service rancherData, label string) (string, error) {
for key, value := range service.Labels {
if key == label {
return value, nil
}
}
return "", fmt.Errorf("label not found: %s", label)
}
// Provide allows either the Rancher API or metadata service provider to
// seed configuration into Traefik using the given configuration channel.
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
@ -223,120 +57,16 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
return p.metadataProvide(configurationChan, pool, constraints)
}
func (p *Provider) loadRancherConfig(services []rancherData) *types.Configuration {
var RancherFuncMap = template.FuncMap{
"getPort": p.getPort,
"getBackend": p.getBackend,
"getWeight": p.getWeight,
"getDomain": p.getDomain,
"getProtocol": p.getProtocol,
"getPassHostHeader": p.getPassHostHeader,
"getPriority": p.getPriority,
"getEntryPoints": p.getEntryPoints,
"getBasicAuth": p.getBasicAuth,
"getFrontendRule": p.getFrontendRule,
"hasCircuitBreakerLabel": p.hasCircuitBreakerLabel,
"getCircuitBreakerExpression": p.getCircuitBreakerExpression,
"hasLoadBalancerLabel": p.hasLoadBalancerLabel,
"getLoadBalancerMethod": p.getLoadBalancerMethod,
"hasMaxConnLabels": p.hasMaxConnLabels,
"getMaxConnAmount": p.getMaxConnAmount,
"getMaxConnExtractorFunc": p.getMaxConnExtractorFunc,
"getSticky": p.getSticky,
"hasStickinessLabel": p.hasStickinessLabel,
"getStickinessCookieName": p.getStickinessCookieName,
"getRedirect": p.getRedirect,
}
// filter services
filteredServices := fun.Filter(p.serviceFilter, services).([]rancherData)
frontends := map[string]rancherData{}
backends := map[string]rancherData{}
for _, service := range filteredServices {
frontendName := p.getFrontendName(service)
frontends[frontendName] = service
backendName := p.getBackend(service)
backends[backendName] = service
}
templateObjects := struct {
Frontends map[string]rancherData
Backends map[string]rancherData
Domain string
}{
frontends,
backends,
p.Domain,
}
configuration, err := p.GetConfiguration("templates/rancher.tmpl", RancherFuncMap, templateObjects)
if err != nil {
log.Error(err)
}
return configuration
}
func containerFilter(name, healthState, state string) bool {
if healthState != "" && healthState != "healthy" && healthState != "updating-healthy" {
if healthState != "" && healthState != healthy && healthState != updatingHealthy {
log.Debugf("Filtering container %s with healthState of %s", name, healthState)
return false
}
if state != "" && state != "running" && state != "updating-running" {
if state != "" && state != running && state != updatingRunning {
log.Debugf("Filtering container %s with state of %s", name, state)
return false
}
return true
}
func (p *Provider) serviceFilter(service rancherData) bool {
if service.Labels[types.LabelPort] == "" {
log.Debugf("Filtering service %s without traefik.port label", service.Name)
return false
}
if !isServiceEnabled(service, p.ExposedByDefault) {
log.Debugf("Filtering disabled service %s", service.Name)
return false
}
constraintTags := strings.Split(service.Labels[types.LabelTags], ",")
if ok, failingConstraint := p.MatchConstraints(constraintTags); !ok {
if failingConstraint != nil {
log.Debugf("Filtering service %s with constraint %s", service.Name, failingConstraint.String())
}
return false
}
// Only filter services by Health (HealthState) and State if EnableServiceHealthFilter is true
if p.EnableServiceHealthFilter {
if service.Health != "" && service.Health != "healthy" && service.Health != "updating-healthy" {
log.Debugf("Filtering service %s with healthState of %s", service.Name, service.Health)
return false
}
if service.State != "" && service.State != "active" && service.State != "updating-active" && service.State != "upgraded" {
log.Debugf("Filtering service %s with state of %s", service.Name, service.State)
return false
}
}
return true
}
func isServiceEnabled(service rancherData, exposedByDefault bool) bool {
if service.Labels[types.LabelEnable] != "" {
var v = service.Labels[types.LabelEnable]
return exposedByDefault && v != "false" || v == "true"
}
return exposedByDefault
}