Skip to content

Commit

Permalink
feat(aws-codedeploy): add support for setting CloudWatch alarms on a …
Browse files Browse the repository at this point in the history
…server Deployment Group. (#926)
  • Loading branch information
skinny85 committed Oct 15, 2018
1 parent d251a46 commit 27b26b1
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 37 deletions.
6 changes: 6 additions & 0 deletions packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ export class Alarm extends Construct {
*/
public readonly alarmArn: string;

/**
* Name of this alarm.
*/
public readonly alarmName: string;

/**
* The metric object this alarm was based on
*/
Expand Down Expand Up @@ -163,6 +168,7 @@ export class Alarm extends Construct {
});

this.alarmArn = alarm.alarmArn;
this.alarmName = alarm.alarmName;
this.metric = props.metric;
this.annotation = {
// tslint:disable-next-line:max-line-length
Expand Down
7 changes: 7 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'CodeDeployDe
'key2': ['v3'],
},
),
// CloudWatch alarms
alarms: [
new cloudwatch.Alarm(/* ... */),
],
// whether to ignore failure to fetch the status of alarms from CloudWatch
// default: false
ignorePollAlarmsFailure: false,
});
```

Expand Down
53 changes: 52 additions & 1 deletion packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import autoscaling = require("@aws-cdk/aws-autoscaling");
import cloudwatch = require("@aws-cdk/aws-cloudwatch");
import codedeploylb = require("@aws-cdk/aws-codedeploy-api");
import ec2 = require("@aws-cdk/aws-ec2");
import iam = require('@aws-cdk/aws-iam');
Expand Down Expand Up @@ -167,6 +168,8 @@ export interface ServerDeploymentGroupProps {
/**
* The auto-scaling groups belonging to this Deployment Group.
*
* Auto-scaling groups can also be added after the Deployment Group is created using the {@link #addAutoScalingGroup} method.
*
* @default []
*/
autoScalingGroups?: autoscaling.AutoScalingGroup[];
Expand All @@ -189,7 +192,7 @@ export interface ServerDeploymentGroupProps {
*/
loadBalancer?: codedeploylb.ILoadBalancer;

/*
/**
* All EC2 instances matching the given set of tags when a deployment occurs will be added to this Deployment Group.
*
* @default no additional EC2 instances will be added to the Deployment Group
Expand All @@ -202,6 +205,25 @@ export interface ServerDeploymentGroupProps {
* @default no additional on-premise instances will be added to the Deployment Group
*/
onPremiseInstanceTags?: InstanceTagSet;

/**
* The CloudWatch alarms associated with this Deployment Group.
* CodeDeploy will stop (and optionally roll back)
* a deployment if during it any of the alarms trigger.
*
* Alarms can also be added after the Deployment Group is created using the {@link #addAlarm} method.
*
* @default []
* @see https://docs.aws.amazon.com/codedeploy/latest/userguide/monitoring-create-alarms.html
*/
alarms?: cloudwatch.Alarm[];

/**
* Whether to continue a deployment even if fetching the alarm status from CloudWatch failed.
*
* @default false
*/
ignorePollAlarmsFailure?: boolean;
}

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

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

this.alarms = props.alarms || [];

const resource = new cloudformation.DeploymentGroupResource(this, 'Resource', {
applicationName: this.application.applicationName,
deploymentGroupName: props.deploymentGroupName,
Expand All @@ -255,18 +280,33 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
},
ec2TagSet: this.ec2TagSet(props.ec2InstanceTags),
onPremisesTagSet: this.onPremiseTagSet(props.onPremiseInstanceTags),
alarmConfiguration: new cdk.Token(() => this.renderAlarmConfiguration(props.ignorePollAlarmsFailure)),
});

this.deploymentGroupName = resource.deploymentGroupName;
this.deploymentGroupArn = deploymentGroupName2Arn(this.application.applicationName,
this.deploymentGroupName);
}

/**
* Adds an additional auto-scaling group to this Deployment Group.
*
* @param asg the auto-scaling group to add to this Deployment Group
*/
public addAutoScalingGroup(asg: autoscaling.AutoScalingGroup): void {
this._autoScalingGroups.push(asg);
this.addCodeDeployAgentInstallUserData(asg);
}

/**
* Associates an additional alarm with this Deployment Group.
*
* @param alarm the alarm to associate with this Deployment Group
*/
public addAlarm(alarm: cloudwatch.Alarm): void {
this.alarms.push(alarm);
}

public get autoScalingGroups(): autoscaling.AutoScalingGroup[] | undefined {
return this._autoScalingGroups.slice();
}
Expand Down Expand Up @@ -404,6 +444,17 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
}
return tagsInGroup;
}

private renderAlarmConfiguration(ignorePollAlarmFailure?: boolean):
cloudformation.DeploymentGroupResource.AlarmConfigurationProperty | undefined {
return this.alarms.length === 0
? undefined
: {
alarms: this.alarms.map(a => ({ name: a.alarmName })),
enabled: true,
ignorePollAlarmFailure,
};
}
}

function deploymentGroupName2Arn(applicationName: string, deploymentGroupName: string): string {
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-codedeploy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@aws-cdk/aws-autoscaling": "^0.12.0",
"@aws-cdk/aws-codedeploy-api": "^0.12.0",
"@aws-cdk/aws-codepipeline-api": "^0.12.0",
"@aws-cdk/aws-cloudwatch": "^0.12.0",
"@aws-cdk/aws-iam": "^0.12.0",
"@aws-cdk/aws-s3": "^0.12.0",
"@aws-cdk/cdk": "^0.12.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@
}
}
},
"VPCPublicSubnet1DefaultRoute91CEF279": {
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": {
"Ref": "VPCPublicSubnet1RouteTableFEE4B781"
},
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "VPCIGWB7E252D3"
}
}
},
"VPCPublicSubnet1EIP6AD938E8": {
"Type": "AWS::EC2::EIP",
"Properties": {
Expand All @@ -83,18 +95,6 @@
]
}
},
"VPCPublicSubnet1DefaultRoute91CEF279": {
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": {
"Ref": "VPCPublicSubnet1RouteTableFEE4B781"
},
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "VPCIGWB7E252D3"
}
}
},
"VPCPublicSubnet2Subnet74179F39": {
"Type": "AWS::EC2::Subnet",
"Properties": {
Expand Down Expand Up @@ -137,6 +137,18 @@
}
}
},
"VPCPublicSubnet2DefaultRouteB7481BBA": {
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": {
"Ref": "VPCPublicSubnet2RouteTable6F1A15F1"
},
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "VPCIGWB7E252D3"
}
}
},
"VPCPublicSubnet2EIP4947BC00": {
"Type": "AWS::EC2::EIP",
"Properties": {
Expand All @@ -163,18 +175,6 @@
]
}
},
"VPCPublicSubnet2DefaultRouteB7481BBA": {
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": {
"Ref": "VPCPublicSubnet2RouteTable6F1A15F1"
},
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "VPCIGWB7E252D3"
}
}
},
"VPCPublicSubnet3Subnet631C5E25": {
"Type": "AWS::EC2::Subnet",
"Properties": {
Expand Down Expand Up @@ -217,6 +217,18 @@
}
}
},
"VPCPublicSubnet3DefaultRouteA0D29D46": {
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": {
"Ref": "VPCPublicSubnet3RouteTable98AE0E14"
},
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "VPCIGWB7E252D3"
}
}
},
"VPCPublicSubnet3EIPAD4BC883": {
"Type": "AWS::EC2::EIP",
"Properties": {
Expand All @@ -243,18 +255,6 @@
]
}
},
"VPCPublicSubnet3DefaultRouteA0D29D46": {
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": {
"Ref": "VPCPublicSubnet3RouteTable98AE0E14"
},
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "VPCIGWB7E252D3"
}
}
},
"VPCPrivateSubnet1Subnet8BCA10E0": {
"Type": "AWS::EC2::Subnet",
"Properties": {
Expand Down Expand Up @@ -712,6 +712,18 @@
]
}
},
"Alarm1F9009D71": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"EvaluationPeriods": 1,
"MetricName": "Errors",
"Namespace": "my.namespace",
"Period": 300,
"Threshold": 1,
"Statistic": "Average"
}
},
"CodeDeployGroupApplication13EFBDA6": {
"Type": "AWS::CodeDeploy::Application",
"Properties": {
Expand Down Expand Up @@ -750,6 +762,16 @@
"Arn"
]
},
"AlarmConfiguration": {
"Alarms": [
{
"Name": {
"Ref": "Alarm1F9009D71"
}
}
],
"Enabled": true
},
"AutoScalingGroups": [
{
"Ref": "ASG46ED3070"
Expand Down
11 changes: 11 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import autoscaling = require('@aws-cdk/aws-autoscaling');
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
import ec2 = require('@aws-cdk/aws-ec2');
import lb = require('@aws-cdk/aws-elasticloadbalancing');
import cdk = require('@aws-cdk/cdk');
Expand All @@ -25,6 +26,16 @@ new codedeploy.ServerDeploymentGroup(stack, 'CodeDeployGroup', {
deploymentConfig: codedeploy.ServerDeploymentConfig.AllAtOnce,
autoScalingGroups: [asg],
loadBalancer: elb,
alarms: [
new cloudwatch.Alarm(stack, 'Alarm1', {
metric: new cloudwatch.Metric({
metricName: 'Errors',
namespace: 'my.namespace',
}),
threshold: 1,
evaluationPeriods: 1,
}),
],
});

app.run();
32 changes: 32 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, haveResource } from '@aws-cdk/assert';
import autoscaling = require('@aws-cdk/aws-autoscaling');
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
import ec2 = require('@aws-cdk/aws-ec2');
import lbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
import cdk = require('@aws-cdk/cdk');
Expand Down Expand Up @@ -264,5 +265,36 @@ export = {

test.done();
},

'can have alarms added to it after being created'(test: Test) {
const stack = new cdk.Stack();

const alarm = new cloudwatch.Alarm(stack, 'Alarm1', {
metric: new cloudwatch.Metric({
metricName: 'Errors',
namespace: 'my.namespace',
}),
threshold: 1,
evaluationPeriods: 1,
});

const deploymentGroup = new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup');
deploymentGroup.addAlarm(alarm);

expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', {
"AlarmConfiguration": {
"Alarms": [
{
"Name": {
"Ref": "Alarm1F9009D71",
},
},
],
"Enabled": true,
},
}));

test.done();
},
},
};

0 comments on commit 27b26b1

Please sign in to comment.