Skip to content

Commit

Permalink
feat(aws-autoscaling): Add the ability to tag ASG and propagate the t…
Browse files Browse the repository at this point in the history
…ags (#766)
  • Loading branch information
moofish32 committed Sep 26, 2018
1 parent a006f2c commit 8606f8d
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 119 deletions.
30 changes: 29 additions & 1 deletion packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import cdk = require('@aws-cdk/cdk');

import { cloudformation } from './autoscaling.generated';

/**
* Name tag constant
*/
const NAME_TAG: string = 'Name';

/**
* Properties of a Fleet
*/
Expand Down Expand Up @@ -124,6 +129,11 @@ export interface AutoScalingGroupProps {
* @default 300 (5 minutes)
*/
resourceSignalTimeoutSec?: number;

/**
* The AWS resource tags to associate with the ASG.
*/
tags?: cdk.Tags;
}

/**
Expand All @@ -137,7 +147,7 @@ export interface AutoScalingGroupProps {
*
* The ASG spans all availability zones.
*/
export class AutoScalingGroup extends cdk.Construct implements elb.ILoadBalancerTarget, ec2.IConnectable,
export class AutoScalingGroup extends cdk.Construct implements cdk.ITaggable, elb.ILoadBalancerTarget, ec2.IConnectable,
elbv2.IApplicationLoadBalancerTarget, elbv2.INetworkLoadBalancerTarget {
/**
* The type of OS instances of this fleet are running.
Expand All @@ -154,6 +164,11 @@ export class AutoScalingGroup extends cdk.Construct implements elb.ILoadBalancer
*/
public readonly role: iam.Role;

/**
* Manage tags for this construct and children
*/
public readonly tags: cdk.TagManager;

private readonly userDataLines = new Array<string>();
private readonly autoScalingGroup: cloudformation.AutoScalingGroupResource;
private readonly securityGroup: ec2.SecurityGroupRef;
Expand All @@ -167,6 +182,8 @@ export class AutoScalingGroup extends cdk.Construct implements elb.ILoadBalancer
this.securityGroup = new ec2.SecurityGroup(this, 'InstanceSecurityGroup', { vpc: props.vpc });
this.connections = new ec2.Connections({ securityGroup: this.securityGroup });
this.securityGroups.push(this.securityGroup);
this.tags = new TagManager(this, {initialTags: props.tags});
this.tags.setTag(NAME_TAG, this.path, { overwrite: false });

if (props.allowAllOutbound !== false) {
this.connections.allowTo(new ec2.AnyIPv4(), new ec2.AllConnections(), 'Outbound traffic allowed by default');
Expand Down Expand Up @@ -211,6 +228,7 @@ export class AutoScalingGroup extends cdk.Construct implements elb.ILoadBalancer
launchConfigurationName: launchConfig.ref,
loadBalancerNames: new cdk.Token(() => this.loadBalancerNames.length > 0 ? this.loadBalancerNames : undefined),
targetGroupArns: new cdk.Token(() => this.targetGroupArns.length > 0 ? this.targetGroupArns : undefined),
tags: this.tags,
};

if (props.notificationsTopic) {
Expand Down Expand Up @@ -472,6 +490,16 @@ function renderRollingUpdateConfig(config: RollingUpdateConfiguration = {}): cdk
};
}

class TagManager extends cdk.TagManager {
protected tagFormatResolve(tagGroups: cdk.TagGroups): any {
const tags = {...tagGroups.nonSitckyTags, ...tagGroups.ancestorTags, ...tagGroups.stickyTags};
return Object.keys(tags).map( (key) => {
const propagateAtLaunch = !!tagGroups.propagateTags[key] || !!tagGroups.ancestorTags[key];
return {key, value: tags[key], propagateAtLaunch};
});
}
}

/**
* Render a number of seconds to a PTnX string.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,13 @@
"Ref": "LB8A12904C"
}
],
"Tags": [
{
"Key": "Name",
"PropagateAtLaunch": true,
"Value": "aws-cdk-ec2-integ/Fleet"
}
],
"VPCZoneIdentifier": [
{
"Ref": "VPCPrivateSubnet1Subnet8BCA10E0"
Expand Down
213 changes: 105 additions & 108 deletions packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ export = {
"LaunchConfigurationName": {
"Ref": "MyFleetLaunchConfig5D7F9801"
},
"LoadBalancerNames": [],
"Tags": [
{
"Key": "Name",
"PropagateAtLaunch": true,
"Value": "MyFleet"
}
],
"MaxSize": "1",
"MinSize": "1",
"VPCZoneIdentifier": [
Expand All @@ -123,121 +131,69 @@ export = {
});

fleet.addToRolePolicy(new cdk.PolicyStatement()
.addAction('*')
.addAction('test:SpecialName')
.addAllResources());

expect(stack).toMatch({
"Resources": {
"MyFleetInstanceSecurityGroup774E8234": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "MyFleet/InstanceSecurityGroup",
"SecurityGroupEgress": [
{
"CidrIp": "0.0.0.0/0",
"Description": "Outbound traffic allowed by default",
"FromPort": -1,
"IpProtocol": "-1",
"ToPort": -1
}
],
"SecurityGroupIngress": [],
"VpcId": "my-vpc"
}
},
MyFleetInstanceRole25A84AB8: {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
},
MyFleetInstanceRoleDefaultPolicy7B0197E7: {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "*",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "MyFleetInstanceRoleDefaultPolicy7B0197E7",
"Roles": [
expect(stack).to(haveResource('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [
{
"Ref": "MyFleetInstanceRole25A84AB8"
Action: "test:SpecialName",
Effect: "Allow",
Resource: "*"
}
]
}
},
MyFleetInstanceProfile70A58496: {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Roles": [
],
Version: "2012-10-17"
},
}));
test.done();
},

'can configure replacing update'(test: Test) {
// GIVEN
const stack = new cdk.Stack(undefined, 'MyStack', { env: { region: 'us-east-1', account: '1234' }});
const vpc = mockVpc(stack);

// WHEN
new autoscaling.AutoScalingGroup(stack, 'MyFleet', {
instanceType: new ec2.InstanceTypePair(ec2.InstanceClass.M4, ec2.InstanceSize.Micro),
machineImage: new ec2.AmazonLinuxImage(),
vpc,
updateType: autoscaling.UpdateType.ReplacingUpdate,
replacingUpdateMinSuccessfulInstancesPercent: 50
});

// THEN
expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", {
UpdatePolicy: {
AutoScalingReplacingUpdate: {
WillReplace: true
}
},
CreationPolicy: {
AutoScalingCreationPolicy: {
MinSuccessfulInstancesPercent: 50
}
}
}, ResourcePart.CompleteDefinition));

test.done();
},

'can configure rolling update'(test: Test) {
// GIVEN
const stack = new cdk.Stack(undefined, 'MyStack', { env: { region: 'us-east-1', account: '1234' }});
const vpc = mockVpc(stack);

// WHEN
new autoscaling.AutoScalingGroup(stack, 'MyFleet', {
{
"Ref": "MyFleetInstanceRole25A84AB8"
}
]
}
},
MyFleetLaunchConfig5D7F9801: {
Type: "AWS::AutoScaling::LaunchConfiguration",
Properties: {
"IamInstanceProfile": {
"Ref": "MyFleetInstanceProfile70A58496"
},
"ImageId": "dummy",
"InstanceType": "m4.micro",
"SecurityGroups": [
{
"Fn::GetAtt": [
"MyFleetInstanceSecurityGroup774E8234",
"GroupId"
]
}
],
"UserData": {
"Fn::Base64": "#!/bin/bash\n"
}
},
DependsOn: [
"MyFleetInstanceRole25A84AB8",
"MyFleetInstanceRoleDefaultPolicy7B0197E7"
]
},
MyFleetASG88E55886: {
Type: "AWS::AutoScaling::AutoScalingGroup",
UpdatePolicy: {
AutoScalingScheduledAction: { IgnoreUnmodifiedGroupSizeProperties: true }
},
Properties: {
DesiredCapacity: "1",
LaunchConfigurationName: {
Ref: "MyFleetLaunchConfig5D7F9801"
},
MaxSize: "1",
MinSize: "1",
VPCZoneIdentifier: [
"pri1"
]
}
}
}
});

],
Version: "2012-10-17"
},
}));
test.done();
},

Expand Down Expand Up @@ -354,6 +310,47 @@ export = {
}));
test.done();
},
'can set tags'(test: Test) {
// GIVEN
const stack = new cdk.Stack(undefined, 'MyStack', { env: { region: 'us-east-1', account: '1234' }});
const vpc = mockVpc(stack);

// WHEN
const asg = new autoscaling.AutoScalingGroup(stack, 'MyFleet', {
instanceType: new ec2.InstanceTypePair(ec2.InstanceClass.M4, ec2.InstanceSize.Micro),
machineImage: new ec2.AmazonLinuxImage(),
vpc,
updateType: autoscaling.UpdateType.RollingUpdate,
rollingUpdateConfiguration: {
minSuccessfulInstancesPercent: 50,
pauseTimeSec: 345
},
tags: {superfood: 'acai'},
});
asg.tags.setTag('notsuper', 'caramel', {propagate: false});

// THEN
expect(stack).to(haveResource("AWS::AutoScaling::AutoScalingGroup", {
Tags: [
{
Key: 'superfood',
Value: 'acai',
PropagateAtLaunch: true,
},
{
Key: 'Name',
Value: 'MyFleet',
PropagateAtLaunch: true,
},
{
Key: 'notsuper',
Value: 'caramel',
PropagateAtLaunch: false,
},
]
}));
test.done();
},
};

function mockVpc(stack: cdk.Stack) {
Expand Down
25 changes: 15 additions & 10 deletions packages/@aws-cdk/cdk/lib/core/tag-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ export interface TagProps {
overwrite?: boolean;
}

/**
* This is the interface for arguments to `tagFormatResolve` to enable extensions
*/
export interface TagGroups {
/**
* Tags that overwrite ancestor tags
Expand Down Expand Up @@ -82,19 +85,14 @@ export interface RemoveProps {
blockPropagate?: boolean;
}

/**
* Properties for Tag Manager
*/
export interface TagManagerProps {
initialTags?: Tags;
<<<<<<< HEAD
autoScalingGroup?: boolean;
||||||| parent of 99e87ce7... tag manager refactor to extract only a single protected method

/**
* If set this tag Manager will resolve to Autoscaling Group Tags with
* PropagateAtLaunch set based on the tag property `propagate`
* Initial tags to set on the tag manager using TAG_DEFAULTS
*/
autoScalingGroup?: boolean;
=======
>>>>>>> 99e87ce7... tag manager refactor to extract only a single protected method
initialTags?: Tags;
}

/**
Expand Down Expand Up @@ -257,6 +255,13 @@ export class TagManager extends Token {
delete this._tags[key];
}

/**
* Handles returning the tags in the desired format
*
* This function can be overridden to support another tag format. This was
* specifically designed to enable AutoScalingGroup Tags that have an
* additional CloudFormation key for `PropagateAtLaunch`
*/
protected tagFormatResolve(tagGroups: TagGroups): any {
const tags = {...tagGroups.nonSitckyTags, ...tagGroups.ancestorTags, ...tagGroups.stickyTags};
for (const key of this.blockedTags) { delete tags[key]; }
Expand Down

0 comments on commit 8606f8d

Please sign in to comment.