From 777eade17323fd9f6362c551ddb303b52736a57a Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Fri, 12 Oct 2018 16:59:28 -0700 Subject: [PATCH] feat(aws-codedeploy): add auto rollback configuration to server Deployment Group. --- packages/@aws-cdk/aws-codedeploy/README.md | 6 ++ .../aws-codedeploy/lib/deployment-group.ts | 67 +++++++++++++++++++ .../test/integ.deployment-group.ts | 4 ++ .../test/test.deployment-group.ts | 64 ++++++++++++++++++ .../integ.pipeline-code-deploy.expected.json | 6 ++ 5 files changed, 147 insertions(+) diff --git a/packages/@aws-cdk/aws-codedeploy/README.md b/packages/@aws-cdk/aws-codedeploy/README.md index 27f02ba6af705..4b79deb7b9343 100644 --- a/packages/@aws-cdk/aws-codedeploy/README.md +++ b/packages/@aws-cdk/aws-codedeploy/README.md @@ -60,6 +60,12 @@ const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'CodeDeployDe // whether to ignore failure to fetch the status of alarms from CloudWatch // default: false ignorePollAlarmsFailure: false, + // auto-rollback configuration + autoRollback: { + failedDeployment: true, // default: true + stoppedDeployment: true, // default: false + deploymentInAlarm: true, // default: true if you provided any alarms, false otherwise + }, }); ``` diff --git a/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts index 0d34e3692725a..5857d64be0a86 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts @@ -135,6 +135,33 @@ export class InstanceTagSet { } } +/** + * The configuration for automatically rolling back deployments in a given Deployment Group. + */ +export interface AutoRollbackConfig { + /** + * Whether to automatically roll back a deployment that fails. + * + * @default true + */ + failedDeployment?: boolean; + + /** + * Whether to automatically roll back a deployment that was manually stopped. + * + * @default false + */ + stoppedDeployment?: boolean; + + /** + * Whether to automatically roll back a deployment during which one of the configured + * CloudWatch alarms for this Deployment Group went off. + * + * @default true if you've provided any Alarms with the `alarms` property, false otherwise + */ + deploymentInAlarm?: boolean; +} + /** * Construction properties for {@link ServerDeploymentGroup}. */ @@ -224,6 +251,11 @@ export interface ServerDeploymentGroupProps { * @default false */ ignorePollAlarmsFailure?: boolean; + + /** + * The auto-rollback configuration for this Deployment Group. + */ + autoRollback?: AutoRollbackConfig; } /** @@ -281,6 +313,7 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef { ec2TagSet: this.ec2TagSet(props.ec2InstanceTags), onPremisesTagSet: this.onPremiseTagSet(props.onPremiseInstanceTags), alarmConfiguration: new cdk.Token(() => this.renderAlarmConfiguration(props.ignorePollAlarmsFailure)), + autoRollbackConfiguration: new cdk.Token(() => this.renderAutoRollbackConfiguration(props.autoRollback)), }); this.deploymentGroupName = resource.deploymentGroupName; @@ -455,6 +488,40 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef { ignorePollAlarmFailure, }; } + + private renderAutoRollbackConfiguration(autoRollbackConfig: AutoRollbackConfig = {}): + cloudformation.DeploymentGroupResource.AutoRollbackConfigurationProperty | undefined { + const events = new Array(); + + // we roll back failed deployments by default + if (autoRollbackConfig.failedDeployment !== false) { + events.push('DEPLOYMENT_FAILURE'); + } + + // we _do not_ roll back stopped deployments by default + if (autoRollbackConfig.stoppedDeployment === true) { + events.push('DEPLOYMENT_STOP_ON_REQUEST'); + } + + // we _do not_ roll back alarm-triggering deployments by default + // unless the Deployment Group has at least one alarm + if (autoRollbackConfig.deploymentInAlarm !== false) { + if (this.alarms.length > 0) { + events.push('DEPLOYMENT_STOP_ON_ALARM'); + } else if (autoRollbackConfig.deploymentInAlarm === true) { + throw new Error( + "The auto-rollback setting 'deploymentInAlarm' does not have any effect unless you associate " + + "at least one CloudWatch alarm with the Deployment Group"); + } + } + + return events.length > 0 + ? { + enabled: true, + events, + } + : undefined; + } } function deploymentGroupName2Arn(applicationName: string, deploymentGroupName: string): string { diff --git a/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts index 3035bc22aa089..6e096629fada3 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/integ.deployment-group.ts @@ -36,6 +36,10 @@ new codedeploy.ServerDeploymentGroup(stack, 'CodeDeployGroup', { evaluationPeriods: 1, }), ], + autoRollback: { + failedDeployment: false, + deploymentInAlarm: false, + }, }); app.run(); diff --git a/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts index 9ce6a26947e3b..3e8d47bfc0798 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts @@ -296,5 +296,69 @@ export = { test.done(); }, + + 'only automatically rolls back failed deployments by default'(test: Test) { + const stack = new cdk.Stack(); + + new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup'); + + expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', { + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + ], + }, + })); + + test.done(); + }, + + 'rolls back alarmed deployments if at least one alarm has been added'(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', { + autoRollback: { + failedDeployment: false, + }, + }); + deploymentGroup.addAlarm(alarm); + + expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', { + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_STOP_ON_ALARM", + ], + }, + })); + + test.done(); + }, + + 'setting to roll back on alarms without providing any results in an exception'(test: Test) { + const stack = new cdk.Stack(); + + new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup', { + autoRollback: { + deploymentInAlarm: true, + }, + }); + + test.throws(() => { + stack.toCloudFormation(); + }, /deploymentInAlarm/); + + test.done(); + }, }, }; diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-deploy.expected.json index c1eb90b667bbb..6d331d9f379e3 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-deploy.expected.json @@ -48,6 +48,12 @@ "Arn" ] }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE" + ] + }, "DeploymentConfigName": { "Ref": "CustomDeployConfig52EEBC13" },