From aa971793bb7e3cc67c31b63b50faa965a6272842 Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Tue, 11 Jul 2023 22:41:45 +0200 Subject: [PATCH 01/19] Add support for container port ranges for ECS --- ...work-multiple-target-groups-ecs-service.ts | 8 +- ...-multiple-target-groups-fargate-service.ts | 8 +- .../load-balanced-fargate-service-v2.test.ts | 23 +++ .../aws-ecs/lib/base/base-service.ts | 6 +- .../aws-ecs/lib/base/task-definition.ts | 6 +- .../aws-ecs/lib/container-definition.ts | 55 +++++- .../aws-ecs/test/container-definition.test.ts | 163 ++++++++++++++++-- .../aws-ecs/test/ec2/ec2-service.test.ts | 58 +++++++ .../test/fargate/fargate-service.test.ts | 159 ++++++++++++++++- 9 files changed, 458 insertions(+), 28 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts b/packages/aws-cdk-lib/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts index d68ac374b7c76..c9c9550112ce2 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts @@ -148,9 +148,15 @@ export class NetworkMultipleTargetGroupsEc2Service extends NetworkMultipleTarget this.addPortMappingForTargets(this.taskDefinition.defaultContainer, props.targetGroups); this.targetGroup = this.registerECSTargets(this.service, this.taskDefinition.defaultContainer, props.targetGroups); } else { + const containerPort = this.taskDefinition.defaultContainer.portMappings[0].containerPort; + + if (!containerPort) { + throw new Error('You must specify a containerPort'); + } + this.targetGroup = this.listener.addTargets('ECS', { targets: [this.service], - port: this.taskDefinition.defaultContainer.portMappings[0].containerPort, + port: containerPort, }); } } diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts b/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts index 2786c30ae4682..7dedc995ff5c8 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts @@ -103,9 +103,15 @@ export class NetworkMultipleTargetGroupsFargateService extends NetworkMultipleTa this.addPortMappingForTargets(this.taskDefinition.defaultContainer, props.targetGroups); this.targetGroup = this.registerECSTargets(this.service, this.taskDefinition.defaultContainer, props.targetGroups); } else { + const containerPort = this.taskDefinition.defaultContainer.portMappings[0].containerPort; + + if (!containerPort) { + throw new Error('The first port mapping added to the essential container must expose a single port'); + } + this.targetGroup = this.listener.addTargets('ECS', { targets: [this.service], - port: this.taskDefinition.defaultContainer.portMappings[0].containerPort, + port: containerPort, }); } } diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts index a84159c14dbee..ed85cd7df44e8 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts @@ -837,4 +837,27 @@ describe('When Network Load Balancer', () => { }, }); }); + + test('Fargate multinetworkloadbalanced construct errors when container port range is set for essential container', () => { + // GIVEN + const stack = new Stack(); + const vpc = new Vpc(stack, 'VPC'); + const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + taskDefinition.addContainer('MainContainer', { + image: ecs.ContainerImage.fromRegistry('test'), + portMappings: [{ + containerPortRange: '8080-8081', + }], + }); + + // THEN + expect(() => { + new NetworkMultipleTargetGroupsFargateService(stack, 'Service', { + cluster, + taskDefinition, + }); + }).toThrow('The first port mapping added to the essential container must expose a single port'); + }); }); diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts index 7eaaccbb86b01..11f1b1bb9199f 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts @@ -1029,14 +1029,14 @@ export abstract class BaseService extends Resource return { attachToApplicationTargetGroup(targetGroup: elbv2.ApplicationTargetGroup): elbv2.LoadBalancerTargetProps { targetGroup.registerConnectable(self, self.taskDefinition._portRangeFromPortMapping(target.portMapping)); - return self.attachToELBv2(targetGroup, target.containerName, target.portMapping.containerPort); + return self.attachToELBv2(targetGroup, target.containerName, target.portMapping.containerPort!); }, attachToNetworkTargetGroup(targetGroup: elbv2.NetworkTargetGroup): elbv2.LoadBalancerTargetProps { - return self.attachToELBv2(targetGroup, target.containerName, target.portMapping.containerPort); + return self.attachToELBv2(targetGroup, target.containerName, target.portMapping.containerPort!); }, connections, attachToClassicLB(loadBalancer: elb.LoadBalancer): void { - return self.attachToELB(loadBalancer, target.containerName, target.portMapping.containerPort); + return self.attachToELB(loadBalancer, target.containerName, target.portMapping.containerPort!); }, }; } diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts index 1280af3e5cd09..3833fe1794065 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts @@ -590,7 +590,11 @@ export class TaskDefinition extends TaskDefinitionBase { if (this.networkMode === NetworkMode.BRIDGE || this.networkMode === NetworkMode.NAT) { return EPHEMERAL_PORT_RANGE; } - return portMapping.protocol === Protocol.UDP ? ec2.Port.udp(portMapping.containerPort) : ec2.Port.tcp(portMapping.containerPort); + if (portMapping.containerPort !== undefined) { + return portMapping.protocol === Protocol.UDP ? ec2.Port.udp(portMapping.containerPort) : ec2.Port.tcp(portMapping.containerPort); + } + const [startPort, endPort] = portMapping.containerPortRange!.split('-', 2).map(v => +v); + return portMapping.protocol === Protocol.UDP ? ec2.Port.udpRange(startPort, endPort) : ec2.Port.tcpRange(startPort, endPort); } /** diff --git a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts index 26d64f1e28062..358245f828c13 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts @@ -707,16 +707,18 @@ export class ContainerDefinition extends Construct { } /** - * Set HostPort to 0 When netowork mode is Brdige + * This method sets the host port to 0 if the network mode is Bridge and neither + * the host port nor the container port range is already set. */ private addHostPortIfNeeded(pm: PortMapping) :PortMapping { - const newPM = { + if (this.taskDefinition.networkMode !== NetworkMode.BRIDGE || pm.hostPort !== undefined || pm.containerPortRange !== undefined) { + return pm; + } + + return { ...pm, + hostPort: 0, }; - if (this.taskDefinition.networkMode !== NetworkMode.BRIDGE) return newPM; - if (pm.hostPort !== undefined) return newPM; - newPM.hostPort = 0; - return newPM; } /** @@ -750,6 +752,11 @@ export class ContainerDefinition extends Construct { if (this.taskDefinition.networkMode === NetworkMode.BRIDGE) { return 0; } + + if (defaultPortMapping.containerPort === undefined) { + throw new Error(`The first port mapping of the container ${this.containerName} must expose a single port.`); + } + return defaultPortMapping.containerPort; } @@ -761,6 +768,11 @@ export class ContainerDefinition extends Construct { throw new Error(`Container ${this.containerName} hasn't defined any ports. Call addPortMappings().`); } const defaultPortMapping = this.portMappings[0]; + + if (defaultPortMapping.containerPort === undefined) { + throw new Error(`The first port mapping of the container ${this.containerName} must expose a single port.`); + } + return defaultPortMapping.containerPort; } @@ -1073,7 +1085,21 @@ export interface PortMapping { * For more information, see hostPort. * Port mappings that are automatically assigned in this way do not count toward the 100 reserved ports limit of a container instance. */ - readonly containerPort: number; + readonly containerPort?: number; + + /** + * The port number range on the container that's bound to the dynamically mapped host port range. + * + * The following rules apply when you specify a `containerPortRange`: + * + * - You must use either the `bridge` network mode or the `awsvpc` network mode. + * - The container instance must have at least version 1.67.0 of the container agent and at least version 1.67.0-1 of the `ecs-init` package + * - You can specify a maximum of 100 port ranges per container. + * - A port can only be included in one port mapping per container. + * - You cannot specify overlapping port ranges. + * - The first port in the range must be less than last port in the range. + */ + readonly containerPortRange?: string; /** * The port number on the container instance to reserve for your container. @@ -1150,6 +1176,20 @@ export class PortMap { const pm = this.portmapping; throw new Error(`Host port (${pm.hostPort}) must be left out or equal to container port ${pm.containerPort} for network mode ${this.networkmode}`); } + + if (this.portmapping.containerPort === undefined && this.portmapping.containerPortRange === undefined) { + throw new Error('Either "containerPort" or "containerPortRange" must be set.'); + } + + if (this.portmapping.containerPortRange !== undefined) { + if (this.portmapping.hostPort !== undefined || this.portmapping.containerPort !== undefined) { + throw new Error('Cannot set "hostPort" or "containerPort" while using the port range for the container.'); + } + + if (this.networkmode !== NetworkMode.BRIDGE && this.networkmode !== NetworkMode.AWS_VPC) { + throw new Error('Either AwsVpc or Bridge network mode is required to set a port range for the container.'); + } + } } private isvalidPortName(): boolean { @@ -1272,6 +1312,7 @@ export class AppProtocol { function renderPortMapping(pm: PortMapping): CfnTaskDefinition.PortMappingProperty { return { containerPort: pm.containerPort, + containerPortRange: pm.containerPortRange, hostPort: pm.hostPort, protocol: pm.protocol || Protocol.TCP, appProtocol: pm.appProtocol?.value, diff --git a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts index ca23735204d5f..81578c8ac2cd7 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts @@ -50,6 +50,90 @@ describe('container definition', () => { }).toThrow(); }); + test('throws when neither PortMapping.containerPort nor PortMapping.containerPortRange is set', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, {}); + + // THEN + expect(() => portMap.validate()).toThrow('Either "containerPort" or "containerPortRange" must be set.'); + }); + + test('throws when PortMapping.containerPortRange is used along with PortMapping.containerPort', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: 8080, + containerPortRange: '8080-8081', + }); + + // THEN + expect(() => portMap.validate()).toThrow('Cannot set "hostPort" or "containerPort" while using the port range for the container.'); + }); + + test('throws when PortMapping.containerPortRange is used along with PortMapping.hostPort', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + hostPort: 8080, + containerPortRange: '8080-8081', + }); + + // THEN + expect(() => portMap.validate()).toThrow('Cannot set "hostPort" or "containerPort" while using the port range for the container.'); + }); + + describe('throws when PortMapping.containerPortRange is used with an unsupported network mode', () => { + test('when network mode is Host', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.HOST, { + containerPortRange: '8080-8081', + }); + + // THEN + expect(() => portMap.validate()).toThrow('Either AwsVpc or Bridge network mode is required to set a port range for the container.'); + }); + + test('when network mode is NAT', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.NAT, { + containerPortRange: '8080-8081', + }); + + // THEN + expect(() => portMap.validate()).toThrow('Either AwsVpc or Bridge network mode is required to set a port range for the container.'); + }); + + test('when network mode is None', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.NONE, { + containerPortRange: '8080-8081', + }); + + // THEN + expect(() => portMap.validate()).toThrow('Either AwsVpc or Bridge network mode is required to set a port range for the container.'); + }); + }); + + describe('ContainerPortRange can be used with AwsVpc or Bridge network mode', () => { + test('when network mode is AwsVpc', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPortRange: '8080-8081', + }); + + // THEN + expect(() => portMap.validate()).not.toThrow(); + }); + + test('when network mode is Bridge', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.BRIDGE, { + containerPortRange: '8080-8081', + }); + + // THEN + expect(() => portMap.validate()).not.toThrow(); + }); + }); + describe('ContainerPort should not eqaul Hostport', () => { test('when AWS_VPC Networkmode', () => { // GIVEN @@ -661,7 +745,7 @@ describe('container definition', () => { }); describe('With network mode Bridge', () => { - test('when Host port is empty ', () => { + test('host post is forcefully set to 0 when both it and containerPortRange are not set', () => { // GIVEN const stack = new cdk.Stack(); const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { @@ -671,16 +755,16 @@ describe('container definition', () => { const container = taskDefinition.addContainer('Container', { image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'), memoryLimitMiB: 2048, + portMappings: [{ + containerPort: 8080, + }], }); - container.addPortMappings({ - containerPort: 8080, - }); - - // THEN no exception raises + // THEN + expect(container.portMappings[0].hostPort).toEqual(0); }); - test('when Host port is not empty ', () => { + test('host post is left unchanged when it is set already', () => { // GIVEN const stack = new cdk.Stack(); const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { @@ -690,14 +774,33 @@ describe('container definition', () => { const container = taskDefinition.addContainer('Container', { image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'), memoryLimitMiB: 2048, + portMappings: [{ + containerPort: 8080, + hostPort: 8084, + }], }); - container.addPortMappings({ - containerPort: 8080, - hostPort: 8084, + // THEN + expect(container.portMappings[0].hostPort).toEqual(8084); + }); + + test('host post is left undefined when containerPortRange is set', () => { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef', { + networkMode: ecs.NetworkMode.BRIDGE, }); - // THEN no exception raises + const container = taskDefinition.addContainer('Container', { + image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'), + memoryLimitMiB: 2048, + portMappings: [{ + containerPortRange: '8080-8081', + }], + }); + + // THEN + expect(container.portMappings[0].hostPort).toBeUndefined(); }); test('allows adding links', () => { @@ -788,6 +891,25 @@ describe('container definition', () => { expect(actual).toEqual(expected); }).toThrow(/Container MyContainer hasn't defined any ports. Call addPortMappings\(\)./); }); + + test('throws when calling containerPort with the first PortMapping not exposing a single port', () => { + // 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, + portMappings: [{ + containerPortRange: '8080-8081', + }], + }); + + // THEN + expect(() => container.containerPort).toThrow('The first port mapping of the container MyContainer must expose a single port.'); + }); }); describe('Ingress Port', () => { @@ -836,6 +958,25 @@ describe('container definition', () => { }); }); + test('throws when calling ingressPort with the first PortMapping not exposing a single port', () => { + // 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, + portMappings: [{ + containerPortRange: '8080-8081', + }], + }); + + // THEN + expect(() => container.ingressPort).toThrow('The first port mapping of the container MyContainer must expose a single port.'); + }); + describe('With network mode Host ', () => { test('Ingress port should be the same as container port', () => { // GIVEN diff --git a/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts index a07841af5d75f..ab2eb89343dca 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts @@ -2705,6 +2705,35 @@ describe('ec2 service', () => { }); + test('throws when the first port mapping added to the container does not expose a single port', () => { + // 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'); + const container = taskDefinition.addContainer('MainContainer', { + image: ecs.ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPortRange: '8000-8001' }); + + 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 + expect(() => { + service.attachToApplicationTargetGroup(targetGroup); + }).toThrow(/The first port mapping of the container MainContainer must expose a single port./); + + }); + describe('correctly setting ingress and egress port', () => { test('with bridge/NAT network mode and 0 host port', () => { [ecs.NetworkMode.BRIDGE, ecs.NetworkMode.NAT].forEach((networkMode: ecs.NetworkMode) => { @@ -2949,6 +2978,35 @@ describe('ec2 service', () => { }).toThrow(/Cannot use a load balancer if NetworkMode is None. Use Bridge, Host or AwsVpc instead./); }); + + test('throws when the first port mapping added to the container does not expose a single port', () => { + // 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'); + const container = taskDefinition.addContainer('MainContainer', { + image: ecs.ContainerImage.fromRegistry('hello'), + }); + container.addPortMappings({ containerPortRange: '8000-8001' }); + + 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 + expect(() => { + service.attachToNetworkTargetGroup(targetGroup); + }).toThrow(/The first port mapping of the container MainContainer must expose a single port./); + + }); }); describe('classic ELB', () => { diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts index baac3e41f44dc..e85a108b6fe16 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts @@ -1660,7 +1660,7 @@ describe('fargate service', () => { }); }); - test('with TCP protocol', () => { + test('with TCP protocol and container hostPort unset', () => { // GIVEN const stack = new cdk.Stack(); const vpc = new ec2.Vpc(stack, 'MyVpc', {}); @@ -1681,7 +1681,6 @@ describe('fargate service', () => { const lb = new elbv2.ApplicationLoadBalancer(stack, 'lb', { vpc }); const listener = lb.addListener('listener', { port: 80 }); - // THEN listener.addTargets('target', { port: 80, targets: [service.loadBalancerTarget({ @@ -1690,9 +1689,54 @@ describe('fargate service', () => { protocol: ecs.Protocol.TCP, })], }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { + Port: 8001, + Protocol: 'HTTP', + }); }); - test('with UDP protocol', () => { + test('with TCP protocol and container hostPort set', () => { + // 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 service = new ecs.FargateService(stack, 'Service', { + cluster, + taskDefinition, + }); + + taskDefinition.addContainer('MainContainer', { + image: ecs.ContainerImage.fromRegistry('hello'), + portMappings: [{ + containerPort: 8000, + hostPort: 8000, + }], + }); + + // WHEN + const lb = new elbv2.ApplicationLoadBalancer(stack, 'lb', { vpc }); + const listener = lb.addListener('listener', { port: 80 }); + + listener.addTargets('target', { + port: 80, + targets: [service.loadBalancerTarget({ + containerName: 'MainContainer', + containerPort: 8000, + protocol: ecs.Protocol.TCP, + })], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { + Port: 8000, + Protocol: 'HTTP', + }); + }); + + test('with UDP protocol and container hostPort unset', () => { // GIVEN const stack = new cdk.Stack(); const vpc = new ec2.Vpc(stack, 'MyVpc', {}); @@ -1713,7 +1757,6 @@ describe('fargate service', () => { const lb = new elbv2.ApplicationLoadBalancer(stack, 'lb', { vpc }); const listener = lb.addListener('listener', { port: 80 }); - // THEN listener.addTargets('target', { port: 80, targets: [service.loadBalancerTarget({ @@ -1722,6 +1765,52 @@ describe('fargate service', () => { protocol: ecs.Protocol.UDP, })], }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { + Port: 8001, + Protocol: 'HTTP', + }); + }); + + test('with UDP protocol and container hostPort set', () => { + // 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 service = new ecs.FargateService(stack, 'Service', { + cluster, + taskDefinition, + }); + + taskDefinition.addContainer('MainContainer', { + image: ecs.ContainerImage.fromRegistry('hello'), + portMappings: [{ + containerPort: 8000, + hostPort: 8000, + protocol: ecs.Protocol.UDP, + }], + }); + + // WHEN + const lb = new elbv2.ApplicationLoadBalancer(stack, 'lb', { vpc }); + const listener = lb.addListener('listener', { port: 80 }); + + listener.addTargets('target', { + port: 80, + targets: [service.loadBalancerTarget({ + containerName: 'MainContainer', + containerPort: 8000, + protocol: ecs.Protocol.UDP, + })], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { + Port: 8000, + Protocol: 'HTTP', + }); }); test('throws when protocol does not match', () => { @@ -2025,6 +2114,37 @@ describe('fargate service', () => { Protocol: 'HTTP', }); }); + + test('throws when containerPortRange is used instead of containerPort', () => { + // 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 service = new ecs.FargateService(stack, 'Service', { + cluster, + taskDefinition, + }); + + taskDefinition.addContainer('MainContainer', { + image: ecs.ContainerImage.fromRegistry('hello'), + portMappings: [{ + containerPortRange: '8000-8001', + }], + }); + + // WHEN + const lb = new elbv2.ApplicationLoadBalancer(stack, 'lb', { vpc }); + const listener = lb.addListener('listener', { port: 80 }); + + // THEN + expect(() => service.registerLoadBalancerTargets({ + containerName: 'MainContainer', + containerPort: 8000, + listener: ecs.ListenerConfig.applicationListener(listener), + newTargetGroupId: 'target1', + })).toThrow(/Container 'Default\/FargateTaskDef\/MainContainer' has no mapping for port 8000 and protocol tcp. Did you call "container.addPortMappings\(\)"\?/); + }); }); describe('with network load balancers', () => { @@ -2125,6 +2245,37 @@ describe('fargate service', () => { Protocol: 'TCP', }); }); + + test('throws when containerPortRange is used instead of containerPort', () => { + // 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 service = new ecs.FargateService(stack, 'Service', { + cluster, + taskDefinition, + }); + + taskDefinition.addContainer('MainContainer', { + image: ecs.ContainerImage.fromRegistry('hello'), + portMappings: [{ + containerPortRange: '8000-8001', + }], + }); + + // WHEN + const lb = new elbv2.NetworkLoadBalancer(stack, 'lb', { vpc }); + const listener = lb.addListener('listener', { port: 80 }); + + // THEN + expect(() => service.registerLoadBalancerTargets({ + containerName: 'MainContainer', + containerPort: 8000, + listener: ecs.ListenerConfig.networkListener(listener), + newTargetGroupId: 'target1', + })).toThrow(/Container 'Default\/FargateTaskDef\/MainContainer' has no mapping for port 8000 and protocol tcp. Did you call "container.addPortMappings\(\)"\?/); + }); }); }); }); From bbdea4b4f38e7b684a331a196b06d20fa5e2de8e Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Wed, 9 Aug 2023 15:01:34 +0000 Subject: [PATCH 02/19] Update the README --- packages/aws-cdk-lib/aws-ecs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ecs/README.md b/packages/aws-cdk-lib/aws-ecs/README.md index c7927b54b747f..a07df015d9159 100644 --- a/packages/aws-cdk-lib/aws-ecs/README.md +++ b/packages/aws-cdk-lib/aws-ecs/README.md @@ -358,7 +358,7 @@ declare const taskDefinition: ecs.TaskDefinition; taskDefinition.addContainer("WebContainer", { image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), memoryLimitMiB: 1024, - portMappings: [{ containerPort: 3000 }], + portMappings: [{ containerPort: 3000 }, { containerPortRange: '3000-4000' }], }); ``` From 21315e3c9968f03194746e69ed450d0c0ec079b3 Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Thu, 10 Aug 2023 11:11:13 +0200 Subject: [PATCH 03/19] Fix unit tests and rework port mapping validation logic --- .../aws-ecs/lib/container-definition.ts | 29 +++++++++---------- .../aws-ecs/test/container-definition.test.ts | 28 ++++++++++++++++-- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts index 358245f828c13..c4a71369b1cb1 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts @@ -1172,18 +1172,25 @@ export class PortMap { if (!this.isvalidPortName()) { throw new Error('Port mapping name cannot be an empty string.'); } - if (!this.isValidPorts()) { - const pm = this.portmapping; - throw new Error(`Host port (${pm.hostPort}) must be left out or equal to container port ${pm.containerPort} for network mode ${this.networkmode}`); - } if (this.portmapping.containerPort === undefined && this.portmapping.containerPortRange === undefined) { throw new Error('Either "containerPort" or "containerPortRange" must be set.'); } + if (this.portmapping.containerPort !== undefined && this.portmapping.containerPortRange !== undefined) { + throw new Error('Cannot set "containerPort" and "containerPortRange" at the same time.'); + } + + if (this.portmapping.containerPort !== undefined) { + if ((this.networkmode === NetworkMode.AWS_VPC || this.networkmode === NetworkMode.HOST) + && this.portmapping.hostPort !== undefined && this.portmapping.hostPort !== this.portmapping.containerPort) { + throw new Error('The host port must be left out or must be the same as the container port for AwsVpc or Host network mode.'); + } + } + if (this.portmapping.containerPortRange !== undefined) { - if (this.portmapping.hostPort !== undefined || this.portmapping.containerPort !== undefined) { - throw new Error('Cannot set "hostPort" or "containerPort" while using the port range for the container.'); + if (this.portmapping.hostPort !== undefined) { + throw new Error('Cannot set "hostPort" while using a port range for the container.'); } if (this.networkmode !== NetworkMode.BRIDGE && this.networkmode !== NetworkMode.AWS_VPC) { @@ -1199,16 +1206,6 @@ export class PortMap { return true; } - private isValidPorts() :boolean { - const isAwsVpcMode = this.networkmode == NetworkMode.AWS_VPC; - const isHostMode = this.networkmode == NetworkMode.HOST; - if (!isAwsVpcMode && !isHostMode) return true; - const hostPort = this.portmapping.hostPort; - const containerPort = this.portmapping.containerPort; - if (containerPort !== hostPort && hostPort !== undefined ) return false; - return true; - } - } /** diff --git a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts index 81578c8ac2cd7..11cd33f114186 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts @@ -66,7 +66,31 @@ describe('container definition', () => { }); // THEN - expect(() => portMap.validate()).toThrow('Cannot set "hostPort" or "containerPort" while using the port range for the container.'); + expect(() => portMap.validate()).toThrow('Cannot set "containerPort" and "containerPortRange" at the same time.'); + }); + + describe('throws when PortMapping.hostPort is set to a different port than the container port', () => { + test('when network mode is AwsVpc', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: 8080, + hostPort: 8081, + }); + + // THEN + expect(() => portMap.validate()).toThrow('The host port must be left out or must be the same as the container port for AwsVpc or Host network mode.'); + }); + + test('when network mode is Host', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.HOST, { + containerPort: 8080, + hostPort: 8081, + }); + + // THEN + expect(() => portMap.validate()).toThrow('The host port must be left out or must be the same as the container port for AwsVpc or Host network mode.'); + }); }); test('throws when PortMapping.containerPortRange is used along with PortMapping.hostPort', () => { @@ -77,7 +101,7 @@ describe('container definition', () => { }); // THEN - expect(() => portMap.validate()).toThrow('Cannot set "hostPort" or "containerPort" while using the port range for the container.'); + expect(() => portMap.validate()).toThrow('Cannot set "hostPort" while using a port range for the container.'); }); describe('throws when PortMapping.containerPortRange is used with an unsupported network mode', () => { From 38d3a3969576e70fb05ef81131106b24754b1b2c Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Thu, 10 Aug 2023 13:14:40 +0200 Subject: [PATCH 04/19] Fix wrong expectations in unit tests --- .../aws-ecs/test/fargate/fargate-service.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts index e85a108b6fe16..307256a3a4a45 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts @@ -1692,7 +1692,7 @@ describe('fargate service', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { - Port: 8001, + Port: 80, Protocol: 'HTTP', }); }); @@ -1731,7 +1731,7 @@ describe('fargate service', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { - Port: 8000, + Port: 80, Protocol: 'HTTP', }); }); @@ -1768,7 +1768,7 @@ describe('fargate service', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { - Port: 8001, + Port: 80, Protocol: 'HTTP', }); }); @@ -1808,7 +1808,7 @@ describe('fargate service', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { - Port: 8000, + Port: 80, Protocol: 'HTTP', }); }); From ea90bd896436e97c4d788091d7d21f9a852336f2 Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Thu, 10 Aug 2023 23:16:01 +0200 Subject: [PATCH 05/19] Add integration test --- ...efaultTestDeployAssert87C0580C.assets.json | 19 + ...aultTestDeployAssert87C0580C.template.json | 36 + .../__entrypoint__.js | 147 +++ .../index.js | 81 ++ .../aws-ecs-container-port-range.assets.json | 32 + ...aws-ecs-container-port-range.template.json | 661 +++++++++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 291 +++++ .../tree.json | 1013 +++++++++++++++++ .../fargate/integ.container-port-range.ts | 35 + 11 files changed, 2328 insertions(+) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/EcsContainerPortRangeDefaultTestDeployAssert87C0580C.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/EcsContainerPortRangeDefaultTestDeployAssert87C0580C.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/asset.ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd/__entrypoint__.js create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/asset.ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd/index.js create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/EcsContainerPortRangeDefaultTestDeployAssert87C0580C.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/EcsContainerPortRangeDefaultTestDeployAssert87C0580C.assets.json new file mode 100644 index 0000000000000..3d21a54fcea0b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/EcsContainerPortRangeDefaultTestDeployAssert87C0580C.assets.json @@ -0,0 +1,19 @@ +{ + "version": "33.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "EcsContainerPortRangeDefaultTestDeployAssert87C0580C.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/EcsContainerPortRangeDefaultTestDeployAssert87C0580C.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/EcsContainerPortRangeDefaultTestDeployAssert87C0580C.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/EcsContainerPortRangeDefaultTestDeployAssert87C0580C.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/asset.ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd/__entrypoint__.js b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/asset.ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd/__entrypoint__.js new file mode 100644 index 0000000000000..c83ecebaaadac --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/asset.ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd/__entrypoint__.js @@ -0,0 +1,147 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withRetries = exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { + 'content-type': '', + 'content-length': Buffer.byteLength(responseBody, 'utf8'), + }, + }; + const retryOptions = { + attempts: 5, + sleep: 1000, + }; + await withRetries(retryOptions, exports.external.sendHttpRequest)(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +function withRetries(options, fn) { + return async (...xs) => { + let attempts = options.attempts; + let ms = options.sleep; + while (true) { + try { + return await fn(...xs); + } + catch (e) { + if (attempts-- <= 0) { + throw e; + } + await sleep(Math.floor(Math.random() * ms)); + ms *= 2; + } + } + }; +} +exports.withRetries = withRetries; +async function sleep(ms) { + return new Promise((ok) => setTimeout(ok, ms)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFNLE1BQU0sR0FBRyxNQUFNLFdBQVcsQ0FBQyxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFMUQsdURBQXVEO1FBQ3ZELE1BQU0sYUFBYSxHQUFHLGNBQWMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFFcEQsMkJBQTJCO1FBQzNCLE1BQU0sY0FBYyxDQUFDLFNBQVMsRUFBRSxhQUFhLENBQUMsQ0FBQztLQUNoRDtJQUFDLE9BQU8sQ0FBTSxFQUFFO1FBQ2YsTUFBTSxJQUFJLEdBQWE7WUFDckIsR0FBRyxLQUFLO1lBQ1IsTUFBTSxFQUFFLGdCQUFRLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPO1NBQzFELENBQUM7UUFFRixJQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFO1lBQzVCLHlFQUF5RTtZQUN6RSxtRUFBbUU7WUFDbkUsd0VBQXdFO1lBQ3hFLHFFQUFxRTtZQUNyRSxnQ0FBZ0M7WUFDaEMsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsRUFBRTtnQkFDbEMsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsNEdBQTRHLENBQUMsQ0FBQztnQkFDM0gsSUFBSSxDQUFDLGtCQUFrQixHQUFHLGdDQUFnQyxDQUFDO2FBQzVEO2lCQUFNO2dCQUNMLGtFQUFrRTtnQkFDbEUsNkRBQTZEO2dCQUM3RCxnQkFBUSxDQUFDLEdBQUcsQ0FBQyw2REFBNkQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDcEc7U0FDRjtRQUVELG1FQUFtRTtRQUNuRSxNQUFNLGNBQWMsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLENBQUM7S0FDdEM7QUFDSCxDQUFDO0FBbkRELDBCQW1EQztBQUVELFNBQVMsY0FBYyxDQUNyQixVQUF5RixFQUN6RixrQkFBMEMsRUFBRztJQUU3QyxzRUFBc0U7SUFDdEUsdUJBQXVCO0lBQ3ZCLE1BQU0sa0JBQWtCLEdBQUcsZUFBZSxDQUFDLGtCQUFrQixJQUFJLFVBQVUsQ0FBQyxrQkFBa0IsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDO0lBRXZILGtFQUFrRTtJQUNsRSxJQUFJLFVBQVUsQ0FBQyxXQUFXLEtBQUssUUFBUSxJQUFJLGtCQUFrQixLQUFLLFVBQVUsQ0FBQyxrQkFBa0IsRUFBRTtRQUMvRixNQUFNLElBQUksS0FBSyxDQUFDLHdEQUF3RCxVQUFVLENBQUMsa0JBQWtCLFNBQVMsZUFBZSxDQUFDLGtCQUFrQixtQkFBbUIsQ0FBQyxDQUFDO0tBQ3RLO0lBRUQsMERBQTBEO0lBQzFELE9BQU87UUFDTCxHQUFHLFVBQVU7UUFDYixHQUFHLGVBQWU7UUFDbEIsa0JBQWtCLEVBQUUsa0JBQWtCO0tBQ3ZDLENBQUM7QUFDSixDQUFDO0FBRUQsS0FBSyxVQUFVLGNBQWMsQ0FBQyxNQUE0QixFQUFFLEtBQWU7SUFDekUsTUFBTSxJQUFJLEdBQW1EO1FBQzNELE1BQU0sRUFBRSxNQUFNO1FBQ2QsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLElBQUksTUFBTTtRQUM5QixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87UUFDdEIsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO1FBQzFCLGtCQUFrQixFQUFFLEtBQUssQ0FBQyxrQkFBa0IsSUFBSSwwQkFBMEI7UUFDMUUsaUJBQWlCLEVBQUUsS0FBSyxDQUFDLGlCQUFpQjtRQUMxQyxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07UUFDcEIsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO0tBQ2pCLENBQUM7SUFFRixnQkFBUSxDQUFDLEdBQUcsQ0FBQyxtQ0FBbUMsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUV4RCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFDLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQy9DLE1BQU0sR0FBRyxHQUFHO1FBQ1YsUUFBUSxFQUFFLFNBQVMsQ0FBQyxRQUFRO1FBQzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRTtZQUNQLGNBQWMsRUFBRSxFQUFFO1lBQ2xCLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxVQUFVLENBQUMsWUFBWSxFQUFFLE1BQU0sQ0FBQztTQUMxRDtLQUNGLENBQUM7SUFFRixNQUFNLFlBQVksR0FBRztRQUNuQixRQUFRLEVBQUUsQ0FBQztRQUNYLEtBQUssRUFBRSxJQUFJO0tBQ1osQ0FBQztJQUNGLE1BQU0sV0FBVyxDQUFDLFlBQVksRUFBRSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxDQUFDLEdBQUcsRUFBRSxZQUFZLENBQUMsQ0FBQztBQUMvRSxDQUFDO0FBRUQsS0FBSyxVQUFVLHNCQUFzQixDQUFDLE9BQTZCLEVBQUUsWUFBb0I7SUFDdkYsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUNyQyxJQUFJO1lBQ0YsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3ZELE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUM7WUFDNUIsT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDO1NBQ2Y7UUFBQyxPQUFPLENBQUMsRUFBRTtZQUNWLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUNYO0lBQ0gsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsU0FBUyxVQUFVLENBQUMsR0FBVyxFQUFFLEdBQUcsTUFBYTtJQUMvQyxzQ0FBc0M7SUFDdEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxNQUFNLENBQUMsQ0FBQztBQUM5QixDQUFDO0FBU0QsU0FBZ0IsV0FBVyxDQUEwQixPQUFxQixFQUFFLEVBQTRCO0lBQ3RHLE9BQU8sS0FBSyxFQUFFLEdBQUcsRUFBSyxFQUFFLEVBQUU7UUFDeEIsSUFBSSxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUNoQyxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDO1FBQ3ZCLE9BQU8sSUFBSSxFQUFFO1lBQ1gsSUFBSTtnQkFDRixPQUFPLE1BQU0sRUFBRSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7YUFDeEI7WUFBQyxPQUFPLENBQUMsRUFBRTtnQkFDVixJQUFJLFFBQVEsRUFBRSxJQUFJLENBQUMsRUFBRTtvQkFDbkIsTUFBTSxDQUFDLENBQUM7aUJBQ1Q7Z0JBQ0QsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztnQkFDNUMsRUFBRSxJQUFJLENBQUMsQ0FBQzthQUNUO1NBQ0Y7SUFDSCxDQUFDLENBQUM7QUFDSixDQUFDO0FBaEJELGtDQWdCQztBQUVELEtBQUssVUFBVSxLQUFLLENBQUMsRUFBVTtJQUM3QixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDakQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgY29uc3QgcmVzcDogUmVzcG9uc2UgPSB7XG4gICAgICAuLi5ldmVudCxcbiAgICAgIFJlYXNvbjogZXh0ZXJuYWwuaW5jbHVkZVN0YWNrVHJhY2VzID8gZS5zdGFjayA6IGUubWVzc2FnZSxcbiAgICB9O1xuXG4gICAgaWYgKCFyZXNwLlBoeXNpY2FsUmVzb3VyY2VJZCkge1xuICAgICAgLy8gc3BlY2lhbCBjYXNlOiBpZiBDUkVBVEUgZmFpbHMsIHdoaWNoIHVzdWFsbHkgaW1wbGllcywgd2UgdXN1YWxseSBkb24ndFxuICAgICAgLy8gaGF2ZSBhIHBoeXNpY2FsIHJlc291cmNlIGlkLiBpbiB0aGlzIGNhc2UsIHRoZSBzdWJzZXF1ZW50IERFTEVURVxuICAgICAgLy8gb3BlcmF0aW9uIGRvZXMgbm90IGhhdmUgYW55IG1lYW5pbmcsIGFuZCB3aWxsIGxpa2VseSBmYWlsIGFzIHdlbGwuIHRvXG4gICAgICAvLyBhZGRyZXNzIHRoaXMsIHdlIHVzZSBhIG1hcmtlciBzbyB0aGUgcHJvdmlkZXIgZnJhbWV3b3JrIGNhbiBzaW1wbHlcbiAgICAgIC8vIGlnbm9yZSB0aGUgc3Vic2VxdWVudCBERUxFVEUuXG4gICAgICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdDcmVhdGUnKSB7XG4gICAgICAgIGV4dGVybmFsLmxvZygnQ1JFQVRFIGZhaWxlZCwgcmVzcG9uZGluZyB3aXRoIGEgbWFya2VyIHBoeXNpY2FsIHJlc291cmNlIGlkIHNvIHRoYXQgdGhlIHN1YnNlcXVlbnQgREVMRVRFIHdpbGwgYmUgaWdub3JlZCcpO1xuICAgICAgICByZXNwLlBoeXNpY2FsUmVzb3VyY2VJZCA9IENSRUFURV9GQUlMRURfUEhZU0lDQUxfSURfTUFSS0VSO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gb3RoZXJ3aXNlLCBpZiBQaHlzaWNhbFJlc291cmNlSWQgaXMgbm90IHNwZWNpZmllZCwgc29tZXRoaW5nIGlzXG4gICAgICAgIC8vIHRlcnJpYmx5IHdyb25nIGJlY2F1c2UgYWxsIG90aGVyIGV2ZW50cyBzaG91bGQgaGF2ZSBhbiBJRC5cbiAgICAgICAgZXh0ZXJuYWwubG9nKGBFUlJPUjogTWFsZm9ybWVkIGV2ZW50LiBcIlBoeXNpY2FsUmVzb3VyY2VJZFwiIGlzIHJlcXVpcmVkOiAke0pTT04uc3RyaW5naWZ5KGV2ZW50KX1gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyB0aGlzIGlzIGFuIGFjdHVhbCBlcnJvciwgZmFpbCB0aGUgYWN0aXZpdHkgYWx0b2dldGhlciBhbmQgZXhpc3QuXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ0ZBSUxFRCcsIHJlc3ApO1xuICB9XG59XG5cbmZ1bmN0aW9uIHJlbmRlclJlc3BvbnNlKFxuICBjZm5SZXF1ZXN0OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgeyBQaHlzaWNhbFJlc291cmNlSWQ/OiBzdHJpbmcgfSxcbiAgaGFuZGxlclJlc3BvbnNlOiB2b2lkIHwgSGFuZGxlclJlc3BvbnNlID0geyB9KTogUmVzcG9uc2Uge1xuXG4gIC8vIGlmIHBoeXNpY2FsIElEIGlzIG5vdCByZXR1cm5lZCwgd2UgaGF2ZSBzb21lIGRlZmF1bHRzIGZvciB5b3UgYmFzZWRcbiAgLy8gb24gdGhlIHJlcXVlc3QgdHlwZS5cbiAgY29uc3QgcGh5c2ljYWxSZXNvdXJjZUlkID0gaGFuZGxlclJlc3BvbnNlLlBoeXNpY2FsUmVzb3VyY2VJZCA/PyBjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZCA/PyBjZm5SZXF1ZXN0LlJlcXVlc3RJZDtcblxuICAvLyBpZiB3ZSBhcmUgaW4gREVMRVRFIGFuZCBwaHlzaWNhbCBJRCB3YXMgY2hhbmdlZCwgaXQncyBhbiBlcnJvci5cbiAgaWYgKGNmblJlcXVlc3QuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIHBoeXNpY2FsUmVzb3VyY2VJZCAhPT0gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYERFTEVURTogY2Fubm90IGNoYW5nZSB0aGUgcGh5c2ljYWwgcmVzb3VyY2UgSUQgZnJvbSBcIiR7Y2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWR9XCIgdG8gXCIke2hhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWR9XCIgZHVyaW5nIGRlbGV0aW9uYCk7XG4gIH1cblxuICAvLyBtZXJnZSByZXF1ZXN0IGV2ZW50IGFuZCByZXN1bHQgZXZlbnQgKHJlc3VsdCBwcmV2YWlscykuXG4gIHJldHVybiB7XG4gICAgLi4uY2ZuUmVxdWVzdCxcbiAgICAuLi5oYW5kbGVyUmVzcG9uc2UsXG4gICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBwaHlzaWNhbFJlc291cmNlSWQsXG4gIH07XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHN1Ym1pdFJlc3BvbnNlKHN0YXR1czogJ1NVQ0NFU1MnIHwgJ0ZBSUxFRCcsIGV2ZW50OiBSZXNwb25zZSkge1xuICBjb25zdCBqc29uOiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZVJlc3BvbnNlID0ge1xuICAgIFN0YXR1czogc3RhdHVzLFxuICAgIFJlYXNvbjogZXZlbnQuUmVhc29uID8/IHN0YXR1cyxcbiAgICBTdGFja0lkOiBldmVudC5TdGFja0lkLFxuICAgIFJlcXVlc3RJZDogZXZlbnQuUmVxdWVzdElkLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogZXZlbnQuUGh5c2ljYWxSZXNvdXJjZUlkIHx8IE1JU1NJTkdfUEhZU0lDQUxfSURfTUFSS0VSLFxuICAgIExvZ2ljYWxSZXNvdXJjZUlkOiBldmVudC5Mb2dpY2FsUmVzb3VyY2VJZCxcbiAgICBOb0VjaG86IGV2ZW50Lk5vRWNobyxcbiAgICBEYXRhOiBldmVudC5EYXRhLFxuICB9O1xuXG4gIGV4dGVybmFsLmxvZygnc3VibWl0IHJlc3BvbnNlIHRvIGNsb3VkZm9ybWF0aW9uJywganNvbik7XG5cbiAgY29uc3QgcmVzcG9uc2VCb2R5ID0gSlNPTi5zdHJpbmdpZnkoanNvbik7XG4gIGNvbnN0IHBhcnNlZFVybCA9IHVybC5wYXJzZShldmVudC5SZXNwb25zZVVSTCk7XG4gIGNvbnN0IHJlcSA9IHtcbiAgICBob3N0bmFtZTogcGFyc2VkVXJsLmhvc3RuYW1lLFxuICAgIHBhdGg6IHBhcnNlZFVybC5wYXRoLFxuICAgIG1ldGhvZDogJ1BVVCcsXG4gICAgaGVhZGVyczoge1xuICAgICAgJ2NvbnRlbnQtdHlwZSc6ICcnLFxuICAgICAgJ2NvbnRlbnQtbGVuZ3RoJzogQnVmZmVyLmJ5dGVMZW5ndGgocmVzcG9uc2VCb2R5LCAndXRmOCcpLFxuICAgIH0sXG4gIH07XG5cbiAgY29uc3QgcmV0cnlPcHRpb25zID0ge1xuICAgIGF0dGVtcHRzOiA1LFxuICAgIHNsZWVwOiAxMDAwLFxuICB9O1xuICBhd2FpdCB3aXRoUmV0cmllcyhyZXRyeU9wdGlvbnMsIGV4dGVybmFsLnNlbmRIdHRwUmVxdWVzdCkocmVxLCByZXNwb25zZUJvZHkpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBkZWZhdWx0U2VuZEh0dHBSZXF1ZXN0KG9wdGlvbnM6IGh0dHBzLlJlcXVlc3RPcHRpb25zLCByZXNwb25zZUJvZHk6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXF1ZXN0ID0gaHR0cHMucmVxdWVzdChvcHRpb25zLCBfID0+IHJlc29sdmUoKSk7XG4gICAgICByZXF1ZXN0Lm9uKCdlcnJvcicsIHJlamVjdCk7XG4gICAgICByZXF1ZXN0LndyaXRlKHJlc3BvbnNlQm9keSk7XG4gICAgICByZXF1ZXN0LmVuZCgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJlamVjdChlKTtcbiAgICB9XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBkZWZhdWx0TG9nKGZtdDogc3RyaW5nLCAuLi5wYXJhbXM6IGFueVtdKSB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gIGNvbnNvbGUubG9nKGZtdCwgLi4ucGFyYW1zKTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSZXRyeU9wdGlvbnMge1xuICAvKiogSG93IG1hbnkgcmV0cmllcyAod2lsbCBhdCBsZWFzdCB0cnkgb25jZSkgKi9cbiAgcmVhZG9ubHkgYXR0ZW1wdHM6IG51bWJlcjtcbiAgLyoqIFNsZWVwIGJhc2UsIGluIG1zICovXG4gIHJlYWRvbmx5IHNsZWVwOiBudW1iZXI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB3aXRoUmV0cmllczxBIGV4dGVuZHMgQXJyYXk8YW55PiwgQj4ob3B0aW9uczogUmV0cnlPcHRpb25zLCBmbjogKC4uLnhzOiBBKSA9PiBQcm9taXNlPEI+KTogKC4uLnhzOiBBKSA9PiBQcm9taXNlPEI+IHtcbiAgcmV0dXJuIGFzeW5jICguLi54czogQSkgPT4ge1xuICAgIGxldCBhdHRlbXB0cyA9IG9wdGlvbnMuYXR0ZW1wdHM7XG4gICAgbGV0IG1zID0gb3B0aW9ucy5zbGVlcDtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IGZuKC4uLnhzKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgaWYgKGF0dGVtcHRzLS0gPD0gMCkge1xuICAgICAgICAgIHRocm93IGU7XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgc2xlZXAoTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogbXMpKTtcbiAgICAgICAgbXMgKj0gMjtcbiAgICAgIH1cbiAgICB9XG4gIH07XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHNsZWVwKG1zOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChvaykgPT4gc2V0VGltZW91dChvaywgbXMpKTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/asset.ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd/index.js b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/asset.ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd/index.js new file mode 100644 index 0000000000000..cf597f535efd3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/asset.ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd/index.js @@ -0,0 +1,81 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +// eslint-disable-next-line import/no-extraneous-dependencies +const aws_sdk_1 = require("aws-sdk"); +const ec2 = new aws_sdk_1.EC2(); +/** + * The default security group ingress rule. This can be used to both revoke and authorize the rules + */ +function ingressRuleParams(groupId, account) { + return { + GroupId: groupId, + IpPermissions: [{ + UserIdGroupPairs: [{ + GroupId: groupId, + UserId: account, + }], + IpProtocol: '-1', + }], + }; +} +/** + * The default security group egress rule. This can be used to both revoke and authorize the rules + */ +function egressRuleParams(groupId) { + return { + GroupId: groupId, + IpPermissions: [{ + IpRanges: [{ + CidrIp: '0.0.0.0/0', + }], + IpProtocol: '-1', + }], + }; +} +/** + * Process a custom resource request to restrict the default security group + * ingress & egress rules. + * + * When someone turns off the property then this custom resource will be deleted in which + * case we should add back the rules that were removed. + */ +async function handler(event) { + const securityGroupId = event.ResourceProperties.DefaultSecurityGroupId; + const account = event.ResourceProperties.Account; + switch (event.RequestType) { + case 'Create': + return revokeRules(securityGroupId, account); + case 'Update': + return onUpdate(event); + case 'Delete': + return authorizeRules(securityGroupId, account); + } +} +exports.handler = handler; +async function onUpdate(event) { + const oldSg = event.OldResourceProperties.DefaultSecurityGroupId; + const newSg = event.ResourceProperties.DefaultSecurityGroupId; + if (oldSg !== newSg) { + await authorizeRules(oldSg, event.ResourceProperties.Account); + await revokeRules(newSg, event.ResourceProperties.Account); + } + return; +} +/** + * Revoke both ingress and egress rules + */ +async function revokeRules(groupId, account) { + await ec2.revokeSecurityGroupEgress(egressRuleParams(groupId)).promise(); + await ec2.revokeSecurityGroupIngress(ingressRuleParams(groupId, account)).promise(); + return; +} +/** + * Authorize both ingress and egress rules + */ +async function authorizeRules(groupId, account) { + await ec2.authorizeSecurityGroupIngress(ingressRuleParams(groupId, account)).promise(); + await ec2.authorizeSecurityGroupEgress(egressRuleParams(groupId)).promise(); + return; +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2REFBNkQ7QUFDN0QscUNBQThCO0FBRTlCLE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxFQUFFLENBQUM7QUFFdEI7O0dBRUc7QUFDSCxTQUFTLGlCQUFpQixDQUFDLE9BQWUsRUFBRSxPQUFlO0lBQ3pELE9BQU87UUFDTCxPQUFPLEVBQUUsT0FBTztRQUNoQixhQUFhLEVBQUUsQ0FBQztnQkFDZCxnQkFBZ0IsRUFBRSxDQUFDO3dCQUNqQixPQUFPLEVBQUUsT0FBTzt3QkFDaEIsTUFBTSxFQUFFLE9BQU87cUJBQ2hCLENBQUM7Z0JBQ0YsVUFBVSxFQUFFLElBQUk7YUFDakIsQ0FBQztLQUNILENBQUM7QUFDSixDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFTLGdCQUFnQixDQUFDLE9BQWU7SUFDdkMsT0FBTztRQUNMLE9BQU8sRUFBRSxPQUFPO1FBQ2hCLGFBQWEsRUFBRSxDQUFDO2dCQUNkLFFBQVEsRUFBRSxDQUFDO3dCQUNULE1BQU0sRUFBRSxXQUFXO3FCQUNwQixDQUFDO2dCQUNGLFVBQVUsRUFBRSxJQUFJO2FBQ2pCLENBQUM7S0FDSCxDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNJLEtBQUssVUFBVSxPQUFPLENBQUMsS0FBa0Q7SUFDOUUsTUFBTSxlQUFlLEdBQUcsS0FBSyxDQUFDLGtCQUFrQixDQUFDLHNCQUFzQixDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLENBQUM7SUFDakQsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1FBQ3pCLEtBQUssUUFBUTtZQUNYLE9BQU8sV0FBVyxDQUFDLGVBQWUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMvQyxLQUFLLFFBQVE7WUFDWCxPQUFPLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN6QixLQUFLLFFBQVE7WUFDWCxPQUFPLGNBQWMsQ0FBQyxlQUFlLEVBQUUsT0FBTyxDQUFDLENBQUM7S0FDbkQ7QUFDSCxDQUFDO0FBWEQsMEJBV0M7QUFDRCxLQUFLLFVBQVUsUUFBUSxDQUFDLEtBQXdEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxzQkFBc0IsQ0FBQztJQUNqRSxNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsa0JBQWtCLENBQUMsc0JBQXNCLENBQUM7SUFDOUQsSUFBSSxLQUFLLEtBQUssS0FBSyxFQUFFO1FBQ25CLE1BQU0sY0FBYyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDOUQsTUFBTSxXQUFXLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsQ0FBQztLQUM1RDtJQUNELE9BQU87QUFDVCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxLQUFLLFVBQVUsV0FBVyxDQUFDLE9BQWUsRUFBRSxPQUFlO0lBQ3pELE1BQU0sR0FBRyxDQUFDLHlCQUF5QixDQUFDLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDekUsTUFBTSxHQUFHLENBQUMsMEJBQTBCLENBQUMsaUJBQWlCLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDcEYsT0FBTztBQUNULENBQUM7QUFFRDs7R0FFRztBQUNILEtBQUssVUFBVSxjQUFjLENBQUMsT0FBZSxFQUFFLE9BQWU7SUFDNUQsTUFBTSxHQUFHLENBQUMsNkJBQTZCLENBQUMsaUJBQWlCLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDdkYsTUFBTSxHQUFHLENBQUMsNEJBQTRCLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUM1RSxPQUFPO0FBQ1QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXNcbmltcG9ydCB7IEVDMiB9IGZyb20gJ2F3cy1zZGsnO1xuXG5jb25zdCBlYzIgPSBuZXcgRUMyKCk7XG5cbi8qKlxuICogVGhlIGRlZmF1bHQgc2VjdXJpdHkgZ3JvdXAgaW5ncmVzcyBydWxlLiBUaGlzIGNhbiBiZSB1c2VkIHRvIGJvdGggcmV2b2tlIGFuZCBhdXRob3JpemUgdGhlIHJ1bGVzXG4gKi9cbmZ1bmN0aW9uIGluZ3Jlc3NSdWxlUGFyYW1zKGdyb3VwSWQ6IHN0cmluZywgYWNjb3VudDogc3RyaW5nKTogRUMyLlJldm9rZVNlY3VyaXR5R3JvdXBJbmdyZXNzUmVxdWVzdCB8IEVDMi5BdXRob3JpemVTZWN1cml0eUdyb3VwSW5ncmVzc1JlcXVlc3Qge1xuICByZXR1cm4ge1xuICAgIEdyb3VwSWQ6IGdyb3VwSWQsXG4gICAgSXBQZXJtaXNzaW9uczogW3tcbiAgICAgIFVzZXJJZEdyb3VwUGFpcnM6IFt7XG4gICAgICAgIEdyb3VwSWQ6IGdyb3VwSWQsXG4gICAgICAgIFVzZXJJZDogYWNjb3VudCxcbiAgICAgIH1dLFxuICAgICAgSXBQcm90b2NvbDogJy0xJyxcbiAgICB9XSxcbiAgfTtcbn1cblxuLyoqXG4gKiBUaGUgZGVmYXVsdCBzZWN1cml0eSBncm91cCBlZ3Jlc3MgcnVsZS4gVGhpcyBjYW4gYmUgdXNlZCB0byBib3RoIHJldm9rZSBhbmQgYXV0aG9yaXplIHRoZSBydWxlc1xuICovXG5mdW5jdGlvbiBlZ3Jlc3NSdWxlUGFyYW1zKGdyb3VwSWQ6IHN0cmluZyk6IEVDMi5SZXZva2VTZWN1cml0eUdyb3VwRWdyZXNzUmVxdWVzdCB8IEVDMi5BdXRob3JpemVTZWN1cml0eUdyb3VwRWdyZXNzUmVxdWVzdCB7XG4gIHJldHVybiB7XG4gICAgR3JvdXBJZDogZ3JvdXBJZCxcbiAgICBJcFBlcm1pc3Npb25zOiBbe1xuICAgICAgSXBSYW5nZXM6IFt7XG4gICAgICAgIENpZHJJcDogJzAuMC4wLjAvMCcsXG4gICAgICB9XSxcbiAgICAgIElwUHJvdG9jb2w6ICctMScsXG4gICAgfV0sXG4gIH07XG59XG5cbi8qKlxuICogUHJvY2VzcyBhIGN1c3RvbSByZXNvdXJjZSByZXF1ZXN0IHRvIHJlc3RyaWN0IHRoZSBkZWZhdWx0IHNlY3VyaXR5IGdyb3VwXG4gKiBpbmdyZXNzICYgZWdyZXNzIHJ1bGVzLlxuICpcbiAqIFdoZW4gc29tZW9uZSB0dXJucyBvZmYgdGhlIHByb3BlcnR5IHRoZW4gdGhpcyBjdXN0b20gcmVzb3VyY2Ugd2lsbCBiZSBkZWxldGVkIGluIHdoaWNoXG4gKiBjYXNlIHdlIHNob3VsZCBhZGQgYmFjayB0aGUgcnVsZXMgdGhhdCB3ZXJlIHJlbW92ZWQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50KTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHNlY3VyaXR5R3JvdXBJZCA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5EZWZhdWx0U2VjdXJpdHlHcm91cElkO1xuICBjb25zdCBhY2NvdW50ID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkFjY291bnQ7XG4gIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICBjYXNlICdDcmVhdGUnOlxuICAgICAgcmV0dXJuIHJldm9rZVJ1bGVzKHNlY3VyaXR5R3JvdXBJZCwgYWNjb3VudCk7XG4gICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgIHJldHVybiBvblVwZGF0ZShldmVudCk7XG4gICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgIHJldHVybiBhdXRob3JpemVSdWxlcyhzZWN1cml0eUdyb3VwSWQsIGFjY291bnQpO1xuICB9XG59XG5hc3luYyBmdW5jdGlvbiBvblVwZGF0ZShldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VVcGRhdGVFdmVudCk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBvbGRTZyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcy5EZWZhdWx0U2VjdXJpdHlHcm91cElkO1xuICBjb25zdCBuZXdTZyA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5EZWZhdWx0U2VjdXJpdHlHcm91cElkO1xuICBpZiAob2xkU2cgIT09IG5ld1NnKSB7XG4gICAgYXdhaXQgYXV0aG9yaXplUnVsZXMob2xkU2csIGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5BY2NvdW50KTtcbiAgICBhd2FpdCByZXZva2VSdWxlcyhuZXdTZywgZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkFjY291bnQpO1xuICB9XG4gIHJldHVybjtcbn1cblxuLyoqXG4gKiBSZXZva2UgYm90aCBpbmdyZXNzIGFuZCBlZ3Jlc3MgcnVsZXNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmV2b2tlUnVsZXMoZ3JvdXBJZDogc3RyaW5nLCBhY2NvdW50OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgZWMyLnJldm9rZVNlY3VyaXR5R3JvdXBFZ3Jlc3MoZWdyZXNzUnVsZVBhcmFtcyhncm91cElkKSkucHJvbWlzZSgpO1xuICBhd2FpdCBlYzIucmV2b2tlU2VjdXJpdHlHcm91cEluZ3Jlc3MoaW5ncmVzc1J1bGVQYXJhbXMoZ3JvdXBJZCwgYWNjb3VudCkpLnByb21pc2UoKTtcbiAgcmV0dXJuO1xufVxuXG4vKipcbiAqIEF1dGhvcml6ZSBib3RoIGluZ3Jlc3MgYW5kIGVncmVzcyBydWxlc1xuICovXG5hc3luYyBmdW5jdGlvbiBhdXRob3JpemVSdWxlcyhncm91cElkOiBzdHJpbmcsIGFjY291bnQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBlYzIuYXV0aG9yaXplU2VjdXJpdHlHcm91cEluZ3Jlc3MoaW5ncmVzc1J1bGVQYXJhbXMoZ3JvdXBJZCwgYWNjb3VudCkpLnByb21pc2UoKTtcbiAgYXdhaXQgZWMyLmF1dGhvcml6ZVNlY3VyaXR5R3JvdXBFZ3Jlc3MoZWdyZXNzUnVsZVBhcmFtcyhncm91cElkKSkucHJvbWlzZSgpO1xuICByZXR1cm47XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.assets.json new file mode 100644 index 0000000000000..e473762e2b9a7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.assets.json @@ -0,0 +1,32 @@ +{ + "version": "33.0.0", + "files": { + "ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd": { + "source": { + "path": "asset.ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "ed794a1018779c6037c9e21f6ee72baaab56db80b04c8227ef61e5edf5464fe4": { + "source": { + "path": "aws-ecs-container-port-range.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "ed794a1018779c6037c9e21f6ee72baaab56db80b04c8227ef61e5edf5464fe4.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.template.json new file mode 100644 index 0000000000000..0b25b5ce9d7f4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.template.json @@ -0,0 +1,661 @@ +{ + "Resources": { + "EcsCluster97242B84": { + "Type": "AWS::ECS::Cluster" + }, + "EcsClusterVpc779914AB": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc" + } + ] + } + }, + "EcsClusterVpcPublicSubnet1Subnet4AC37B0F": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "EcsClusterVpcPublicSubnet1RouteTable4AE3113D": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "EcsClusterVpcPublicSubnet1RouteTableAssociation49C4CDBB": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "EcsClusterVpcPublicSubnet1RouteTable4AE3113D" + }, + "SubnetId": { + "Ref": "EcsClusterVpcPublicSubnet1Subnet4AC37B0F" + } + } + }, + "EcsClusterVpcPublicSubnet1DefaultRoute8C7EFC96": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "EcsClusterVpcIGW3663B083" + }, + "RouteTableId": { + "Ref": "EcsClusterVpcPublicSubnet1RouteTable4AE3113D" + } + }, + "DependsOn": [ + "EcsClusterVpcVPCGW944296C0" + ] + }, + "EcsClusterVpcPublicSubnet1EIP2D3759A3": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1" + } + ] + } + }, + "EcsClusterVpcPublicSubnet1NATGateway2F1E7764": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "EcsClusterVpcPublicSubnet1EIP2D3759A3", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "EcsClusterVpcPublicSubnet1Subnet4AC37B0F" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "EcsClusterVpcPublicSubnet1DefaultRoute8C7EFC96", + "EcsClusterVpcPublicSubnet1RouteTableAssociation49C4CDBB" + ] + }, + "EcsClusterVpcPublicSubnet2Subnet4A959A4C": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.64.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "EcsClusterVpcPublicSubnet2RouteTable89A2F6C5": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "EcsClusterVpcPublicSubnet2RouteTableAssociationE4D42FC1": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "EcsClusterVpcPublicSubnet2RouteTable89A2F6C5" + }, + "SubnetId": { + "Ref": "EcsClusterVpcPublicSubnet2Subnet4A959A4C" + } + } + }, + "EcsClusterVpcPublicSubnet2DefaultRoute048730F7": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "EcsClusterVpcIGW3663B083" + }, + "RouteTableId": { + "Ref": "EcsClusterVpcPublicSubnet2RouteTable89A2F6C5" + } + }, + "DependsOn": [ + "EcsClusterVpcVPCGW944296C0" + ] + }, + "EcsClusterVpcPublicSubnet2EIP26E3EEEF": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2" + } + ] + } + }, + "EcsClusterVpcPublicSubnet2NATGatewayBD015416": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "EcsClusterVpcPublicSubnet2EIP26E3EEEF", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "EcsClusterVpcPublicSubnet2Subnet4A959A4C" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2" + } + ] + }, + "DependsOn": [ + "EcsClusterVpcPublicSubnet2DefaultRoute048730F7", + "EcsClusterVpcPublicSubnet2RouteTableAssociationE4D42FC1" + ] + }, + "EcsClusterVpcPrivateSubnet1SubnetFAB0E487": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "EcsClusterVpcPrivateSubnet1RouteTable2EA148EE": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "EcsClusterVpcPrivateSubnet1RouteTableAssociationF4E8ACD7": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "EcsClusterVpcPrivateSubnet1RouteTable2EA148EE" + }, + "SubnetId": { + "Ref": "EcsClusterVpcPrivateSubnet1SubnetFAB0E487" + } + } + }, + "EcsClusterVpcPrivateSubnet1DefaultRoute0239F5D0": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "EcsClusterVpcPublicSubnet1NATGateway2F1E7764" + }, + "RouteTableId": { + "Ref": "EcsClusterVpcPrivateSubnet1RouteTable2EA148EE" + } + } + }, + "EcsClusterVpcPrivateSubnet2SubnetC2B7B1BA": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.192.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "EcsClusterVpcPrivateSubnet2RouteTable1D430E45": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "EcsClusterVpcPrivateSubnet2RouteTableAssociation329A2412": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "EcsClusterVpcPrivateSubnet2RouteTable1D430E45" + }, + "SubnetId": { + "Ref": "EcsClusterVpcPrivateSubnet2SubnetC2B7B1BA" + } + } + }, + "EcsClusterVpcPrivateSubnet2DefaultRoute27221D27": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "EcsClusterVpcPublicSubnet2NATGatewayBD015416" + }, + "RouteTableId": { + "Ref": "EcsClusterVpcPrivateSubnet2RouteTable1D430E45" + } + } + }, + "EcsClusterVpcIGW3663B083": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-container-port-range/EcsCluster/Vpc" + } + ] + } + }, + "EcsClusterVpcVPCGW944296C0": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "InternetGatewayId": { + "Ref": "EcsClusterVpcIGW3663B083" + }, + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "EcsClusterVpcRestrictDefaultSecurityGroupCustomResource8F588911": { + "Type": "Custom::VpcRestrictDefaultSG", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E", + "Arn" + ] + }, + "DefaultSecurityGroupId": { + "Fn::GetAtt": [ + "EcsClusterVpc779914AB", + "DefaultSecurityGroup" + ] + }, + "Account": { + "Ref": "AWS::AccountId" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:AuthorizeSecurityGroupEgress", + "ec2:RevokeSecurityGroupIngress", + "ec2:RevokeSecurityGroupEgress" + ], + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":security-group/", + { + "Fn::GetAtt": [ + "EcsClusterVpc779914AB", + "DefaultSecurityGroup" + ] + } + ] + ] + } + ] + } + ] + } + } + ] + } + }, + "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "ba598c1f1d84f7077ea9c16a6b921e4f8acf18e996100e72a8f17da980e64fdd.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0", + "Arn" + ] + }, + "Runtime": "nodejs16.x", + "Description": "Lambda function for removing all inbound/outbound rules from the VPC default security group" + }, + "DependsOn": [ + "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0" + ] + }, + "FargateTaskDefTaskRole0B257552": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "FargateTaskDefC6FB60B4": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "amazon/amazon-ecs-sample", + "Name": "web", + "PortMappings": [ + { + "ContainerPortRange": "8080-8081", + "Protocol": "tcp" + } + ] + } + ], + "Cpu": "256", + "Family": "awsecscontainerportrangeFargateTaskDefD6480B74", + "Memory": "512", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "FargateTaskDefTaskRole0B257552", + "Arn" + ] + } + } + }, + "FargateServiceAC2B3B85": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { + "Ref": "EcsCluster97242B84" + }, + "DeploymentConfiguration": { + "Alarms": { + "AlarmNames": [], + "Enable": false, + "Rollback": false + }, + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "EnableECSManagedTags": false, + "LaunchType": "FARGATE", + "NetworkConfiguration": { + "AwsvpcConfiguration": { + "AssignPublicIp": "DISABLED", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "FargateServiceSecurityGroup0A0E79CB", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "EcsClusterVpcPrivateSubnet1SubnetFAB0E487" + }, + { + "Ref": "EcsClusterVpcPrivateSubnet2SubnetC2B7B1BA" + } + ] + } + }, + "TaskDefinition": { + "Ref": "FargateTaskDefC6FB60B4" + } + }, + "DependsOn": [ + "FargateTaskDefTaskRole0B257552" + ] + }, + "FargateServiceSecurityGroup0A0E79CB": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-container-port-range/FargateService/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "EcsClusterVpc779914AB" + } + }, + "DependsOn": [ + "FargateTaskDefTaskRole0B257552" + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/cdk.out new file mode 100644 index 0000000000000..560dae10d018f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"33.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/integ.json new file mode 100644 index 0000000000000..662bbbe2bd34f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "33.0.0", + "testCases": { + "EcsContainerPortRange/DefaultTest": { + "stacks": [ + "aws-ecs-container-port-range" + ], + "assertionStack": "EcsContainerPortRange/DefaultTest/DeployAssert", + "assertionStackName": "EcsContainerPortRangeDefaultTestDeployAssert87C0580C" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/manifest.json new file mode 100644 index 0000000000000..536ff2001012f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/manifest.json @@ -0,0 +1,291 @@ +{ + "version": "33.0.0", + "artifacts": { + "aws-ecs-container-port-range.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-ecs-container-port-range.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-ecs-container-port-range": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-ecs-container-port-range.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/ed794a1018779c6037c9e21f6ee72baaab56db80b04c8227ef61e5edf5464fe4.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-ecs-container-port-range.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-ecs-container-port-range.assets" + ], + "metadata": { + "/aws-ecs-container-port-range/EcsCluster/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsCluster97242B84" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpc779914AB" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet1Subnet4AC37B0F" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet1RouteTable4AE3113D" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet1RouteTableAssociation49C4CDBB" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet1DefaultRoute8C7EFC96" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet1EIP2D3759A3" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet1NATGateway2F1E7764" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet2Subnet4A959A4C" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet2RouteTable89A2F6C5" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet2RouteTableAssociationE4D42FC1" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet2DefaultRoute048730F7" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet2EIP26E3EEEF" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPublicSubnet2NATGatewayBD015416" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPrivateSubnet1SubnetFAB0E487" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPrivateSubnet1RouteTable2EA148EE" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPrivateSubnet1RouteTableAssociationF4E8ACD7" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPrivateSubnet1DefaultRoute0239F5D0" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPrivateSubnet2SubnetC2B7B1BA" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPrivateSubnet2RouteTable1D430E45" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPrivateSubnet2RouteTableAssociation329A2412" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcPrivateSubnet2DefaultRoute27221D27" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcIGW3663B083" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcVPCGW944296C0" + } + ], + "/aws-ecs-container-port-range/EcsCluster/Vpc/RestrictDefaultSecurityGroupCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterVpcRestrictDefaultSecurityGroupCustomResource8F588911" + } + ], + "/aws-ecs-container-port-range/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0" + } + ], + "/aws-ecs-container-port-range/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E" + } + ], + "/aws-ecs-container-port-range/FargateTaskDef/TaskRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FargateTaskDefTaskRole0B257552" + } + ], + "/aws-ecs-container-port-range/FargateTaskDef/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FargateTaskDefC6FB60B4" + } + ], + "/aws-ecs-container-port-range/FargateService/Service": [ + { + "type": "aws:cdk:logicalId", + "data": "FargateServiceAC2B3B85" + } + ], + "/aws-ecs-container-port-range/FargateService/SecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FargateServiceSecurityGroup0A0E79CB" + } + ], + "/aws-ecs-container-port-range/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-ecs-container-port-range/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-ecs-container-port-range" + }, + "EcsContainerPortRangeDefaultTestDeployAssert87C0580C.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "EcsContainerPortRangeDefaultTestDeployAssert87C0580C.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "EcsContainerPortRangeDefaultTestDeployAssert87C0580C": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "EcsContainerPortRangeDefaultTestDeployAssert87C0580C.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "EcsContainerPortRangeDefaultTestDeployAssert87C0580C.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "EcsContainerPortRangeDefaultTestDeployAssert87C0580C.assets" + ], + "metadata": { + "/EcsContainerPortRange/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/EcsContainerPortRange/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "EcsContainerPortRange/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/tree.json new file mode 100644 index 0000000000000..2b083d1a47ec6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/tree.json @@ -0,0 +1,1013 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-ecs-container-port-range": { + "id": "aws-ecs-container-port-range", + "path": "aws-ecs-container-port-range", + "children": { + "EcsCluster": { + "id": "EcsCluster", + "path": "aws-ecs-container-port-range/EcsCluster", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-container-port-range/EcsCluster/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::Cluster", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnCluster", + "version": "0.0.0" + } + }, + "Vpc": { + "id": "Vpc", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "EcsClusterVpcPublicSubnet1RouteTable4AE3113D" + }, + "subnetId": { + "Ref": "EcsClusterVpcPublicSubnet1Subnet4AC37B0F" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "EcsClusterVpcIGW3663B083" + }, + "routeTableId": { + "Ref": "EcsClusterVpcPublicSubnet1RouteTable4AE3113D" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "EcsClusterVpcPublicSubnet1EIP2D3759A3", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "EcsClusterVpcPublicSubnet1Subnet4AC37B0F" + }, + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "EcsClusterVpcPublicSubnet2RouteTable89A2F6C5" + }, + "subnetId": { + "Ref": "EcsClusterVpcPublicSubnet2Subnet4A959A4C" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "EcsClusterVpcIGW3663B083" + }, + "routeTableId": { + "Ref": "EcsClusterVpcPublicSubnet2RouteTable89A2F6C5" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "EcsClusterVpcPublicSubnet2EIP26E3EEEF", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "EcsClusterVpcPublicSubnet2Subnet4A959A4C" + }, + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "EcsClusterVpcPrivateSubnet1RouteTable2EA148EE" + }, + "subnetId": { + "Ref": "EcsClusterVpcPrivateSubnet1SubnetFAB0E487" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "EcsClusterVpcPublicSubnet1NATGateway2F1E7764" + }, + "routeTableId": { + "Ref": "EcsClusterVpcPrivateSubnet1RouteTable2EA148EE" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "EcsClusterVpcPrivateSubnet2RouteTable1D430E45" + }, + "subnetId": { + "Ref": "EcsClusterVpcPrivateSubnet2SubnetC2B7B1BA" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "EcsClusterVpcPublicSubnet2NATGatewayBD015416" + }, + "routeTableId": { + "Ref": "EcsClusterVpcPrivateSubnet2RouteTable1D430E45" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-ecs-container-port-range/EcsCluster/Vpc" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "internetGatewayId": { + "Ref": "EcsClusterVpcIGW3663B083" + }, + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + }, + "RestrictDefaultSecurityGroupCustomResource": { + "id": "RestrictDefaultSecurityGroupCustomResource", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/RestrictDefaultSecurityGroupCustomResource", + "children": { + "Default": { + "id": "Default", + "path": "aws-ecs-container-port-range/EcsCluster/Vpc/RestrictDefaultSecurityGroupCustomResource/Default", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.CustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Vpc", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.Cluster", + "version": "0.0.0" + } + }, + "Custom::VpcRestrictDefaultSGCustomResourceProvider": { + "id": "Custom::VpcRestrictDefaultSGCustomResourceProvider", + "path": "aws-ecs-container-port-range/Custom::VpcRestrictDefaultSGCustomResourceProvider", + "children": { + "Staging": { + "id": "Staging", + "path": "aws-ecs-container-port-range/Custom::VpcRestrictDefaultSGCustomResourceProvider/Staging", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "aws-ecs-container-port-range/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "aws-ecs-container-port-range/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.CustomResourceProvider", + "version": "0.0.0" + } + }, + "FargateTaskDef": { + "id": "FargateTaskDef", + "path": "aws-ecs-container-port-range/FargateTaskDef", + "children": { + "TaskRole": { + "id": "TaskRole", + "path": "aws-ecs-container-port-range/FargateTaskDef/TaskRole", + "children": { + "ImportTaskRole": { + "id": "ImportTaskRole", + "path": "aws-ecs-container-port-range/FargateTaskDef/TaskRole/ImportTaskRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-ecs-container-port-range/FargateTaskDef/TaskRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-ecs-container-port-range/FargateTaskDef/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::TaskDefinition", + "aws:cdk:cloudformation:props": { + "containerDefinitions": [ + { + "essential": true, + "image": "amazon/amazon-ecs-sample", + "name": "web", + "portMappings": [ + { + "containerPortRange": "8080-8081", + "protocol": "tcp" + } + ] + } + ], + "cpu": "256", + "family": "awsecscontainerportrangeFargateTaskDefD6480B74", + "memory": "512", + "networkMode": "awsvpc", + "requiresCompatibilities": [ + "FARGATE" + ], + "taskRoleArn": { + "Fn::GetAtt": [ + "FargateTaskDefTaskRole0B257552", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnTaskDefinition", + "version": "0.0.0" + } + }, + "web": { + "id": "web", + "path": "aws-ecs-container-port-range/FargateTaskDef/web", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.ContainerDefinition", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.FargateTaskDefinition", + "version": "0.0.0" + } + }, + "FargateService": { + "id": "FargateService", + "path": "aws-ecs-container-port-range/FargateService", + "children": { + "Service": { + "id": "Service", + "path": "aws-ecs-container-port-range/FargateService/Service", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::Service", + "aws:cdk:cloudformation:props": { + "cluster": { + "Ref": "EcsCluster97242B84" + }, + "deploymentConfiguration": { + "maximumPercent": 200, + "minimumHealthyPercent": 50, + "alarms": { + "alarmNames": [], + "enable": false, + "rollback": false + } + }, + "enableEcsManagedTags": false, + "launchType": "FARGATE", + "networkConfiguration": { + "awsvpcConfiguration": { + "assignPublicIp": "DISABLED", + "subnets": [ + { + "Ref": "EcsClusterVpcPrivateSubnet1SubnetFAB0E487" + }, + { + "Ref": "EcsClusterVpcPrivateSubnet2SubnetC2B7B1BA" + } + ], + "securityGroups": [ + { + "Fn::GetAtt": [ + "FargateServiceSecurityGroup0A0E79CB", + "GroupId" + ] + } + ] + } + }, + "taskDefinition": { + "Ref": "FargateTaskDefC6FB60B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnService", + "version": "0.0.0" + } + }, + "SecurityGroup": { + "id": "SecurityGroup", + "path": "aws-ecs-container-port-range/FargateService/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecs-container-port-range/FargateService/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "aws-ecs-container-port-range/FargateService/SecurityGroup", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "vpcId": { + "Ref": "EcsClusterVpc779914AB" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.SecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.FargateService", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-ecs-container-port-range/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-ecs-container-port-range/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "EcsContainerPortRange": { + "id": "EcsContainerPortRange", + "path": "EcsContainerPortRange", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "EcsContainerPortRange/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "EcsContainerPortRange/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "EcsContainerPortRange/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "EcsContainerPortRange/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "EcsContainerPortRange/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.69" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts new file mode 100644 index 0000000000000..4a00cafe64a82 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts @@ -0,0 +1,35 @@ +import * as constructs from 'constructs'; +import * as cdk from 'aws-cdk-lib/core'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import * as ecs from 'aws-cdk-lib/aws-ecs'; + +class EcsContainerPortRangeStack extends cdk.Stack { + readonly cluster: ecs.Cluster; + + constructor(scope: constructs.Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + this.cluster = new ecs.Cluster(this, 'EcsCluster'); + + const taskDefinition = new ecs.FargateTaskDefinition(this, 'FargateTaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + portMappings: [{ containerPortRange: '8080-8081' }], + }); + + new ecs.FargateService(this, 'FargateService', { + cluster: this.cluster, + taskDefinition, + }); + } +} + +const app = new cdk.App(); +const stack = new EcsContainerPortRangeStack(app, 'aws-ecs-container-port-range'); + +new integ.IntegTest(app, 'EcsContainerPortRange', { + testCases: [stack], +}); + +app.synth(); From 2251c5d97255630260591da0e6849d089f11597f Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Mon, 14 Aug 2023 14:10:14 +0200 Subject: [PATCH 06/19] Update the README --- packages/aws-cdk-lib/aws-ecs/README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ecs/README.md b/packages/aws-cdk-lib/aws-ecs/README.md index a07df015d9159..ecc3d96801b32 100644 --- a/packages/aws-cdk-lib/aws-ecs/README.md +++ b/packages/aws-cdk-lib/aws-ecs/README.md @@ -358,7 +358,7 @@ declare const taskDefinition: ecs.TaskDefinition; taskDefinition.addContainer("WebContainer", { image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), memoryLimitMiB: 1024, - portMappings: [{ containerPort: 3000 }, { containerPortRange: '3000-4000' }], + portMappings: [{ containerPort: 3000 }], }); ``` @@ -372,6 +372,22 @@ container.addPortMappings({ }); ``` +Sometimes it is useful to be able to configure port ranges for a container, e.g. to run applications such as game servers +and real-time streaming which typically require multiple ports to be opened simultaneously. This feature is supported on +both Linux and Windows operating systems and both the EC2 and AWS Fargate launch types. There is a maximum limit of 100 +port ranges per container, and you cannot specify overlapping port ranges. + +Docker recommends that you turn off the `docker-proxy` in the Docker daemon config file when you have a large number of ports. +For more information, see [Issue #11185](https://github.com/moby/moby/issues/11185) on the GitHub website. + +```ts +declare const container: ecs.ContainerDefintion; + +container.addPortMappings({ + containerPortRange: '8080-8081', +}); +``` + To add data volumes to a task definition, call `addVolume()`: ```ts From d64f317d73a428d5c99dc0236280484c9a242cea Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Mon, 14 Aug 2023 20:52:16 +0200 Subject: [PATCH 07/19] Reword the error message and add missing unit test --- ...work-multiple-target-groups-ecs-service.ts | 2 +- ...-multiple-target-groups-fargate-service.ts | 2 +- .../aws-ecs-patterns/test/ec2/l3s-v2.test.ts | 23 +++++++++++++++++++ .../load-balanced-fargate-service-v2.test.ts | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts b/packages/aws-cdk-lib/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts index c9c9550112ce2..0fe436a01de5f 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts @@ -151,7 +151,7 @@ export class NetworkMultipleTargetGroupsEc2Service extends NetworkMultipleTarget const containerPort = this.taskDefinition.defaultContainer.portMappings[0].containerPort; if (!containerPort) { - throw new Error('You must specify a containerPort'); + throw new Error('The first port mapping added to the default container must expose a single port'); } this.targetGroup = this.listener.addTargets('ECS', { diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts b/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts index 7dedc995ff5c8..21d97d8e9d5cb 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts @@ -106,7 +106,7 @@ export class NetworkMultipleTargetGroupsFargateService extends NetworkMultipleTa const containerPort = this.taskDefinition.defaultContainer.portMappings[0].containerPort; if (!containerPort) { - throw new Error('The first port mapping added to the essential container must expose a single port'); + throw new Error('The first port mapping added to the default container must expose a single port'); } this.targetGroup = this.listener.addTargets('ECS', { diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts b/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts index 25afeb033eebf..7984cbe18a8d2 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts @@ -1791,4 +1791,27 @@ describe('When Network Load Balancer', () => { desiredCount: 0, })).toThrow(/You must specify a desiredCount greater than 0/); }); + + test('errors when container port range is set for essential container', () => { + // GIVEN + const stack = new Stack(); + const vpc = new Vpc(stack, 'VPC'); + const cluster = new Cluster(stack, 'Cluster', { vpc }); + const taskDefinition = new Ec2TaskDefinition(stack, 'FargateTaskDef'); + + taskDefinition.addContainer('MainContainer', { + image: ContainerImage.fromRegistry('test'), + portMappings: [{ + containerPortRange: '8080-8081', + }], + }); + + // THEN + expect(() => { + new NetworkMultipleTargetGroupsEc2Service(stack, 'Service', { + cluster, + taskDefinition, + }); + }).toThrow('The first port mapping added to the default container must expose a single port'); + }); }); diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts index ed85cd7df44e8..b100e8b4701f5 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts @@ -858,6 +858,6 @@ describe('When Network Load Balancer', () => { cluster, taskDefinition, }); - }).toThrow('The first port mapping added to the essential container must expose a single port'); + }).toThrow('The first port mapping added to the default container must expose a single port'); }); }); From 9e234ca360962c066e65aec03a5e641a81789d9e Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Mon, 14 Aug 2023 22:48:09 +0200 Subject: [PATCH 08/19] Add validation of string format for `containerPortRange` prop --- .../aws-ecs/lib/base/task-definition.ts | 2 +- .../aws-ecs/lib/container-definition.ts | 4 ++ .../aws-ecs/test/container-definition.test.ts | 50 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts index 3833fe1794065..e5452a544f531 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts @@ -593,7 +593,7 @@ export class TaskDefinition extends TaskDefinitionBase { if (portMapping.containerPort !== undefined) { return portMapping.protocol === Protocol.UDP ? ec2.Port.udp(portMapping.containerPort) : ec2.Port.tcp(portMapping.containerPort); } - const [startPort, endPort] = portMapping.containerPortRange!.split('-', 2).map(v => +v); + const [startPort, endPort] = portMapping.containerPortRange!.split('-', 2).map(v => Number(v)); return portMapping.protocol === Protocol.UDP ? ec2.Port.udpRange(startPort, endPort) : ec2.Port.tcpRange(startPort, endPort); } diff --git a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts index c4a71369b1cb1..0d18eadb321b6 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts @@ -1196,6 +1196,10 @@ export class PortMap { if (this.networkmode !== NetworkMode.BRIDGE && this.networkmode !== NetworkMode.AWS_VPC) { throw new Error('Either AwsVpc or Bridge network mode is required to set a port range for the container.'); } + + if (!/^\d+-\d+$/.test(this.portmapping.containerPortRange)) { + throw new Error('The containerPortRange must be a string in the format [start port]-[end port].'); + } } } diff --git a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts index 11cd33f114186..ec0da0b68741a 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts @@ -104,6 +104,56 @@ describe('container definition', () => { expect(() => portMap.validate()).toThrow('Cannot set "hostPort" while using a port range for the container.'); }); + test('throws when PortMapping.containerPortRange string is invalid', () => { + expect(() => { + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPortRange: '-', + }); + + portMap.validate(); + }).toThrow('The containerPortRange must be a string in the format [start port]-[end port].'); + + expect(() => { + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPortRange: 'foo-', + }); + + portMap.validate(); + }).toThrow('The containerPortRange must be a string in the format [start port]-[end port].'); + + expect(() => { + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPortRange: '-bar', + }); + + portMap.validate(); + }).toThrow('The containerPortRange must be a string in the format [start port]-[end port].'); + + expect(() => { + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPortRange: 'foo-bar', + }); + + portMap.validate(); + }).toThrow('The containerPortRange must be a string in the format [start port]-[end port].'); + + expect(() => { + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPortRange: '808a-8081', + }); + + portMap.validate(); + }).toThrow('The containerPortRange must be a string in the format [start port]-[end port].'); + + expect(() => { + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPortRange: '8080-808a', + }); + + portMap.validate(); + }).toThrow('The containerPortRange must be a string in the format [start port]-[end port].'); + }); + describe('throws when PortMapping.containerPortRange is used with an unsupported network mode', () => { test('when network mode is Host', () => { // GIVEN From 84f0755445bb4eac4e1af5b44838af34d617fcbe Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Thu, 17 Aug 2023 21:44:59 +0200 Subject: [PATCH 09/19] Make `containerPort` `non-nullable` again and handle BC --- .../fargate/integ.container-port-range.ts | 5 +++- .../aws-ecs-patterns/test/ec2/l3s-v2.test.ts | 2 ++ .../load-balanced-fargate-service-v2.test.ts | 1 + packages/aws-cdk-lib/aws-ecs/README.md | 3 ++- .../aws-ecs/lib/base/task-definition.ts | 4 +-- .../aws-ecs/lib/container-definition.ts | 20 +++++++++------ .../aws-ecs/test/container-definition.test.ts | 25 ++++++++++++++++--- .../aws-ecs/test/ec2/ec2-service.test.ts | 10 ++++++-- .../test/fargate/fargate-service.test.ts | 2 ++ 9 files changed, 55 insertions(+), 17 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts index 4a00cafe64a82..432436d1bd638 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts @@ -15,7 +15,10 @@ class EcsContainerPortRangeStack extends cdk.Stack { taskDefinition.addContainer('web', { image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), - portMappings: [{ containerPortRange: '8080-8081' }], + portMappings: [{ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPortRange: '8080-8081', + }], }); new ecs.FargateService(this, 'FargateService', { diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts b/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts index 7984cbe18a8d2..7f2e5ad3f56f9 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts @@ -4,6 +4,7 @@ import { Certificate } from '../../../aws-certificatemanager'; import * as ec2 from '../../../aws-ec2'; import { MachineImage, Vpc } from '../../../aws-ec2'; import { + CONTAINER_PORT_UNSET_VALUE, AsgCapacityProvider, AwsLogDriver, Cluster, @@ -1802,6 +1803,7 @@ describe('When Network Load Balancer', () => { taskDefinition.addContainer('MainContainer', { image: ContainerImage.fromRegistry('test'), portMappings: [{ + containerPort: CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }], }); diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts index b100e8b4701f5..9227cca5ae1a5 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts @@ -848,6 +848,7 @@ describe('When Network Load Balancer', () => { taskDefinition.addContainer('MainContainer', { image: ecs.ContainerImage.fromRegistry('test'), portMappings: [{ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }], }); diff --git a/packages/aws-cdk-lib/aws-ecs/README.md b/packages/aws-cdk-lib/aws-ecs/README.md index ecc3d96801b32..d1bd25254abe0 100644 --- a/packages/aws-cdk-lib/aws-ecs/README.md +++ b/packages/aws-cdk-lib/aws-ecs/README.md @@ -374,7 +374,7 @@ container.addPortMappings({ Sometimes it is useful to be able to configure port ranges for a container, e.g. to run applications such as game servers and real-time streaming which typically require multiple ports to be opened simultaneously. This feature is supported on -both Linux and Windows operating systems and both the EC2 and AWS Fargate launch types. There is a maximum limit of 100 +both Linux and Windows operating systems for both the EC2 and AWS Fargate launch types. There is a maximum limit of 100 port ranges per container, and you cannot specify overlapping port ranges. Docker recommends that you turn off the `docker-proxy` in the Docker daemon config file when you have a large number of ports. @@ -384,6 +384,7 @@ For more information, see [Issue #11185](https://github.com/moby/moby/issues/111 declare const container: ecs.ContainerDefintion; container.addPortMappings({ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }); ``` diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts index e5452a544f531..67497368f3fdc 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts @@ -3,7 +3,7 @@ import { ImportedTaskDefinition } from './_imported-task-definition'; import * as ec2 from '../../../aws-ec2'; import * as iam from '../../../aws-iam'; import { IResource, Lazy, Names, PhysicalName, Resource } from '../../../core'; -import { ContainerDefinition, ContainerDefinitionOptions, PortMapping, Protocol } from '../container-definition'; +import { CONTAINER_PORT_UNSET_VALUE, ContainerDefinition, ContainerDefinitionOptions, PortMapping, Protocol } from '../container-definition'; import { CfnTaskDefinition } from '../ecs.generated'; import { FirelensLogRouter, FirelensLogRouterDefinitionOptions, FirelensLogRouterType, obtainDefaultFluentBitECRImage } from '../firelens-log-router'; import { AwsLogDriver } from '../log-drivers/aws-log-driver'; @@ -590,7 +590,7 @@ export class TaskDefinition extends TaskDefinitionBase { if (this.networkMode === NetworkMode.BRIDGE || this.networkMode === NetworkMode.NAT) { return EPHEMERAL_PORT_RANGE; } - if (portMapping.containerPort !== undefined) { + if (portMapping.containerPort !== CONTAINER_PORT_UNSET_VALUE) { return portMapping.protocol === Protocol.UDP ? ec2.Port.udp(portMapping.containerPort) : ec2.Port.tcp(portMapping.containerPort); } const [startPort, endPort] = portMapping.containerPortRange!.split('-', 2).map(v => Number(v)); diff --git a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts index 0d18eadb321b6..4d22d3c64feb2 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts @@ -10,6 +10,8 @@ import * as secretsmanager from '../../aws-secretsmanager'; import * as ssm from '../../aws-ssm'; import * as cdk from '../../core'; +export const CONTAINER_PORT_UNSET_VALUE = 0; + /** * Specify the secret's version id or version stage */ @@ -753,7 +755,7 @@ export class ContainerDefinition extends Construct { return 0; } - if (defaultPortMapping.containerPort === undefined) { + if (defaultPortMapping.containerPortRange !== undefined) { throw new Error(`The first port mapping of the container ${this.containerName} must expose a single port.`); } @@ -769,7 +771,7 @@ export class ContainerDefinition extends Construct { } const defaultPortMapping = this.portMappings[0]; - if (defaultPortMapping.containerPort === undefined) { + if (defaultPortMapping.containerPortRange !== undefined) { throw new Error(`The first port mapping of the container ${this.containerName} must expose a single port.`); } @@ -1085,7 +1087,7 @@ export interface PortMapping { * For more information, see hostPort. * Port mappings that are automatically assigned in this way do not count toward the 100 reserved ports limit of a container instance. */ - readonly containerPort?: number; + readonly containerPort: number; /** * The port number range on the container that's bound to the dynamically mapped host port range. @@ -1173,15 +1175,15 @@ export class PortMap { throw new Error('Port mapping name cannot be an empty string.'); } - if (this.portmapping.containerPort === undefined && this.portmapping.containerPortRange === undefined) { - throw new Error('Either "containerPort" or "containerPortRange" must be set.'); + if (this.portmapping.containerPort === CONTAINER_PORT_UNSET_VALUE && this.portmapping.containerPortRange === undefined) { + throw new Error(`The containerPortRange must be set when containerPort is equal to ${CONTAINER_PORT_UNSET_VALUE}`); } - if (this.portmapping.containerPort !== undefined && this.portmapping.containerPortRange !== undefined) { + if (this.portmapping.containerPort !== CONTAINER_PORT_UNSET_VALUE && this.portmapping.containerPortRange !== undefined) { throw new Error('Cannot set "containerPort" and "containerPortRange" at the same time.'); } - if (this.portmapping.containerPort !== undefined) { + if (this.portmapping.containerPort !== CONTAINER_PORT_UNSET_VALUE) { if ((this.networkmode === NetworkMode.AWS_VPC || this.networkmode === NetworkMode.HOST) && this.portmapping.hostPort !== undefined && this.portmapping.hostPort !== this.portmapping.containerPort) { throw new Error('The host port must be left out or must be the same as the container port for AwsVpc or Host network mode.'); @@ -1189,6 +1191,10 @@ export class PortMap { } if (this.portmapping.containerPortRange !== undefined) { + if (cdk.Token.isUnresolved(this.portmapping.containerPortRange)) { + throw new Error('The value of containerPortRange must be concrete (no Tokens)'); + } + if (this.portmapping.hostPort !== undefined) { throw new Error('Cannot set "hostPort" while using a port range for the container.'); } diff --git a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts index ec0da0b68741a..c9465a18abd7f 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts @@ -50,12 +50,14 @@ describe('container definition', () => { }).toThrow(); }); - test('throws when neither PortMapping.containerPort nor PortMapping.containerPortRange is set', () => { + test('throws when PortMapping.containerPortRange is not set and PortMapping.containerPort is set to 0', () => { // GIVEN - const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, {}); + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + }); // THEN - expect(() => portMap.validate()).toThrow('Either "containerPort" or "containerPortRange" must be set.'); + expect(() => portMap.validate()).toThrow(`The containerPortRange must be set when containerPort is equal to ${ecs.CONTAINER_PORT_UNSET_VALUE}`); }); test('throws when PortMapping.containerPortRange is used along with PortMapping.containerPort', () => { @@ -96,8 +98,9 @@ describe('container definition', () => { test('throws when PortMapping.containerPortRange is used along with PortMapping.hostPort', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - hostPort: 8080, + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', + hostPort: 8080, }); // THEN @@ -107,6 +110,7 @@ describe('container definition', () => { test('throws when PortMapping.containerPortRange string is invalid', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '-', }); @@ -115,6 +119,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: 'foo-', }); @@ -123,6 +128,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '-bar', }); @@ -131,6 +137,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: 'foo-bar', }); @@ -139,6 +146,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '808a-8081', }); @@ -147,6 +155,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-808a', }); @@ -158,6 +167,7 @@ describe('container definition', () => { test('when network mode is Host', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.HOST, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }); @@ -168,6 +178,7 @@ describe('container definition', () => { test('when network mode is NAT', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.NAT, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }); @@ -178,6 +189,7 @@ describe('container definition', () => { test('when network mode is None', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.NONE, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }); @@ -190,6 +202,7 @@ describe('container definition', () => { test('when network mode is AwsVpc', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }); @@ -200,6 +213,7 @@ describe('container definition', () => { test('when network mode is Bridge', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.BRIDGE, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }); @@ -869,6 +883,7 @@ describe('container definition', () => { image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'), memoryLimitMiB: 2048, portMappings: [{ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }], }); @@ -977,6 +992,7 @@ describe('container definition', () => { image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'), memoryLimitMiB: 2048, portMappings: [{ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }], }); @@ -1043,6 +1059,7 @@ describe('container definition', () => { image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'), memoryLimitMiB: 2048, portMappings: [{ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8080-8081', }], }); diff --git a/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts index ab2eb89343dca..8ad224410b94c 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts @@ -2714,7 +2714,10 @@ describe('ec2 service', () => { const container = taskDefinition.addContainer('MainContainer', { image: ecs.ContainerImage.fromRegistry('hello'), }); - container.addPortMappings({ containerPortRange: '8000-8001' }); + container.addPortMappings({ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPortRange: '8000-8001', + }); const service = new ecs.Ec2Service(stack, 'Service', { cluster, @@ -2988,7 +2991,10 @@ describe('ec2 service', () => { const container = taskDefinition.addContainer('MainContainer', { image: ecs.ContainerImage.fromRegistry('hello'), }); - container.addPortMappings({ containerPortRange: '8000-8001' }); + container.addPortMappings({ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPortRange: '8000-8001', + }); const service = new ecs.Ec2Service(stack, 'Service', { cluster, diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts index 307256a3a4a45..7ecdedfbb156e 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts @@ -2129,6 +2129,7 @@ describe('fargate service', () => { taskDefinition.addContainer('MainContainer', { image: ecs.ContainerImage.fromRegistry('hello'), portMappings: [{ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8000-8001', }], }); @@ -2260,6 +2261,7 @@ describe('fargate service', () => { taskDefinition.addContainer('MainContainer', { image: ecs.ContainerImage.fromRegistry('hello'), portMappings: [{ + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, containerPortRange: '8000-8001', }], }); From e0e9044ede64f379350388077344191070bcdf48 Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Fri, 18 Aug 2023 00:43:34 +0200 Subject: [PATCH 10/19] Improve the docblock for `containerPort` and `containerPortRange` --- packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts index 4d22d3c64feb2..6c8f178ad2cdc 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts @@ -1086,6 +1086,8 @@ export interface PortMapping { * * For more information, see hostPort. * Port mappings that are automatically assigned in this way do not count toward the 100 reserved ports limit of a container instance. + * + * If you want to expose a port range, you must specify CONTAINER_PORT_UNSET_VALUE as container port. */ readonly containerPort: number; @@ -1100,6 +1102,8 @@ export interface PortMapping { * - A port can only be included in one port mapping per container. * - You cannot specify overlapping port ranges. * - The first port in the range must be less than last port in the range. + * + * If you want to expose a single port, you must not set a range. */ readonly containerPortRange?: string; From 24069048631a0c0ca0382fe3eb40ba161e343aff Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Fri, 18 Aug 2023 01:26:38 +0200 Subject: [PATCH 11/19] Add test for case where `containerPortRange` value is not concrete --- .../aws-ecs/test/container-definition.test.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts index c9465a18abd7f..fb83a0cb4c37e 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts @@ -4,7 +4,7 @@ import * as s3 from '../../aws-s3'; import * as secretsmanager from '../../aws-secretsmanager'; import * as ssm from '../../aws-ssm'; import * as cdk from '../../core'; -import { Duration } from '../../core'; +import { Duration, Lazy } from '../../core'; import * as cxapi from '../../cx-api'; import * as ecs from '../lib'; import { AppProtocol } from '../lib'; @@ -163,6 +163,17 @@ describe('container definition', () => { }).toThrow('The containerPortRange must be a string in the format [start port]-[end port].'); }); + describe('throws when PortMapping.containerPortRange is not a concrete value', () => { + // GIVEN + const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { + containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPortRange: Lazy.string({ produce: () => '8080-8081' }), + }); + + // THEN + expect(() => portMap.validate()).toThrow('The value of containerPortRange must be concrete (no Tokens)'); + }); + describe('throws when PortMapping.containerPortRange is used with an unsupported network mode', () => { test('when network mode is Host', () => { // GIVEN From 078eec303d6f92ae3fcba66c2c216c346d599f9c Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Fri, 18 Aug 2023 01:51:50 +0200 Subject: [PATCH 12/19] Fix rendering of L1 construct for port mapping --- packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts index 6c8f178ad2cdc..3fb2346d5d7a6 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts @@ -1322,7 +1322,7 @@ export class AppProtocol { function renderPortMapping(pm: PortMapping): CfnTaskDefinition.PortMappingProperty { return { - containerPort: pm.containerPort, + containerPort: pm.containerPort !== CONTAINER_PORT_UNSET_VALUE ? pm.containerPort : undefined, containerPortRange: pm.containerPortRange, hostPort: pm.hostPort, protocol: pm.protocol || Protocol.TCP, From b6ea1a4351116e90dcdfe63cc830bc5ca49d5ece Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Fri, 18 Aug 2023 02:27:51 +0200 Subject: [PATCH 13/19] Fix typehint typo in README --- packages/aws-cdk-lib/aws-ecs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ecs/README.md b/packages/aws-cdk-lib/aws-ecs/README.md index d1bd25254abe0..984bca86f49b1 100644 --- a/packages/aws-cdk-lib/aws-ecs/README.md +++ b/packages/aws-cdk-lib/aws-ecs/README.md @@ -381,7 +381,7 @@ Docker recommends that you turn off the `docker-proxy` in the Docker daemon conf For more information, see [Issue #11185](https://github.com/moby/moby/issues/11185) on the GitHub website. ```ts -declare const container: ecs.ContainerDefintion; +declare const container: ecs.ContainerDefinition; container.addPortMappings({ containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, From 77d073e89bb44d985ff794520ed5f6759bee56a5 Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Fri, 18 Aug 2023 11:47:14 +0200 Subject: [PATCH 14/19] Inline class property to local scoped variable --- .../aws-ecs/test/fargate/integ.container-port-range.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts index 432436d1bd638..ea166d64a5913 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts @@ -4,13 +4,10 @@ import * as integ from '@aws-cdk/integ-tests-alpha'; import * as ecs from 'aws-cdk-lib/aws-ecs'; class EcsContainerPortRangeStack extends cdk.Stack { - readonly cluster: ecs.Cluster; - constructor(scope: constructs.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); - this.cluster = new ecs.Cluster(this, 'EcsCluster'); - + const cluster = new ecs.Cluster(this, 'EcsCluster'); const taskDefinition = new ecs.FargateTaskDefinition(this, 'FargateTaskDef'); taskDefinition.addContainer('web', { @@ -22,7 +19,7 @@ class EcsContainerPortRangeStack extends cdk.Stack { }); new ecs.FargateService(this, 'FargateService', { - cluster: this.cluster, + cluster, taskDefinition, }); } From 539f8107b8372a5c5ea5a95f49153d1454c1ce4d Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Fri, 18 Aug 2023 11:55:45 +0200 Subject: [PATCH 15/19] Use `test()` instead of `describe()` when possible --- packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts index fb83a0cb4c37e..974e8bd49c3e1 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts @@ -163,7 +163,7 @@ describe('container definition', () => { }).toThrow('The containerPortRange must be a string in the format [start port]-[end port].'); }); - describe('throws when PortMapping.containerPortRange is not a concrete value', () => { + test('throws when PortMapping.containerPortRange is not a concrete value', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, From c4e3278eaf08c370a7661a229d2d997a3d6cd1e9 Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Fri, 18 Aug 2023 13:14:05 +0200 Subject: [PATCH 16/19] Improve usage documentation for `containerPort` and `containerPortRange` --- packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts index 3fb2346d5d7a6..8d51ebdd4846a 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts @@ -1087,7 +1087,7 @@ export interface PortMapping { * For more information, see hostPort. * Port mappings that are automatically assigned in this way do not count toward the 100 reserved ports limit of a container instance. * - * If you want to expose a port range, you must specify CONTAINER_PORT_UNSET_VALUE as container port. + * If you want to expose a port range, you must specify `CONTAINER_PORT_UNSET_VALUE` as container port. */ readonly containerPort: number; @@ -1096,6 +1096,7 @@ export interface PortMapping { * * The following rules apply when you specify a `containerPortRange`: * + * - You must specify `CONTAINER_PORT_UNSET_VALUE` as `containerPort` * - You must use either the `bridge` network mode or the `awsvpc` network mode. * - The container instance must have at least version 1.67.0 of the container agent and at least version 1.67.0-1 of the `ecs-init` package * - You can specify a maximum of 100 port ranges per container. From 24fb02fea4cdf5ea1eb5c5426ff0ea699372e04a Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Fri, 18 Aug 2023 13:25:41 +0200 Subject: [PATCH 17/19] Rename the `CONTAINER_PORT_UNSET_VALUE` and move it --- .../fargate/integ.container-port-range.ts | 2 +- .../aws-ecs-patterns/test/ec2/l3s-v2.test.ts | 4 +- .../load-balanced-fargate-service-v2.test.ts | 4 +- .../aws-ecs/lib/base/task-definition.ts | 4 +- .../aws-ecs/lib/container-definition.ts | 18 ++++----- .../aws-ecs/test/container-definition.test.ts | 39 +++++++++---------- .../aws-ecs/test/ec2/ec2-service.test.ts | 4 +- .../test/fargate/fargate-service.test.ts | 4 +- 8 files changed, 39 insertions(+), 40 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts index ea166d64a5913..eff32a7285a4c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.ts @@ -13,7 +13,7 @@ class EcsContainerPortRangeStack extends cdk.Stack { taskDefinition.addContainer('web', { image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), portMappings: [{ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }], }); diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts b/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts index 7f2e5ad3f56f9..35d4ba589927b 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/test/ec2/l3s-v2.test.ts @@ -4,7 +4,6 @@ import { Certificate } from '../../../aws-certificatemanager'; import * as ec2 from '../../../aws-ec2'; import { MachineImage, Vpc } from '../../../aws-ec2'; import { - CONTAINER_PORT_UNSET_VALUE, AsgCapacityProvider, AwsLogDriver, Cluster, @@ -14,6 +13,7 @@ import { Protocol, PlacementStrategy, PlacementConstraint, + ContainerDefinition, } from '../../../aws-ecs'; import { ApplicationProtocol, SslPolicy } from '../../../aws-elasticloadbalancingv2'; import { CompositePrincipal, Role, ServicePrincipal } from '../../../aws-iam'; @@ -1803,7 +1803,7 @@ describe('When Network Load Balancer', () => { taskDefinition.addContainer('MainContainer', { image: ContainerImage.fromRegistry('test'), portMappings: [{ - containerPort: CONTAINER_PORT_UNSET_VALUE, + containerPort: ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }], }); diff --git a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts index 9227cca5ae1a5..07e369d2464de 100644 --- a/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts +++ b/packages/aws-cdk-lib/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts @@ -1,7 +1,7 @@ import { Match, Template } from '../../../assertions'; import { Vpc } from '../../../aws-ec2'; import * as ecs from '../../../aws-ecs'; -import { ContainerImage } from '../../../aws-ecs'; +import { ContainerDefinition, ContainerImage } from '../../../aws-ecs'; import { CompositePrincipal, Role, ServicePrincipal } from '../../../aws-iam'; import { Duration, Stack } from '../../../core'; import { ApplicationLoadBalancedFargateService, ApplicationMultipleTargetGroupsFargateService, NetworkLoadBalancedFargateService, NetworkMultipleTargetGroupsFargateService } from '../../lib'; @@ -848,7 +848,7 @@ describe('When Network Load Balancer', () => { taskDefinition.addContainer('MainContainer', { image: ecs.ContainerImage.fromRegistry('test'), portMappings: [{ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }], }); diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts index 67497368f3fdc..7d2b925408285 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts @@ -3,7 +3,7 @@ import { ImportedTaskDefinition } from './_imported-task-definition'; import * as ec2 from '../../../aws-ec2'; import * as iam from '../../../aws-iam'; import { IResource, Lazy, Names, PhysicalName, Resource } from '../../../core'; -import { CONTAINER_PORT_UNSET_VALUE, ContainerDefinition, ContainerDefinitionOptions, PortMapping, Protocol } from '../container-definition'; +import { ContainerDefinition, ContainerDefinitionOptions, PortMapping, Protocol } from '../container-definition'; import { CfnTaskDefinition } from '../ecs.generated'; import { FirelensLogRouter, FirelensLogRouterDefinitionOptions, FirelensLogRouterType, obtainDefaultFluentBitECRImage } from '../firelens-log-router'; import { AwsLogDriver } from '../log-drivers/aws-log-driver'; @@ -590,7 +590,7 @@ export class TaskDefinition extends TaskDefinitionBase { if (this.networkMode === NetworkMode.BRIDGE || this.networkMode === NetworkMode.NAT) { return EPHEMERAL_PORT_RANGE; } - if (portMapping.containerPort !== CONTAINER_PORT_UNSET_VALUE) { + if (portMapping.containerPort !== ContainerDefinition.CONTAINER_PORT_USE_RANGE) { return portMapping.protocol === Protocol.UDP ? ec2.Port.udp(portMapping.containerPort) : ec2.Port.tcp(portMapping.containerPort); } const [startPort, endPort] = portMapping.containerPortRange!.split('-', 2).map(v => Number(v)); diff --git a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts index 8d51ebdd4846a..5c9ebf3df039c 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/container-definition.ts @@ -10,8 +10,6 @@ import * as secretsmanager from '../../aws-secretsmanager'; import * as ssm from '../../aws-ssm'; import * as cdk from '../../core'; -export const CONTAINER_PORT_UNSET_VALUE = 0; - /** * Specify the secret's version id or version stage */ @@ -384,6 +382,8 @@ export interface ContainerDefinitionProps extends ContainerDefinitionOptions { * A container definition is used in a task definition to describe the containers that are launched as part of a task. */ export class ContainerDefinition extends Construct { + public static readonly CONTAINER_PORT_USE_RANGE = 0; + /** * The Linux-specific modifications that are applied to the container, such as Linux kernel capabilities. */ @@ -1087,7 +1087,7 @@ export interface PortMapping { * For more information, see hostPort. * Port mappings that are automatically assigned in this way do not count toward the 100 reserved ports limit of a container instance. * - * If you want to expose a port range, you must specify `CONTAINER_PORT_UNSET_VALUE` as container port. + * If you want to expose a port range, you must specify `CONTAINER_PORT_USE_RANGE` as container port. */ readonly containerPort: number; @@ -1096,7 +1096,7 @@ export interface PortMapping { * * The following rules apply when you specify a `containerPortRange`: * - * - You must specify `CONTAINER_PORT_UNSET_VALUE` as `containerPort` + * - You must specify `CONTAINER_PORT_USE_RANGE` as `containerPort` * - You must use either the `bridge` network mode or the `awsvpc` network mode. * - The container instance must have at least version 1.67.0 of the container agent and at least version 1.67.0-1 of the `ecs-init` package * - You can specify a maximum of 100 port ranges per container. @@ -1180,15 +1180,15 @@ export class PortMap { throw new Error('Port mapping name cannot be an empty string.'); } - if (this.portmapping.containerPort === CONTAINER_PORT_UNSET_VALUE && this.portmapping.containerPortRange === undefined) { - throw new Error(`The containerPortRange must be set when containerPort is equal to ${CONTAINER_PORT_UNSET_VALUE}`); + if (this.portmapping.containerPort === ContainerDefinition.CONTAINER_PORT_USE_RANGE && this.portmapping.containerPortRange === undefined) { + throw new Error(`The containerPortRange must be set when containerPort is equal to ${ContainerDefinition.CONTAINER_PORT_USE_RANGE}`); } - if (this.portmapping.containerPort !== CONTAINER_PORT_UNSET_VALUE && this.portmapping.containerPortRange !== undefined) { + if (this.portmapping.containerPort !== ContainerDefinition.CONTAINER_PORT_USE_RANGE && this.portmapping.containerPortRange !== undefined) { throw new Error('Cannot set "containerPort" and "containerPortRange" at the same time.'); } - if (this.portmapping.containerPort !== CONTAINER_PORT_UNSET_VALUE) { + if (this.portmapping.containerPort !== ContainerDefinition.CONTAINER_PORT_USE_RANGE) { if ((this.networkmode === NetworkMode.AWS_VPC || this.networkmode === NetworkMode.HOST) && this.portmapping.hostPort !== undefined && this.portmapping.hostPort !== this.portmapping.containerPort) { throw new Error('The host port must be left out or must be the same as the container port for AwsVpc or Host network mode.'); @@ -1323,7 +1323,7 @@ export class AppProtocol { function renderPortMapping(pm: PortMapping): CfnTaskDefinition.PortMappingProperty { return { - containerPort: pm.containerPort !== CONTAINER_PORT_UNSET_VALUE ? pm.containerPort : undefined, + containerPort: pm.containerPort !== ContainerDefinition.CONTAINER_PORT_USE_RANGE ? pm.containerPort : undefined, containerPortRange: pm.containerPortRange, hostPort: pm.hostPort, protocol: pm.protocol || Protocol.TCP, diff --git a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts index 974e8bd49c3e1..f537852fae364 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/container-definition.test.ts @@ -7,7 +7,6 @@ import * as cdk from '../../core'; import { Duration, Lazy } from '../../core'; import * as cxapi from '../../cx-api'; import * as ecs from '../lib'; -import { AppProtocol } from '../lib'; describe('container definition', () => { describe('When creating a Task Definition', () => { @@ -53,11 +52,11 @@ describe('container definition', () => { test('throws when PortMapping.containerPortRange is not set and PortMapping.containerPort is set to 0', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, }); // THEN - expect(() => portMap.validate()).toThrow(`The containerPortRange must be set when containerPort is equal to ${ecs.CONTAINER_PORT_UNSET_VALUE}`); + expect(() => portMap.validate()).toThrow(`The containerPortRange must be set when containerPort is equal to ${ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE}`); }); test('throws when PortMapping.containerPortRange is used along with PortMapping.containerPort', () => { @@ -98,7 +97,7 @@ describe('container definition', () => { test('throws when PortMapping.containerPortRange is used along with PortMapping.hostPort', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', hostPort: 8080, }); @@ -110,7 +109,7 @@ describe('container definition', () => { test('throws when PortMapping.containerPortRange string is invalid', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '-', }); @@ -119,7 +118,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: 'foo-', }); @@ -128,7 +127,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '-bar', }); @@ -137,7 +136,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: 'foo-bar', }); @@ -146,7 +145,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '808a-8081', }); @@ -155,7 +154,7 @@ describe('container definition', () => { expect(() => { const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-808a', }); @@ -166,7 +165,7 @@ describe('container definition', () => { test('throws when PortMapping.containerPortRange is not a concrete value', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: Lazy.string({ produce: () => '8080-8081' }), }); @@ -178,7 +177,7 @@ describe('container definition', () => { test('when network mode is Host', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.HOST, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }); @@ -189,7 +188,7 @@ describe('container definition', () => { test('when network mode is NAT', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.NAT, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }); @@ -200,7 +199,7 @@ describe('container definition', () => { test('when network mode is None', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.NONE, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }); @@ -213,7 +212,7 @@ describe('container definition', () => { test('when network mode is AwsVpc', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.AWS_VPC, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }); @@ -224,7 +223,7 @@ describe('container definition', () => { test('when network mode is Bridge', () => { // GIVEN const portMap = new ecs.PortMap(ecs.NetworkMode.BRIDGE, { - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }); @@ -399,7 +398,7 @@ describe('container definition', () => { container.addPortMappings( { containerPort: 443, - appProtocol: AppProtocol.grpc, + appProtocol: ecs.AppProtocol.grpc, }, ); }).toThrow(/Service connect-related port mapping field 'appProtocol' cannot be set without 'name'/); @@ -894,7 +893,7 @@ describe('container definition', () => { image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'), memoryLimitMiB: 2048, portMappings: [{ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }], }); @@ -1003,7 +1002,7 @@ describe('container definition', () => { image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'), memoryLimitMiB: 2048, portMappings: [{ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }], }); @@ -1070,7 +1069,7 @@ describe('container definition', () => { image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'), memoryLimitMiB: 2048, portMappings: [{ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }], }); diff --git a/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts index 8ad224410b94c..a289b75162b20 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts @@ -2715,7 +2715,7 @@ describe('ec2 service', () => { image: ecs.ContainerImage.fromRegistry('hello'), }); container.addPortMappings({ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8000-8001', }); @@ -2992,7 +2992,7 @@ describe('ec2 service', () => { image: ecs.ContainerImage.fromRegistry('hello'), }); container.addPortMappings({ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8000-8001', }); diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts index 7ecdedfbb156e..4470d8c785ea3 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts @@ -2129,7 +2129,7 @@ describe('fargate service', () => { taskDefinition.addContainer('MainContainer', { image: ecs.ContainerImage.fromRegistry('hello'), portMappings: [{ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8000-8001', }], }); @@ -2261,7 +2261,7 @@ describe('fargate service', () => { taskDefinition.addContainer('MainContainer', { image: ecs.ContainerImage.fromRegistry('hello'), portMappings: [{ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8000-8001', }], }); From f5089a50cad654cd08b65727ffc783bcf248d0dc Mon Sep 17 00:00:00 2001 From: Stefano Arlandini Date: Fri, 18 Aug 2023 14:37:58 +0200 Subject: [PATCH 18/19] Update typo in README (again) --- packages/aws-cdk-lib/aws-ecs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ecs/README.md b/packages/aws-cdk-lib/aws-ecs/README.md index 984bca86f49b1..2e3f4fab8f761 100644 --- a/packages/aws-cdk-lib/aws-ecs/README.md +++ b/packages/aws-cdk-lib/aws-ecs/README.md @@ -384,7 +384,7 @@ For more information, see [Issue #11185](https://github.com/moby/moby/issues/111 declare const container: ecs.ContainerDefinition; container.addPortMappings({ - containerPort: ecs.CONTAINER_PORT_UNSET_VALUE, + containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE, containerPortRange: '8080-8081', }); ``` From 5544967c02652e89d18629f5492ce8b555010402 Mon Sep 17 00:00:00 2001 From: Rico Hermans Date: Mon, 21 Aug 2023 11:43:17 +0200 Subject: [PATCH 19/19] Update aws-ecs-container-port-range.template.json --- .../aws-ecs-container-port-range.template.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.template.json index 0b25b5ce9d7f4..2bf83498fb6e4 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.container-port-range.js.snapshot/aws-ecs-container-port-range.template.json @@ -503,7 +503,7 @@ "Arn" ] }, - "Runtime": "nodejs16.x", + "Runtime": "nodejs18.x", "Description": "Lambda function for removing all inbound/outbound rules from the VPC default security group" }, "DependsOn": [ @@ -658,4 +658,4 @@ ] } } -} \ No newline at end of file +}