refactor(ecs): rewrite configuration system.
This commit is contained in:
parent
be718aea11
commit
c705d6f9b3
5 changed files with 642 additions and 857 deletions
|
@ -305,7 +305,7 @@ func templatesDockerTmpl() (*asset, error) {
|
|||
var _templatesEcsTmpl = []byte(`[backends]{{range $serviceName, $instances := .Services}}
|
||||
[backends.backend-{{ $serviceName }}.loadbalancer]
|
||||
method = "{{ getLoadBalancerMethod $instances}}"
|
||||
sticky = {{ getLoadBalancerSticky $instances}}
|
||||
sticky = {{ getSticky $instances}}
|
||||
{{if hasStickinessLabel $instances}}
|
||||
[backends.backend-{{ $serviceName }}.loadbalancer.stickiness]
|
||||
cookieName = "{{getStickinessCookieName $instances}}"
|
||||
|
|
170
provider/ecs/config.go
Normal file
170
provider/ecs/config.go
Normal file
|
@ -0,0 +1,170 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
// buildConfiguration fills the config template with the given instances
|
||||
func (p *Provider) buildConfiguration(services map[string][]ecsInstance) (*types.Configuration, error) {
|
||||
var ecsFuncMap = template.FuncMap{
|
||||
"filterFrontends": filterFrontends,
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
"getBasicAuth": getFuncSliceString(label.TraefikFrontendAuthBasic),
|
||||
"getLoadBalancerMethod": getFuncFirstStringValue(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod),
|
||||
"getSticky": getSticky,
|
||||
"hasStickinessLabel": getFuncFirstBoolValue(label.TraefikBackendLoadBalancerStickiness, false),
|
||||
"getStickinessCookieName": getFuncFirstStringValue(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName),
|
||||
"getProtocol": getFuncStringValue(label.TraefikProtocol, label.DefaultProtocol),
|
||||
"getHost": getHost,
|
||||
"getPort": getPort,
|
||||
"getWeight": getFuncStringValue(label.TraefikWeight, label.DefaultWeight),
|
||||
"getPassHostHeader": getFuncStringValue(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||
"getPriority": getFuncStringValue(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||
"getEntryPoints": getFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||
"hasHealthCheckLabels": hasFuncFirst(label.TraefikBackendHealthCheckPath),
|
||||
"getHealthCheckPath": getFuncFirstStringValue(label.TraefikBackendHealthCheckPath, ""),
|
||||
"getHealthCheckInterval": getFuncFirstStringValue(label.TraefikBackendHealthCheckInterval, ""),
|
||||
}
|
||||
return p.GetConfiguration("templates/ecs.tmpl", ecsFuncMap, struct {
|
||||
Services map[string][]ecsInstance
|
||||
}{
|
||||
services,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Provider) getFrontendRule(i ecsInstance) string {
|
||||
defaultRule := "Host:" + strings.ToLower(strings.Replace(i.Name, "_", "-", -1)) + "." + p.Domain
|
||||
return getStringValue(i, label.TraefikFrontendRule, defaultRule)
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
// Deprecated replaced by Stickiness
|
||||
func getSticky(instances []ecsInstance) string {
|
||||
if hasFirst(instances, label.TraefikBackendLoadBalancerSticky) {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
|
||||
}
|
||||
return getFirstStringValue(instances, label.TraefikBackendLoadBalancerSticky, "false")
|
||||
}
|
||||
|
||||
func getHost(i ecsInstance) string {
|
||||
return *i.machine.PrivateIpAddress
|
||||
}
|
||||
|
||||
func getPort(i ecsInstance) string {
|
||||
if value := getStringValue(i, label.TraefikPort, ""); len(value) > 0 {
|
||||
return value
|
||||
}
|
||||
return strconv.FormatInt(*i.container.NetworkBindings[0].HostPort, 10)
|
||||
}
|
||||
|
||||
func filterFrontends(instances []ecsInstance) []ecsInstance {
|
||||
byName := make(map[string]struct{})
|
||||
|
||||
return fun.Filter(func(i ecsInstance) bool {
|
||||
_, found := byName[i.Name]
|
||||
if !found {
|
||||
byName[i.Name] = struct{}{}
|
||||
}
|
||||
return !found
|
||||
}, instances).([]ecsInstance)
|
||||
}
|
||||
|
||||
// Label functions
|
||||
|
||||
func getFuncStringValue(labelName string, defaultValue string) func(i ecsInstance) string {
|
||||
return func(i ecsInstance) string {
|
||||
return getStringValue(i, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncSliceString(labelName string) func(i ecsInstance) []string {
|
||||
return func(i ecsInstance) []string {
|
||||
return getSliceString(i, labelName)
|
||||
}
|
||||
}
|
||||
|
||||
func hasFuncFirst(labelName string) func(instances []ecsInstance) bool {
|
||||
return func(instances []ecsInstance) bool {
|
||||
return hasFirst(instances, labelName)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncFirstStringValue(labelName string, defaultValue string) func(instances []ecsInstance) string {
|
||||
return func(instances []ecsInstance) string {
|
||||
return getFirstStringValue(instances, labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncFirstBoolValue(labelName string, defaultValue bool) func(instances []ecsInstance) bool {
|
||||
return func(instances []ecsInstance) bool {
|
||||
if len(instances) < 0 {
|
||||
return defaultValue
|
||||
}
|
||||
return getBoolValue(instances[0], labelName, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
func getStringValue(i ecsInstance, labelName string, defaultValue string) string {
|
||||
if v, ok := i.containerDefinition.DockerLabels[labelName]; ok {
|
||||
if v == nil {
|
||||
return defaultValue
|
||||
}
|
||||
if len(*v) == 0 {
|
||||
return defaultValue
|
||||
}
|
||||
return *v
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getBoolValue(i ecsInstance, labelName string, defaultValue bool) bool {
|
||||
rawValue, ok := i.containerDefinition.DockerLabels[labelName]
|
||||
if ok {
|
||||
if rawValue != nil {
|
||||
v, err := strconv.ParseBool(*rawValue)
|
||||
if err == nil {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getSliceString(i ecsInstance, labelName string) []string {
|
||||
if value, ok := i.containerDefinition.DockerLabels[labelName]; ok {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
if len(*value) == 0 {
|
||||
return nil
|
||||
}
|
||||
return label.SplitAndTrimString(*value, ",")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasFirst(instances []ecsInstance, labelName string) bool {
|
||||
if len(instances) > 0 {
|
||||
v, ok := instances[0].containerDefinition.DockerLabels[labelName]
|
||||
return ok && v != nil && len(*v) != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getFirstStringValue(instances []ecsInstance, labelName string, defaultValue string) string {
|
||||
if len(instances) == 0 {
|
||||
return defaultValue
|
||||
}
|
||||
return getStringValue(instances[0], labelName, defaultValue)
|
||||
}
|
||||
|
||||
func isEnabled(i ecsInstance, exposedByDefault bool) bool {
|
||||
return getBoolValue(i, label.TraefikEnable, exposedByDefault)
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -3,9 +3,7 @@ package ecs
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
|
@ -21,6 +19,7 @@ import (
|
|||
"github.com/containous/traefik/job"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
@ -178,34 +177,6 @@ func wrapAws(ctx context.Context, req *request.Request) error {
|
|||
return req.Send()
|
||||
}
|
||||
|
||||
// generateECSConfig fills the config template with the given instances
|
||||
func (p *Provider) generateECSConfig(services map[string][]ecsInstance) (*types.Configuration, error) {
|
||||
var ecsFuncMap = template.FuncMap{
|
||||
"filterFrontends": p.filterFrontends,
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
"getBasicAuth": p.getBasicAuth,
|
||||
"getLoadBalancerMethod": p.getLoadBalancerMethod,
|
||||
"getLoadBalancerSticky": p.getLoadBalancerSticky,
|
||||
"hasStickinessLabel": p.hasStickinessLabel,
|
||||
"getStickinessCookieName": p.getStickinessCookieName,
|
||||
"getProtocol": p.getProtocol,
|
||||
"getHost": p.getHost,
|
||||
"getPort": p.getPort,
|
||||
"getWeight": p.getWeight,
|
||||
"getPassHostHeader": p.getPassHostHeader,
|
||||
"getPriority": p.getPriority,
|
||||
"getEntryPoints": p.getEntryPoints,
|
||||
"hasHealthCheckLabels": p.hasHealthCheckLabels,
|
||||
"getHealthCheckPath": p.getHealthCheckPath,
|
||||
"getHealthCheckInterval": p.getHealthCheckInterval,
|
||||
}
|
||||
return p.GetConfiguration("templates/ecs.tmpl", ecsFuncMap, struct {
|
||||
Services map[string][]ecsInstance
|
||||
}{
|
||||
services,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Provider) loadECSConfig(ctx context.Context, client *awsClient) (*types.Configuration, error) {
|
||||
instances, err := p.listInstances(ctx, client)
|
||||
if err != nil {
|
||||
|
@ -223,7 +194,7 @@ func (p *Provider) loadECSConfig(ctx context.Context, client *awsClient) (*types
|
|||
services[instance.Name] = []ecsInstance{instance}
|
||||
}
|
||||
}
|
||||
return p.generateECSConfig(services)
|
||||
return p.buildConfiguration(services)
|
||||
}
|
||||
|
||||
// Find all running Provider tasks in a cluster, also collect the task definitions (for docker labels)
|
||||
|
@ -285,7 +256,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
|||
continue
|
||||
}
|
||||
|
||||
chunkedTaskArns := p.chunkedTaskArns(taskArns)
|
||||
chunkedTaskArns := chunkedTaskArns(taskArns)
|
||||
var tasks []*ecs.Task
|
||||
|
||||
for _, arns := range chunkedTaskArns {
|
||||
|
@ -424,22 +395,14 @@ func (p *Provider) lookupTaskDefinitions(ctx context.Context, client *awsClient,
|
|||
return taskDefinitions, nil
|
||||
}
|
||||
|
||||
func (p *Provider) label(i ecsInstance, k string) string {
|
||||
if v, found := i.containerDefinition.DockerLabels[k]; found {
|
||||
return *v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Provider) filterInstance(i ecsInstance) bool {
|
||||
if labelPort := p.label(i, types.LabelPort); len(i.container.NetworkBindings) == 0 && labelPort == "" {
|
||||
|
||||
if labelPort := getStringValue(i, label.TraefikPort, ""); len(i.container.NetworkBindings) == 0 && labelPort == "" {
|
||||
log.Debugf("Filtering ecs instance without port %s (%s)", i.Name, i.ID)
|
||||
return false
|
||||
}
|
||||
|
||||
if i.machine == nil ||
|
||||
i.machine.State == nil ||
|
||||
i.machine.State.Name == nil {
|
||||
if i.machine == nil || i.machine.State == nil || i.machine.State.Name == nil {
|
||||
log.Debugf("Filtering ecs instance in an missing ec2 information %s (%s)", i.Name, i.ID)
|
||||
return false
|
||||
}
|
||||
|
@ -454,99 +417,20 @@ func (p *Provider) filterInstance(i ecsInstance) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
label := p.label(i, types.LabelEnable)
|
||||
enabled := p.ExposedByDefault && label != "false" || label == "true"
|
||||
if !enabled {
|
||||
log.Debugf("Filtering disabled ecs instance %s (%s) (traefik.enabled = '%s')", i.Name, i.ID, label)
|
||||
if !isEnabled(i, p.ExposedByDefault) {
|
||||
log.Debugf("Filtering disabled ecs instance %s (%s)", i.Name, i.ID)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Provider) filterFrontends(instances []ecsInstance) []ecsInstance {
|
||||
byName := make(map[string]bool)
|
||||
|
||||
return fun.Filter(func(i ecsInstance) bool {
|
||||
if _, found := byName[i.Name]; !found {
|
||||
byName[i.Name] = true
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}, instances).([]ecsInstance)
|
||||
}
|
||||
|
||||
func (p *Provider) getFrontendRule(i ecsInstance) string {
|
||||
if label := p.label(i, types.LabelFrontendRule); label != "" {
|
||||
return label
|
||||
}
|
||||
return "Host:" + strings.ToLower(strings.Replace(i.Name, "_", "-", -1)) + "." + p.Domain
|
||||
}
|
||||
|
||||
func (p *Provider) getBasicAuth(i ecsInstance) []string {
|
||||
label := p.label(i, types.LabelFrontendAuthBasic)
|
||||
if label != "" {
|
||||
return strings.Split(label, ",")
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (p *Provider) getFirstInstanceLabel(instances []ecsInstance, labelName string) string {
|
||||
if len(instances) > 0 {
|
||||
return p.label(instances[0], labelName)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Provider) getLoadBalancerSticky(instances []ecsInstance) string {
|
||||
if len(instances) > 0 {
|
||||
label := p.getFirstInstanceLabel(instances, types.LabelBackendLoadbalancerSticky)
|
||||
if label != "" {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", types.LabelBackendLoadbalancerSticky, types.LabelBackendLoadbalancerStickiness)
|
||||
return label
|
||||
}
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
func (p *Provider) hasStickinessLabel(instances []ecsInstance) bool {
|
||||
stickinessLabel := p.getFirstInstanceLabel(instances, types.LabelBackendLoadbalancerStickiness)
|
||||
return len(stickinessLabel) > 0 && strings.EqualFold(strings.TrimSpace(stickinessLabel), "true")
|
||||
}
|
||||
|
||||
func (p *Provider) getStickinessCookieName(instances []ecsInstance) string {
|
||||
return p.getFirstInstanceLabel(instances, types.LabelBackendLoadbalancerStickinessCookieName)
|
||||
}
|
||||
|
||||
func (p *Provider) getLoadBalancerMethod(instances []ecsInstance) string {
|
||||
if len(instances) > 0 {
|
||||
label := p.label(instances[0], types.LabelBackendLoadbalancerMethod)
|
||||
if label != "" {
|
||||
return label
|
||||
}
|
||||
}
|
||||
return "wrr"
|
||||
}
|
||||
|
||||
func (p *Provider) hasHealthCheckLabels(instances []ecsInstance) bool {
|
||||
return p.getHealthCheckPath(instances) != ""
|
||||
}
|
||||
|
||||
func (p *Provider) getHealthCheckPath(instances []ecsInstance) string {
|
||||
return p.getFirstInstanceLabel(instances, types.LabelBackendHealthcheckPath)
|
||||
}
|
||||
|
||||
func (p *Provider) getHealthCheckInterval(instances []ecsInstance) string {
|
||||
return p.getFirstInstanceLabel(instances, types.LabelBackendHealthcheckInterval)
|
||||
}
|
||||
|
||||
// Provider expects no more than 100 parameters be passed to a DescribeTask call; thus, pack
|
||||
// each string into an array capped at 100 elements
|
||||
func (p *Provider) chunkedTaskArns(tasks []*string) [][]*string {
|
||||
func chunkedTaskArns(tasks []*string) [][]*string {
|
||||
var chunkedTasks [][]*string
|
||||
for i := 0; i < len(tasks); i += 100 {
|
||||
sliceEnd := -1
|
||||
var sliceEnd int
|
||||
if i+100 < len(tasks) {
|
||||
sliceEnd = i + 100
|
||||
} else {
|
||||
|
@ -556,49 +440,3 @@ func (p *Provider) chunkedTaskArns(tasks []*string) [][]*string {
|
|||
}
|
||||
return chunkedTasks
|
||||
}
|
||||
|
||||
func (p *Provider) getProtocol(i ecsInstance) string {
|
||||
if label := p.label(i, types.LabelProtocol); label != "" {
|
||||
return label
|
||||
}
|
||||
return "http"
|
||||
}
|
||||
|
||||
func (p *Provider) getHost(i ecsInstance) string {
|
||||
return *i.machine.PrivateIpAddress
|
||||
}
|
||||
|
||||
func (p *Provider) getPort(i ecsInstance) string {
|
||||
if port := p.label(i, types.LabelPort); port != "" {
|
||||
return port
|
||||
}
|
||||
return strconv.FormatInt(*i.container.NetworkBindings[0].HostPort, 10)
|
||||
}
|
||||
|
||||
func (p *Provider) getWeight(i ecsInstance) string {
|
||||
if label := p.label(i, types.LabelWeight); label != "" {
|
||||
return label
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
func (p *Provider) getPassHostHeader(i ecsInstance) string {
|
||||
if label := p.label(i, types.LabelFrontendPassHostHeader); label != "" {
|
||||
return label
|
||||
}
|
||||
return "true"
|
||||
}
|
||||
|
||||
func (p *Provider) getPriority(i ecsInstance) string {
|
||||
if label := p.label(i, types.LabelFrontendPriority); label != "" {
|
||||
return label
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
func (p *Provider) getEntryPoints(i ecsInstance) []string {
|
||||
if label := p.label(i, types.LabelFrontendEntryPoints); label != "" {
|
||||
return strings.Split(label, ",")
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[backends]{{range $serviceName, $instances := .Services}}
|
||||
[backends.backend-{{ $serviceName }}.loadbalancer]
|
||||
method = "{{ getLoadBalancerMethod $instances}}"
|
||||
sticky = {{ getLoadBalancerSticky $instances}}
|
||||
sticky = {{ getSticky $instances}}
|
||||
{{if hasStickinessLabel $instances}}
|
||||
[backends.backend-{{ $serviceName }}.loadbalancer.stickiness]
|
||||
cookieName = "{{getStickinessCookieName $instances}}"
|
||||
|
|
Loading…
Reference in a new issue