From 48d1bbe1f9586b79e11809755141315bb0c60f0b Mon Sep 17 00:00:00 2001 From: penghaoh Date: Fri, 16 Aug 2019 15:06:09 -0700 Subject: [PATCH 1/3] Add additional missing unit tests --- .../aws-ecs/lib/container-definition.ts | 5 + .../@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts | 2 +- .../aws-ecs/test/ec2/test.ec2-service.ts | 335 +++++++++++++ .../test/ec2/test.ec2-task-definition.ts | 453 +++++++++++++++++- .../test/fargate/test.fargate-service.ts | 94 ++++ .../fargate/test.fargate-task-definition.ts | 76 ++- .../aws-ecs/test/test.aws-log-driver.ts | 19 + .../aws-ecs/test/test.container-definition.ts | 355 +++++++++++++- .../@aws-cdk/aws-ecs/test/test.ecs-cluster.ts | 154 ++++++ 9 files changed, 1487 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index bee0698be7f63..11dc3eb1abdc7 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -323,6 +323,11 @@ export class ContainerDefinition extends cdk.Construct { */ constructor(scope: cdk.Construct, id: string, private readonly props: ContainerDefinitionProps) { super(scope, id); + if (props.memoryLimitMiB !== undefined && props.memoryReservationMiB !== undefined) { + if (props.memoryLimitMiB < props.memoryReservationMiB) { + throw new Error(`MemoryLimitMiB should not be less than MemoryReservationMiB.`); + } + } this.essential = props.essential !== undefined ? props.essential : true; this.taskDefinition = props.taskDefinition; this.memoryLimitSpecified = props.memoryLimitMiB !== undefined || props.memoryReservationMiB !== undefined; diff --git a/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts index 96cf8e57b5bfd..49615cd008f33 100644 --- a/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts @@ -190,7 +190,7 @@ export class Ec2Service extends BaseService implements IEc2Service, elb.ILoadBal throw new Error("Cannot use a Classic Load Balancer if NetworkMode is Bridge. Use Host or AwsVpc instead."); } if (this.taskDefinition.networkMode === NetworkMode.NONE) { - throw new Error("Cannot use a load balancer if NetworkMode is None. Use Host or AwsVpc instead."); + throw new Error("Cannot use a Classic Load Balancer if NetworkMode is None. Use Host or AwsVpc instead."); } this.loadBalancers.push({ diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts index ed504a74f0cef..b98b0930b3caf 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts @@ -50,6 +50,140 @@ export = { test.done(); }, + "with all properties set"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + networkMode: NetworkMode.AWS_VPC + }); + + cluster.addDefaultCloudMapNamespace({ + name: 'foo.com', + type: cloudmap.NamespaceType.DNS_PRIVATE + }); + + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + memoryLimitMiB: 512, + }); + + // WHEN + const service = new ecs.Ec2Service(stack, "Ec2Service", { + cluster, + taskDefinition, + desiredCount: 2, + assignPublicIp: true, + cloudMapOptions: { + name: "myapp", + dnsRecordType: cloudmap.DnsRecordType.A, + dnsTtl: cdk.Duration.seconds(50), + failureThreshold: 20 + }, + daemon: false, + healthCheckGracePeriod: cdk.Duration.seconds(60), + maxHealthyPercent: 150, + minHealthyPercent: 55, + securityGroup: new ec2.SecurityGroup(stack, 'SecurityGroup1', { + allowAllOutbound: true, + description: 'Example', + securityGroupName: 'Bob', + vpc, + }), + serviceName: "bonjour", + vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC } + }); + + service.addPlacementConstraints(PlacementConstraint.memberOf("attribute:ecs.instance-type =~ t2.*")); + service.addPlacementStrategies(PlacementStrategy.spreadAcross(BuiltInAttributes.AVAILABILITY_ZONE)); + + // THEN + expect(stack).to(haveResource("AWS::ECS::Service", { + TaskDefinition: { + Ref: "Ec2TaskDef0226F28C" + }, + Cluster: { + Ref: "EcsCluster97242B84" + }, + DeploymentConfiguration: { + MaximumPercent: 150, + MinimumHealthyPercent: 55 + }, + DesiredCount: 2, + LaunchType: LaunchType.EC2, + LoadBalancers: [], + NetworkConfiguration: { + AwsvpcConfiguration: { + AssignPublicIp: "ENABLED", + SecurityGroups: [ + { + "Fn::GetAtt": [ + "SecurityGroup1F554B36F", + "GroupId" + ] + } + ], + Subnets: [ + { + Ref: "MyVpcPublicSubnet1SubnetF6608456" + }, + { + Ref: "MyVpcPublicSubnet2Subnet492B6BFB" + } + ] + } + }, + PlacementConstraints: [ + { + Expression: "attribute:ecs.instance-type =~ t2.*", + Type: "memberOf" + } + ], + PlacementStrategies: [ + { + Field: "attribute:ecs.availability-zone", + Type: "spread" + } + ], + SchedulingStrategy: "REPLICA", + ServiceName: "bonjour", + ServiceRegistries: [ + { + RegistryArn: { + "Fn::GetAtt": [ + "Ec2ServiceCloudmapService45B52C0F", + "Arn" + ] + } + } + ] + })); + + test.done(); + }, + + "allows a cluster without any capacity added to it"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + taskDefinition.addContainer('BaseContainer', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryReservationMiB: 10, + }); + + new ecs.Ec2Service(stack, "Ec2Service", { + cluster, + taskDefinition, + }); + + // THEN + test.done(); + }, + "errors if daemon and desiredCount both specified"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -398,6 +532,38 @@ export = { test.done(); }, + "with spreadAcross container instances strategy"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + memoryLimitMiB: 512 + }); + + const service = new ecs.Ec2Service(stack, "Ec2Service", { + cluster, + taskDefinition, + }); + + // WHEN + service.addPlacementStrategies(PlacementStrategy.spreadAcrossInstances()); + + // THEN + expect(stack).to(haveResource("AWS::ECS::Service", { + PlacementStrategies: [{ + Field: "instanceId", + Type: "spread" + }] + })); + + test.done(); + }, + "with spreadAcross placement strategy"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -429,6 +595,51 @@ export = { test.done(); }, + "can turn PlacementStrategy into json format"(test: Test) { + // THEN + test.deepEqual(PlacementStrategy.spreadAcross(BuiltInAttributes.AVAILABILITY_ZONE).toJson(), [{ + type: 'spread', + field: 'attribute:ecs.availability-zone' + }]); + + test.done(); + }, + + "can turn PlacementConstraints into json format"(test: Test) { + // THEN + test.deepEqual(PlacementConstraint.distinctInstances().toJson(), [{ + type: 'distinctInstance' + }]); + + test.done(); + }, + + "errors when spreadAcross with no input"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + memoryLimitMiB: 512 + }); + + const service = new ecs.Ec2Service(stack, "Ec2Service", { + cluster, + taskDefinition, + }); + + // THEN + test.throws(() => { + service.addPlacementStrategies(PlacementStrategy.spreadAcross()); + }, 'spreadAcross: give at least one field to spread by'); + + test.done(); + }, + "errors with spreadAcross placement strategy if daemon specified"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -456,6 +667,59 @@ export = { test.done(); }, + "with no placement constraints"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + memoryLimitMiB: 512 + }); + + new ecs.Ec2Service(stack, "Ec2Service", { + cluster, + taskDefinition, + }); + + // THEN + expect(stack).notTo(haveResource("AWS::ECS::Service", { + PlacementConstraints: [] + })); + + test.done(); + }, + + "with no placement strategy if daemon specified"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + memoryLimitMiB: 512 + }); + + new ecs.Ec2Service(stack, "Ec2Service", { + cluster, + taskDefinition, + daemon: true + }); + + // THEN + expect(stack).notTo(haveResource("AWS::ECS::Service", { + PlacementStrategies: [] + })); + + test.done(); + }, + "with random placement strategy"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -670,6 +934,77 @@ export = { HealthCheckGracePeriodSeconds: 60 })); + test.done(); + }, + + 'allows network mode of task definition to be AwsVpc'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TD', { networkMode: ecs.NetworkMode.AWS_VPC }); + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + }); + container.addPortMappings({ containerPort: 808 }); + const service = new ecs.Ec2Service(stack, 'Service', { cluster, taskDefinition}); + + // THEN + const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); + lb.addTarget(service); + + test.done(); + }, + + 'throws when network mode of task definition is bridge'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TD', { networkMode: ecs.NetworkMode.BRIDGE }); + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + }); + container.addPortMappings({ containerPort: 808 }); + const service = new ecs.Ec2Service(stack, 'Service', { cluster, taskDefinition}); + + // WHEN + const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); + + // THEN + test.throws(() => { + lb.addTarget(service); + }, /Cannot use a Classic Load Balancer if NetworkMode is Bridge. Use Host or AwsVpc instead./); + + test.done(); + }, + + 'throws when network mode of task definition is none'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TD', { networkMode: ecs.NetworkMode.NONE }); + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + }); + container.addPortMappings({ containerPort: 808 }); + const service = new ecs.Ec2Service(stack, 'Service', { cluster, taskDefinition}); + + // WHEN + const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); + + // THEN + test.throws(() => { + lb.addTarget(service); + }, /Cannot use a Classic Load Balancer if NetworkMode is None. Use Host or AwsVpc instead./); + test.done(); } }, diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts index e0e8ebcc7a494..229c1750168e9 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts @@ -2,8 +2,11 @@ import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert'; import { Protocol } from '@aws-cdk/aws-ec2'; import { Repository } from '@aws-cdk/aws-ecr'; import iam = require('@aws-cdk/aws-iam'); +import secretsmanager = require('@aws-cdk/aws-secretsmanager'); +import ssm = require('@aws-cdk/aws-ssm'); import cdk = require('@aws-cdk/core'); import { Test } from 'nodeunit'; +import path = require('path'); import ecs = require('../../lib'); export = { @@ -26,6 +29,92 @@ export = { test.done(); }, + "with all properties set"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { + executionRole: new iam.Role(stack, 'ExecutionRole', { + path: '/', + assumedBy: new iam.CompositePrincipal( + new iam.ServicePrincipal("ecs.amazonaws.com"), + new iam.ServicePrincipal("ecs-tasks.amazonaws.com") + ) + }), + family: "ecs-tasks", + networkMode: ecs.NetworkMode.AWS_VPC, + placementConstraints: [ecs.PlacementConstraint.memberOf("attribute:ecs.instance-type =~ t2.*")], + taskRole: new iam.Role(stack, 'TaskRole', { + assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'), + }), + volumes: [{ + host: { + sourcePath: "/tmp/cache", + }, + name: "scratch" + }] + }); + + // THEN + expect(stack).to(haveResource("AWS::ECS::TaskDefinition", { + ContainerDefinitions: [], + ExecutionRoleArn: { + "Fn::GetAtt": [ + "ExecutionRole605A040B", + "Arn" + ] + }, + Family: "ecs-tasks", + NetworkMode: "awsvpc", + PlacementConstraints: [ + { + Expression: "attribute:ecs.instance-type =~ t2.*", + Type: "memberOf" + } + ], + RequiresCompatibilities: [ + "EC2" + ], + TaskRoleArn: { + "Fn::GetAtt": [ + "TaskRole30FC0FBB", + "Arn" + ] + }, + Volumes: [ + { + Host: { + SourcePath: "/tmp/cache" + }, + Name: "scratch" + } + ] + })); + + test.done(); + }, + + "correctly sets placement constraint"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + // WHEN + taskDefinition.addPlacementConstraint(ecs.PlacementConstraint.memberOf("attribute:ecs.instance-type =~ t2.*")); + + // THEN + expect(stack).to(haveResource("AWS::ECS::TaskDefinition", { + PlacementConstraints: [ + { + Expression: "attribute:ecs.instance-type =~ t2.*", + Type: "memberOf" + } + ], + + })); + + test.done(); + }, + "correctly sets network mode"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -117,18 +206,201 @@ export = { test.done(); }, - "correctly sets containers from ECR repository"(test: Test) { + "all container definition options defined"(test: Test) { // GIVEN const stack = new cdk.Stack(); const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + const secret = new secretsmanager.Secret(stack, 'Secret'); + const parameter = ssm.StringParameter.fromSecureStringParameterAttributes(stack, 'Parameter', { + parameterName: '/name', + version: 1 + }); taskDefinition.addContainer("web", { - image: ecs.ContainerImage.fromEcrRepository(new Repository(stack, "myECRImage")), + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + memoryLimitMiB: 2048, + cpu: 256, + disableNetworking: true, + command: ['CMD env'], + dnsSearchDomains: ['0.0.0.0'], + dnsServers: ['1.1.1.1'], + dockerLabels: {LABEL: 'label'}, + dockerSecurityOptions: ['ECS_SELINUX_CAPABLE=true'], + entryPoint: ["/app/node_modules/.bin/cdk"], + environment: {TEST_ENVIRONMENT_VARIABLE: "test environment variable value"}, + essential: true, + extraHosts: {EXTRAHOST: 'extra host'}, + healthCheck: { + command: ["curl localhost:8000"], + interval: cdk.Duration.seconds(20), + retries: 5, + startPeriod: cdk.Duration.seconds(10) + }, + hostname: "webHost", + linuxParameters: new ecs.LinuxParameters(stack, 'LinuxParameters', { + initProcessEnabled: true, + sharedMemorySize: 1024, + }), + logging: new ecs.AwsLogDriver({ streamPrefix: 'prefix' }), + memoryReservationMiB: 1024, + privileged: true, + readonlyRootFilesystem: true, + secrets: { + SECRET: ecs.Secret.fromSecretsManager(secret), + PARAMETER: ecs.Secret.fromSsmParameter(parameter), + }, + user: "amazon", + workingDirectory: "app/" + }); + + // THEN + expect(stack).to(haveResource("AWS::ECS::TaskDefinition", { + Family: "Ec2TaskDef", + ContainerDefinitions: [ + { + Command: [ + "CMD env" + ], + Cpu: 256, + DisableNetworking: true, + DnsSearchDomains: [ + "0.0.0.0" + ], + DnsServers: [ + "1.1.1.1" + ], + DockerLabels: { + LABEL: "label" + }, + DockerSecurityOptions: [ + "ECS_SELINUX_CAPABLE=true" + ], + EntryPoint: [ + "/app/node_modules/.bin/cdk" + ], + Environment: [ + { + Name: "TEST_ENVIRONMENT_VARIABLE", + Value: "test environment variable value" + } + ], + Essential: true, + ExtraHosts: [ + { + Hostname: "EXTRAHOST", + IpAddress: "extra host" + } + ], + HealthCheck: { + Command: [ + "CMD-SHELL", + "curl localhost:8000" + ], + Interval: 20, + Retries: 5, + StartPeriod: 10, + Timeout: 5 + }, + Hostname: "webHost", + Image: "amazon/amazon-ecs-sample", + LinuxParameters: { + Capabilities: { + Add: [], + Drop: [] + }, + Devices: [], + InitProcessEnabled: true, + SharedMemorySize: 1024, + Tmpfs: [] + }, + LogConfiguration: { + LogDriver: "awslogs", + Options: { + "awslogs-group": { + Ref: "Ec2TaskDefwebLogGroup7F786C6B" + }, + "awslogs-stream-prefix": "prefix", + "awslogs-region": { + Ref: "AWS::Region" + } + } + }, + Memory: 2048, + MemoryReservation: 1024, + Name: "web", + Privileged: true, + ReadonlyRootFilesystem: true, + Secrets: [ + { + Name: "SECRET", + ValueFrom: { + Ref: "SecretA720EF05" + } + }, + { + Name: "PARAMETER", + ValueFrom: { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition" + }, + ":ssm:", + { + Ref: "AWS::Region" + }, + ":", + { + Ref: "AWS::AccountId" + }, + ":parameter/name" + ] + ] + } + } + ], + User: "amazon", + WorkingDirectory: "app/" + } + ], + })); + + test.done(); + }, + + "correctly sets containers from ECR repository using all props"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromEcrRepository(new Repository(stack, "myECRImage", { + lifecycleRegistryId: '123456789101', + lifecycleRules: [{ + rulePriority: 10, + tagPrefixList: ['abc'], + maxImageCount: 1 + }], + removalPolicy: cdk.RemovalPolicy.DESTROY, + repositoryName: 'project-a/amazon-ecs-sample' + })), memoryLimitMiB: 512 }); // THEN + expect(stack).to(haveResource('AWS::ECR::Repository', { + LifecyclePolicy: { + // tslint:disable-next-line:max-line-length + LifecyclePolicyText: "{\"rules\":[{\"rulePriority\":10,\"selection\":{\"tagStatus\":\"tagged\",\"tagPrefixList\":[\"abc\"],\"countType\":\"imageCountMoreThan\",\"countNumber\":1},\"action\":{\"type\":\"expire\"}}]}", + RegistryId: "123456789101" + }, + RepositoryName: "project-a/amazon-ecs-sample" + })); + expect(stack).to(haveResource("AWS::ECS::TaskDefinition", { Family: "Ec2TaskDef", ContainerDefinitions: [{ @@ -190,6 +462,170 @@ export = { test.done(); }, + "correctly sets containers from ECR repository using default props"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + // WHEN + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromEcrRepository(new Repository(stack, "myECRImage")), + memoryLimitMiB: 512 + }); + + // THEN + expect(stack).notTo(haveResource('AWS::ECR::Repository', {})); + + test.done(); + }, + + "correctly sets containers from asset using default props"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + // WHEN + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromAsset(path.join(__dirname, '..', 'demo-image')), + memoryLimitMiB: 512 + }); + + // THEN + expect(stack).to(haveResource("AWS::ECS::TaskDefinition", { + Family: "Ec2TaskDef", + ContainerDefinitions: [{ + Essential: true, + Memory: 512, + Image: { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + ":", + { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition" + }, + ":ecr:", + { + Ref: "AWS::Region" + }, + ":", + { + Ref: "AWS::AccountId" + }, + ":repository/", + { + "Fn::GetAtt": [ + "Ec2TaskDefwebAssetImageAdoptRepositoryEA698962", + "RepositoryName" + ] + } + ] + ] + } + ] + } + ] + }, + ".dkr.ecr.", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition" + }, + ":ecr:", + { + Ref: "AWS::Region" + }, + ":", + { + Ref: "AWS::AccountId" + }, + ":repository/", + { + "Fn::GetAtt": [ + "Ec2TaskDefwebAssetImageAdoptRepositoryEA698962", + "RepositoryName" + ] + } + ] + ] + } + ] + } + ] + }, + ".", + { + Ref: "AWS::URLSuffix" + }, + "/", + { + "Fn::GetAtt": [ + "Ec2TaskDefwebAssetImageAdoptRepositoryEA698962", + "RepositoryName" + ] + }, + "@sha256:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "@sha256:", + { + Ref: "Ec2TaskDefwebAssetImageImageNameCBACAA57" + } + ] + } + ] + } + ] + ] + }, + Name: "web" + }], + })); + + test.done(); + }, + + "correctly sets containers from asset using all props"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromAsset(path.join(__dirname, '..', 'demo-image'), { + buildArgs: {HTTP_PROXY: 'http://10.20.30.2:1234'} + }), + memoryLimitMiB: 512 + }); + + test.done(); + }, + "correctly sets scratch space"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -513,6 +949,19 @@ export = { test.done(); }, + "automatically sets taskRole by default"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + // THEN + expect(stack).to(haveResourceLike("AWS::ECS::TaskDefinition", { + TaskRoleArn: stack.resolve(taskDefinition.taskRole.roleArn) + })); + + test.done(); + }, + "correctly sets dockerVolumeConfiguration"(test: Test) { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts index 93f3582f02ce6..d8fa43f87fd14 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts @@ -84,6 +84,100 @@ export = { test.done(); }, + "with all properties set"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + + cluster.addDefaultCloudMapNamespace({ + name: 'foo.com', + type: cloudmap.NamespaceType.DNS_PRIVATE + }); + + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + taskDefinition.addContainer("web", { + image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + }); + + new ecs.FargateService(stack, "FargateService", { + cluster, + taskDefinition, + desiredCount: 2, + assignPublicIp: true, + cloudMapOptions: { + name: "myapp", + dnsRecordType: cloudmap.DnsRecordType.A, + dnsTtl: cdk.Duration.seconds(50), + failureThreshold: 20 + }, + healthCheckGracePeriod: cdk.Duration.seconds(60), + maxHealthyPercent: 150, + minHealthyPercent: 55, + securityGroup: new ec2.SecurityGroup(stack, 'SecurityGroup1', { + allowAllOutbound: true, + description: 'Example', + securityGroupName: 'Bob', + vpc, + }), + serviceName: "bonjour", + vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC } + }); + + // THEN + expect(stack).to(haveResource("AWS::ECS::Service", { + TaskDefinition: { + Ref: "FargateTaskDefC6FB60B4" + }, + Cluster: { + Ref: "EcsCluster97242B84" + }, + DeploymentConfiguration: { + MaximumPercent: 150, + MinimumHealthyPercent: 55 + }, + DesiredCount: 2, + HealthCheckGracePeriodSeconds: 60, + LaunchType: LaunchType.FARGATE, + LoadBalancers: [], + NetworkConfiguration: { + AwsvpcConfiguration: { + AssignPublicIp: "ENABLED", + SecurityGroups: [ + { + "Fn::GetAtt": [ + "SecurityGroup1F554B36F", + "GroupId" + ] + } + ], + Subnets: [ + { + Ref: "MyVpcPublicSubnet1SubnetF6608456" + }, + { + Ref: "MyVpcPublicSubnet2Subnet492B6BFB" + } + ] + } + }, + ServiceName: "bonjour", + ServiceRegistries: [ + { + RegistryArn: { + "Fn::GetAtt": [ + "FargateServiceCloudmapService9544B753", + "Arn" + ] + } + } + ] + })); + + test.done(); + }, + "errors when no container specified on task definition"(test: Test) { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-task-definition.ts b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-task-definition.ts index 75ca6c0920b0d..3ea513bae5ed7 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-task-definition.ts @@ -1,4 +1,5 @@ import { expect, haveResourceLike } from '@aws-cdk/assert'; +import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/core'); import { Test } from 'nodeunit'; import ecs = require('../../lib'); @@ -21,8 +22,81 @@ export = { Memory: "512", })); - // test error if no container defs? test.done(); }, + + "with all properties set"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef', { + cpu: 128, + executionRole: new iam.Role(stack, 'ExecutionRole', { + path: '/', + assumedBy: new iam.CompositePrincipal( + new iam.ServicePrincipal("ecs.amazonaws.com"), + new iam.ServicePrincipal("ecs-tasks.amazonaws.com") + ) + }), + family: "myApp", + memoryLimitMiB: 1024, + taskRole: new iam.Role(stack, 'TaskRole', { + assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'), + }) + }); + + taskDefinition.addVolume({ + host: { + sourcePath: "/tmp/cache", + }, + name: "scratch" + }); + + // THEN + expect(stack).to(haveResourceLike("AWS::ECS::TaskDefinition", { + ContainerDefinitions: [], + Cpu: "128", + ExecutionRoleArn: { + "Fn::GetAtt": [ + "ExecutionRole605A040B", + "Arn" + ] + }, + Family: "myApp", + Memory: "1024", + NetworkMode: "awsvpc", + RequiresCompatibilities: [ + ecs.LaunchType.FARGATE + ], + TaskRoleArn: { + "Fn::GetAtt": [ + "TaskRole30FC0FBB", + "Arn" + ] + }, + Volumes: [ + { + Host: { + SourcePath: "/tmp/cache" + }, + Name: "scratch" + } + ] + })); + + test.done(); + }, + + 'throws when adding placement constraint'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + // THEN + test.throws(() => { + taskDefinition.addPlacementConstraint(ecs.PlacementConstraint.memberOf("attribute:ecs.instance-type =~ t2.*")); + }, /Cannot set placement constraints on tasks that run on Fargate/); + + test.done(); + } } }; diff --git a/packages/@aws-cdk/aws-ecs/test/test.aws-log-driver.ts b/packages/@aws-cdk/aws-ecs/test/test.aws-log-driver.ts index f33b3de7731d4..10f94c2e88173 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.aws-log-driver.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.aws-log-driver.ts @@ -104,6 +104,10 @@ export = { }); // THEN + expect(stack).to(haveResource('AWS::Logs::LogGroup', { + RetentionInDays: logs.RetentionDays.TWO_YEARS + })); + expect(stack).to(haveResourceLike('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { @@ -122,6 +126,21 @@ export = { test.done(); }, + 'without a defined log group'(test: Test) { + // GIVEN + td.addContainer('Container', { + image, + logging: new ecs.AwsLogDriver({ + streamPrefix: 'hello', + }) + }); + + // THEN + expect(stack).notTo(haveResource('AWS::Logs::LogGroup', {})); + + test.done(); + }, + 'throws when specifying log retention and log group'(test: Test) { // GIVEN const logGroup = new logs.LogGroup(stack, 'LogGroup'); diff --git a/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts b/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts index dd531d624a813..0975989a8a182 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts @@ -8,8 +8,52 @@ import ecs = require('../lib'); export = { "When creating a Task Definition": { // Validating portMapping inputs + "add a container using minimum set of ContainerDefinitionProps"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + + new ecs.ContainerDefinition(stack, "Container", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + taskDefinition, + memoryLimitMiB: 2048, + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + { + Essential: true, + Image: "/aws/aws-example-app", + Memory: 2048, + Name: "Container" + } + ] + })); + + test.done(); + }, + + "throws when MemoryLimit is less than MemoryReservationLimit"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + + // THEN + test.throws(() => { + new ecs.ContainerDefinition(stack, "Container", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + taskDefinition, + memoryLimitMiB: 512, + memoryReservationMiB: 1024, + }); + }, /MemoryLimitMiB should not be less than MemoryReservationMiB./); + + test.done(); + }, + "With network mode AwsVpc": { - "Host port should be the same as container port"(test: Test) { + "throws when Host port is different from container port"(test: Test) { // GIVEN const stack = new cdk.Stack(); const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { @@ -32,6 +76,27 @@ export = { test.done(); }, + "Host port is the same as container port"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.AWS_VPC, + }); + + const container = taskDefinition.addContainer("Container", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + memoryLimitMiB: 2048, + }); + + container.addPortMappings({ + containerPort: 8080, + hostPort: 8080 + }); + + // THEN no exception raised + test.done(); + }, + "Host port can be empty "(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -55,7 +120,7 @@ export = { }, "With network mode Host ": { - "Host port should be the same as container port"(test: Test) { + "throws when Host port is different from container port"(test: Test) { // GIVEN const stack = new cdk.Stack(); const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { @@ -78,6 +143,27 @@ export = { test.done(); }, + "when host port is the same as container port"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.HOST, + }); + + const container = taskDefinition.addContainer("Container", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + memoryLimitMiB: 2048, + }); + + container.addPortMappings({ + containerPort: 8080, + hostPort: 8080 + }); + + // THEN no exception raised + test.done(); + }, + "Host port can be empty "(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -126,6 +212,47 @@ export = { }, "With network mode Bridge": { + "when Host port is empty "(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.BRIDGE, + }); + + const container = taskDefinition.addContainer("Container", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + memoryLimitMiB: 2048, + }); + + container.addPortMappings({ + containerPort: 8080, + }); + + // THEN no exception raises + test.done(); + }, + + "when Host port is not empty "(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.BRIDGE, + }); + + const container = taskDefinition.addContainer("Container", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + memoryLimitMiB: 2048, + }); + + container.addPortMappings({ + containerPort: 8080, + hostPort: 8084 + }); + + // THEN no exception raises + test.done(); + }, + "allows adding links"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -151,6 +278,58 @@ export = { } }, + "Container Port": { + "should return the first container port in PortMappings"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.AWS_VPC, + }); + + const container = taskDefinition.addContainer("Container", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + memoryLimitMiB: 2048, + }); + + // WHEN + container.addPortMappings({ + containerPort: 8080, + }); + + container.addPortMappings({ + containerPort: 8081, + }); + const actual = container.containerPort; + + // THEN + const expected = 8080; + test.equal(actual, expected, "containerPort should return the first container port in PortMappings"); + test.done(); + }, + + "throws when calling containerPort with no PortMappings"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.AWS_VPC, + }); + + const container = taskDefinition.addContainer("MyContainer", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + memoryLimitMiB: 2048 + }); + + // THEN + test.throws(() => { + const actual = container.containerPort; + const expected = 8080; + test.equal(actual, expected); + }, /Container MyContainer hasn't defined any ports. Call addPortMappings()./); + + test.done(); + }, + }, + "Ingress Port": { "With network mode AwsVpc": { "Ingress port should be the same as container port"(test: Test) { @@ -176,6 +355,28 @@ export = { test.equal(actual, expected, "Ingress port should be the same as container port"); test.done(); }, + + "throws when calling ingressPort with no PortMappings"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.AWS_VPC, + }); + + const container = taskDefinition.addContainer("MyContainer", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + memoryLimitMiB: 2048 + }); + + // THEN + test.throws(() => { + const actual = container.ingressPort; + const expected = 8080; + test.equal(actual, expected); + }, /Container MyContainer hasn't defined any ports. Call addPortMappings()./); + + test.done(); + }, }, "With network mode Host ": { @@ -202,6 +403,28 @@ export = { test.equal(actual, expected); test.done(); }, + + "throws when calling ingressPort with no PortMappings"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.HOST, + }); + + const container = taskDefinition.addContainer("MyContainer", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + memoryLimitMiB: 2048 + }); + + // THEN + test.throws(() => { + const actual = container.ingressPort; + const expected = 8080; + test.equal(actual, expected); + }, /Container MyContainer hasn't defined any ports. Call addPortMappings()./); + + test.done(); + }, }, "With network mode Bridge": { @@ -253,6 +476,28 @@ export = { test.equal(actual, expected); test.done(); }, + + "throws when calling ingressPort with no PortMappings"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.BRIDGE, + }); + + const container = taskDefinition.addContainer("MyContainer", { + image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), + memoryLimitMiB: 2048 + }); + + // THEN + test.throws(() => { + const actual = container.ingressPort; + const expected = 8080; + test.equal(actual, expected); + }, /Container MyContainer hasn't defined any ports. Call addPortMappings()./); + + test.done(); + }, }, }, @@ -437,6 +682,7 @@ export = { test.done(); }, + 'can set Health Check with defaults'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -469,6 +715,39 @@ export = { test.done(); }, + 'throws when setting Health Check with no commands'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + + // WHEN + taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + healthCheck: { + command: [] + } + }); + + // THEN + test.throws(() => { + expect(stack).to(haveResourceLike('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + { + HealthCheck: { + Command: [], + Interval: 30, + Retries: 3, + Timeout: 5 + }, + } + ] + })); + }, /At least one argument must be supplied for health check command./); + + test.done(); + }, + 'can specify Health Check values in shell form'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -620,7 +899,79 @@ export = { test.done(); }, + '_linkContainer works properly': { + 'when the props passed in is an essential container'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + + // WHEN + const container = taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + essential: true + }); + + // THEN + test.equal(taskDefinition.defaultContainer, container); + + test.done(); + }, + + 'when the props passed in is not an essential container'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + + // WHEN + taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + essential: false + }); + + // THEN + test.equal(taskDefinition.defaultContainer, undefined); + + test.done(); + } + }, + 'Can specify linux parameters': { + 'with only required properties set, it correctly sets default properties'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + + const linuxParameters = new ecs.LinuxParameters(stack, 'LinuxParameters'); + + // WHEN + taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + linuxParameters, + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + { + Image: 'test', + LinuxParameters: { + Capabilities: { + Add: [], + Drop: [] + }, + Devices: [], + Tmpfs: [] + } + } + ] + })); + + test.done(); + }, + 'before calling addContainer'(test: Test) { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts index 3713422d3dd1e..bac88409973e7 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts @@ -338,6 +338,109 @@ export = { RoleARN: { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B", "Arn" ] } })); + expect(stack).to(haveResource('AWS::Lambda::Function', { + Timeout: 310, + Environment: { + Variables: { + CLUSTER: { + Ref: "EcsCluster97242B84" + } + } + }, + Handler: "index.lambda_handler" + })); + + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + "ec2:DescribeInstances", + "ec2:DescribeInstanceAttribute", + "ec2:DescribeInstanceStatus", + "ec2:DescribeHosts" + ], + Effect: "Allow", + Resource: "*" + }, + { + Action: "autoscaling:CompleteLifecycleAction", + Effect: "Allow", + Resource: { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition" + }, + ":autoscaling:", + { + Ref: "AWS::Region" + }, + ":", + { + Ref: "AWS::AccountId" + }, + ":autoScalingGroup:*:autoScalingGroupName/", + { + Ref: "EcsClusterDefaultAutoScalingGroupASGC1A785DB" + } + ] + ] + } + }, + { + Action: [ + "ecs:DescribeContainerInstances", + "ecs:DescribeTasks" + ], + Effect: "Allow", + Resource: "*" + }, + { + Action: [ + "ecs:ListContainerInstances", + "ecs:SubmitContainerStateChange", + "ecs:SubmitTaskStateChange" + ], + Effect: "Allow", + Resource: { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + Action: [ + "ecs:UpdateContainerInstancesState", + "ecs:ListTasks" + ], + Condition: { + ArnEquals: { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + }, + Effect: "Allow", + Resource: "*" + } + ], + Version: "2012-10-17" + }, + PolicyName: "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396", + Roles: [ + { + Ref: "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" + } + ] + })); + test.done(); }, @@ -838,6 +941,57 @@ export = { test.done(); }, + "allows specifying drain time"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { + instanceType: new ec2.InstanceType('t2.micro'), + taskDrainTime: cdk.Duration.minutes(1) + }); + + // THEN + expect(stack).to(haveResource("AWS::AutoScaling::LifecycleHook", { + HeartbeatTimeout: 60 + })); + + test.done(); + }, + + "allows containers access to instance metadata service"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { + instanceType: new ec2.InstanceType('t2.micro'), + canContainersAccessInstanceRole: true + }); + + // THEN + expect(stack).to(haveResource("AWS::AutoScaling::LaunchConfiguration", { + UserData: { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash\necho ECS_CLUSTER=", + { + Ref: "EcsCluster97242B84" + }, + " >> /etc/ecs/ecs.config" + ] + ] + } + } + })); + + test.done(); + }, + "allows adding default service discovery namespace"(test: Test) { // GIVEN const stack = new cdk.Stack(); From f1ee254a50e79dc250630091b0f506c0ea1ce2cc Mon Sep 17 00:00:00 2001 From: penghaoh Date: Mon, 19 Aug 2019 15:24:31 -0700 Subject: [PATCH 2/3] Update on aug 19 feedbacks --- .../aws-ecs/test/ec2/test.ec2-service.ts | 244 +++++++++++++++--- .../test/fargate/test.fargate-service.ts | 81 ++---- .../aws-ecs/test/test.container-definition.ts | 51 +--- 3 files changed, 228 insertions(+), 148 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts index b98b0930b3caf..f31ba72fe3888 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts @@ -1,6 +1,7 @@ import { expect, haveResource } from '@aws-cdk/assert'; import ec2 = require('@aws-cdk/aws-ec2'); import elb = require('@aws-cdk/aws-elasticloadbalancing'); +import elbv2 = require("@aws-cdk/aws-elasticloadbalancingv2"); import cloudmap = require('@aws-cdk/aws-servicediscovery'); import cdk = require('@aws-cdk/core'); import { Test } from 'nodeunit'; @@ -10,7 +11,7 @@ import { LaunchType } from '../../lib/base/base-service'; import { PlacementConstraint, PlacementStrategy } from '../../lib/placement'; export = { - "When creating an ECS Service": { + "When creating an EC2 Service": { "with only required properties set, it correctly sets default properties"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -164,23 +165,28 @@ export = { test.done(); }, - "allows a cluster without any capacity added to it"(test: Test) { - // GIVEN + "throws when task definition is not EC2 compatible"(test: Test) { const stack = new cdk.Stack(); const vpc = new ec2.Vpc(stack, 'MyVpc', {}); const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + const taskDefinition = new ecs.TaskDefinition(stack, 'FargateTaskDef', { + compatibility: ecs.Compatibility.FARGATE, + cpu: "256", + memoryMiB: "512" + }); taskDefinition.addContainer('BaseContainer', { image: ecs.ContainerImage.fromRegistry('test'), memoryReservationMiB: 10, }); - new ecs.Ec2Service(stack, "Ec2Service", { - cluster, - taskDefinition, - }); - // THEN + test.throws(() => { + new ecs.Ec2Service(stack, "Ec2Service", { + cluster, + taskDefinition, + }); + }, /Supplied TaskDefinition is not configured for compatibility with EC2/); + test.done(); }, @@ -898,8 +904,8 @@ export = { } }, - 'classic ELB': { - 'can attach to classic ELB'(test: Test) { + "attachToClassicLB": { + "allows network mode of task definition to be host"(test: Test) { // GIVEN const stack = new cdk.Stack(); const vpc = new ec2.Vpc(stack, 'VPC'); @@ -911,28 +917,14 @@ export = { memoryLimitMiB: 1024, }); container.addPortMappings({ containerPort: 808 }); - const service = new ecs.Ec2Service(stack, 'Service', { cluster, taskDefinition}); - - // WHEN - const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); - lb.addTarget(service); + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); // THEN - expect(stack).to(haveResource('AWS::ECS::Service', { - LoadBalancers: [ - { - ContainerName: "web", - ContainerPort: 808, - LoadBalancerName: { Ref: "LB8A12904C" } - } - ] - })); - - expect(stack).to(haveResource('AWS::ECS::Service', { - // if any load balancer is configured and healthCheckGracePeriodSeconds is not - // set, then it should default to 60 seconds. - HealthCheckGracePeriodSeconds: 60 - })); + const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); + service.attachToClassicLB(lb); test.done(); }, @@ -949,11 +941,14 @@ export = { memoryLimitMiB: 1024, }); container.addPortMappings({ containerPort: 808 }); - const service = new ecs.Ec2Service(stack, 'Service', { cluster, taskDefinition}); + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); // THEN const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); - lb.addTarget(service); + service.attachToClassicLB(lb); test.done(); }, @@ -970,14 +965,15 @@ export = { memoryLimitMiB: 1024, }); container.addPortMappings({ containerPort: 808 }); - const service = new ecs.Ec2Service(stack, 'Service', { cluster, taskDefinition}); - - // WHEN - const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); // THEN + const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); test.throws(() => { - lb.addTarget(service); + service.attachToClassicLB(lb); }, /Cannot use a Classic Load Balancer if NetworkMode is Bridge. Use Host or AwsVpc instead./); test.done(); @@ -995,15 +991,179 @@ export = { memoryLimitMiB: 1024, }); container.addPortMappings({ containerPort: 808 }); - const service = new ecs.Ec2Service(stack, 'Service', { cluster, taskDefinition}); + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); + + // THEN + const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); + test.throws(() => { + service.attachToClassicLB(lb); + }, /Cannot use a Classic Load Balancer if NetworkMode is None. Use Host or AwsVpc instead./); + + test.done(); + } + }, + + "attachToApplicationTargetGroup": { + "allows network mode of task definition to be other than none"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.AWS_VPC }); + const container = taskDefinition.addContainer('MainContainer', { + image: ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPort: 8000 }); + + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); + + const lb = new elbv2.ApplicationLoadBalancer(stack, "lb", { vpc }); + const listener = lb.addListener("listener", { port: 80 }); + const targetGroup = listener.addTargets("target", { + port: 80, + }); + + // THEN + service.attachToApplicationTargetGroup(targetGroup); + + test.done(); + }, + + "throws when network mode of task definition is none"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.NONE }); + const container = taskDefinition.addContainer('MainContainer', { + image: ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPort: 8000 }); + + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); + + const lb = new elbv2.ApplicationLoadBalancer(stack, "lb", { vpc }); + const listener = lb.addListener("listener", { port: 80 }); + const targetGroup = listener.addTargets("target", { + port: 80, + }); + + // THEN + test.throws(() => { + service.attachToApplicationTargetGroup(targetGroup); + }, /Cannot use a load balancer if NetworkMode is None. Use Bridge, Host or AwsVpc instead./); + + test.done(); + } + }, + + "attachToNetworkTargetGroup": { + "allows network mode of task definition to be other than none"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.AWS_VPC }); + const container = taskDefinition.addContainer('MainContainer', { + image: ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPort: 8000 }); + + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); + + const lb = new elbv2.NetworkLoadBalancer(stack, "lb", { vpc }); + const listener = lb.addListener("listener", { port: 80 }); + const targetGroup = listener.addTargets("target", { + port: 80, + }); + + // THEN + service.attachToNetworkTargetGroup(targetGroup); + + test.done(); + }, + + "throws when network mode of task definition is none"(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.NONE }); + const container = taskDefinition.addContainer('MainContainer', { + image: ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPort: 8000 }); + + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); + + const lb = new elbv2.NetworkLoadBalancer(stack, "lb", { vpc }); + const listener = lb.addListener("listener", { port: 80 }); + const targetGroup = listener.addTargets("target", { + port: 80, + }); + + // THEN + test.throws(() => { + service.attachToNetworkTargetGroup(targetGroup); + }, /Cannot use a load balancer if NetworkMode is None. Use Bridge, Host or AwsVpc instead./); + + test.done(); + } + }, + + 'classic ELB': { + 'can attach to classic ELB'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TD', { networkMode: ecs.NetworkMode.HOST }); + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryLimitMiB: 1024, + }); + container.addPortMappings({ containerPort: 808 }); + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); // WHEN const lb = new elb.LoadBalancer(stack, 'LB', { vpc }); + lb.addTarget(service); // THEN - test.throws(() => { - lb.addTarget(service); - }, /Cannot use a Classic Load Balancer if NetworkMode is None. Use Host or AwsVpc instead./); + expect(stack).to(haveResource('AWS::ECS::Service', { + LoadBalancers: [ + { + ContainerName: "web", + ContainerPort: 808, + LoadBalancerName: { Ref: "LB8A12904C" } + } + ] + })); + + expect(stack).to(haveResource('AWS::ECS::Service', { + // if any load balancer is configured and healthCheckGracePeriodSeconds is not + // set, then it should default to 60 seconds. + HealthCheckGracePeriodSeconds: 60 + })); test.done(); } diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts index d8fa43f87fd14..1be80dd9946b3 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts @@ -178,6 +178,29 @@ export = { test.done(); }, + "throws when task definition is not EC2 compatible"(test: Test) { + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.TaskDefinition(stack, 'Ec2TaskDef', { + compatibility: ecs.Compatibility.EC2, + }); + taskDefinition.addContainer('BaseContainer', { + image: ecs.ContainerImage.fromRegistry('test'), + memoryReservationMiB: 10, + }); + + // THEN + test.throws(() => { + new ecs.FargateService(stack, "FargateService", { + cluster, + taskDefinition, + }); + }, /Supplied TaskDefinition is not configured for compatibility with Fargate/); + + test.done(); + }, + "errors when no container specified on task definition"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -609,64 +632,6 @@ export = { test.done(); }, - "allow adding a load balancing target to an application target group"(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'MyVpc', {}); - const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); - const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); - const container = taskDefinition.addContainer('MainContainer', { - image: ContainerImage.fromRegistry('hello'), - }); - container.addPortMappings({ containerPort: 8000 }); - - const service = new ecs.FargateService(stack, 'Service', { - cluster, - taskDefinition - }); - - const lb = new elbv2.ApplicationLoadBalancer(stack, "lb", { vpc }); - const listener = lb.addListener("listener", { port: 80 }); - const targetGroup = listener.addTargets("target", { - port: 80, - }); - - // WHEN - targetGroup.addTarget(service); - - const capacity = service.autoScaleTaskCount({ maxCapacity: 10, minCapacity: 1 }); - capacity.scaleOnRequestCount("ScaleOnRequests", { - requestsPerTarget: 1000, - targetGroup - }); - - // THEN - expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { - MaxCapacity: 10, - MinCapacity: 1, - ResourceId: { - "Fn::Join": [ - "", - [ - "service/", - { - Ref: "EcsCluster97242B84" - }, - "/", - { - "Fn::GetAtt": [ - "ServiceD69D759B", - "Name" - ] - } - ] - ] - }, - })); - - test.done(); - }, - 'When enabling service discovery': { 'throws if namespace has not been added to cluster'(test: Test) { // GIVEN diff --git a/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts b/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts index 0975989a8a182..fdcd422b2fe0e 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts @@ -7,8 +7,7 @@ import ecs = require('../lib'); export = { "When creating a Task Definition": { - // Validating portMapping inputs - "add a container using minimum set of ContainerDefinitionProps"(test: Test) { + "add a container using default props"(test: Test) { // GIVEN const stack = new cdk.Stack(); const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); @@ -402,29 +401,7 @@ export = { const expected = 8080; test.equal(actual, expected); test.done(); - }, - - "throws when calling ingressPort with no PortMappings"(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { - networkMode: ecs.NetworkMode.HOST, - }); - - const container = taskDefinition.addContainer("MyContainer", { - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), - memoryLimitMiB: 2048 - }); - - // THEN - test.throws(() => { - const actual = container.ingressPort; - const expected = 8080; - test.equal(actual, expected); - }, /Container MyContainer hasn't defined any ports. Call addPortMappings()./); - - test.done(); - }, + } }, "With network mode Bridge": { @@ -475,29 +452,7 @@ export = { const expected = 0; test.equal(actual, expected); test.done(); - }, - - "throws when calling ingressPort with no PortMappings"(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { - networkMode: ecs.NetworkMode.BRIDGE, - }); - - const container = taskDefinition.addContainer("MyContainer", { - image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), - memoryLimitMiB: 2048 - }); - - // THEN - test.throws(() => { - const actual = container.ingressPort; - const expected = 8080; - test.equal(actual, expected); - }, /Container MyContainer hasn't defined any ports. Call addPortMappings()./); - - test.done(); - }, + } }, }, From be45c16b10a18857da9924342941ac3c94113edf Mon Sep 17 00:00:00 2001 From: penghaoh Date: Mon, 19 Aug 2019 15:48:37 -0700 Subject: [PATCH 3/3] Indentations and minor change --- .../aws-ecs/test/ec2/test.ec2-service.ts | 170 +++++++++--------- .../test/fargate/test.fargate-service.ts | 2 +- 2 files changed, 86 insertions(+), 86 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts index f31ba72fe3888..eb59e9f20bc91 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts @@ -1009,120 +1009,120 @@ export = { "attachToApplicationTargetGroup": { "allows network mode of task definition to be other than none"(test: Test) { // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'MyVpc', {}); - const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.AWS_VPC }); - const container = taskDefinition.addContainer('MainContainer', { - image: ContainerImage.fromRegistry('hello'), - }); - container.addPortMappings({ containerPort: 8000 }); + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.AWS_VPC }); + const container = taskDefinition.addContainer('MainContainer', { + image: ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPort: 8000 }); - const service = new ecs.Ec2Service(stack, 'Service', { - cluster, - taskDefinition - }); + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); - const lb = new elbv2.ApplicationLoadBalancer(stack, "lb", { vpc }); - const listener = lb.addListener("listener", { port: 80 }); - const targetGroup = listener.addTargets("target", { - port: 80, - }); + const lb = new elbv2.ApplicationLoadBalancer(stack, "lb", { vpc }); + const listener = lb.addListener("listener", { port: 80 }); + const targetGroup = listener.addTargets("target", { + port: 80, + }); - // THEN - service.attachToApplicationTargetGroup(targetGroup); + // THEN + service.attachToApplicationTargetGroup(targetGroup); - test.done(); - }, + test.done(); + }, "throws when network mode of task definition is none"(test: Test) { // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'MyVpc', {}); - const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.NONE }); - const container = taskDefinition.addContainer('MainContainer', { - image: ContainerImage.fromRegistry('hello'), - }); - container.addPortMappings({ containerPort: 8000 }); + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.NONE }); + const container = taskDefinition.addContainer('MainContainer', { + image: ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPort: 8000 }); - const service = new ecs.Ec2Service(stack, 'Service', { - cluster, - taskDefinition - }); + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); - const lb = new elbv2.ApplicationLoadBalancer(stack, "lb", { vpc }); - const listener = lb.addListener("listener", { port: 80 }); - const targetGroup = listener.addTargets("target", { - port: 80, - }); + const lb = new elbv2.ApplicationLoadBalancer(stack, "lb", { vpc }); + const listener = lb.addListener("listener", { port: 80 }); + const targetGroup = listener.addTargets("target", { + port: 80, + }); - // THEN - test.throws(() => { - service.attachToApplicationTargetGroup(targetGroup); - }, /Cannot use a load balancer if NetworkMode is None. Use Bridge, Host or AwsVpc instead./); + // THEN + test.throws(() => { + service.attachToApplicationTargetGroup(targetGroup); + }, /Cannot use a load balancer if NetworkMode is None. Use Bridge, Host or AwsVpc instead./); - test.done(); + test.done(); } }, "attachToNetworkTargetGroup": { "allows network mode of task definition to be other than none"(test: Test) { // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'MyVpc', {}); - const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.AWS_VPC }); - const container = taskDefinition.addContainer('MainContainer', { - image: ContainerImage.fromRegistry('hello'), - }); - container.addPortMappings({ containerPort: 8000 }); + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.AWS_VPC }); + const container = taskDefinition.addContainer('MainContainer', { + image: ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPort: 8000 }); - const service = new ecs.Ec2Service(stack, 'Service', { - cluster, - taskDefinition - }); + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); - const lb = new elbv2.NetworkLoadBalancer(stack, "lb", { vpc }); - const listener = lb.addListener("listener", { port: 80 }); - const targetGroup = listener.addTargets("target", { - port: 80, - }); + const lb = new elbv2.NetworkLoadBalancer(stack, "lb", { vpc }); + const listener = lb.addListener("listener", { port: 80 }); + const targetGroup = listener.addTargets("target", { + port: 80, + }); - // THEN - service.attachToNetworkTargetGroup(targetGroup); + // THEN + service.attachToNetworkTargetGroup(targetGroup); - test.done(); + test.done(); }, "throws when network mode of task definition is none"(test: Test) { // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'MyVpc', {}); - const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.NONE }); - const container = taskDefinition.addContainer('MainContainer', { - image: ContainerImage.fromRegistry('hello'), - }); - container.addPortMappings({ containerPort: 8000 }); + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { networkMode: ecs.NetworkMode.NONE }); + const container = taskDefinition.addContainer('MainContainer', { + image: ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPort: 8000 }); - const service = new ecs.Ec2Service(stack, 'Service', { - cluster, - taskDefinition - }); + const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition + }); - const lb = new elbv2.NetworkLoadBalancer(stack, "lb", { vpc }); - const listener = lb.addListener("listener", { port: 80 }); - const targetGroup = listener.addTargets("target", { - port: 80, - }); + const lb = new elbv2.NetworkLoadBalancer(stack, "lb", { vpc }); + const listener = lb.addListener("listener", { port: 80 }); + const targetGroup = listener.addTargets("target", { + port: 80, + }); - // THEN - test.throws(() => { - service.attachToNetworkTargetGroup(targetGroup); - }, /Cannot use a load balancer if NetworkMode is None. Use Bridge, Host or AwsVpc instead./); + // THEN + test.throws(() => { + service.attachToNetworkTargetGroup(targetGroup); + }, /Cannot use a load balancer if NetworkMode is None. Use Bridge, Host or AwsVpc instead./); - test.done(); + test.done(); } }, diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts index 1be80dd9946b3..11929be069e6c 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts @@ -178,7 +178,7 @@ export = { test.done(); }, - "throws when task definition is not EC2 compatible"(test: Test) { + "throws when task definition is not Fargate compatible"(test: Test) { const stack = new cdk.Stack(); const vpc = new ec2.Vpc(stack, 'MyVpc', {}); const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc });