Skip to content

Commit 27b26b1

Browse files
authored
feat(aws-codedeploy): add support for setting CloudWatch alarms on a server Deployment Group. (#926)
1 parent d251a46 commit 27b26b1

File tree

7 files changed

+167
-37
lines changed

7 files changed

+167
-37
lines changed

packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ export class Alarm extends Construct {
121121
*/
122122
public readonly alarmArn: string;
123123

124+
/**
125+
* Name of this alarm.
126+
*/
127+
public readonly alarmName: string;
128+
124129
/**
125130
* The metric object this alarm was based on
126131
*/
@@ -163,6 +168,7 @@ export class Alarm extends Construct {
163168
});
164169

165170
this.alarmArn = alarm.alarmArn;
171+
this.alarmName = alarm.alarmName;
166172
this.metric = props.metric;
167173
this.annotation = {
168174
// tslint:disable-next-line:max-line-length

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'CodeDeployDe
5353
'key2': ['v3'],
5454
},
5555
),
56+
// CloudWatch alarms
57+
alarms: [
58+
new cloudwatch.Alarm(/* ... */),
59+
],
60+
// whether to ignore failure to fetch the status of alarms from CloudWatch
61+
// default: false
62+
ignorePollAlarmsFailure: false,
5663
});
5764
```
5865

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import autoscaling = require("@aws-cdk/aws-autoscaling");
2+
import cloudwatch = require("@aws-cdk/aws-cloudwatch");
23
import codedeploylb = require("@aws-cdk/aws-codedeploy-api");
34
import ec2 = require("@aws-cdk/aws-ec2");
45
import iam = require('@aws-cdk/aws-iam');
@@ -167,6 +168,8 @@ export interface ServerDeploymentGroupProps {
167168
/**
168169
* The auto-scaling groups belonging to this Deployment Group.
169170
*
171+
* Auto-scaling groups can also be added after the Deployment Group is created using the {@link #addAutoScalingGroup} method.
172+
*
170173
* @default []
171174
*/
172175
autoScalingGroups?: autoscaling.AutoScalingGroup[];
@@ -189,7 +192,7 @@ export interface ServerDeploymentGroupProps {
189192
*/
190193
loadBalancer?: codedeploylb.ILoadBalancer;
191194

192-
/*
195+
/**
193196
* All EC2 instances matching the given set of tags when a deployment occurs will be added to this Deployment Group.
194197
*
195198
* @default no additional EC2 instances will be added to the Deployment Group
@@ -202,6 +205,25 @@ export interface ServerDeploymentGroupProps {
202205
* @default no additional on-premise instances will be added to the Deployment Group
203206
*/
204207
onPremiseInstanceTags?: InstanceTagSet;
208+
209+
/**
210+
* The CloudWatch alarms associated with this Deployment Group.
211+
* CodeDeploy will stop (and optionally roll back)
212+
* a deployment if during it any of the alarms trigger.
213+
*
214+
* Alarms can also be added after the Deployment Group is created using the {@link #addAlarm} method.
215+
*
216+
* @default []
217+
* @see https://docs.aws.amazon.com/codedeploy/latest/userguide/monitoring-create-alarms.html
218+
*/
219+
alarms?: cloudwatch.Alarm[];
220+
221+
/**
222+
* Whether to continue a deployment even if fetching the alarm status from CloudWatch failed.
223+
*
224+
* @default false
225+
*/
226+
ignorePollAlarmsFailure?: boolean;
205227
}
206228

207229
/**
@@ -216,6 +238,7 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
216238
private readonly _autoScalingGroups: autoscaling.AutoScalingGroup[];
217239
private readonly installAgent: boolean;
218240
private readonly codeDeployBucket: s3.BucketRef;
241+
private readonly alarms: cloudwatch.Alarm[];
219242

220243
constructor(parent: cdk.Construct, id: string, props: ServerDeploymentGroupProps = {}) {
221244
super(parent, id, props.deploymentConfig);
@@ -237,6 +260,8 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
237260
this.addCodeDeployAgentInstallUserData(asg);
238261
}
239262

263+
this.alarms = props.alarms || [];
264+
240265
const resource = new cloudformation.DeploymentGroupResource(this, 'Resource', {
241266
applicationName: this.application.applicationName,
242267
deploymentGroupName: props.deploymentGroupName,
@@ -255,18 +280,33 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
255280
},
256281
ec2TagSet: this.ec2TagSet(props.ec2InstanceTags),
257282
onPremisesTagSet: this.onPremiseTagSet(props.onPremiseInstanceTags),
283+
alarmConfiguration: new cdk.Token(() => this.renderAlarmConfiguration(props.ignorePollAlarmsFailure)),
258284
});
259285

260286
this.deploymentGroupName = resource.deploymentGroupName;
261287
this.deploymentGroupArn = deploymentGroupName2Arn(this.application.applicationName,
262288
this.deploymentGroupName);
263289
}
264290

291+
/**
292+
* Adds an additional auto-scaling group to this Deployment Group.
293+
*
294+
* @param asg the auto-scaling group to add to this Deployment Group
295+
*/
265296
public addAutoScalingGroup(asg: autoscaling.AutoScalingGroup): void {
266297
this._autoScalingGroups.push(asg);
267298
this.addCodeDeployAgentInstallUserData(asg);
268299
}
269300

301+
/**
302+
* Associates an additional alarm with this Deployment Group.
303+
*
304+
* @param alarm the alarm to associate with this Deployment Group
305+
*/
306+
public addAlarm(alarm: cloudwatch.Alarm): void {
307+
this.alarms.push(alarm);
308+
}
309+
270310
public get autoScalingGroups(): autoscaling.AutoScalingGroup[] | undefined {
271311
return this._autoScalingGroups.slice();
272312
}
@@ -404,6 +444,17 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
404444
}
405445
return tagsInGroup;
406446
}
447+
448+
private renderAlarmConfiguration(ignorePollAlarmFailure?: boolean):
449+
cloudformation.DeploymentGroupResource.AlarmConfigurationProperty | undefined {
450+
return this.alarms.length === 0
451+
? undefined
452+
: {
453+
alarms: this.alarms.map(a => ({ name: a.alarmName })),
454+
enabled: true,
455+
ignorePollAlarmFailure,
456+
};
457+
}
407458
}
408459

409460
function deploymentGroupName2Arn(applicationName: string, deploymentGroupName: string): string {

packages/@aws-cdk/aws-codedeploy/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"@aws-cdk/aws-autoscaling": "^0.12.0",
6666
"@aws-cdk/aws-codedeploy-api": "^0.12.0",
6767
"@aws-cdk/aws-codepipeline-api": "^0.12.0",
68+
"@aws-cdk/aws-cloudwatch": "^0.12.0",
6869
"@aws-cdk/aws-iam": "^0.12.0",
6970
"@aws-cdk/aws-s3": "^0.12.0",
7071
"@aws-cdk/cdk": "^0.12.0"

packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.expected.json

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@
5757
}
5858
}
5959
},
60+
"VPCPublicSubnet1DefaultRoute91CEF279": {
61+
"Type": "AWS::EC2::Route",
62+
"Properties": {
63+
"RouteTableId": {
64+
"Ref": "VPCPublicSubnet1RouteTableFEE4B781"
65+
},
66+
"DestinationCidrBlock": "0.0.0.0/0",
67+
"GatewayId": {
68+
"Ref": "VPCIGWB7E252D3"
69+
}
70+
}
71+
},
6072
"VPCPublicSubnet1EIP6AD938E8": {
6173
"Type": "AWS::EC2::EIP",
6274
"Properties": {
@@ -83,18 +95,6 @@
8395
]
8496
}
8597
},
86-
"VPCPublicSubnet1DefaultRoute91CEF279": {
87-
"Type": "AWS::EC2::Route",
88-
"Properties": {
89-
"RouteTableId": {
90-
"Ref": "VPCPublicSubnet1RouteTableFEE4B781"
91-
},
92-
"DestinationCidrBlock": "0.0.0.0/0",
93-
"GatewayId": {
94-
"Ref": "VPCIGWB7E252D3"
95-
}
96-
}
97-
},
9898
"VPCPublicSubnet2Subnet74179F39": {
9999
"Type": "AWS::EC2::Subnet",
100100
"Properties": {
@@ -137,6 +137,18 @@
137137
}
138138
}
139139
},
140+
"VPCPublicSubnet2DefaultRouteB7481BBA": {
141+
"Type": "AWS::EC2::Route",
142+
"Properties": {
143+
"RouteTableId": {
144+
"Ref": "VPCPublicSubnet2RouteTable6F1A15F1"
145+
},
146+
"DestinationCidrBlock": "0.0.0.0/0",
147+
"GatewayId": {
148+
"Ref": "VPCIGWB7E252D3"
149+
}
150+
}
151+
},
140152
"VPCPublicSubnet2EIP4947BC00": {
141153
"Type": "AWS::EC2::EIP",
142154
"Properties": {
@@ -163,18 +175,6 @@
163175
]
164176
}
165177
},
166-
"VPCPublicSubnet2DefaultRouteB7481BBA": {
167-
"Type": "AWS::EC2::Route",
168-
"Properties": {
169-
"RouteTableId": {
170-
"Ref": "VPCPublicSubnet2RouteTable6F1A15F1"
171-
},
172-
"DestinationCidrBlock": "0.0.0.0/0",
173-
"GatewayId": {
174-
"Ref": "VPCIGWB7E252D3"
175-
}
176-
}
177-
},
178178
"VPCPublicSubnet3Subnet631C5E25": {
179179
"Type": "AWS::EC2::Subnet",
180180
"Properties": {
@@ -217,6 +217,18 @@
217217
}
218218
}
219219
},
220+
"VPCPublicSubnet3DefaultRouteA0D29D46": {
221+
"Type": "AWS::EC2::Route",
222+
"Properties": {
223+
"RouteTableId": {
224+
"Ref": "VPCPublicSubnet3RouteTable98AE0E14"
225+
},
226+
"DestinationCidrBlock": "0.0.0.0/0",
227+
"GatewayId": {
228+
"Ref": "VPCIGWB7E252D3"
229+
}
230+
}
231+
},
220232
"VPCPublicSubnet3EIPAD4BC883": {
221233
"Type": "AWS::EC2::EIP",
222234
"Properties": {
@@ -243,18 +255,6 @@
243255
]
244256
}
245257
},
246-
"VPCPublicSubnet3DefaultRouteA0D29D46": {
247-
"Type": "AWS::EC2::Route",
248-
"Properties": {
249-
"RouteTableId": {
250-
"Ref": "VPCPublicSubnet3RouteTable98AE0E14"
251-
},
252-
"DestinationCidrBlock": "0.0.0.0/0",
253-
"GatewayId": {
254-
"Ref": "VPCIGWB7E252D3"
255-
}
256-
}
257-
},
258258
"VPCPrivateSubnet1Subnet8BCA10E0": {
259259
"Type": "AWS::EC2::Subnet",
260260
"Properties": {
@@ -712,6 +712,18 @@
712712
]
713713
}
714714
},
715+
"Alarm1F9009D71": {
716+
"Type": "AWS::CloudWatch::Alarm",
717+
"Properties": {
718+
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
719+
"EvaluationPeriods": 1,
720+
"MetricName": "Errors",
721+
"Namespace": "my.namespace",
722+
"Period": 300,
723+
"Threshold": 1,
724+
"Statistic": "Average"
725+
}
726+
},
715727
"CodeDeployGroupApplication13EFBDA6": {
716728
"Type": "AWS::CodeDeploy::Application",
717729
"Properties": {
@@ -750,6 +762,16 @@
750762
"Arn"
751763
]
752764
},
765+
"AlarmConfiguration": {
766+
"Alarms": [
767+
{
768+
"Name": {
769+
"Ref": "Alarm1F9009D71"
770+
}
771+
}
772+
],
773+
"Enabled": true
774+
},
753775
"AutoScalingGroups": [
754776
{
755777
"Ref": "ASG46ED3070"

packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import autoscaling = require('@aws-cdk/aws-autoscaling');
2+
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
23
import ec2 = require('@aws-cdk/aws-ec2');
34
import lb = require('@aws-cdk/aws-elasticloadbalancing');
45
import cdk = require('@aws-cdk/cdk');
@@ -25,6 +26,16 @@ new codedeploy.ServerDeploymentGroup(stack, 'CodeDeployGroup', {
2526
deploymentConfig: codedeploy.ServerDeploymentConfig.AllAtOnce,
2627
autoScalingGroups: [asg],
2728
loadBalancer: elb,
29+
alarms: [
30+
new cloudwatch.Alarm(stack, 'Alarm1', {
31+
metric: new cloudwatch.Metric({
32+
metricName: 'Errors',
33+
namespace: 'my.namespace',
34+
}),
35+
threshold: 1,
36+
evaluationPeriods: 1,
37+
}),
38+
],
2839
});
2940

3041
app.run();

packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect, haveResource } from '@aws-cdk/assert';
22
import autoscaling = require('@aws-cdk/aws-autoscaling');
3+
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
34
import ec2 = require('@aws-cdk/aws-ec2');
45
import lbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
56
import cdk = require('@aws-cdk/cdk');
@@ -264,5 +265,36 @@ export = {
264265

265266
test.done();
266267
},
268+
269+
'can have alarms added to it after being created'(test: Test) {
270+
const stack = new cdk.Stack();
271+
272+
const alarm = new cloudwatch.Alarm(stack, 'Alarm1', {
273+
metric: new cloudwatch.Metric({
274+
metricName: 'Errors',
275+
namespace: 'my.namespace',
276+
}),
277+
threshold: 1,
278+
evaluationPeriods: 1,
279+
});
280+
281+
const deploymentGroup = new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup');
282+
deploymentGroup.addAlarm(alarm);
283+
284+
expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', {
285+
"AlarmConfiguration": {
286+
"Alarms": [
287+
{
288+
"Name": {
289+
"Ref": "Alarm1F9009D71",
290+
},
291+
},
292+
],
293+
"Enabled": true,
294+
},
295+
}));
296+
297+
test.done();
298+
},
267299
},
268300
};

0 commit comments

Comments
 (0)