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. (#739)
  • Loading branch information
skinny85 authored Sep 21, 2018
1 parent e6b67ad commit 0b28886
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 5 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],
// adds User Data that installs the CodeDeploy agent on your auto-scaling groups hosts
// default: true
installAgent: true,
});
```

Expand Down
96 changes: 94 additions & 2 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 @@ -56,9 +59,11 @@ export abstract class ServerDeploymentGroupRef extends cdk.Construct {
}

public abstract readonly application: ServerApplicationRef;
public abstract readonly role?: iam.Role;
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 All @@ -71,14 +76,17 @@ export abstract class ServerDeploymentGroupRef extends cdk.Construct {
deploymentGroupName: new cdk.Output(this, 'DeploymentGroupName', {
value: this.deploymentGroupName
}).makeImportValue().toString(),
deploymentConfig: this.deploymentConfig,
};
}
}

class ImportedServerDeploymentGroupRef extends ServerDeploymentGroupRef {
public readonly application: ServerApplicationRef;
public readonly role?: iam.Role = undefined;
public readonly deploymentGroupName: string;
public readonly deploymentGroupArn: string;
public readonly autoScalingGroups?: autoscaling.AutoScalingGroup[] = undefined;

constructor(parent: cdk.Construct, id: string, props: ServerDeploymentGroupRefProps) {
super(parent, id, props.deploymentConfig);
Expand Down Expand Up @@ -119,19 +127,39 @@ 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 add User Data that installs the CodeDeploy agent on the instances.
*
* @default true
* @see https://docs.aws.amazon.com/codedeploy/latest/userguide/codedeploy-agent-operations-install.html
*/
installAgent?: boolean;
}

/**
* A CodeDeploy Deployment Group that deploys to EC2/on-premise instances.
*/
export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
public readonly application: ServerApplicationRef;
public readonly role: iam.Role;
public readonly role?: iam.Role;
public readonly deploymentGroupArn: string;
public readonly deploymentGroupName: string;

private readonly _autoScalingGroups: autoscaling.AutoScalingGroup[];
private readonly installAgent: boolean;
private readonly codeDeployBucket: s3.BucketRef;

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

this.application = props.application || new ServerApplication(this, 'Application');

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

this._autoScalingGroups = props.autoScalingGroups || [];
this.installAgent = props.installAgent === undefined ? true : props.installAgent;
const region = (new cdk.AwsRegion()).toString();
this.codeDeployBucket = s3.BucketRef.import(this, 'CodeDeployBucket', {
bucketName: `aws-codedeploy-${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.deploymentGroupName;
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[] | undefined {
return this._autoScalingGroups.slice();
}

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

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

const region = (new cdk.AwsRegion()).toString();
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-${region}/latest/install . --region ${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-${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
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,15 @@
"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/aws-s3": "^0.9.2",
"@aws-cdk/cdk": "^0.9.2"
},
"homepage": "https://github.com/awslabs/aws-cdk"
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 0b28886

Please sign in to comment.