Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws-codedeploy): add auto rollback configuration to server Deployment Group #925

Merged
merged 1 commit into from
Oct 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
});
```

Expand Down
67 changes: 67 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am assuming this requires that alarms are associated with the deployment group. Is there any meaning for this without alarms? Should we validate?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point on validation - by default can we set this to false if customers create a deployment group without any cloudWatch alarm and set this true when they create/update deployment with CloudWatch alarms?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea. I had to change the order the PRs have been submitted in to merge the alarms one (#926) before this one, but thankfully that one was approved already.

I've rebased this PR on top of the alarms one, added validation, and changed the default to depend on whether any Alarms were provided.

}

/**
* Construction properties for {@link ServerDeploymentGroup}.
*/
Expand Down Expand Up @@ -224,6 +251,11 @@ export interface ServerDeploymentGroupProps {
* @default false
*/
ignorePollAlarmsFailure?: boolean;

/**
* The auto-rollback configuration for this Deployment Group.
*/
autoRollback?: AutoRollbackConfig;
}

/**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -455,6 +488,40 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
ignorePollAlarmFailure,
};
}

private renderAutoRollbackConfiguration(autoRollbackConfig: AutoRollbackConfig = {}):
cloudformation.DeploymentGroupResource.AutoRollbackConfigurationProperty | undefined {
const events = new Array<string>();

// 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ new codedeploy.ServerDeploymentGroup(stack, 'CodeDeployGroup', {
evaluationPeriods: 1,
}),
],
autoRollback: {
failedDeployment: false,
deploymentInAlarm: false,
},
});

app.run();
64 changes: 64 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@
"Arn"
]
},
"AutoRollbackConfiguration": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you specify false to all three? Why are they all enabled?

Also, add a unit test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you specify false to all three? Why are they all enabled?

Those are actually 2 different tests :) I've specified false for all 3 in the integ.deployment-group, while this is integ.pipeline-code-deploy.

Also, add a unit test

Done.

"Enabled": true,
"Events": [
"DEPLOYMENT_FAILURE"
]
},
"DeploymentConfigName": {
"Ref": "CustomDeployConfig52EEBC13"
},
Expand Down