From dbbd24d270857e618c395ee494746be8a4953344 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Wed, 15 Jul 2020 18:02:57 +0200 Subject: [PATCH] Don't create a LoadBalancer if compose app has no port exposed Signed-off-by: Nicolas De Loof --- ecs/pkg/amazon/backend/cloudformation.go | 27 +- ecs/pkg/amazon/backend/cloudformation_test.go | 23 +- .../simple-single-service-with-overrides.yaml | 4 - .../testdata/input/simple-single-service.yaml | 4 +- .../simple-cloudformation-conversion.golden | 80 ++++- ...formation-with-overrides-conversion.golden | 292 ------------------ 6 files changed, 117 insertions(+), 313 deletions(-) delete mode 100644 ecs/pkg/amazon/backend/testdata/input/simple-single-service-with-overrides.yaml delete mode 100644 ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-with-overrides-conversion.golden diff --git a/ecs/pkg/amazon/backend/cloudformation.go b/ecs/pkg/amazon/backend/cloudformation.go index 3cd72360f..c07f7766d 100644 --- a/ecs/pkg/amazon/backend/cloudformation.go +++ b/ecs/pkg/amazon/backend/cloudformation.go @@ -142,14 +142,16 @@ func (b Backend) Convert(project *types.Project) (*cloudformation.Template, erro protocol = elbv2.ProtocolEnumHttp } } - targetGroupName := createTargetGroup(project, service, port, template, protocol) - listenerName := createListener(service, port, template, targetGroupName, loadBalancerARN, protocol) - dependsOn = append(dependsOn, listenerName) - serviceLB = append(serviceLB, ecs.Service_LoadBalancer{ - ContainerName: service.Name, - ContainerPort: int(port.Target), - TargetGroupArn: cloudformation.Ref(targetGroupName), - }) + if loadBalancerARN != "" { + targetGroupName := createTargetGroup(project, service, port, template, protocol) + listenerName := createListener(service, port, template, targetGroupName, loadBalancerARN, protocol) + dependsOn = append(dependsOn, listenerName) + serviceLB = append(serviceLB, ecs.Service_LoadBalancer{ + ContainerName: service.Name, + ContainerPort: int(port.Target), + TargetGroupArn: cloudformation.Ref(targetGroupName), + }) + } } } @@ -218,6 +220,15 @@ func getLoadBalancerSecurityGroups(project *types.Project, template *cloudformat } func createLoadBalancer(project *types.Project, template *cloudformation.Template) string { + ports := 0 + for _, service := range project.Services { + ports += len(service.Ports) + } + if ports == 0 { + // Project do not expose any port (batch jobs?) + // So no need to create a LoadBalancer + return "" + } // load balancer names are limited to 32 characters total loadBalancerName := fmt.Sprintf("%.32s", fmt.Sprintf("%sLoadBalancer", strings.Title(project.Name))) diff --git a/ecs/pkg/amazon/backend/cloudformation_test.go b/ecs/pkg/amazon/backend/cloudformation_test.go index d362a93fc..032b9cd58 100644 --- a/ecs/pkg/amazon/backend/cloudformation_test.go +++ b/ecs/pkg/amazon/backend/cloudformation_test.go @@ -26,13 +26,6 @@ func TestSimpleConvert(t *testing.T) { golden.Assert(t, result, expected) } -func TestSimpleWithOverrides(t *testing.T) { - project := load(t, "testdata/input/simple-single-service.yaml", "testdata/input/simple-single-service-with-overrides.yaml") - result := convertResultAsString(t, project, "TestCluster") - expected := "simple/simple-cloudformation-with-overrides-conversion.golden" - golden.Assert(t, result, expected) -} - func TestRolePolicy(t *testing.T) { template := convertYaml(t, "test", ` version: "3" @@ -94,6 +87,22 @@ services: assert.Check(t, len(lb.SecurityGroups) > 0) } +func TestNoLoadBalancerIfNoPortExposed(t *testing.T) { + template := convertYaml(t, "test", ` +version: "3" +services: + test: + image: nginx + foo: + image: bar +`) + for _, r := range template.Resources { + assert.Check(t, r.AWSCloudFormationType() != "AWS::ElasticLoadBalancingV2::TargetGroup") + assert.Check(t, r.AWSCloudFormationType() != "AWS::ElasticLoadBalancingV2::Listener") + assert.Check(t, r.AWSCloudFormationType() != "AWS::ElasticLoadBalancingV2::LoadBalancer") + } +} + func TestServiceReplicas(t *testing.T) { template := convertYaml(t, "test", ` version: "3" diff --git a/ecs/pkg/amazon/backend/testdata/input/simple-single-service-with-overrides.yaml b/ecs/pkg/amazon/backend/testdata/input/simple-single-service-with-overrides.yaml deleted file mode 100644 index 3dc8a0b6f..000000000 --- a/ecs/pkg/amazon/backend/testdata/input/simple-single-service-with-overrides.yaml +++ /dev/null @@ -1,4 +0,0 @@ -version: "3" -services: - simple: - image: haproxy diff --git a/ecs/pkg/amazon/backend/testdata/input/simple-single-service.yaml b/ecs/pkg/amazon/backend/testdata/input/simple-single-service.yaml index 4b3f9af21..448f21108 100644 --- a/ecs/pkg/amazon/backend/testdata/input/simple-single-service.yaml +++ b/ecs/pkg/amazon/backend/testdata/input/simple-single-service.yaml @@ -1,4 +1,6 @@ version: "3" services: simple: - image: nginx \ No newline at end of file + image: nginx + ports: + - "80:80" \ No newline at end of file diff --git a/ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-conversion.golden b/ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-conversion.golden index 0256a5868..7e8859050 100644 --- a/ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-conversion.golden +++ b/ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-conversion.golden @@ -71,6 +71,9 @@ "Type": "AWS::Logs::LogGroup" }, "SimpleService": { + "DependsOn": [ + "SimpleTCP80Listener" + ], "Properties": { "Cluster": { "Fn::If": [ @@ -85,6 +88,15 @@ }, "DesiredCount": 1, "LaunchType": "FARGATE", + "LoadBalancers": [ + { + "ContainerName": "simple", + "ContainerPort": 80, + "TargetGroupArn": { + "Ref": "SimpleTCP80TargetGroup" + } + } + ], "NetworkConfiguration": { "AwsvpcConfiguration": { "AssignPublicIp": "ENABLED", @@ -152,6 +164,56 @@ }, "Type": "AWS::ServiceDiscovery::Service" }, + "SimpleTCP80Listener": { + "Properties": { + "DefaultActions": [ + { + "ForwardConfig": { + "TargetGroups": [ + { + "TargetGroupArn": { + "Ref": "SimpleTCP80TargetGroup" + } + } + ] + }, + "Type": "forward" + } + ], + "LoadBalancerArn": { + "Fn::If": [ + "CreateLoadBalancer", + { + "Ref": "TestSimpleConvertLoadBalancer" + }, + { + "Ref": "ParameterLoadBalancerARN" + } + ] + }, + "Port": 80, + "Protocol": "HTTP" + }, + "Type": "AWS::ElasticLoadBalancingV2::Listener" + }, + "SimpleTCP80TargetGroup": { + "Properties": { + "Name": "SimpleTCP80TargetGroup", + "Port": 80, + "Protocol": "HTTP", + "Tags": [ + { + "Key": "com.docker.compose.project", + "Value": "TestSimpleConvert" + } + ], + "TargetType": "ip", + "VpcId": { + "Ref": "ParameterVPCId" + } + }, + "Type": "AWS::ElasticLoadBalancingV2::TargetGroup" + }, "SimpleTaskDefinition": { "Properties": { "ContainerDefinitions": [ @@ -188,7 +250,14 @@ "awslogs-stream-prefix": "TestSimpleConvert" } }, - "Name": "simple" + "Name": "simple", + "PortMappings": [ + { + "ContainerPort": 80, + "HostPort": 80, + "Protocol": "tcp" + } + ] } ], "Cpu": "256", @@ -231,6 +300,15 @@ "Properties": { "GroupDescription": "TestSimpleConvert default Security Group", "GroupName": "TestSimpleConvertDefaultNetwork", + "SecurityGroupIngress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "simple:80/tcp", + "FromPort": 80, + "IpProtocol": "TCP", + "ToPort": 80 + } + ], "Tags": [ { "Key": "com.docker.compose.project", diff --git a/ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-with-overrides-conversion.golden b/ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-with-overrides-conversion.golden deleted file mode 100644 index 0bc56cf99..000000000 --- a/ecs/pkg/amazon/backend/testdata/simple/simple-cloudformation-with-overrides-conversion.golden +++ /dev/null @@ -1,292 +0,0 @@ -{ - "AWSTemplateFormatVersion": "2010-09-09", - "Conditions": { - "CreateCluster": { - "Fn::Equals": [ - "", - { - "Ref": "ParameterClusterName" - } - ] - }, - "CreateLoadBalancer": { - "Fn::Equals": [ - "", - { - "Ref": "ParameterLoadBalancerARN" - } - ] - } - }, - "Parameters": { - "ParameterClusterName": { - "Description": "Name of the ECS cluster to deploy to (optional)", - "Type": "String" - }, - "ParameterLoadBalancerARN": { - "Description": "Name of the LoadBalancer to connect to (optional)", - "Type": "String" - }, - "ParameterSubnet1Id": { - "Description": "SubnetId, for Availability Zone 1 in the region in your VPC", - "Type": "AWS::EC2::Subnet::Id" - }, - "ParameterSubnet2Id": { - "Description": "SubnetId, for Availability Zone 2 in the region in your VPC", - "Type": "AWS::EC2::Subnet::Id" - }, - "ParameterVPCId": { - "Description": "ID of the VPC", - "Type": "AWS::EC2::VPC::Id" - } - }, - "Resources": { - "CloudMap": { - "Properties": { - "Description": "Service Map for Docker Compose project TestSimpleWithOverrides", - "Name": "TestSimpleWithOverrides.local", - "Vpc": { - "Ref": "ParameterVPCId" - } - }, - "Type": "AWS::ServiceDiscovery::PrivateDnsNamespace" - }, - "Cluster": { - "Condition": "CreateCluster", - "Properties": { - "ClusterName": "TestSimpleWithOverrides", - "Tags": [ - { - "Key": "com.docker.compose.project", - "Value": "TestSimpleWithOverrides" - } - ] - }, - "Type": "AWS::ECS::Cluster" - }, - "LogGroup": { - "Properties": { - "LogGroupName": "/docker-compose/TestSimpleWithOverrides" - }, - "Type": "AWS::Logs::LogGroup" - }, - "SimpleService": { - "Properties": { - "Cluster": { - "Fn::If": [ - "CreateCluster", - { - "Ref": "Cluster" - }, - { - "Ref": "ParameterClusterName" - } - ] - }, - "DesiredCount": 1, - "LaunchType": "FARGATE", - "NetworkConfiguration": { - "AwsvpcConfiguration": { - "AssignPublicIp": "ENABLED", - "SecurityGroups": [ - { - "Ref": "TestSimpleWithOverridesDefaultNetwork" - } - ], - "Subnets": [ - { - "Ref": "ParameterSubnet1Id" - }, - { - "Ref": "ParameterSubnet2Id" - } - ] - } - }, - "SchedulingStrategy": "REPLICA", - "ServiceRegistries": [ - { - "RegistryArn": { - "Fn::GetAtt": [ - "SimpleServiceDiscoveryEntry", - "Arn" - ] - } - } - ], - "Tags": [ - { - "Key": "com.docker.compose.project", - "Value": "TestSimpleWithOverrides" - }, - { - "Key": "com.docker.compose.service", - "Value": "simple" - } - ], - "TaskDefinition": { - "Ref": "SimpleTaskDefinition" - } - }, - "Type": "AWS::ECS::Service" - }, - "SimpleServiceDiscoveryEntry": { - "Properties": { - "Description": "\"simple\" service discovery entry in Cloud Map", - "DnsConfig": { - "DnsRecords": [ - { - "TTL": 60, - "Type": "A" - } - ], - "RoutingPolicy": "MULTIVALUE" - }, - "HealthCheckCustomConfig": { - "FailureThreshold": 1 - }, - "Name": "simple", - "NamespaceId": { - "Ref": "CloudMap" - } - }, - "Type": "AWS::ServiceDiscovery::Service" - }, - "SimpleTaskDefinition": { - "Properties": { - "ContainerDefinitions": [ - { - "Environment": [ - { - "Name": "LOCALDOMAIN", - "Value": { - "Fn::Join": [ - "", - [ - { - "Ref": "AWS::Region" - }, - ".compute.internal", - " TestSimpleWithOverrides.local" - ] - ] - } - } - ], - "Essential": true, - "Image": "haproxy", - "LinuxParameters": {}, - "LogConfiguration": { - "LogDriver": "awslogs", - "Options": { - "awslogs-group": { - "Ref": "LogGroup" - }, - "awslogs-region": { - "Ref": "AWS::Region" - }, - "awslogs-stream-prefix": "TestSimpleWithOverrides" - } - }, - "Name": "simple" - } - ], - "Cpu": "256", - "ExecutionRoleArn": { - "Ref": "SimpleTaskExecutionRole" - }, - "Family": "TestSimpleWithOverrides-simple", - "Memory": "512", - "NetworkMode": "awsvpc", - "RequiresCompatibilities": [ - "FARGATE" - ] - }, - "Type": "AWS::ECS::TaskDefinition" - }, - "SimpleTaskExecutionRole": { - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": "ecs-tasks.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy", - "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" - ] - }, - "Type": "AWS::IAM::Role" - }, - "TestSimpleWithOverridesDefaultNetwork": { - "Properties": { - "GroupDescription": "TestSimpleWithOverrides default Security Group", - "GroupName": "TestSimpleWithOverridesDefaultNetwork", - "Tags": [ - { - "Key": "com.docker.compose.project", - "Value": "TestSimpleWithOverrides" - }, - { - "Key": "com.docker.compose.network", - "Value": "default" - } - ], - "VpcId": { - "Ref": "ParameterVPCId" - } - }, - "Type": "AWS::EC2::SecurityGroup" - }, - "TestSimpleWithOverridesDefaultNetworkIngress": { - "Properties": { - "Description": "Allow communication within network default", - "GroupId": { - "Ref": "TestSimpleWithOverridesDefaultNetwork" - }, - "IpProtocol": "-1", - "SourceSecurityGroupId": { - "Ref": "TestSimpleWithOverridesDefaultNetwork" - } - }, - "Type": "AWS::EC2::SecurityGroupIngress" - }, - "TestSimpleWithOverridesLoadBalan": { - "Condition": "CreateLoadBalancer", - "Properties": { - "Name": "TestSimpleWithOverridesLoadBalan", - "Scheme": "internet-facing", - "SecurityGroups": [ - { - "Ref": "TestSimpleWithOverridesDefaultNetwork" - } - ], - "Subnets": [ - { - "Ref": "ParameterSubnet1Id" - }, - { - "Ref": "ParameterSubnet2Id" - } - ], - "Tags": [ - { - "Key": "com.docker.compose.project", - "Value": "TestSimpleWithOverrides" - } - ], - "Type": "application" - }, - "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer" - } - } -}