mirror of
https://github.com/containous/traefik.git
synced 2024-12-22 13:34:03 +03:00
refactor(rancher): rewrite configuration system.
This commit is contained in:
parent
5f71a43758
commit
ca680710a2
@ -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
170
provider/rancher/config.go
Normal 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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user