From 849874045cd1e877619c3b636e6f16a58c85b4a1 Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Wed, 24 May 2023 10:20:46 -0700 Subject: [PATCH] feat(batch-alpha): tag instances launched from your managed CEs (#25643) `Tags.of()` now applies to `ComputeResources.Tags` ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-batch-alpha/README.md | 16 ++ .../lib/managed-compute-environment.ts | 8 +- .../batch-stack.assets.json | 4 +- .../batch-stack.template.json | 134 +++++++++++ .../manifest.json | 35 +-- .../tree.json | 208 +++++++++++++++++- .../test/integ.managed-compute-environment.ts | 12 +- .../test/managed-compute-environment.test.ts | 24 +- 8 files changed, 420 insertions(+), 21 deletions(-) diff --git a/packages/@aws-cdk/aws-batch-alpha/README.md b/packages/@aws-cdk/aws-batch-alpha/README.md index 343b76fa4ce64..8ee8f6fb6c50c 100644 --- a/packages/@aws-cdk/aws-batch-alpha/README.md +++ b/packages/@aws-cdk/aws-batch-alpha/README.md @@ -204,6 +204,22 @@ new batch.ManagedEc2EcsComputeEnvironment(this, 'myEc2ComputeEnv', { }); ``` +### Tagging Instances + +You can tag any instances launched by your managed EC2 ComputeEnvironments by using the CDK `Tags` API: + +```ts +import { Tags } from 'aws-cdk-lib'; + +declare const vpc: ec2.IVpc; + +const tagCE = new batch.ManagedEc2EcsComputeEnvironment(this, 'CEThatMakesTaggedInstnaces', { + vpc, +}); + +Tags.of(tagCE).add('super', 'salamander'); +``` + Unmanaged `ComputeEnvironment`s do not support `maxvCpus` or `minvCpus` because you must provision and manage the instances yourself; that is, Batch will not scale them up and down as needed. diff --git a/packages/@aws-cdk/aws-batch-alpha/lib/managed-compute-environment.ts b/packages/@aws-cdk/aws-batch-alpha/lib/managed-compute-environment.ts index 35b5d9d7328a5..e532089cbeb4c 100644 --- a/packages/@aws-cdk/aws-batch-alpha/lib/managed-compute-environment.ts +++ b/packages/@aws-cdk/aws-batch-alpha/lib/managed-compute-environment.ts @@ -2,7 +2,7 @@ import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as eks from 'aws-cdk-lib/aws-eks'; import * as iam from 'aws-cdk-lib/aws-iam'; import { IRole } from 'aws-cdk-lib/aws-iam'; -import { ArnFormat, Duration, Lazy, Resource, Stack } from 'aws-cdk-lib'; +import { ArnFormat, Duration, ITaggable, Lazy, Resource, Stack, TagManager, TagType } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { CfnComputeEnvironment } from 'aws-cdk-lib/aws-batch'; import { IComputeEnvironment, ComputeEnvironmentBase, ComputeEnvironmentProps } from './compute-environment-base'; @@ -11,7 +11,7 @@ import { IComputeEnvironment, ComputeEnvironmentBase, ComputeEnvironmentProps } * Represents a Managed ComputeEnvironment. Batch will provision EC2 Instances to * meet the requirements of the jobs executing in this ComputeEnvironment. */ -export interface IManagedComputeEnvironment extends IComputeEnvironment, ec2.IConnectable { +export interface IManagedComputeEnvironment extends IComputeEnvironment, ec2.IConnectable, ITaggable { /** * The maximum vCpus this `ManagedComputeEnvironment` can scale up to. * @@ -205,6 +205,7 @@ export abstract class ManagedComputeEnvironmentBase extends ComputeEnvironmentBa public readonly terminateOnUpdate?: boolean; public readonly securityGroups: ec2.ISecurityGroup[]; public readonly updateToLatestImageVersion?: boolean; + public readonly tags: TagManager = new TagManager(TagType.MAP, 'AWS::Batch::ComputeEnvironment'); public readonly connections: ec2.Connections; @@ -594,6 +595,7 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa public readonly maxvCpus = 1; public readonly connections = { } as any; public readonly securityGroups = []; + public readonly tags: TagManager = new TagManager(TagType.MAP, 'AWS::Batch::ComputeEnvironment'); public addInstanceClass(_instanceClass: ec2.InstanceClass): void { throw new Error(`cannot add instance class to imported ComputeEnvironment '${id}'`); @@ -673,6 +675,7 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa }; }), placementGroup: this.placementGroup?.placementGroupName, + tags: this.tags.renderedTags as any, }, }); @@ -1019,6 +1022,7 @@ export class ManagedEc2EksComputeEnvironment extends ManagedComputeEnvironmentBa }; }), placementGroup: this.placementGroup?.placementGroupName, + tags: this.tags.renderedTags as any, }, }); diff --git a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/batch-stack.assets.json b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/batch-stack.assets.json index 2f0e3b17154d0..7a42d47a88c9b 100644 --- a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/batch-stack.assets.json +++ b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/batch-stack.assets.json @@ -1,7 +1,7 @@ { "version": "31.0.0", "files": { - "f47b7d60111f82dad5c04d0bef76e1b62fe75dc319951520566c9d9bce188d10": { + "6ebdcdec29ca32bb55c4daa83140fbc6af6c8a2663beb1c1a833a3d4c6ee12c0": { "source": { "path": "batch-stack.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "f47b7d60111f82dad5c04d0bef76e1b62fe75dc319951520566c9d9bce188d10.json", + "objectKey": "6ebdcdec29ca32bb55c4daa83140fbc6af6c8a2663beb1c1a833a3d4c6ee12c0.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/batch-stack.template.json b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/batch-stack.template.json index 0b0ccd376482a..39f6afe175e92 100644 --- a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/batch-stack.template.json +++ b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/batch-stack.template.json @@ -882,6 +882,140 @@ "State": "ENABLED", "UpdatePolicy": {} } + }, + "taggedCESecurityGroup82CCF59F": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "batch-stack/taggedCE/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "Tags": [ + { + "Key": "foo", + "Value": "bar" + }, + { + "Key": "super", + "Value": "salamander" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "taggedCEInstanceProfileRoleC239DAF9": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" + ] + ] + } + ], + "Tags": [ + { + "Key": "foo", + "Value": "bar" + }, + { + "Key": "super", + "Value": "salamander" + } + ] + } + }, + "taggedCEInstanceProfileB29F2197": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "taggedCEInstanceProfileRoleC239DAF9" + } + ] + } + }, + "taggedCE5029E6F8": { + "Type": "AWS::Batch::ComputeEnvironment", + "Properties": { + "Type": "managed", + "ComputeResources": { + "AllocationStrategy": "BEST_FIT_PROGRESSIVE", + "Ec2Configuration": [ + { + "ImageIdOverride": { + "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamznamihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "ImageType": "ECS_AL2" + } + ], + "InstanceRole": { + "Fn::GetAtt": [ + "taggedCEInstanceProfileB29F2197", + "Arn" + ] + }, + "InstanceTypes": [ + "optimal" + ], + "MaxvCpus": 256, + "MinvCpus": 0, + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "taggedCESecurityGroup82CCF59F", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + }, + { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + } + ], + "Tags": { + "foo": "bar", + "super": "salamander" + }, + "Type": "EC2", + "UpdateToLatestImageVersion": true + }, + "ReplaceComputeEnvironment": false, + "State": "ENABLED", + "Tags": { + "foo": "bar", + "super": "salamander" + }, + "UpdatePolicy": {} + } } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/manifest.json b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/manifest.json index b0cfa1b0b3bed..a34d57abde72f 100644 --- a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/f47b7d60111f82dad5c04d0bef76e1b62fe75dc319951520566c9d9bce188d10.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/6ebdcdec29ca32bb55c4daa83140fbc6af6c8a2663beb1c1a833a3d4c6ee12c0.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -291,35 +291,44 @@ "data": "SpotEc2A0470C83" } ], - "/batch-stack/BootstrapVersion": [ + "/batch-stack/taggedCE/SecurityGroup/Resource": [ { "type": "aws:cdk:logicalId", - "data": "BootstrapVersion" + "data": "taggedCESecurityGroup82CCF59F" } ], - "/batch-stack/CheckBootstrapVersion": [ + "/batch-stack/taggedCE/InstanceProfileRole/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CheckBootstrapVersion" + "data": "taggedCEInstanceProfileRoleC239DAF9" } ], - "minimalPropsFargate8E9B9556": [ + "/batch-stack/taggedCE/InstanceProfile": [ { "type": "aws:cdk:logicalId", - "data": "minimalPropsFargate8E9B9556", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] + "data": "taggedCEInstanceProfileB29F2197" } ], - "maximalPropsFargateA2E688D8": [ + "/batch-stack/taggedCE/Resource": [ { "type": "aws:cdk:logicalId", - "data": "maximalPropsFargateA2E688D8", + "data": "taggedCE5029E6F8", "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" ] } + ], + "/batch-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/batch-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } ] }, "displayName": "batch-stack" diff --git a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/tree.json b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/tree.json index f9e0f8c3e62a6..8b24aaded4e56 100644 --- a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.js.snapshot/tree.json @@ -1504,6 +1504,210 @@ "version": "0.0.0" } }, + "taggedCE": { + "id": "taggedCE", + "path": "batch-stack/taggedCE", + "children": { + "SecurityGroup": { + "id": "SecurityGroup", + "path": "batch-stack/taggedCE/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "batch-stack/taggedCE/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "batch-stack/taggedCE/SecurityGroup", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "tags": [ + { + "key": "foo", + "value": "bar" + }, + { + "key": "super", + "value": "salamander" + } + ], + "vpcId": { + "Ref": "vpcA2121C38" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.SecurityGroup", + "version": "0.0.0" + } + }, + "InstanceProfileRole": { + "id": "InstanceProfileRole", + "path": "batch-stack/taggedCE/InstanceProfileRole", + "children": { + "ImportInstanceProfileRole": { + "id": "ImportInstanceProfileRole", + "path": "batch-stack/taggedCE/InstanceProfileRole/ImportInstanceProfileRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "batch-stack/taggedCE/InstanceProfileRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" + ] + ] + } + ], + "tags": [ + { + "key": "foo", + "value": "bar" + }, + { + "key": "super", + "value": "salamander" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "InstanceProfile": { + "id": "InstanceProfile", + "path": "batch-stack/taggedCE/InstanceProfile", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::InstanceProfile", + "aws:cdk:cloudformation:props": { + "roles": [ + { + "Ref": "taggedCEInstanceProfileRoleC239DAF9" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnInstanceProfile", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "batch-stack/taggedCE/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Batch::ComputeEnvironment", + "aws:cdk:cloudformation:props": { + "type": "managed", + "computeResources": { + "maxvCpus": 256, + "type": "EC2", + "updateToLatestImageVersion": true, + "securityGroupIds": [ + { + "Fn::GetAtt": [ + "taggedCESecurityGroup82CCF59F", + "GroupId" + ] + } + ], + "subnets": [ + { + "Ref": "vpcPrivateSubnet1Subnet934893E8" + }, + { + "Ref": "vpcPrivateSubnet2Subnet7031C2BA" + } + ], + "minvCpus": 0, + "instanceRole": { + "Fn::GetAtt": [ + "taggedCEInstanceProfileB29F2197", + "Arn" + ] + }, + "instanceTypes": [ + "optimal" + ], + "allocationStrategy": "BEST_FIT_PROGRESSIVE", + "ec2Configuration": [ + { + "imageIdOverride": { + "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamznamihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "imageType": "ECS_AL2" + } + ], + "tags": { + "foo": "bar", + "super": "salamander" + } + }, + "replaceComputeEnvironment": false, + "state": "ENABLED", + "tags": { + "foo": "bar", + "super": "salamander" + }, + "updatePolicy": {} + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_batch.CfnComputeEnvironment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-batch-alpha.ManagedEc2EcsComputeEnvironment", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "batch-stack/BootstrapVersion", @@ -1539,7 +1743,7 @@ "path": "BatchManagedComputeEnvironmentTest/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.270" + "version": "10.2.26" } }, "DeployAssert": { @@ -1585,7 +1789,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.270" + "version": "10.2.26" } } }, diff --git a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.ts b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.ts index d19e596f9ad0a..a2976e021f3f6 100644 --- a/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.ts +++ b/packages/@aws-cdk/aws-batch-alpha/test/integ.managed-compute-environment.ts @@ -1,7 +1,7 @@ import * as ec2 from 'aws-cdk-lib/aws-ec2'; import { LaunchTemplate } from 'aws-cdk-lib/aws-ec2'; import { Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; -import { App, Duration, Stack } from 'aws-cdk-lib'; +import { App, Duration, Stack, Tags } from 'aws-cdk-lib'; import * as integ from '@aws-cdk/integ-tests-alpha'; import { AllocationStrategy, FargateComputeEnvironment, ManagedEc2EcsComputeEnvironment } from '../lib'; @@ -59,6 +59,16 @@ new ManagedEc2EcsComputeEnvironment(stack, 'SpotEc2', { }), }); +const taggedEc2Ecs = new ManagedEc2EcsComputeEnvironment(stack, 'taggedCE', { + vpc, + images: [{ + image: new ec2.AmazonLinuxImage(), + }], +}); + +Tags.of(taggedEc2Ecs).add('foo', 'bar'); +Tags.of(taggedEc2Ecs).add('super', 'salamander'); + new integ.IntegTest(app, 'BatchManagedComputeEnvironmentTest', { testCases: [stack], }); diff --git a/packages/@aws-cdk/aws-batch-alpha/test/managed-compute-environment.test.ts b/packages/@aws-cdk/aws-batch-alpha/test/managed-compute-environment.test.ts index 687666dd6947e..bc8bb7aae6b3c 100644 --- a/packages/@aws-cdk/aws-batch-alpha/test/managed-compute-environment.test.ts +++ b/packages/@aws-cdk/aws-batch-alpha/test/managed-compute-environment.test.ts @@ -2,7 +2,7 @@ import { Template } from 'aws-cdk-lib/assertions'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as eks from 'aws-cdk-lib/aws-eks'; import { ArnPrincipal, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; -import { Stack, Duration } from 'aws-cdk-lib'; +import { Stack, Duration, Tags } from 'aws-cdk-lib'; import { capitalizePropertyNames } from './utils'; import * as batch from '../lib'; import { AllocationStrategy, ManagedEc2EcsComputeEnvironment, ManagedEc2EcsComputeEnvironmentProps, ManagedEc2EksComputeEnvironment, ManagedEc2EksComputeEnvironmentProps } from '../lib'; @@ -564,6 +564,28 @@ describe.each([ManagedEc2EcsComputeEnvironment, ManagedEc2EksComputeEnvironment] }); }); + test('respects tags', () => { + // WHEN + const ce = new ComputeEnvironment(stack, 'MyCE', { + ...defaultProps, + }); + + Tags.of(ce).add('superfood', 'acai'); + Tags.of(ce).add('super', 'salamander'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Batch::ComputeEnvironment', { + ...expectedProps, + ComputeResources: { + ...defaultComputeResources, + Tags: { + superfood: 'acai', + super: 'salamander', + }, + }, + }); + }); + test('can be imported from arn', () => { // WHEN const ce = ManagedEc2EcsComputeEnvironment.fromManagedEc2EcsComputeEnvironmentArn(stack, 'import', 'arn:aws:batch:us-east-1:123456789012:compute-environment/ce-name');