Skip to content

Commit

Permalink
feat(aws-codedeploy): Add the auto-scaling groups property to ServerD…
Browse files Browse the repository at this point in the history
…eploymentGroup.
  • Loading branch information
skinny85 committed Sep 20, 2018
1 parent 16a9b5a commit e770a19
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 3 deletions.
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,8 @@ export class AutoScalingGroup extends cdk.Construct implements elb.ILoadBalancer
* Add command to the startup script of fleet instances.
* The command must be in the scripting language supported by the fleet's OS (i.e. Linux/Windows).
*/
public addUserData(script: string) {
this.userDataLines.push(script);
public addUserData(...scriptLines: string[]) {
scriptLines.forEach(scriptLine => this.userDataLines.push(scriptLine));
}

public autoScalingGroupName() {
Expand Down
4 changes: 4 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ To create a new CodeDeploy Deployment Group that deploys to EC2/on-premise insta
const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'CodeDeployDeploymentGroup', {
application,
deploymentGroupName: 'MyDeploymentGroup',
autoScalingGroups: [asg1, asg2],
// automatically installs the CodeDeploy agent on your auto-scaling groups hosts
// default: true
autoInstallAgent: true,
});
```

Expand Down
91 changes: 91 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import autoscaling = require("@aws-cdk/aws-autoscaling");
import ec2 = require("@aws-cdk/aws-ec2");
import s3 = require("@aws-cdk/aws-s3");
import cdk = require("@aws-cdk/cdk");
import iam = require("../../aws-iam/lib/role");
import { ServerApplication, ServerApplicationRef } from "./application";
Expand Down Expand Up @@ -59,6 +62,7 @@ export abstract class ServerDeploymentGroupRef extends cdk.Construct {
public abstract readonly deploymentGroupName: string;
public abstract readonly deploymentGroupArn: string;
public readonly deploymentConfig: IServerDeploymentConfig;
public abstract readonly autoScalingGroups: autoscaling.AutoScalingGroup[];

constructor(parent: cdk.Construct, id: string, deploymentConfig?: IServerDeploymentConfig) {
super(parent, id);
Expand Down Expand Up @@ -88,6 +92,10 @@ class ImportedServerDeploymentGroupRef extends ServerDeploymentGroupRef {
this.deploymentGroupArn = deploymentGroupName2Arn(props.application.applicationName,
props.deploymentGroupName);
}

public get autoScalingGroups(): autoscaling.AutoScalingGroup[] {
return [];
}
}

/**
Expand Down Expand Up @@ -119,6 +127,22 @@ export interface ServerDeploymentGroupProps {
* @default ServerDeploymentConfig#OneAtATime
*/
deploymentConfig?: IServerDeploymentConfig;

/**
* The auto-scaling groups belonging to this Deployment Group.
*
* @default []
*/
autoScalingGroups?: autoscaling.AutoScalingGroup[];

/**
* If you've provided any auto-scaling groups with the {@link #autoScalingGroups} property,
* you can set this property to automatically install the CodeDeploy agent on the instances.
*
* @default true
* @see https://docs.aws.amazon.com/codedeploy/latest/userguide/codedeploy-agent-operations-install.html
*/
autoInstallAgent?: boolean;
}

/**
Expand All @@ -130,6 +154,11 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
public readonly deploymentGroupArn: string;
public readonly deploymentGroupName: string;

private readonly _autoScalingGroups: autoscaling.AutoScalingGroup[];
private readonly autoInstallAgent: boolean;
private readonly region: string = (new cdk.AwsRegion()).toString();
private readonly codeDeployBucket: s3.BucketRef;

constructor(parent: cdk.Construct, id: string, props?: ServerDeploymentGroupProps) {
super(parent, id, props && props.deploymentConfig);

Expand All @@ -142,18 +171,80 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
managedPolicyArns: ['arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole'],
});

this._autoScalingGroups = props.autoScalingGroups || [];
this.autoInstallAgent = props.autoInstallAgent !== false;
this.codeDeployBucket = s3.BucketRef.import(this, 'CodeDeployBucket', {
bucketName: `aws-codedeploy-${this.region}`,
});
for (const asg of this._autoScalingGroups) {
this.addCodeDeployAgentInstallUserData(asg);
}

const resource = new cloudformation.DeploymentGroupResource(this, 'Resource', {
applicationName: this.application.applicationName,
deploymentGroupName: props.deploymentGroupName,
serviceRoleArn: this.role.roleArn,
deploymentConfigName: props.deploymentConfig &&
props.deploymentConfig.deploymentConfigName,
autoScalingGroups: new cdk.Token(() =>
this._autoScalingGroups.length === 0
? undefined
: this._autoScalingGroups.map(asg => asg.autoScalingGroupName())),
});

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

public addAutoScalingGroup(asg: autoscaling.AutoScalingGroup): void {
this._autoScalingGroups.push(asg);
this.addCodeDeployAgentInstallUserData(asg);
}

public get autoScalingGroups(): autoscaling.AutoScalingGroup[] {
return this._autoScalingGroups.slice();
}

private addCodeDeployAgentInstallUserData(asg: autoscaling.AutoScalingGroup): void {
if (!this.autoInstallAgent) {
return;
}

this.codeDeployBucket.grantRead(asg.role, 'latest/*');

switch (asg.osType) {
case ec2.OperatingSystemType.Linux:
asg.addUserData(
'PKG_CMD=`which yum 2>/dev/null`',
'if [ -z "$PKG_CMD" ]; then',
'PKG_CMD=apt-get',
'else',
'PKG=CMD=yum',
'fi',
'$PKG_CMD update -y',
'$PKG_CMD install -y ruby2.0',
'if [ $? -ne 0 ]; then',
'$PKG_CMD install -y ruby',
'fi',
'$PKG_CMD install -y awscli',
'TMP_DIR=`mktemp -d`',
'cd $TMP_DIR',
`aws s3 cp s3://aws-codedeploy-${this.region}/latest/install . --region ${this.region}`,
'chmod +x ./install',
'./install auto',
'rm -fr $TMP_DIR',
);
break;
case ec2.OperatingSystemType.Windows:
asg.addUserData(
'Set-Variable -Name TEMPDIR -Value (New-TemporaryFile).DirectoryName',
`aws s3 cp s3://aws-codedeploy-${this.region}/latest/codedeploy-agent.msi $TEMPDIR\\codedeploy-agent.msi`,
'$TEMPDIR\\codedeploy-agent.msi /quiet /l c:\\temp\\host-agent-install-log.txt',
);
break;
}
}
}

