diff --git a/provider/ecs/config.go b/provider/ecs/config.go index 50e6cfce1..282dfc3ae 100644 --- a/provider/ecs/config.go +++ b/provider/ecs/config.go @@ -68,7 +68,7 @@ func (p *Provider) filterInstance(i ecsInstance) bool { return false } - if labelPort := label.GetStringValue(i.TraefikLabels, label.TraefikPort, ""); i.machine.port == 0 && labelPort == "" { + if labelPort := label.GetStringValue(i.TraefikLabels, label.TraefikPort, ""); len(i.machine.ports) == 0 && labelPort == "" { log.Debugf("Filtering ecs instance without port %s (%s)", i.Name, i.ID) return false } @@ -119,9 +119,17 @@ func getHost(i ecsInstance) string { func getPort(i ecsInstance) string { if value := label.GetStringValue(i.TraefikLabels, label.TraefikPort, ""); len(value) > 0 { - return value + port, err := strconv.ParseInt(value, 10, 64) + if err == nil { + for _, mapping := range i.machine.ports { + if port == mapping.hostPort || port == mapping.containerPort { + return strconv.FormatInt(mapping.hostPort, 10) + } + } + return value + } } - return strconv.FormatInt(i.machine.port, 10) + return strconv.FormatInt(i.machine.ports[0].hostPort, 10) } func filterFrontends(instances []ecsInstance) []ecsInstance { diff --git a/provider/ecs/config_test.go b/provider/ecs/config_test.go index e86f8d4e5..dac1dfb1d 100644 --- a/provider/ecs/config_test.go +++ b/provider/ecs/config_test.go @@ -32,7 +32,7 @@ func TestBuildConfiguration(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.1", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, }, @@ -75,7 +75,7 @@ func TestBuildConfiguration(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.1", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, }, @@ -195,7 +195,7 @@ func TestBuildConfiguration(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.1", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, }, @@ -435,7 +435,7 @@ func TestBuildConfiguration(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.1", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, { @@ -522,7 +522,7 @@ func TestBuildConfiguration(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.2.2.1", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, }, @@ -876,6 +876,27 @@ func TestGetPort(t *testing.T) { label.TraefikPort: aws.String("80"), }), }, + { + desc: "Container label should provide exposed port", + expected: "6536", + instanceInfo: simpleEcsInstanceDynamicPorts(map[string]*string{ + label.TraefikPort: aws.String("8080"), + }), + }, + { + desc: "Wrong port container label should provide default exposed port", + expected: "9000", + instanceInfo: simpleEcsInstanceDynamicPorts(map[string]*string{ + label.TraefikPort: aws.String("9000"), + }), + }, + { + desc: "Invalid port container label should provide default exposed port", + expected: "6535", + instanceInfo: simpleEcsInstanceDynamicPorts(map[string]*string{ + label.TraefikPort: aws.String("foo"), + }), + }, } for _, test := range testCases { @@ -987,7 +1008,7 @@ func makeEcsInstance(containerDef *ecs.ContainerDefinition) ecsInstance { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.0", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, } @@ -1003,7 +1024,7 @@ func simpleEcsInstance(labels map[string]*string) ecsInstance { Name: aws.String("http"), DockerLabels: labels, }) - instance.machine.port = 80 + instance.machine.ports = []portMapping{{hostPort: 80}} return instance } @@ -1012,7 +1033,25 @@ func simpleEcsInstanceNoNetwork(labels map[string]*string) ecsInstance { Name: aws.String("http"), DockerLabels: labels, }) - instance.machine.port = 0 + instance.machine.ports = []portMapping{} + return instance +} + +func simpleEcsInstanceDynamicPorts(labels map[string]*string) ecsInstance { + instance := makeEcsInstance(&ecs.ContainerDefinition{ + Name: aws.String("http"), + DockerLabels: labels, + }) + instance.machine.ports = []portMapping{ + { + containerPort: 80, + hostPort: 6535, + }, + { + containerPort: 8080, + hostPort: 6536, + }, + } return instance } diff --git a/provider/ecs/deprecated_config.go b/provider/ecs/deprecated_config.go index 8fe7ad02b..32dfcad75 100644 --- a/provider/ecs/deprecated_config.go +++ b/provider/ecs/deprecated_config.go @@ -86,7 +86,7 @@ func (p *Provider) filterInstanceV1(i ecsInstance) bool { return false } - if labelPort := getStringValueV1(i, label.TraefikPort, ""); i.machine.port == 0 && labelPort == "" { + if labelPort := getStringValueV1(i, label.TraefikPort, ""); len(i.machine.ports) == 0 && labelPort == "" { log.Debugf("Filtering ecs instance without port %s (%s)", i.Name, i.ID) return false } diff --git a/provider/ecs/deprecated_config_test.go b/provider/ecs/deprecated_config_test.go index 3f8267673..aa0ad933a 100644 --- a/provider/ecs/deprecated_config_test.go +++ b/provider/ecs/deprecated_config_test.go @@ -30,7 +30,7 @@ func TestBuildConfigurationV1(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.1", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, }, @@ -76,7 +76,7 @@ func TestBuildConfigurationV1(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.1", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, }, @@ -142,7 +142,7 @@ func TestBuildConfigurationV1(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.1", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, }, @@ -219,7 +219,7 @@ func TestBuildConfigurationV1(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.1", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, { Name: "testing-instance-v2", @@ -248,7 +248,7 @@ func TestBuildConfigurationV1(t *testing.T) { machine: &machine{ state: ec2.InstanceStateNameRunning, privateIP: "10.0.0.2", - port: 1337, + ports: []portMapping{{hostPort: 1337}}, }, }, }, diff --git a/provider/ecs/ecs.go b/provider/ecs/ecs.go index 78c157835..fc8a0c953 100644 --- a/provider/ecs/ecs.go +++ b/provider/ecs/ecs.go @@ -48,11 +48,16 @@ type ecsInstance struct { TraefikLabels map[string]string } +type portMapping struct { + containerPort int64 + hostPort int64 +} + type machine struct { name string state string privateIP string - port int64 + ports []portMapping } type awsClient struct { @@ -203,8 +208,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI } 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.") + log.Warn("Deprecated configuration found: ecs.cluster. Please use ecs.clusters instead.") } else { clusters = p.Clusters } @@ -280,23 +284,33 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI var mach *machine if aws.StringValue(task.LaunchType) == ecs.LaunchTypeFargate { - var hostPort int64 - if len(containerDefinition.PortMappings) > 0 && containerDefinition.PortMappings[0] != nil { - hostPort = aws.Int64Value(containerDefinition.PortMappings[0].HostPort) + var ports []portMapping + for _, mapping := range containerDefinition.PortMappings { + if mapping != nil { + ports = append(ports, portMapping{ + hostPort: aws.Int64Value(mapping.HostPort), + containerPort: aws.Int64Value(mapping.ContainerPort), + }) + } } mach = &machine{ privateIP: aws.StringValue(container.NetworkInterfaces[0].PrivateIpv4Address), - port: hostPort, + ports: ports, state: aws.StringValue(task.LastStatus), } } else { - var hostPort int64 - if len(container.NetworkBindings) > 0 && container.NetworkBindings[0] != nil { - hostPort = aws.Int64Value(container.NetworkBindings[0].HostPort) + var ports []portMapping + for _, mapping := range container.NetworkBindings { + if mapping != nil { + ports = append(ports, portMapping{ + hostPort: aws.Int64Value(mapping.HostPort), + containerPort: aws.Int64Value(mapping.ContainerPort), + }) + } } mach = &machine{ privateIP: aws.StringValue(containerInstance.PrivateIpAddress), - port: hostPort, + ports: ports, state: aws.StringValue(containerInstance.State.Name), } }