Add support for several ECS backends
This commit is contained in:
parent
05665f4eec
commit
8765494cbd
7 changed files with 268 additions and 95 deletions
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
"github.com/containous/traefik/provider/kubernetes"
|
||||
"github.com/containous/traefik/provider/rancher"
|
||||
"github.com/containous/traefik/safe"
|
||||
|
@ -144,6 +145,7 @@ Complete documentation is available at https://traefik.io`,
|
|||
f.AddParser(reflect.TypeOf(server.RootCAs{}), &server.RootCAs{})
|
||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
||||
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
||||
f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{})
|
||||
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
|
||||
|
||||
|
|
21
docs/toml.md
21
docs/toml.md
|
@ -1708,10 +1708,16 @@ Træfik can be configured to use Amazon ECS as a backend configuration:
|
|||
|
||||
# ECS Cluster Name
|
||||
#
|
||||
# Optional
|
||||
# Default: "default"
|
||||
# Deprecated - Please use Clusters
|
||||
#
|
||||
Cluster = "default"
|
||||
# Cluster = "default"
|
||||
|
||||
# ECS Clusters Name
|
||||
#
|
||||
# Optional
|
||||
# Default: ["default"]
|
||||
#
|
||||
Clusters = ["default"]
|
||||
|
||||
# Enable watch ECS changes
|
||||
#
|
||||
|
@ -1720,6 +1726,13 @@ Cluster = "default"
|
|||
#
|
||||
Watch = true
|
||||
|
||||
# Enable auto discover ECS clusters
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
AutoDiscoverClusters = false
|
||||
|
||||
# Polling interval (in seconds)
|
||||
#
|
||||
# Optional
|
||||
|
@ -1780,6 +1793,8 @@ Træfik needs the following policy to read ECS information:
|
|||
"Sid": "Traefik ECS read access",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ecs:ListClusters",
|
||||
"ecs:DescribeClusters",
|
||||
"ecs:ListTasks",
|
||||
"ecs:DescribeTasks",
|
||||
"ecs:DescribeContainerInstances",
|
||||
|
|
32
provider/ecs/cluster.go
Normal file
32
provider/ecs/cluster.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Clusters holds ecs clusters name
|
||||
type Clusters []string
|
||||
|
||||
// Set adds strings elem into the the parser
|
||||
// it splits str on , and ;
|
||||
func (c *Clusters) Set(str string) error {
|
||||
fargs := func(c rune) bool {
|
||||
return c == ',' || c == ';'
|
||||
}
|
||||
// get function
|
||||
slice := strings.FieldsFunc(str, fargs)
|
||||
*c = append(*c, slice...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get Clusters
|
||||
func (c *Clusters) Get() interface{} { return Clusters(*c) }
|
||||
|
||||
// String return slice in a string
|
||||
func (c *Clusters) String() string { return fmt.Sprintf("%v", *c) }
|
||||
|
||||
// SetValue sets Clusters into the parser
|
||||
func (c *Clusters) SetValue(val interface{}) {
|
||||
*c = Clusters(val.(Clusters))
|
||||
}
|
80
provider/ecs/cluster_test.go
Normal file
80
provider/ecs/cluster_test.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClustersSet(t *testing.T) {
|
||||
checkMap := map[string]Clusters{
|
||||
"cluster": {"cluster"},
|
||||
"cluster1,cluster2": {"cluster1", "cluster2"},
|
||||
"cluster1;cluster2": {"cluster1", "cluster2"},
|
||||
"cluster1,cluster2;cluster3": {"cluster1", "cluster2", "cluster3"},
|
||||
}
|
||||
for str, check := range checkMap {
|
||||
var clusters Clusters
|
||||
if err := clusters.Set(str); err != nil {
|
||||
t.Fatalf("Error :%s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(clusters, check) {
|
||||
t.Fatalf("Expected:%s\ngot:%s", check, clusters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClustersGet(t *testing.T) {
|
||||
slices := []Clusters{
|
||||
{"cluster"},
|
||||
{"cluster1", "cluster2"},
|
||||
{"cluster1", "cluster2", "cluster3"},
|
||||
}
|
||||
check := []Clusters{
|
||||
{"cluster"},
|
||||
{"cluster1", "cluster2"},
|
||||
{"cluster1", "cluster2", "cluster3"},
|
||||
}
|
||||
for i, slice := range slices {
|
||||
if !reflect.DeepEqual(slice.Get(), check[i]) {
|
||||
t.Fatalf("Expected:%s\ngot:%s", check[i], slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClustersString(t *testing.T) {
|
||||
slices := []Clusters{
|
||||
{"cluster"},
|
||||
{"cluster1", "cluster2"},
|
||||
{"cluster1", "cluster2", "cluster3"},
|
||||
}
|
||||
check := []string{
|
||||
"[cluster]",
|
||||
"[cluster1 cluster2]",
|
||||
"[cluster1 cluster2 cluster3]",
|
||||
}
|
||||
for i, slice := range slices {
|
||||
if !reflect.DeepEqual(slice.String(), check[i]) {
|
||||
t.Fatalf("Expected:%s\ngot:%s", check[i], slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClustersSetValue(t *testing.T) {
|
||||
check := []Clusters{
|
||||
{"cluster"},
|
||||
{"cluster1", "cluster2"},
|
||||
{"cluster1", "cluster2", "cluster3"},
|
||||
}
|
||||
slices := []Clusters{
|
||||
{"cluster"},
|
||||
{"cluster1", "cluster2"},
|
||||
{"cluster1", "cluster2", "cluster3"},
|
||||
}
|
||||
for i, s := range slices {
|
||||
var slice Clusters
|
||||
slice.SetValue(s)
|
||||
if !reflect.DeepEqual(slice, check[i]) {
|
||||
t.Fatalf("Expected:%s\ngot:%s", check[i], slice)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,7 +36,9 @@ type Provider struct {
|
|||
RefreshSeconds int `description:"Polling interval (in seconds)"`
|
||||
|
||||
// Provider lookup parameters
|
||||
Cluster string `description:"ECS Cluster Name"`
|
||||
Clusters Clusters `description:"ECS Clusters name"`
|
||||
Cluster string `description:"deprecated - ECS Cluster name"` // deprecated
|
||||
AutoDiscoverClusters bool `description:"Auto discover cluster"`
|
||||
Region string `description:"The AWS region to use for requests"`
|
||||
AccessKeyID string `description:"The AWS credentials access key to use for making requests"`
|
||||
SecretAccessKey string `description:"The AWS credentials access key to use for making requests"`
|
||||
|
@ -200,8 +202,43 @@ func (p *Provider) loadECSConfig(ctx context.Context, client *awsClient) (*types
|
|||
// and the EC2 instance data
|
||||
func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsInstance, error) {
|
||||
var taskArns []*string
|
||||
var instances []ecsInstance
|
||||
var clustersArn []*string
|
||||
var clusters Clusters
|
||||
|
||||
if p.AutoDiscoverClusters {
|
||||
input := &ecs.ListClustersInput{}
|
||||
for {
|
||||
result, err := client.ecs.ListClusters(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result != nil {
|
||||
clustersArn = append(clustersArn, result.ClusterArns...)
|
||||
input.NextToken = result.NextToken
|
||||
if result.NextToken == nil {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, carns := range clustersArn {
|
||||
clusters = append(clusters, *carns)
|
||||
}
|
||||
} else if p.Cluster != "" {
|
||||
// TODO: Deprecated configuration - Need to be removed in the future
|
||||
clusters = Clusters{p.Cluster}
|
||||
log.Warn("Deprecated configuration found: ecs.cluster " +
|
||||
"Please use ecs.clusters instead.")
|
||||
} else {
|
||||
clusters = p.Clusters
|
||||
}
|
||||
log.Debugf("ECS Clusters: %s", clusters)
|
||||
for _, c := range clusters {
|
||||
|
||||
req, _ := client.ecs.ListTasksRequest(&ecs.ListTasksInput{
|
||||
Cluster: &p.Cluster,
|
||||
Cluster: &c,
|
||||
DesiredStatus: aws.String(ecs.DesiredStatusRunning),
|
||||
})
|
||||
|
||||
|
@ -227,7 +264,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
|||
for _, arns := range chunkedTaskArns {
|
||||
req, taskResp := client.ecs.DescribeTasksRequest(&ecs.DescribeTasksInput{
|
||||
Tasks: arns,
|
||||
Cluster: &p.Cluster,
|
||||
Cluster: &c,
|
||||
})
|
||||
|
||||
if err := wrapAws(ctx, req); err != nil {
|
||||
|
@ -254,7 +291,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
|||
}
|
||||
}
|
||||
|
||||
machines, err := p.lookupEc2Instances(ctx, client, containerInstanceArns)
|
||||
machines, err := p.lookupEc2Instances(ctx, client, &c, containerInstanceArns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -264,7 +301,6 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var instances []ecsInstance
|
||||
for _, task := range tasks {
|
||||
|
||||
machineIdx := byContainerInstance[*task.ContainerInstanceArn]
|
||||
|
@ -292,11 +328,12 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
|||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instances, nil
|
||||
}
|
||||
|
||||
func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, containerArns []*string) ([]*ec2.Instance, error) {
|
||||
func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, clusterName *string, containerArns []*string) ([]*ec2.Instance, error) {
|
||||
|
||||
order := make(map[string]int)
|
||||
instanceIds := make([]*string, len(containerArns))
|
||||
|
@ -307,7 +344,7 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, co
|
|||
|
||||
req, _ := client.ecs.DescribeContainerInstancesRequest(&ecs.DescribeContainerInstancesInput{
|
||||
ContainerInstances: containerArns,
|
||||
Cluster: &p.Cluster,
|
||||
Cluster: clusterName,
|
||||
})
|
||||
|
||||
for ; req != nil; req = req.NextPage() {
|
||||
|
|
|
@ -534,8 +534,9 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
|||
var defaultECS ecs.Provider
|
||||
defaultECS.Watch = true
|
||||
defaultECS.ExposedByDefault = true
|
||||
defaultECS.AutoDiscoverClusters = false
|
||||
defaultECS.Clusters = ecs.Clusters{"default"}
|
||||
defaultECS.RefreshSeconds = 15
|
||||
defaultECS.Cluster = "default"
|
||||
defaultECS.Constraints = types.Constraints{}
|
||||
|
||||
//default Rancher
|
||||
|
|
|
@ -1095,11 +1095,17 @@
|
|||
|
||||
# ECS Cluster Name
|
||||
#
|
||||
# Optional
|
||||
# Default: "default"
|
||||
# Deprecated - Please use Clusters
|
||||
#
|
||||
# Cluster = "default"
|
||||
|
||||
# ECS Clusters Name
|
||||
#
|
||||
# Optional
|
||||
# Default: ["default"]
|
||||
#
|
||||
# Clusters = ["default"]
|
||||
|
||||
# Enable watch ECS changes
|
||||
#
|
||||
# Optional
|
||||
|
|
Loading…
Reference in a new issue