From 9eb6173ba56e96c739bfd84ca719de98901518b9 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Fri, 31 May 2019 15:43:58 +0200 Subject: [PATCH 1/3] feat(event-targets): add support for fargate/awsvpc tasks The target is "enhanced" using an AwsCustomResource calling `CloudWatchEvents.putTargets`. Fix wrong reference to container name in `containerOverrides` (must be `name`). BREAKING CHANGE: `targets.EcsEc2Task` renamed to `targets.EcsTask` --- .../lib/ecs/scheduled-ecs-task.ts | 2 +- packages/@aws-cdk/aws-ecs/README.md | 2 +- .../aws-events-targets/lib/ecs-ec2-task.ts | 125 -- .../lib/ecs-task-properties.ts | 4 +- .../aws-events-targets/lib/ecs-task.ts | 166 +++ .../@aws-cdk/aws-events-targets/lib/index.ts | 2 +- .../@aws-cdk/aws-events-targets/package.json | 4 +- .../test/ecs/ec2-event-rule-target.test.ts | 57 - .../test/ecs/event-rule-target.test.ts | 165 +++ ...=> integ.event-ec2-task.lit.expected.json} | 4 +- ...ask.lit.ts => integ.event-ec2-task.lit.ts} | 6 +- .../integ.event-fargate-task.expected.json | 1111 +++++++++++++++++ .../test/ecs/integ.event-fargate-task.ts | 51 + 13 files changed, 1506 insertions(+), 193 deletions(-) delete mode 100644 packages/@aws-cdk/aws-events-targets/lib/ecs-ec2-task.ts create mode 100644 packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts delete mode 100644 packages/@aws-cdk/aws-events-targets/test/ecs/ec2-event-rule-target.test.ts create mode 100644 packages/@aws-cdk/aws-events-targets/test/ecs/event-rule-target.test.ts rename packages/@aws-cdk/aws-events-targets/test/ecs/{integ.event-task.lit.expected.json => integ.event-ec2-task.lit.expected.json} (99%) rename packages/@aws-cdk/aws-events-targets/test/ecs/{integ.event-task.lit.ts => integ.event-ec2-task.lit.ts} (92%) create mode 100644 packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json create mode 100644 packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.ts diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts index cce306f82589f..6f28112031fab 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts @@ -98,7 +98,7 @@ export class ScheduledEc2Task extends cdk.Construct { }); // Use Ec2TaskEventRuleTarget as the target of the EventRule - const eventRuleTarget = new eventsTargets.EcsEc2Task( { + const eventRuleTarget = new eventsTargets.EcsTask( { cluster: props.cluster, taskDefinition, taskCount: props.desiredTaskCount diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 881765847dce0..15bf2cb0fa3f8 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -287,7 +287,7 @@ you can configure on your instances. ## Integration with CloudWatch Events To start an Amazon ECS task on an Amazon EC2-backed Cluster, instantiate an -`@aws-cdk/aws-events-targets.EcsEc2Task` instead of an `Ec2Service`: +`@aws-cdk/aws-events-targets.EcsTask` instead of an `Ec2Service`: ```ts import targets = require('@aws-cdk/aws-events-targets'); diff --git a/packages/@aws-cdk/aws-events-targets/lib/ecs-ec2-task.ts b/packages/@aws-cdk/aws-events-targets/lib/ecs-ec2-task.ts deleted file mode 100644 index 2fd38253fb8dd..0000000000000 --- a/packages/@aws-cdk/aws-events-targets/lib/ecs-ec2-task.ts +++ /dev/null @@ -1,125 +0,0 @@ -import ec2 = require('@aws-cdk/aws-ec2'); -import ecs = require('@aws-cdk/aws-ecs'); -import events = require ('@aws-cdk/aws-events'); -import iam = require('@aws-cdk/aws-iam'); -import { Construct, Token } from '@aws-cdk/cdk'; -import { ContainerOverride } from './ecs-task-properties'; -import { singletonEventRole } from './util'; - -/** - * Properties to define an EC2 Event Task - */ -export interface EcsEc2TaskProps { - /** - * Cluster where service will be deployed - */ - readonly cluster: ecs.ICluster; - - /** - * Task Definition of the task that should be started - */ - readonly taskDefinition: ecs.TaskDefinition; - - /** - * How many tasks should be started when this event is triggered - * - * @default 1 - */ - readonly taskCount?: number; - - /** - * Container setting overrides - * - * Key is the name of the container to override, value is the - * values you want to override. - */ - readonly containerOverrides?: ContainerOverride[]; - - /** - * In what subnets to place the task's ENIs - * - * (Only applicable in case the TaskDefinition is configured for AwsVpc networking) - * - * @default Private subnets - */ - readonly subnetSelection?: ec2.SubnetSelection; - - /** - * Existing security group to use for the task's ENIs - * - * (Only applicable in case the TaskDefinition is configured for AwsVpc networking) - * - * @default A new security group is created - */ - readonly securityGroup?: ec2.ISecurityGroup; -} - -/** - * Start a service on an EC2 cluster - */ -export class EcsEc2Task implements events.IRuleTarget { - private readonly cluster: ecs.ICluster; - private readonly taskDefinition: ecs.TaskDefinition; - private readonly taskCount: number; - - constructor(private readonly props: EcsEc2TaskProps) { - if (!props.taskDefinition.isEc2Compatible) { - throw new Error('Supplied TaskDefinition is not configured for compatibility with EC2'); - } - - this.cluster = props.cluster; - this.taskDefinition = props.taskDefinition; - this.taskCount = props.taskCount !== undefined ? props.taskCount : 1; - } - - /** - * Allows using containers as target of CloudWatch events - */ - public bind(rule: events.IRule): events.RuleTargetProperties { - const policyStatements = [new iam.PolicyStatement() - .addAction('ecs:RunTask') - .addResource(this.taskDefinition.taskDefinitionArn) - .addCondition('ArnEquals', { "ecs:cluster": this.cluster.clusterArn }) - ]; - - // If it so happens that a Task Execution Role was created for the TaskDefinition, - // then the CloudWatch Events Role must have permissions to pass it (otherwise it doesn't). - // - // It never needs permissions to the Task Role. - if (this.taskDefinition.executionRole !== undefined) { - policyStatements.push(new iam.PolicyStatement() - .addAction('iam:PassRole') - .addResource(this.taskDefinition.executionRole.roleArn)); - } - - return { - id: this.taskDefinition.node.id + ' on ' + this.cluster.node.id, - arn: this.cluster.clusterArn, - role: singletonEventRole(this.taskDefinition, policyStatements), - ecsParameters: { - taskCount: this.taskCount, - taskDefinitionArn: this.taskDefinition.taskDefinitionArn - }, - input: events.RuleTargetInput.fromObject({ - containerOverrides: this.props.containerOverrides, - networkConfiguration: this.renderNetworkConfiguration(rule as events.Rule), - }) - }; - } - - private renderNetworkConfiguration(scope: Construct) { - if (this.props.taskDefinition.networkMode !== ecs.NetworkMode.AwsVpc) { - return undefined; - } - - const subnetSelection = this.props.subnetSelection || { subnetType: ec2.SubnetType.Private }; - const securityGroup = this.props.securityGroup || new ec2.SecurityGroup(scope, 'SecurityGroup', { vpc: this.props.cluster.vpc }); - - return { - awsvpcConfiguration: { - subnets: this.props.cluster.vpc.selectSubnets(subnetSelection).subnetIds, - securityGroups: new Token(() => [securityGroup.securityGroupId]), - } - }; - } -} diff --git a/packages/@aws-cdk/aws-events-targets/lib/ecs-task-properties.ts b/packages/@aws-cdk/aws-events-targets/lib/ecs-task-properties.ts index 11deb4cd8d8c5..ab28bc293332a 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/ecs-task-properties.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/ecs-task-properties.ts @@ -2,7 +2,7 @@ export interface ContainerOverride { /** * Name of the container inside the task definition */ - readonly containerName: string; + readonly name: string; /** * Command to run inside the container @@ -55,4 +55,4 @@ export interface TaskEnvironmentVariable { * Exactly one of `value` and `valuePath` must be specified. */ readonly value: string; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts b/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts new file mode 100644 index 0000000000000..5645f8c76f818 --- /dev/null +++ b/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts @@ -0,0 +1,166 @@ +import cloudformation = require('@aws-cdk/aws-cloudformation'); +import ec2 = require('@aws-cdk/aws-ec2'); +import ecs = require('@aws-cdk/aws-ecs'); +import events = require ('@aws-cdk/aws-events'); +import iam = require('@aws-cdk/aws-iam'); +import { ContainerOverride } from './ecs-task-properties'; +import { singletonEventRole } from './util'; + +/** + * Properties to define an ECS Event Task + */ +export interface EcsTaskProps { + /** + * Cluster where service will be deployed + */ + readonly cluster: ecs.ICluster; + + /** + * Task Definition of the task that should be started + */ + readonly taskDefinition: ecs.TaskDefinition; + + /** + * How many tasks should be started when this event is triggered + * + * @default 1 + */ + readonly taskCount?: number; + + /** + * Container setting overrides + * + * Key is the name of the container to override, value is the + * values you want to override. + */ + readonly containerOverrides?: ContainerOverride[]; + + /** + * In what subnets to place the task's ENIs + * + * (Only applicable in case the TaskDefinition is configured for AwsVpc networking) + * + * @default Private subnets + */ + readonly subnetSelection?: ec2.SubnetSelection; + + /** + * Existing security group to use for the task's ENIs + * + * (Only applicable in case the TaskDefinition is configured for AwsVpc networking) + * + * @default A new security group is created + */ + readonly securityGroup?: ec2.ISecurityGroup; +} + +/** + * Start a task on an ECS cluster + */ +export class EcsTask implements events.IRuleTarget { + public readonly securityGroup?: ec2.ISecurityGroup; + private readonly cluster: ecs.ICluster; + private readonly taskDefinition: ecs.TaskDefinition; + private readonly taskCount: number; + + constructor(private readonly props: EcsTaskProps) { + this.cluster = props.cluster; + this.taskDefinition = props.taskDefinition; + this.taskCount = props.taskCount !== undefined ? props.taskCount : 1; + + if (this.taskDefinition.networkMode === ecs.NetworkMode.AwsVpc) { + this.securityGroup = props.securityGroup || new ec2.SecurityGroup(this.taskDefinition, 'SecurityGroup', { vpc: this.props.cluster.vpc }); + } + } + + /** + * Allows using tasks as target of CloudWatch events + */ + public bind(rule: events.IRule): events.RuleTargetProperties { + const policyStatements = [new iam.PolicyStatement() + .addAction('ecs:RunTask') + .addResource(this.taskDefinition.taskDefinitionArn) + .addCondition('ArnEquals', { "ecs:cluster": this.cluster.clusterArn }) + ]; + + // If it so happens that a Task Execution Role was created for the TaskDefinition, + // then the CloudWatch Events Role must have permissions to pass it (otherwise it doesn't). + if (this.taskDefinition.executionRole !== undefined) { + policyStatements.push(new iam.PolicyStatement() + .addAction('iam:PassRole') + .addResource(this.taskDefinition.executionRole.roleArn)); + } + + // For Fargate task we need permission to pass the task role. + if (this.taskDefinition.isFargateCompatible) { + policyStatements.push(new iam.PolicyStatement() + .addAction('iam:PassRole') + .addResource(this.taskDefinition.taskRole.roleArn)); + } + + const id = this.taskDefinition.node.id + '-on-' + this.cluster.node.id; + const arn = this.cluster.clusterArn; + const role = singletonEventRole(this.taskDefinition, policyStatements); + const input = { containerOverrides: this.props.containerOverrides }; + const taskCount = this.taskCount; + const taskDefinitionArn = this.taskDefinition.taskDefinitionArn; + + // Use a custom resource to "enhance" the target with network configuration + // when using awsvpc network mode. + if (this.taskDefinition.networkMode === ecs.NetworkMode.AwsVpc) { + const subnetSelection = this.props.subnetSelection || { subnetType: ec2.SubnetType.Private }; + const assignPublicIp = subnetSelection.subnetType === ec2.SubnetType.Private ? 'DISABLED' : 'ENABLED'; + + new cloudformation.AwsCustomResource(this.taskDefinition, 'PutTargets', { + onUpdate: { // We don't need an onDelete here because the target will be owned by CF anyway + service: 'CloudWatchEvents', + apiVersion: '2015-10-07', + action: 'putTargets', + parameters: { + Rule: this.taskDefinition.node.stack.parseArn(rule.ruleArn).resourceName, + Targets: [ + { + Arn: arn, + Id: id, + EcsParameters: { + TaskDefinitionArn: taskDefinitionArn, + LaunchType: this.taskDefinition.isEc2Compatible ? 'EC2' : 'FARGATE', + NetworkConfiguration: { + awsvpcConfiguration: { + Subnets: this.props.cluster.vpc.selectSubnets(subnetSelection).subnetIds, + AssignPublicIp: assignPublicIp, + SecurityGroups: this.securityGroup && [this.securityGroup.securityGroupId], + } + }, + TaskCount: taskCount, + }, + Input: JSON.stringify(input), + RoleArn: role.roleArn + } + ] + }, + physicalResourceId: id, + }, + policyStatements: [ // Cannot use automatic policy statements because we need iam:PassRole + new iam.PolicyStatement() + .addAction('events:PutTargets') + .addResource(rule.ruleArn), + new iam.PolicyStatement() + .addAction('iam:PassRole') + .addResource(role.roleArn) + ] + }); + } + + return { + id, + arn, + role, + ecsParameters: { + taskCount, + taskDefinitionArn + }, + input: events.RuleTargetInput.fromObject(input) + }; + } +} diff --git a/packages/@aws-cdk/aws-events-targets/lib/index.ts b/packages/@aws-cdk/aws-events-targets/lib/index.ts index cd31a43468f19..fc30dbbc2b6ee 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/index.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/index.ts @@ -3,5 +3,5 @@ export * from './sns'; export * from './codebuild'; export * from './lambda'; export * from './ecs-task-properties'; -export * from './ecs-ec2-task'; +export * from './ecs-task'; export * from './state-machine'; diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json index c7f8ae7ee0d23..2ca767af15089 100644 --- a/packages/@aws-cdk/aws-events-targets/package.json +++ b/packages/@aws-cdk/aws-events-targets/package.json @@ -80,6 +80,7 @@ "pkglint": "^0.33.0" }, "dependencies": { + "@aws-cdk/aws-cloudformation": "^0.33.0", "@aws-cdk/aws-codebuild": "^0.33.0", "@aws-cdk/aws-codepipeline": "^0.33.0", "@aws-cdk/aws-ec2": "^0.33.0", @@ -93,6 +94,7 @@ }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { + "@aws-cdk/aws-cloudformation": "^0.33.0", "@aws-cdk/aws-codebuild": "^0.33.0", "@aws-cdk/aws-codepipeline": "^0.33.0", "@aws-cdk/aws-ec2": "^0.33.0", @@ -107,4 +109,4 @@ "engines": { "node": ">= 8.10.0" } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/ec2-event-rule-target.test.ts b/packages/@aws-cdk/aws-events-targets/test/ecs/ec2-event-rule-target.test.ts deleted file mode 100644 index 3e9c62822ea55..0000000000000 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/ec2-event-rule-target.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import '@aws-cdk/assert/jest'; -import ec2 = require('@aws-cdk/aws-ec2'); -import ecs = require('@aws-cdk/aws-ecs'); -import events = require('@aws-cdk/aws-events'); -import cdk = require('@aws-cdk/cdk'); -import targets = require('../../lib'); - -test("Can use EC2 taskdef as EventRule target", () => { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'Vpc', { maxAZs: 1 }); - const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); - cluster.addCapacity('DefaultAutoScalingGroup', { - instanceType: new ec2.InstanceType('t2.micro') - }); - - const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); - taskDefinition.addContainer('TheContainer', { - image: ecs.ContainerImage.fromRegistry('henk'), - memoryLimitMiB: 256 - }); - - const rule = new events.Rule(stack, 'Rule', { - scheduleExpression: 'rate(1 minute)', - }); - - // WHEN - rule.addTarget(new targets.EcsEc2Task({ - cluster, - taskDefinition, - taskCount: 1, - containerOverrides: [{ - containerName: 'TheContainer', - command: ['echo', events.EventField.fromPath('$.detail.event')], - }] - })); - - // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { - Targets: [ - { - Arn: { "Fn::GetAtt": ["EcsCluster97242B84", "Arn"] }, - EcsParameters: { - TaskCount: 1, - TaskDefinitionArn: { Ref: "TaskDef54694570" } - }, - InputTransformer: { - InputPathsMap: { - f1: "$.detail.event" - }, - InputTemplate: "{\"containerOverrides\":[{\"containerName\":\"TheContainer\",\"command\":[\"echo\",]}]}" - }, - RoleArn: { "Fn::GetAtt": ["TaskDefEventsRoleFB3B67B8", "Arn"] } - } - ] - }); -}); diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/event-rule-target.test.ts b/packages/@aws-cdk/aws-events-targets/test/ecs/event-rule-target.test.ts new file mode 100644 index 0000000000000..799160966c97f --- /dev/null +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/event-rule-target.test.ts @@ -0,0 +1,165 @@ +import '@aws-cdk/assert/jest'; +import ec2 = require('@aws-cdk/aws-ec2'); +import ecs = require('@aws-cdk/aws-ecs'); +import events = require('@aws-cdk/aws-events'); +import cdk = require('@aws-cdk/cdk'); +import targets = require('../../lib'); + +test("Can use EC2 taskdef as EventRule target", () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc', { maxAZs: 1 }); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { + instanceType: new ec2.InstanceType('t2.micro') + }); + + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + taskDefinition.addContainer('TheContainer', { + image: ecs.ContainerImage.fromRegistry('henk'), + memoryLimitMiB: 256 + }); + + const rule = new events.Rule(stack, 'Rule', { + scheduleExpression: 'rate(1 minute)', + }); + + // WHEN + rule.addTarget(new targets.EcsTask({ + cluster, + taskDefinition, + taskCount: 1, + containerOverrides: [{ + name: 'TheContainer', + command: ['echo', events.EventField.fromPath('$.detail.event')], + }] + })); + + // THEN + expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Targets: [ + { + Arn: { "Fn::GetAtt": ["EcsCluster97242B84", "Arn"] }, + EcsParameters: { + TaskCount: 1, + TaskDefinitionArn: { Ref: "TaskDef54694570" } + }, + InputTransformer: { + InputPathsMap: { + f1: "$.detail.event" + }, + InputTemplate: "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"command\":[\"echo\",]}]}" + }, + RoleArn: { "Fn::GetAtt": ["TaskDefEventsRoleFB3B67B8", "Arn"] } + } + ] + }); +}); + +test("Can use Fargate taskdef as EventRule target", () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc', { maxAZs: 1 }); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'TaskDef'); + taskDefinition.addContainer('TheContainer', { + image: ecs.ContainerImage.fromRegistry('henk'), + }); + + const rule = new events.Rule(stack, 'Rule', { + scheduleExpression: 'rate(1 minute)', + }); + + // WHEN + rule.addTarget(new targets.EcsTask({ + cluster, + taskDefinition, + taskCount: 1, + containerOverrides: [{ + name: 'TheContainer', + command: ['echo', events.EventField.fromPath('$.detail.event')], + }] + })); + + // THEN + expect(stack).toHaveResourceLike('Custom::AWS', { + Update: { + service: "CloudWatchEvents", + apiVersion: "2015-10-07", + action: "putTargets", + parameters: { + Rule: { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "/", + { + "Fn::Select": [ + 5, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "Rule4C995B7F", + "Arn" + ] + } + ] + } + ] + } + ] + } + ] + }, + Targets: [ + { + Arn: { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + }, + Id: "TaskDef-on-EcsCluster", + EcsParameters: { + TaskDefinitionArn: { + Ref: "TaskDef54694570" + }, + LaunchType: "FARGATE", + NetworkConfiguration: { + awsvpcConfiguration: { + Subnets: [ + { + Ref: "VpcPrivateSubnet1Subnet536B997A" + } + ], + AssignPublicIp: "DISABLED", + SecurityGroups: [ + { + "Fn::GetAtt": [ + "TaskDefSecurityGroupD50E7CF0", + "GroupId" + ] + } + ] + } + }, + TaskCount: 1 + }, + Input: "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"command\":[\"echo\",\"$.detail.event\"]}]}", + RoleArn: { + "Fn::GetAtt": [ + "TaskDefEventsRoleFB3B67B8", + "Arn" + ] + } + } + ] + }, + physicalResourceId: "TaskDef-on-EcsCluster" + } + }); +}); diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-task.lit.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json similarity index 99% rename from packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-task.lit.expected.json rename to packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json index 6ee0890f74067..c9f77ff04f967 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-task.lit.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json @@ -1053,7 +1053,7 @@ } }, "Id": "TaskDef-on-EcsCluster", - "Input": "{\"containerOverrides\":[{\"containerName\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}]}", + "Input": "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}]}", "RoleArn": { "Fn::GetAtt": [ "TaskDefEventsRoleFB3B67B8", @@ -1153,4 +1153,4 @@ "Description": "Artifact hash for asset \"aws-ecs-integ-ecs/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-task.lit.ts b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.ts similarity index 92% rename from packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-task.lit.ts rename to packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.ts index a759421d8c047..3ab4099a4039c 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-task.lit.ts +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.ts @@ -35,13 +35,13 @@ class EventStack extends cdk.Stack { scheduleExpression: 'rate(1 minute)', }); - // Use EcsEc2Task as the target of the Rule - rule.addTarget(new targets.EcsEc2Task({ + // Use EcsTask as the target of the Rule + rule.addTarget(new targets.EcsTask({ cluster, taskDefinition, taskCount: 1, containerOverrides: [{ - containerName: 'TheContainer', + name: 'TheContainer', environment: [ { name: 'I_WAS_TRIGGERED', value: 'From CloudWatch Events' } ] diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json new file mode 100644 index 0000000000000..efe8c37519fa8 --- /dev/null +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json @@ -0,0 +1,1111 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-fargate/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/17", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-fargate/Vpc/PublicSubnet1" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-fargate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc" + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-fargate/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/17", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-fargate/Vpc/PrivateSubnet1" + }, + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-fargate/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-fargate/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "EcsCluster97242B84": { + "Type": "AWS::ECS::Cluster" + }, + "TaskDefTaskRole1EDB4A67": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "ecs-tasks.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDef54694570": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + ":", + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::GetAtt": [ + "EventImageAdoptRepositoryDFAAC242", + "RepositoryName" + ] + } + ] + ] + } + ] + } + ] + }, + ".dkr.ecr.", + { + "Fn::Select": [ + 3, + { + "Fn::Split": [ + ":", + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::GetAtt": [ + "EventImageAdoptRepositoryDFAAC242", + "RepositoryName" + ] + } + ] + ] + } + ] + } + ] + }, + ".amazonaws.com/", + { + "Fn::GetAtt": [ + "EventImageAdoptRepositoryDFAAC242", + "RepositoryName" + ] + }, + "@sha256:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "EventImageImageNameE972A8B1" + } + ] + } + ] + } + ] + ] + }, + "Links": [], + "LogConfiguration": { + "LogDriver": "awslogs", + "Options": { + "awslogs-group": { + "Ref": "TaskLoggingLogGroupC7E938D4" + }, + "awslogs-stream-prefix": "EventDemo", + "awslogs-region": { + "Ref": "AWS::Region" + } + } + }, + "MountPoints": [], + "Name": "TheContainer", + "PortMappings": [], + "Ulimits": [], + "VolumesFrom": [] + } + ], + "Cpu": "256", + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "TaskDefExecutionRoleB4775C97", + "Arn" + ] + }, + "Family": "awsecsintegfargateTaskDef8878AF94", + "Memory": "512", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + }, + "Volumes": [] + } + }, + "TaskDefExecutionRoleB4775C97": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "ecs-tasks.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDefExecutionRoleDefaultPolicy0DBB737A": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::GetAtt": [ + "EventImageAdoptRepositoryDFAAC242", + "RepositoryName" + ] + } + ] + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TaskLoggingLogGroupC7E938D4", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TaskDefExecutionRoleDefaultPolicy0DBB737A", + "Roles": [ + { + "Ref": "TaskDefExecutionRoleB4775C97" + } + ] + } + }, + "TaskDefSecurityGroupD50E7CF0": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-integ-fargate/TaskDef/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "TaskDefEventsRoleFB3B67B8": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "events.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDefEventsRoleDefaultPolicyA124E85B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ecs:RunTask", + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": { + "Ref": "TaskDef54694570" + } + }, + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TaskDefExecutionRoleB4775C97", + "Arn" + ] + } + }, + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TaskDefEventsRoleDefaultPolicyA124E85B", + "Roles": [ + { + "Ref": "TaskDefEventsRoleFB3B67B8" + } + ] + } + }, + "TaskDefPutTargetsF699575F": { + "Type": "Custom::AWS", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "AWS679f53fac002430cb0da5b7982bd22872D164C4C", + "Arn" + ] + }, + "Create": { + "service": "CloudWatchEvents", + "apiVersion": "2015-10-07", + "action": "putTargets", + "parameters": { + "Rule": { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "/", + { + "Fn::Select": [ + 5, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "Rule4C995B7F", + "Arn" + ] + } + ] + } + ] + } + ] + } + ] + }, + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + }, + "Id": "TaskDef-on-EcsCluster", + "EcsParameters": { + "TaskDefinitionArn": { + "Ref": "TaskDef54694570" + }, + "LaunchType": "FARGATE", + "NetworkConfiguration": { + "awsvpcConfiguration": { + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + ], + "AssignPublicIp": "DISABLED", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "TaskDefSecurityGroupD50E7CF0", + "GroupId" + ] + } + ] + } + }, + "TaskCount": 1 + }, + "Input": "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}]}", + "RoleArn": { + "Fn::GetAtt": [ + "TaskDefEventsRoleFB3B67B8", + "Arn" + ] + } + } + ] + }, + "physicalResourceId": "TaskDef-on-EcsCluster" + }, + "Update": { + "service": "CloudWatchEvents", + "apiVersion": "2015-10-07", + "action": "putTargets", + "parameters": { + "Rule": { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "/", + { + "Fn::Select": [ + 5, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "Rule4C995B7F", + "Arn" + ] + } + ] + } + ] + } + ] + } + ] + }, + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + }, + "Id": "TaskDef-on-EcsCluster", + "EcsParameters": { + "TaskDefinitionArn": { + "Ref": "TaskDef54694570" + }, + "LaunchType": "FARGATE", + "NetworkConfiguration": { + "awsvpcConfiguration": { + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + ], + "AssignPublicIp": "DISABLED", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "TaskDefSecurityGroupD50E7CF0", + "GroupId" + ] + } + ] + } + }, + "TaskCount": 1 + }, + "Input": "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}]}", + "RoleArn": { + "Fn::GetAtt": [ + "TaskDefEventsRoleFB3B67B8", + "Arn" + ] + } + } + ] + }, + "physicalResourceId": "TaskDef-on-EcsCluster" + } + } + }, + "EventImageAdoptRepositoryDFAAC242": { + "Type": "Custom::ECRAdoptedRepository", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9", + "Arn" + ] + }, + "RepositoryName": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "EventImageImageNameE972A8B1" + } + ] + } + ] + } + }, + "DependsOn": [ + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" + ] + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "lambda.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecr:GetRepositoryPolicy", + "ecr:SetRepositoryPolicy", + "ecr:DeleteRepository", + "ecr:ListImages", + "ecr:BatchDeleteImage" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "EventImageImageNameE972A8B1" + } + ] + } + ] + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", + "Roles": [ + { + "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" + } + ] + } + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c52BE89E9": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "handler.handler", + "Role": { + "Fn::GetAtt": [ + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Timeout": 300 + }, + "DependsOn": [ + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleDefaultPolicy6BC8737C", + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17" + ] + }, + "TaskLoggingLogGroupC7E938D4": { + "Type": "AWS::Logs::LogGroup", + "DeletionPolicy": "Retain" + }, + "Rule4C995B7F": { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(1 minute)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + }, + "EcsParameters": { + "TaskCount": 1, + "TaskDefinitionArn": { + "Ref": "TaskDef54694570" + } + }, + "Id": "TaskDef-on-EcsCluster", + "Input": "{\"containerOverrides\":[{\"name\":\"TheContainer\",\"environment\":[{\"name\":\"I_WAS_TRIGGERED\",\"value\":\"From CloudWatch Events\"}]}]}", + "RoleArn": { + "Fn::GetAtt": [ + "TaskDefEventsRoleFB3B67B8", + "Arn" + ] + } + } + ] + } + }, + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "lambda.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "events:PutTargets", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Rule4C995B7F", + "Arn" + ] + } + }, + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TaskDefEventsRoleFB3B67B8", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E", + "Roles": [ + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + } + ] + } + }, + "AWS679f53fac002430cb0da5b7982bd22872D164C4C": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287CodeS3BucketF55839B6" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287CodeS3VersionKey3C45B02F" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287CodeS3VersionKey3C45B02F" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2", + "Arn" + ] + }, + "Runtime": "nodejs10.x" + }, + "DependsOn": [ + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E", + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + ] + } + }, + "Parameters": { + "EventImageImageNameE972A8B1": { + "Type": "String", + "Description": "ECR repository name and tag asset \"aws-ecs-integ-fargate/EventImage\"" + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { + "Type": "String", + "Description": "S3 bucket for asset \"aws-ecs-integ-fargate/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { + "Type": "String", + "Description": "S3 key for asset version \"aws-ecs-integ-fargate/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + }, + "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeArtifactHash8BCBAA49": { + "Type": "String", + "Description": "Artifact hash for asset \"aws-ecs-integ-fargate/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + }, + "AWS679f53fac002430cb0da5b7982bd2287CodeS3BucketF55839B6": { + "Type": "String", + "Description": "S3 bucket for asset \"aws-ecs-integ-fargate/AWS679f53fac002430cb0da5b7982bd2287/Code\"" + }, + "AWS679f53fac002430cb0da5b7982bd2287CodeS3VersionKey3C45B02F": { + "Type": "String", + "Description": "S3 key for asset version \"aws-ecs-integ-fargate/AWS679f53fac002430cb0da5b7982bd2287/Code\"" + }, + "AWS679f53fac002430cb0da5b7982bd2287CodeArtifactHash49FACC2E": { + "Type": "String", + "Description": "Artifact hash for asset \"aws-ecs-integ-fargate/AWS679f53fac002430cb0da5b7982bd2287/Code\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.ts b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.ts new file mode 100644 index 0000000000000..953e67c2f5743 --- /dev/null +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.ts @@ -0,0 +1,51 @@ +import ec2 = require('@aws-cdk/aws-ec2'); +import ecs = require('@aws-cdk/aws-ecs'); +import events = require('@aws-cdk/aws-events'); +import cdk = require('@aws-cdk/cdk'); +import targets = require('../../lib'); + +import path = require('path'); + +const app = new cdk.App(); + +class EventStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + const vpc = new ec2.Vpc(this, 'Vpc', { maxAZs: 1 }); + + const cluster = new ecs.Cluster(this, 'EcsCluster', { vpc }); + + /// !show + // Create a Task Definition for the container to start + const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef'); + taskDefinition.addContainer('TheContainer', { + image: ecs.ContainerImage.fromAsset(this, 'EventImage', { + directory: path.resolve(__dirname, 'eventhandler-image') + }), + logging: new ecs.AwsLogDriver(this, 'TaskLogging', { streamPrefix: 'EventDemo' }) + }); + + // A rule that describes the event trigger (in this case a scheduled run) + const rule = new events.Rule(this, 'Rule', { + scheduleExpression: 'rate(1 minute)', + }); + + // Use EcsTask as the target of the Rule + rule.addTarget(new targets.EcsTask({ + cluster, + taskDefinition, + taskCount: 1, + containerOverrides: [{ + name: 'TheContainer', + environment: [ + { name: 'I_WAS_TRIGGERED', value: 'From CloudWatch Events' } + ] + }] + })); + /// !hide + } +} + +new EventStack(app, 'aws-ecs-integ-fargate'); +app.run(); From c5c409136ae19b0b655a29dbe4879ca45dfd236c Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Mon, 3 Jun 2019 11:55:53 +0200 Subject: [PATCH 2/3] containerName --- .../@aws-cdk/aws-events-targets/lib/ecs-task-properties.ts | 2 +- packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts | 4 +++- .../aws-events-targets/test/ecs/event-rule-target.test.ts | 4 ++-- .../aws-events-targets/test/ecs/integ.event-ec2-task.lit.ts | 2 +- .../aws-events-targets/test/ecs/integ.event-fargate-task.ts | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-events-targets/lib/ecs-task-properties.ts b/packages/@aws-cdk/aws-events-targets/lib/ecs-task-properties.ts index ab28bc293332a..4c37724b00dc4 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/ecs-task-properties.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/ecs-task-properties.ts @@ -2,7 +2,7 @@ export interface ContainerOverride { /** * Name of the container inside the task definition */ - readonly name: string; + readonly containerName: string; /** * Command to run inside the container diff --git a/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts b/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts index 5645f8c76f818..4eb3db329a038 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts @@ -101,7 +101,9 @@ export class EcsTask implements events.IRuleTarget { const id = this.taskDefinition.node.id + '-on-' + this.cluster.node.id; const arn = this.cluster.clusterArn; const role = singletonEventRole(this.taskDefinition, policyStatements); - const input = { containerOverrides: this.props.containerOverrides }; + const containerOverrides = this.props.containerOverrides && this.props.containerOverrides + .map(({ containerName, ...overrides }) => ({ name: containerName, ...overrides })); + const input = { containerOverrides }; const taskCount = this.taskCount; const taskDefinitionArn = this.taskDefinition.taskDefinitionArn; diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/event-rule-target.test.ts b/packages/@aws-cdk/aws-events-targets/test/ecs/event-rule-target.test.ts index 799160966c97f..01a2fb6c707a2 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/event-rule-target.test.ts +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/event-rule-target.test.ts @@ -30,7 +30,7 @@ test("Can use EC2 taskdef as EventRule target", () => { taskDefinition, taskCount: 1, containerOverrides: [{ - name: 'TheContainer', + containerName: 'TheContainer', command: ['echo', events.EventField.fromPath('$.detail.event')], }] })); @@ -77,7 +77,7 @@ test("Can use Fargate taskdef as EventRule target", () => { taskDefinition, taskCount: 1, containerOverrides: [{ - name: 'TheContainer', + containerName: 'TheContainer', command: ['echo', events.EventField.fromPath('$.detail.event')], }] })); diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.ts b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.ts index 3ab4099a4039c..ab9499e8abbdc 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.ts +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.ts @@ -41,7 +41,7 @@ class EventStack extends cdk.Stack { taskDefinition, taskCount: 1, containerOverrides: [{ - name: 'TheContainer', + containerName: 'TheContainer', environment: [ { name: 'I_WAS_TRIGGERED', value: 'From CloudWatch Events' } ] diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.ts b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.ts index 953e67c2f5743..730e134e53e18 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.ts +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.ts @@ -37,7 +37,7 @@ class EventStack extends cdk.Stack { taskDefinition, taskCount: 1, containerOverrides: [{ - name: 'TheContainer', + containerName: 'TheContainer', environment: [ { name: 'I_WAS_TRIGGERED', value: 'From CloudWatch Events' } ] From c3c5924564fab3ebbb7daa5059d0acc8d784db2b Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Mon, 3 Jun 2019 12:03:27 +0200 Subject: [PATCH 3/3] improve comment --- packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts b/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts index 4eb3db329a038..9cbd4f7230138 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts @@ -114,7 +114,9 @@ export class EcsTask implements events.IRuleTarget { const assignPublicIp = subnetSelection.subnetType === ec2.SubnetType.Private ? 'DISABLED' : 'ENABLED'; new cloudformation.AwsCustomResource(this.taskDefinition, 'PutTargets', { - onUpdate: { // We don't need an onDelete here because the target will be owned by CF anyway + // `onCreate´ defaults to `onUpdate` and we don't need an `onDelete` here + // because the rule/target will be owned by CF anyway. + onUpdate: { service: 'CloudWatchEvents', apiVersion: '2015-10-07', action: 'putTargets',