Skip to content

Commit 468cb07

Browse files
committed
fixes from PR feedback
1 parent cdf365e commit 468cb07

10 files changed

Lines changed: 222 additions & 398 deletions

File tree

packages/@aws-cdk/aws-codedeploy/README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -357,17 +357,19 @@ To create a new CodeDeploy Deployment Group that deploys to a ECS service:
357357
```ts
358358
declare const myApplication: codedeploy.EcsApplication;
359359
declare const service: ecs.FargateService;
360-
declare const targetGroup = elbv2.ITargetGroup;
361-
declare const listener = elbv2.IApplicationListener;
360+
declare const blueTargetGroup = elbv2.ITargetGroup;
361+
declare const greenTargetGroup = elbv2.ITargetGroup;
362+
declare const prodListener = elbv2.IApplicationListener;
362363

363364
const deploymentGroup = new codedeploy.EcsDeploymentGroup(this, 'BlueGreenDeployment', {
364365
application: myApplication, // optional property: one will be created for you if not provided
365366
services: [
366367
service
367368
],
368-
prodTrafficRoute: {
369-
listener,
370-
targetGroup,
369+
blueGreenDeploymentConfiguration: {
370+
prodListener,
371+
blueTargetGroup,
372+
greenTargetGroup,
371373
}
372374
deploymentConfig: codedeploy.EcsDeploymentConfig.CANARY_10_PERCENT_5_MINUTES,
373375
});

packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,8 @@ export interface EcsDeploymentConfigProps {
3737
* The configuration that specifies how traffic is shifed from one
3838
* Amazon ECS task set to another during an Amazon ECS deployment.
3939
*
40-
* @default a name will be auto-generated
4140
*/
42-
readonly trafficRoutingConfig?: ITrafficRoutingConfig;
41+
readonly trafficRoutingConfig: ITrafficRoutingConfig;
4342

4443
}
4544

@@ -72,7 +71,7 @@ export class EcsDeploymentConfig extends cdk.Resource implements IEcsDeploymentC
7271
public readonly deploymentConfigName: string;
7372
public readonly deploymentConfigArn: string;
7473

75-
constructor(scope: Construct, id: string, props: EcsDeploymentConfigProps = {}) {
74+
constructor(scope: Construct, id: string, props: EcsDeploymentConfigProps) {
7675
super(scope, id, {
7776
physicalName: props.deploymentConfigName,
7877
});

packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-group.ts

Lines changed: 46 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
22
import * as ecs from '@aws-cdk/aws-ecs';
33
import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2';
4-
import { ApplicationTargetGroup, CfnTargetGroup, NetworkTargetGroup } from '@aws-cdk/aws-elasticloadbalancingv2';
54
import * as iam from '@aws-cdk/aws-iam';
65
import * as cdk from '@aws-cdk/core';
76
import { Construct } from 'constructs';
@@ -45,47 +44,6 @@ export interface IEcsDeploymentGroup extends cdk.IResource {
4544
readonly deploymentConfig: IEcsDeploymentConfig;
4645
}
4746

48-
/**
49-
* Details about an ECS service and its corresponding ECS cluster
50-
*/
51-
export type EcsService = ecs.Ec2Service | ecs.FargateService | ecs.ExternalService;
52-
53-
/**
54-
* A route with an ELB listener and target group.
55-
*/
56-
export interface EcsTrafficRoute {
57-
/**
58-
* The load balancer for the traffic route.
59-
*
60-
* @default - blank if empty on test traffic route. Required for prod traffic route.
61-
*/
62-
readonly listener: elbv2.IApplicationListener | elbv2.INetworkListener | undefined;
63-
/**
64-
* The target group for the traffic route.
65-
*/
66-
readonly targetGroup: elbv2.ITargetGroup;
67-
}
68-
69-
/**
70-
* Configuration for the blue/green CodeDeploy deployment.
71-
*/
72-
export interface EcsBlueGreenDeploymentConfig {
73-
/**
74-
* The time to wait for a ContinueDeployment after the deployment is ready.
75-
* Leave blank to automatically continue once the deployment is ready.
76-
*
77-
* @default - automatically continue once deployment is ready
78-
*/
79-
readonly waitTimeForContinueDeployment?: cdk.Duration;
80-
/**
81-
* The time to wait before terminating old tasks.
82-
* Leave blank to keep the old tasks alive and deregistered from the load balancer.
83-
*
84-
* @default - keep the old tasks alive and deregistered from the load balancer.
85-
*/
86-
readonly waitTimeForTermination?: cdk.Duration;
87-
}
88-
8947
/**
9048
* Construction properties for {@link EcsDeploymentGroup}.
9149
*/
@@ -95,22 +53,7 @@ export interface EcsDeploymentGroupProps {
9553
*
9654
* [disable-awslint:ref-via-interface] because the service needs to have the deploy controller changed to Code Deploy
9755
*/
98-
readonly services: EcsService[];
99-
100-
/**
101-
* The reference to the production traffic group for the deployment.
102-
* Leave blank to disable traffic shifting
103-
*
104-
* @default - Traffic shifting will be disabled
105-
*/
106-
readonly prodTrafficRoute?: EcsTrafficRoute;
107-
108-
/**
109-
* The reference to the test traffic group for the deployment.
110-
*
111-
* @default - One will be created for you
112-
*/
113-
readonly testTrafficRoute?: EcsTrafficRoute;
56+
readonly services: ecs.BaseService[];
11457

11558
/**
11659
* The reference to the CodeDeploy ECS Application
@@ -171,11 +114,8 @@ export interface EcsDeploymentGroupProps {
171114

172115
/**
173116
* The blue/green deployment configuration
174-
*
175-
* @default - default BlueGreenDeploymentConfiguration which is configured to
176-
* continue deployment when ready and terminate old instances immediately.
177117
*/
178-
readonly blueGreenDeploymentConfiguration?: EcsBlueGreenDeploymentConfig;
118+
readonly blueGreenDeploymentConfiguration: EcsBlueGreenDeploymentConfig;
179119
}
180120

181121
/**
@@ -220,7 +160,7 @@ export class EcsDeploymentGroup extends cdk.Resource implements IEcsDeploymentGr
220160
this.role = props.role || new iam.Role(this, 'ServiceRole', {
221161
assumedBy: new iam.ServicePrincipal('codedeploy.amazonaws.com'),
222162
});
223-
this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSCodeDeployRoleForECS'));
163+
this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSCodeDeployRoleForECSLimited'));
224164
this.deploymentConfig = props.deploymentConfig || EcsDeploymentConfig.ALL_AT_ONCE;
225165

226166
// Change ECS Service deployment controller to codedeploy
@@ -233,69 +173,26 @@ export class EcsDeploymentGroup extends cdk.Resource implements IEcsDeploymentGr
233173
service.node.addDependency(service.taskDefinition);
234174
}
235175

236-
// create a testTrafficRoute if missing
237-
let testTrafficRoute = props.testTrafficRoute;
238-
if (props.prodTrafficRoute && !testTrafficRoute) {
239-
const prodCfnTargetGroup = props.prodTrafficRoute.targetGroup.node.defaultChild as CfnTargetGroup;
240-
const testCfnTargetGroup = new CfnTargetGroup(this, 'TestTargetGroup', {
241-
healthCheckEnabled: prodCfnTargetGroup.healthCheckEnabled,
242-
healthCheckIntervalSeconds: prodCfnTargetGroup.healthCheckIntervalSeconds,
243-
healthCheckPath: prodCfnTargetGroup.healthCheckPath,
244-
healthCheckPort: prodCfnTargetGroup.healthCheckPort,
245-
healthCheckProtocol: prodCfnTargetGroup.healthCheckProtocol,
246-
healthCheckTimeoutSeconds: prodCfnTargetGroup.healthCheckTimeoutSeconds,
247-
healthyThresholdCount: prodCfnTargetGroup.healthyThresholdCount,
248-
ipAddressType: prodCfnTargetGroup.ipAddressType,
249-
matcher: prodCfnTargetGroup.matcher,
250-
name: prodCfnTargetGroup.name,
251-
port: prodCfnTargetGroup.port,
252-
protocol: prodCfnTargetGroup.protocol,
253-
protocolVersion: prodCfnTargetGroup.protocolVersion,
254-
targetGroupAttributes: prodCfnTargetGroup.targetGroupAttributes,
255-
targets: prodCfnTargetGroup.targets,
256-
targetType: prodCfnTargetGroup.targetType,
257-
unhealthyThresholdCount: prodCfnTargetGroup.unhealthyThresholdCount,
258-
vpcId: prodCfnTargetGroup.vpcId,
259-
});
260-
if (props.prodTrafficRoute.targetGroup instanceof ApplicationTargetGroup) {
261-
testTrafficRoute = {
262-
targetGroup: ApplicationTargetGroup.fromTargetGroupAttributes(this, 'TestTargetGroupRef', {
263-
targetGroupArn: testCfnTargetGroup.ref,
264-
}),
265-
listener: undefined,
266-
};
267-
} else if (props.prodTrafficRoute.targetGroup instanceof NetworkTargetGroup) {
268-
testTrafficRoute = {
269-
targetGroup: NetworkTargetGroup.fromTargetGroupAttributes(this, 'TestTargetGroupRef', {
270-
targetGroupArn: testCfnTargetGroup.ref,
271-
}),
272-
listener: undefined,
273-
};
274-
}
275-
}
276-
277176
const resource = new CfnDeploymentGroup(this, 'Resource', {
278177
applicationName: this.application.applicationName,
279178
serviceRoleArn: this.role.roleArn,
280179
deploymentGroupName: this.physicalName,
281180
deploymentConfigName: this.deploymentConfig.deploymentConfigName,
282-
deploymentStyle: (props.prodTrafficRoute && testTrafficRoute) ? {
181+
deploymentStyle: {
283182
deploymentType: 'BLUE_GREEN',
284183
deploymentOption: 'WITH_TRAFFIC_CONTROL',
285-
} : {
286-
deploymentType: 'IN_PLACE',
287-
deploymentOption: 'WITHOUT_TRAFFIC_CONTROL',
288184
},
289185
ecsServices: props.services.map(s => {
290186
return {
291187
clusterName: s.cluster.clusterName,
292188
serviceName: s.serviceName,
293189
};
294190
}),
295-
loadBalancerInfo: (props.prodTrafficRoute && testTrafficRoute) ?
296-
renderLoadBalancerInfo(props.prodTrafficRoute, testTrafficRoute) : undefined,
191+
loadBalancerInfo: cdk.Lazy.any({
192+
produce: () => renderLoadBalancerInfo(props.blueGreenDeploymentConfiguration),
193+
}),
297194
blueGreenDeploymentConfiguration: cdk.Lazy.any({
298-
produce: () => renderBlueGreenDeploymentConfiguration(props.blueGreenDeploymentConfiguration),
195+
produce: () => renderBlueGreenDeploymentConfiguration( props.blueGreenDeploymentConfiguration ),
299196
}),
300197
alarmConfiguration: cdk.Lazy.any({ produce: () => renderAlarmConfiguration(this.alarms, props.ignorePollAlarmsFailure) }),
301198
autoRollbackConfiguration: cdk.Lazy.any({ produce: () => renderAutoRollbackConfiguration(this.alarms, props.autoRollback) }),
@@ -370,43 +267,50 @@ class ImportedEcsDeploymentGroup extends cdk.Resource implements IEcsDeploymentG
370267
}
371268

372269
/**
373-
* {@link Duration} of infinite days
270+
* Configuraiton for blue green deployments.
374271
*/
375-
//export const DurationInfinity = cdk.Duration.days(Number.POSITIVE_INFINITY);
272+
export interface EcsBlueGreenDeploymentConfig {
273+
/**
274+
* The reference to the production listener for the deployment.
275+
*/
276+
readonly prodListener: elbv2.IApplicationListener | elbv2.INetworkListener;
277+
278+
/**
279+
* The reference to the test listener for the deployment.
280+
*
281+
* @default - No test listener will be used
282+
*/
283+
readonly testListener?: elbv2.IApplicationListener | elbv2.INetworkListener;
376284

377-
export class EcsBlueGreenDeploymentConfig {
378-
static DurationInfinity = cdk.Duration.days(Number.POSITIVE_INFINITY);
285+
/**
286+
* The target group for the blue task set.
287+
*/
288+
readonly blueTargetGroup: elbv2.ITargetGroup;
289+
290+
/**
291+
* The target group for the green task set.
292+
*/
293+
readonly greenTargetGroup: elbv2.ITargetGroup;
379294

380295
/**
381296
* The time to wait for a ContinueDeployment after the deployment is ready.
382297
*
383-
* @default - undefined - automatically continue once the deployment is ready.
298+
* @default 0 - automatically continue once the deployment is ready.
384299
*/
385300
readonly waitTimeForContinueDeployment?: cdk.Duration;
301+
386302
/**
387303
* The time to wait before terminating old tasks.
388-
* Set to {@link DurationInfinity} to keep the old tasks alive and deregistered from the load balancer.
389304
*
390-
* @default - undefined - terminate tasks immediately.
305+
* @default 0 - terminate immediately
391306
*/
392307
readonly waitTimeForTermination?: cdk.Duration;
393308
}
394309

395310
function renderBlueGreenDeploymentConfiguration(
396-
blueGreenDeploymentConfig?: EcsBlueGreenDeploymentConfig,
311+
blueGreenDeploymentConfig: EcsBlueGreenDeploymentConfig,
397312
): CfnDeploymentGroup.BlueGreenDeploymentConfigurationProperty {
398313
let deploymentReadyOption: CfnDeploymentGroup.DeploymentReadyOptionProperty;
399-
if (!blueGreenDeploymentConfig) {
400-
return {
401-
deploymentReadyOption: {
402-
actionOnTimeout: 'CONTINUE_DEPLOYMENT',
403-
},
404-
terminateBlueInstancesOnDeploymentSuccess: {
405-
action: 'TERMINATE',
406-
terminationWaitTimeInMinutes: 0,
407-
},
408-
};
409-
}
410314
if (blueGreenDeploymentConfig.waitTimeForContinueDeployment) {
411315
deploymentReadyOption = {
412316
actionOnTimeout: 'STOP_DEPLOYMENT',
@@ -417,38 +321,31 @@ function renderBlueGreenDeploymentConfiguration(
417321
actionOnTimeout: 'CONTINUE_DEPLOYMENT',
418322
};
419323
}
420-
let terminateBlueInstancesOnDeploymentSuccess: CfnDeploymentGroup.BlueInstanceTerminationOptionProperty;
421-
if (!blueGreenDeploymentConfig.waitTimeForTermination
422-
|| blueGreenDeploymentConfig.waitTimeForTermination === EcsBlueGreenDeploymentConfig.DurationInfinity) {
423-
terminateBlueInstancesOnDeploymentSuccess = {
424-
action: 'KEEP_ALIVE',
425-
};
426-
} else {
427-
terminateBlueInstancesOnDeploymentSuccess = {
428-
action: 'TERMINATE',
429-
terminationWaitTimeInMinutes: blueGreenDeploymentConfig.waitTimeForTermination.toMinutes(),
430-
};
431-
}
324+
let terminateBlueInstancesOnDeploymentSuccess = {
325+
action: 'TERMINATE',
326+
terminationWaitTimeInMinutes: blueGreenDeploymentConfig.waitTimeForTermination ?
327+
blueGreenDeploymentConfig.waitTimeForTermination.toMinutes() : 0,
328+
};
432329
return {
433330
deploymentReadyOption,
434331
terminateBlueInstancesOnDeploymentSuccess,
435332
};
436333
}
334+
437335
function renderLoadBalancerInfo(
438-
prodTrafficRoute: EcsTrafficRoute,
439-
testTrafficRoute: EcsTrafficRoute,
336+
blueGreenDeploymentConfig: EcsBlueGreenDeploymentConfig,
440337
): CfnDeploymentGroup.LoadBalancerInfoProperty {
441338
const targetGroupPairInfo: CfnDeploymentGroup.TargetGroupPairInfoProperty = {
442339
targetGroups: [{
443-
name: prodTrafficRoute.targetGroup.targetGroupName,
340+
name: blueGreenDeploymentConfig.blueTargetGroup.targetGroupName,
444341
}, {
445-
name: testTrafficRoute.targetGroup.targetGroupName,
342+
name: blueGreenDeploymentConfig.greenTargetGroup.targetGroupName,
446343
}],
447344
prodTrafficRoute: {
448-
listenerArns: [prodTrafficRoute.listener!.listenerArn],
345+
listenerArns: [blueGreenDeploymentConfig.prodListener.listenerArn],
449346
},
450-
testTrafficRoute: testTrafficRoute.listener ? {
451-
listenerArns: [testTrafficRoute.listener.listenerArn],
347+
testTrafficRoute: blueGreenDeploymentConfig.testListener? {
348+
listenerArns: [blueGreenDeploymentConfig.testListener.listenerArn],
452349
} : undefined,
453350
};
454351
return {

packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ describe('CodeDeploy DeploymentConfig', () => {
99
test('can be created without props', () => {
1010
const stack = new cdk.Stack();
1111

12-
new codedeploy.EcsDeploymentConfig(stack, 'DeploymentConfig', {});
12+
new codedeploy.EcsDeploymentConfig(stack, 'DeploymentConfig', {
13+
trafficRoutingConfig: new AllAtOnceTrafficRoutingConfig(),
14+
});
1315

1416
Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', {
1517
'ComputePlatform': 'ECS',
@@ -21,6 +23,7 @@ describe('CodeDeploy DeploymentConfig', () => {
2123

2224
new codedeploy.EcsDeploymentConfig(stack, 'DeploymentConfig', {
2325
deploymentConfigName: 'AAA',
26+
trafficRoutingConfig: new AllAtOnceTrafficRoutingConfig(),
2427
});
2528

2629
Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', {
@@ -95,6 +98,7 @@ describe('CodeDeploy DeploymentConfig', () => {
9598
const stack = new cdk.Stack(app);
9699
new codedeploy.EcsDeploymentConfig(stack, 'DeploymentConfig', {
97100
deploymentConfigName: 'a'.repeat(101),
101+
trafficRoutingConfig: new AllAtOnceTrafficRoutingConfig(),
98102
});
99103

100104
expect(() => app.synth()).toThrow(`Deployment config name: "${'a'.repeat(101)}" can be a max of 100 characters.`);
@@ -105,6 +109,7 @@ describe('CodeDeploy DeploymentConfig', () => {
105109
const stack = new cdk.Stack(app);
106110
new codedeploy.EcsDeploymentConfig(stack, 'DeploymentConfig', {
107111
deploymentConfigName: 'my name',
112+
trafficRoutingConfig: new AllAtOnceTrafficRoutingConfig(),
108113
});
109114

110115
expect(() => app.synth()).toThrow('Deployment config name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).');

packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-group.integ.snapshot/aws-cdk-codedeploy-ecs-dg.assets.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
22
"version": "21.0.0",
33
"files": {
4-
"463c83b6ad9e00f692decfa77520fc4cd05b77508abc10649196e4a8bdaabf2a": {
4+
"6fc64dbac5f1d7b309630619bfc02b3cb7cdcc232e7f9270016f783d2ebb5d5b": {
55
"source": {
66
"path": "aws-cdk-codedeploy-ecs-dg.template.json",
77
"packaging": "file"
88
},
99
"destinations": {
1010
"current_account-current_region": {
1111
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12-
"objectKey": "463c83b6ad9e00f692decfa77520fc4cd05b77508abc10649196e4a8bdaabf2a.json",
12+
"objectKey": "6fc64dbac5f1d7b309630619bfc02b3cb7cdcc232e7f9270016f783d2ebb5d5b.json",
1313
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
1414
}
1515
}

0 commit comments

Comments
 (0)