diff --git a/packages/@aws-cdk/aws-codedeploy/README.md b/packages/@aws-cdk/aws-codedeploy/README.md index 601829edab23b..5676114b9a317 100644 --- a/packages/@aws-cdk/aws-codedeploy/README.md +++ b/packages/@aws-cdk/aws-codedeploy/README.md @@ -32,6 +32,25 @@ const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'CodeDeployDe // adds User Data that installs the CodeDeploy agent on your auto-scaling groups hosts // default: true installAgent: true, + // adds EC2 instances matching tags + ec2InstanceTags: new codedeploy.InstanceTagSet( + { + // any instance with key1=v1 or key1=v2 or key2 (any value) + // tags will match this group + 'key1': ['v1', 'v2'], + 'key2': [], + }, + ), + // adds on-premise instances matching tags + onPremiseInstanceTags: new codedeploy.InstanceTagSet( + // only instances with tags k1=v1 AND k2=v2 will match this set + { + 'key1': ['v1'], + }, + { + 'key2': ['v2'], + }, + ) }); ``` diff --git a/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts index 4d488197d80ff..ac6100f1f028a 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts @@ -98,6 +98,37 @@ class ImportedServerDeploymentGroupRef extends ServerDeploymentGroupRef { } } +/** + * Represents a group of instance tags. + * An instance will match a group if it has a tag matching + * any of the group's tags by name and any of the provided values - + * in other words, tag groups follow 'or' semantics. + * If the value for a given key is an empty array, + * an instance will match when it has a tag with the given key, + * regardless of the value. + */ +export interface InstanceTagGroup { + [key: string]: string[], +} + +/** + * Represents a set of instance tag groups. + * An instance will match a set if it matches all of the groups in the set - + * in other words, sets follow 'and' semantics. + * You can have a maximum of 3 tag groups inside a set. + */ +export class InstanceTagSet { + public readonly instanceTagGroups: InstanceTagGroup[]; + + constructor(...instanceTagGroups: InstanceTagGroup[]) { + if (instanceTagGroups.length > 3) { + throw new Error('An instance tag set can have a maximum of 3 instance tag groups, ' + + `but ${instanceTagGroups.length} were provided`); + } + this.instanceTagGroups = instanceTagGroups; + } +} + /** * Construction properties for {@link ServerDeploymentGroup}. */ @@ -143,6 +174,20 @@ export interface ServerDeploymentGroupProps { * @see https://docs.aws.amazon.com/codedeploy/latest/userguide/codedeploy-agent-operations-install.html */ installAgent?: boolean; + + /** + * All EC2 instances matching the given set of tags will be added to this Deployment Group. + * + * @default no additional EC2 instances will be added to the Deployment Group + */ + ec2InstanceTags?: InstanceTagSet; + + /** + * All on-premise instances matching the given set of tags will be added to this Deployment Group. + * + * @default no additional on-premise instances will be added to the Deployment Group + */ + onPremiseInstanceTags?: InstanceTagSet; } /** @@ -188,6 +233,8 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef { this._autoScalingGroups.length === 0 ? undefined : this._autoScalingGroups.map(asg => asg.autoScalingGroupName())), + ec2TagSet: this.ec2TagSet(props.ec2InstanceTags), + onPremisesTagSet: this.onPremiseTagSet(props.onPremiseInstanceTags), }); this.deploymentGroupName = resource.deploymentGroupName; @@ -244,6 +291,62 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef { break; } } + + private ec2TagSet(tagSet?: InstanceTagSet): + cloudformation.DeploymentGroupResource.EC2TagSetProperty | undefined { + if (!tagSet || tagSet.instanceTagGroups.length === 0) { + return undefined; + } + + return { + ec2TagSetList: tagSet.instanceTagGroups.map(tagGroup => { + return { + ec2TagGroup: this.tagGroup2TagsArray(tagGroup) as + cloudformation.DeploymentGroupResource.EC2TagFilterProperty[], + }; + }), + }; + } + + private onPremiseTagSet(tagSet?: InstanceTagSet): + cloudformation.DeploymentGroupResource.OnPremisesTagSetProperty | undefined { + if (!tagSet || tagSet.instanceTagGroups.length === 0) { + return undefined; + } + + return { + onPremisesTagSetList: tagSet.instanceTagGroups.map(tagGroup => { + return { + onPremisesTagGroup: this.tagGroup2TagsArray(tagGroup) as + cloudformation.DeploymentGroupResource.TagFilterProperty[], + }; + }), + }; + } + + private tagGroup2TagsArray(tagGroup: InstanceTagGroup): any[] { + const tagsInGroup = []; + for (const tagKey in tagGroup) { + if (tagGroup.hasOwnProperty(tagKey)) { + const tagValues = tagGroup[tagKey]; + if (tagValues.length > 0) { + for (const tagValue of tagValues) { + tagsInGroup.push({ + key: tagKey, + value: tagValue, + type: 'KEY_AND_VALUE', + }); + } + } else { + tagsInGroup.push({ + key: tagKey, + type: 'KEY_AND_VALUE', + }); + } + } + } + return tagsInGroup; + } } function deploymentGroupName2Arn(applicationName: string, deploymentGroupName: string): string { 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 bd20dfb1bf707..0194836a01814 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/test.deployment-group.ts @@ -88,5 +88,94 @@ export = { test.done(); }, + + 'can be created with a single EC2 instance tag set with a single value'(test: Test) { + const stack = new cdk.Stack(); + + new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup', { + ec2InstanceTags: new codedeploy.InstanceTagSet( + { + 'some-key': ['some-value'], + 'other-key': [], + }, + ), + }); + + expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', { + "Ec2TagSet": { + "Ec2TagSetList": [ + { + "Ec2TagGroup": [ + { + "Key": "some-key", + "Value": "some-value", + "Type": "KEY_AND_VALUE", + }, + { + "Key": "other-key", + "Type": "KEY_AND_VALUE", + }, + ], + }, + ], + }, + })); + + test.done(); + }, + + 'can be created with two on-premise instance tag sets with multiple values'(test: Test) { + const stack = new cdk.Stack(); + + new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup', { + onPremiseInstanceTags: new codedeploy.InstanceTagSet( + { + 'some-key': ['some-value', 'another-value'], + }, + { + 'other-key': [], + }, + ), + }); + + expect(stack).to(haveResource('AWS::CodeDeploy::DeploymentGroup', { + "OnPremisesTagSet": { + "OnPremisesTagSetList": [ + { + "OnPremisesTagGroup": [ + { + "Key": "some-key", + "Value": "some-value", + "Type": "KEY_AND_VALUE", + }, + { + "Key": "some-key", + "Value": "another-value", + "Type": "KEY_AND_VALUE", + }, + ], + }, + { + "OnPremisesTagGroup": [ + { + "Key": "other-key", + "Type": "KEY_AND_VALUE", + }, + ], + }, + ], + }, + })); + + test.done(); + }, + + 'cannot be created with an instance tag set containing 4 instance tag groups'(test: Test) { + test.throws(() => { + new codedeploy.InstanceTagSet({}, {}, {}, {}); + }, /3/i); + + test.done(); + }, }, };