function deploymentGroupName2Arn(applicationName: string, deploymentGroupName: string): string {
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@
"license": "Apache-2.0",
"devDependencies": {
"@aws-cdk/assert": "^0.9.2",
"@aws-cdk/aws-ec2": "^0.9.2",
"cdk-build-tools": "^0.9.2",
"cfn2ts": "^0.9.2",
"pkglint": "^0.9.2"
},
"dependencies": {
"@aws-cdk/aws-autoscaling": "^0.9.2",
"@aws-cdk/aws-codepipeline-api": "^0.9.2",
"@aws-cdk/cdk": "^0.9.2"
},
Expand Down
51 changes: 50 additions & 1 deletion packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { expect, haveResource } from '@aws-cdk/assert';
import autoscaling = require('@aws-cdk/aws-autoscaling');
import ec2 = require('@aws-cdk/aws-ec2');
import cdk = require('@aws-cdk/cdk');
import { Test } from 'nodeunit';
import codedeploy = require('../lib');
Expand Down Expand Up @@ -38,6 +40,53 @@ export = {
test.notEqual(deploymentGroup, undefined);

test.done();
}
},

"created with ASGs contains the ASG names"(test: Test) {
const stack = new cdk.Stack();

const asg = new autoscaling.AutoScalingGroup(stack, 'ASG', {
instanceType: new ec2.InstanceTypePair(ec2.InstanceClass.Standard3, ec2.InstanceSize.Small),
machineImage: new ec2.AmazonLinuxImage(),
vpc: new ec2.VpcNetwork(stack, 'VPC'),
});

new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup', {
autoScalingGroups: [asg],
});

expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', {
"AutoScalingGroups": [
{
"Ref": "ASG46ED3070",
},
]
}));

test.done();
},

"created without ASGs but adding them later contains the ASG names"(test: Test) {
const stack = new cdk.Stack();

const asg = new autoscaling.AutoScalingGroup(stack, 'ASG', {
instanceType: new ec2.InstanceTypePair(ec2.InstanceClass.Standard3, ec2.InstanceSize.Small),
machineImage: new ec2.AmazonLinuxImage(),
vpc: new ec2.VpcNetwork(stack, 'VPC'),
});

const deploymentGroup = new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup');
deploymentGroup.addAutoScalingGroup(asg);

expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', {
"AutoScalingGroups": [
{
"Ref": "ASG46ED3070",
},
]
}));

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

0 comments on commit e770a19

Please sign in to comment.