diff --git a/packages/@aws-cdk/aws-athena/test/athena.test.ts b/packages/@aws-cdk/aws-athena/test/athena.test.ts index 25a43aeec0679..6cadc1a5a8310 100644 --- a/packages/@aws-cdk/aws-athena/test/athena.test.ts +++ b/packages/@aws-cdk/aws-athena/test/athena.test.ts @@ -43,8 +43,8 @@ describe('Athena Workgroup Tags', () => { }); test('test tag aspect spec correction', () => { const stack = new cdk.Stack(); - cdk.Tag.add(stack, 'key1', 'value1'); - cdk.Tag.add(stack, 'key2', 'value2'); + cdk.Tags.of(stack).add('key1', 'value1'); + cdk.Tags.of(stack).add('key2', 'value2'); new CfnWorkGroup(stack, 'Athena-Workgroup', { name: 'HelloWorld', description: 'A WorkGroup', diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index db3de7c3fece2..d3056d32d418a 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -7,7 +7,7 @@ import * as sns from '@aws-cdk/aws-sns'; import { CfnAutoScalingRollingUpdate, Construct, Duration, Fn, IResource, Lazy, PhysicalName, Resource, Stack, - Tag, Tokenization, withResolved, + Tokenization, withResolved, Tags, } from '@aws-cdk/core'; import { CfnAutoScalingGroup, CfnAutoScalingGroupProps, CfnLaunchConfiguration } from './autoscaling.generated'; import { BasicLifecycleHookProps, LifecycleHook } from './lifecycle-hook'; @@ -595,7 +595,7 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements }); this.connections = new ec2.Connections({ securityGroups: [this.securityGroup] }); this.securityGroups.push(this.securityGroup); - this.node.applyAspect(new Tag(NAME_TAG, this.node.path)); + Tags.of(this).add(NAME_TAG, this.node.path); this.role = props.role || new iam.Role(this, 'InstanceRole', { roleName: PhysicalName.GENERATE_IF_NEEDED, diff --git a/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts b/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts index 67335c9cdf4ea..4e4e800828780 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts @@ -512,8 +512,9 @@ nodeunitShim({ pauseTime: cdk.Duration.seconds(345), }, }); - asg.node.applyAspect(new cdk.Tag('superfood', 'acai')); - asg.node.applyAspect(new cdk.Tag('notsuper', 'caramel', { applyToLaunchedInstances: false })); + + cdk.Tags.of(asg).add('superfood', 'acai'); + cdk.Tags.of(asg).add('notsuper', 'caramel', { applyToLaunchedInstances: false }); // THEN expect(stack).to(haveResource('AWS::AutoScaling::AutoScalingGroup', { diff --git a/packages/@aws-cdk/aws-backup/lib/selection.ts b/packages/@aws-cdk/aws-backup/lib/selection.ts index 735d55974ed6f..6b4c1c37b1b97 100644 --- a/packages/@aws-cdk/aws-backup/lib/selection.ts +++ b/packages/@aws-cdk/aws-backup/lib/selection.ts @@ -1,5 +1,5 @@ import * as iam from '@aws-cdk/aws-iam'; -import { Construct, Lazy, Resource } from '@aws-cdk/core'; +import { Construct, Lazy, Resource, Aspects } from '@aws-cdk/core'; import { CfnBackupSelection } from './backup.generated'; import { BackupableResourcesCollector } from './backupable-resources-collector'; import { IBackupPlan } from './plan'; @@ -126,7 +126,7 @@ export class BackupSelection extends Resource implements iam.IGrantable { } if (resource.construct) { - resource.construct.node.applyAspect(this.backupableResourcesCollector); + Aspects.of(resource.construct).add(this.backupableResourcesCollector); // Cannot push `this.backupableResourcesCollector.resources` to // `this.resources` here because it has not been evaluated yet. // Will be concatenated to `this.resources` in a `Lazy.listValue` diff --git a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts index 28f5480ee998d..7576dacdf1c65 100644 --- a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts +++ b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts @@ -2,7 +2,7 @@ import '@aws-cdk/assert/jest'; import { ABSENT } from '@aws-cdk/assert/lib/assertions/have-resource'; import { Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; -import { CfnParameter, Construct, Duration, Stack, Tag } from '@aws-cdk/core'; +import { CfnParameter, Construct, Duration, Stack, Tags } from '@aws-cdk/core'; import { AccountRecovery, Mfa, NumberAttribute, StringAttribute, UserPool, UserPoolIdentityProvider, UserPoolOperation, VerificationEmailStyle } from '../lib'; describe('User Pool', () => { @@ -227,7 +227,7 @@ describe('User Pool', () => { const pool = new UserPool(stack, 'Pool', { userPoolName: 'myPool', }); - Tag.add(pool, 'PoolTag', 'PoolParty'); + Tags.of(pool).add('PoolTag', 'PoolParty'); // THEN expect(stack).toHaveResourceLike('AWS::Cognito::UserPool', { diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts index 5c6efcf4385f0..990e9bcd42094 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts @@ -3,7 +3,7 @@ import '@aws-cdk/assert/jest'; import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; -import { App, CfnDeletionPolicy, ConstructNode, Duration, PhysicalName, RemovalPolicy, Stack, Tag } from '@aws-cdk/core'; +import { App, CfnDeletionPolicy, ConstructNode, Duration, PhysicalName, RemovalPolicy, Stack, Tags } from '@aws-cdk/core'; import { Attribute, AttributeType, @@ -324,7 +324,7 @@ test('when specifying every property', () => { partitionKey: TABLE_PARTITION_KEY, sortKey: TABLE_SORT_KEY, }); - table.node.applyAspect(new Tag('Environment', 'Production')); + Tags.of(table).add('Environment', 'Production'); expect(stack).toHaveResource('AWS::DynamoDB::Table', { @@ -357,7 +357,7 @@ test('when specifying sse with customer managed CMK', () => { encryption: TableEncryption.CUSTOMER_MANAGED, partitionKey: TABLE_PARTITION_KEY, }); - table.node.applyAspect(new Tag('Environment', 'Production')); + Tags.of(table).add('Environment', 'Production'); expect(stack).toHaveResource('AWS::DynamoDB::Table', { 'SSESpecification': { @@ -383,7 +383,7 @@ test('when specifying only encryptionKey', () => { encryptionKey, partitionKey: TABLE_PARTITION_KEY, }); - table.node.applyAspect(new Tag('Environment', 'Production')); + Tags.of(table).add('Environment', 'Production'); expect(stack).toHaveResource('AWS::DynamoDB::Table', { 'SSESpecification': { @@ -410,7 +410,7 @@ test('when specifying sse with customer managed CMK with encryptionKey provided encryptionKey, partitionKey: TABLE_PARTITION_KEY, }); - table.node.applyAspect(new Tag('Environment', 'Production')); + Tags.of(table).add('Environment', 'Production'); expect(stack).toHaveResource('AWS::DynamoDB::Table', { 'SSESpecification': { diff --git a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ondemand.ts b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ondemand.ts index 3304c8defec2e..83495c01355cd 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ondemand.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ondemand.ts @@ -1,4 +1,4 @@ -import { App, RemovalPolicy, Stack, Tag } from '@aws-cdk/core'; +import { App, RemovalPolicy, Stack, Tags } from '@aws-cdk/core'; import { Attribute, AttributeType, BillingMode, ProjectionType, StreamViewType, Table } from '../lib'; // CDK parameters @@ -58,7 +58,7 @@ const tableWithGlobalAndLocalSecondaryIndex = new Table(stack, TABLE_WITH_GLOBAL removalPolicy: RemovalPolicy.DESTROY, }); -tableWithGlobalAndLocalSecondaryIndex.node.applyAspect(new Tag('Environment', 'Production')); +Tags.of(tableWithGlobalAndLocalSecondaryIndex).add('Environment', 'Production'); tableWithGlobalAndLocalSecondaryIndex.addGlobalSecondaryIndex({ indexName: GSI_TEST_CASE_1, diff --git a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.sse.ts b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.sse.ts index b1f3dca8b75a3..de077281b7479 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.sse.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.sse.ts @@ -1,6 +1,6 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; -import { App, RemovalPolicy, Stack, Tag } from '@aws-cdk/core'; +import { App, RemovalPolicy, Stack, Tags } from '@aws-cdk/core'; import { Attribute, AttributeType, ProjectionType, StreamViewType, Table, TableEncryption } from '../lib'; // CDK parameters @@ -58,7 +58,7 @@ const tableWithGlobalAndLocalSecondaryIndex = new Table(stack, TABLE_WITH_GLOBAL removalPolicy: RemovalPolicy.DESTROY, }); -tableWithGlobalAndLocalSecondaryIndex.node.applyAspect(new Tag('Environment', 'Production')); +Tags.of(tableWithGlobalAndLocalSecondaryIndex).add('Environment', 'Production'); tableWithGlobalAndLocalSecondaryIndex.addGlobalSecondaryIndex({ indexName: GSI_TEST_CASE_1, partitionKey: GSI_PARTITION_KEY, diff --git a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts index 35c81b6486d3b..e01d3dd996101 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts @@ -1,5 +1,5 @@ import * as iam from '@aws-cdk/aws-iam'; -import { App, RemovalPolicy, Stack, Tag } from '@aws-cdk/core'; +import { App, RemovalPolicy, Stack, Tags } from '@aws-cdk/core'; import { Attribute, AttributeType, ProjectionType, StreamViewType, Table } from '../lib'; // CDK parameters @@ -56,7 +56,7 @@ const tableWithGlobalAndLocalSecondaryIndex = new Table(stack, TABLE_WITH_GLOBAL removalPolicy: RemovalPolicy.DESTROY, }); -tableWithGlobalAndLocalSecondaryIndex.node.applyAspect(new Tag('Environment', 'Production')); +Tags.of(tableWithGlobalAndLocalSecondaryIndex).add('Environment', 'Production'); tableWithGlobalAndLocalSecondaryIndex.addGlobalSecondaryIndex({ indexName: GSI_TEST_CASE_1, partitionKey: GSI_PARTITION_KEY, diff --git a/packages/@aws-cdk/aws-ec2/lib/instance.ts b/packages/@aws-cdk/aws-ec2/lib/instance.ts index 455b024c62caa..a9ef8112dc379 100644 --- a/packages/@aws-cdk/aws-ec2/lib/instance.ts +++ b/packages/@aws-cdk/aws-ec2/lib/instance.ts @@ -1,7 +1,7 @@ import * as crypto from 'crypto'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct, Duration, Fn, IResource, Lazy, Resource, Stack, Tag } from '@aws-cdk/core'; +import { Construct, Duration, Fn, IResource, Lazy, Resource, Stack, Tags } from '@aws-cdk/core'; import { CloudFormationInit } from './cfn-init'; import { Connections, IConnectable } from './connections'; import { CfnInstance } from './ec2.generated'; @@ -309,7 +309,7 @@ export class Instance extends Resource implements IInstance { } this.connections = new Connections({ securityGroups: [this.securityGroup] }); this.securityGroups.push(this.securityGroup); - Tag.add(this, NAME_TAG, props.instanceName || this.node.path); + Tags.of(this).add(NAME_TAG, props.instanceName || this.node.path); this.role = props.role || new iam.Role(this, 'InstanceRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), diff --git a/packages/@aws-cdk/aws-ec2/lib/volume.ts b/packages/@aws-cdk/aws-ec2/lib/volume.ts index 8817a08025e92..72c9af36fd079 100644 --- a/packages/@aws-cdk/aws-ec2/lib/volume.ts +++ b/packages/@aws-cdk/aws-ec2/lib/volume.ts @@ -2,7 +2,7 @@ import * as crypto from 'crypto'; import { AccountRootPrincipal, Grant, IGrantable } from '@aws-cdk/aws-iam'; import { IKey, ViaServicePrincipal } from '@aws-cdk/aws-kms'; -import { Construct, IResource, Resource, Size, SizeRoundingBehavior, Stack, Tag, Token } from '@aws-cdk/core'; +import { Construct, IResource, Resource, Size, SizeRoundingBehavior, Stack, Token, Tags } from '@aws-cdk/core'; import { CfnInstance, CfnVolume } from './ec2.generated'; import { IInstance } from './instance'; @@ -507,8 +507,8 @@ abstract class VolumeBase extends Resource implements IVolume { // The ResourceTag condition requires that all resources involved in the operation have // the given tag, so we tag this and all constructs given. - Tag.add(this, tagKey, tagValue); - constructs.forEach(construct => Tag.add(construct, tagKey, tagValue)); + Tags.of(this).add(tagKey, tagValue); + constructs.forEach(construct => Tags.of(construct).add(tagKey, tagValue)); return result; } @@ -536,8 +536,8 @@ abstract class VolumeBase extends Resource implements IVolume { // The ResourceTag condition requires that all resources involved in the operation have // the given tag, so we tag this and all constructs given. - Tag.add(this, tagKey, tagValue); - constructs.forEach(construct => Tag.add(construct, tagKey, tagValue)); + Tags.of(this).add(tagKey, tagValue); + constructs.forEach(construct => Tags.of(construct).add(tagKey, tagValue)); return result; } diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index 7c4d792fcc684..6fa50c0de2175 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -1,7 +1,7 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { ConcreteDependable, Construct, ContextProvider, DependableTrait, IConstruct, - IDependable, IResource, Lazy, Resource, Stack, Tag, Token, + IDependable, IResource, Lazy, Resource, Stack, Token, Tags, } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { @@ -1182,7 +1182,7 @@ export class Vpc extends VpcBase { this.vpcDefaultSecurityGroup = this.resource.attrDefaultSecurityGroup; this.vpcIpv6CidrBlocks = this.resource.attrIpv6CidrBlocks; - this.node.applyAspect(new Tag(NAME_TAG, this.node.path)); + Tags.of(this).add(NAME_TAG, this.node.path); this.availabilityZones = stack.availabilityZones; @@ -1369,8 +1369,8 @@ export class Vpc extends VpcBase { // These values will be used to recover the config upon provider import const includeResourceTypes = [CfnSubnet.CFN_RESOURCE_TYPE_NAME]; - subnet.node.applyAspect(new Tag(SUBNETNAME_TAG, subnetConfig.name, { includeResourceTypes })); - subnet.node.applyAspect(new Tag(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType), { includeResourceTypes })); + Tags.of(subnet).add(SUBNETNAME_TAG, subnetConfig.name, { includeResourceTypes }); + Tags.of(subnet).add(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType), { includeResourceTypes }); }); } } @@ -1488,7 +1488,7 @@ export class Subnet extends Resource implements ISubnet { Object.defineProperty(this, VPC_SUBNET_SYMBOL, { value: true }); - this.node.applyAspect(new Tag(NAME_TAG, this.node.path)); + Tags.of(this).add(NAME_TAG, this.node.path); this.availabilityZone = props.availabilityZone; const subnet = new CfnSubnet(this, 'Subnet', { diff --git a/packages/@aws-cdk/aws-ec2/test/volume.test.ts b/packages/@aws-cdk/aws-ec2/test/volume.test.ts index 05ae59f8d7a0f..e0d37a232c6d6 100644 --- a/packages/@aws-cdk/aws-ec2/test/volume.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/volume.test.ts @@ -73,7 +73,7 @@ nodeunitShim({ }); // WHEN - cdk.Tag.add(volume, 'TagKey', 'TagValue'); + cdk.Tags.of(volume).add('TagKey', 'TagValue'); // THEN cdkExpect(stack).to(haveResource('AWS::EC2::Volume', { diff --git a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts index ce94f1c9e10ed..87752fef51bdb 100644 --- a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts @@ -1,5 +1,5 @@ import { countResources, expect, haveResource, haveResourceLike, isSuperObject, MatchStyle } from '@aws-cdk/assert'; -import { CfnOutput, Lazy, Stack, Tag } from '@aws-cdk/core'; +import { CfnOutput, Lazy, Stack, Tags } from '@aws-cdk/core'; import { nodeunitShim, Test } from 'nodeunit-shim'; import { AclCidr, AclTraffic, CfnSubnet, CfnVPC, DefaultInstanceTenancy, GenericLinuxImage, InstanceType, InterfaceVpcEndpoint, @@ -1035,8 +1035,8 @@ nodeunitShim({ const vpc = new Vpc(stack, 'TheVPC'); // overwrite to set propagate - vpc.node.applyAspect(new Tag('BusinessUnit', 'Marketing', { includeResourceTypes: [CfnVPC.CFN_RESOURCE_TYPE_NAME] })); - vpc.node.applyAspect(new Tag('VpcType', 'Good')); + Tags.of(vpc).add('BusinessUnit', 'Marketing', { includeResourceTypes: [CfnVPC.CFN_RESOURCE_TYPE_NAME] }); + Tags.of(vpc).add('VpcType', 'Good'); expect(stack).to(haveResource('AWS::EC2::VPC', hasTags(toCfnTags(allTags)))); const taggables = ['Subnet', 'InternetGateway', 'NatGateway', 'RouteTable']; const propTags = toCfnTags(tags); @@ -1067,7 +1067,7 @@ nodeunitShim({ const vpc = new Vpc(stack, 'TheVPC'); const tag = { Key: 'Late', Value: 'Adder' }; expect(stack).notTo(haveResource('AWS::EC2::VPC', hasTags([tag]))); - vpc.node.applyAspect(new Tag(tag.Key, tag.Value)); + Tags.of(vpc).add(tag.Key, tag.Value); expect(stack).to(haveResource('AWS::EC2::VPC', hasTags([tag]))); test.done(); }, diff --git a/packages/@aws-cdk/aws-efs/lib/efs-file-system.ts b/packages/@aws-cdk/aws-efs/lib/efs-file-system.ts index 854503f388f7d..775eaea806af6 100644 --- a/packages/@aws-cdk/aws-efs/lib/efs-file-system.ts +++ b/packages/@aws-cdk/aws-efs/lib/efs-file-system.ts @@ -1,6 +1,6 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as kms from '@aws-cdk/aws-kms'; -import { ConcreteDependable, Construct, IDependable, IResource, RemovalPolicy, Resource, Size, Tag } from '@aws-cdk/core'; +import { ConcreteDependable, Construct, IDependable, IResource, RemovalPolicy, Resource, Size, Tags } from '@aws-cdk/core'; import { AccessPoint, AccessPointOptions } from './access-point'; import { CfnFileSystem, CfnMountTarget } from './efs.generated'; @@ -255,7 +255,7 @@ export class FileSystem extends Resource implements IFileSystem { filesystem.applyRemovalPolicy(props.removalPolicy); this.fileSystemId = filesystem.ref; - Tag.add(this, 'Name', props.fileSystemName || this.node.path); + Tags.of(this).add('Name', props.fileSystemName || this.node.path); const securityGroup = (props.securityGroup || new ec2.SecurityGroup(this, 'EfsSecurityGroup', { vpc: props.vpc, diff --git a/packages/@aws-cdk/aws-efs/test/efs-file-system.test.ts b/packages/@aws-cdk/aws-efs/test/efs-file-system.test.ts index 738f81cb12369..02369563bd9a5 100644 --- a/packages/@aws-cdk/aws-efs/test/efs-file-system.test.ts +++ b/packages/@aws-cdk/aws-efs/test/efs-file-system.test.ts @@ -1,7 +1,7 @@ import { expect as expectCDK, haveResource, ResourcePart } from '@aws-cdk/assert'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as kms from '@aws-cdk/aws-kms'; -import { RemovalPolicy, Size, Stack, Tag } from '@aws-cdk/core'; +import { RemovalPolicy, Size, Stack, Tags } from '@aws-cdk/core'; import { FileSystem, LifecyclePolicy, PerformanceMode, ThroughputMode } from '../lib'; let stack = new Stack(); @@ -176,7 +176,7 @@ test('support tags', () => { const fileSystem = new FileSystem(stack, 'EfsFileSystem', { vpc, }); - Tag.add(fileSystem, 'Name', 'LookAtMeAndMyFancyTags'); + Tags.of(fileSystem).add('Name', 'LookAtMeAndMyFancyTags'); // THEN expectCDK(stack).to(haveResource('AWS::EFS::FileSystem', { diff --git a/packages/@aws-cdk/aws-eks-legacy/lib/cluster.ts b/packages/@aws-cdk/aws-eks-legacy/lib/cluster.ts index 49fde85d6d49d..c7597df495297 100644 --- a/packages/@aws-cdk/aws-eks-legacy/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks-legacy/lib/cluster.ts @@ -4,7 +4,7 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as ssm from '@aws-cdk/aws-ssm'; -import { CfnOutput, Construct, Duration, IResource, Resource, Stack, Tag, Token } from '@aws-cdk/core'; +import { CfnOutput, Construct, Duration, IResource, Resource, Stack, Token, Tags } from '@aws-cdk/core'; import { AwsAuth } from './aws-auth'; import { ClusterResource } from './cluster-resource'; import { CfnCluster, CfnClusterProps } from './eks.generated'; @@ -523,7 +523,7 @@ export class Cluster extends Resource implements ICluster { autoScalingGroup.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerRegistryReadOnly')); // EKS Required Tags - Tag.add(autoScalingGroup, `kubernetes.io/cluster/${this.clusterName}`, 'owned', { + Tags.of(autoScalingGroup).add(`kubernetes.io/cluster/${this.clusterName}`, 'owned', { applyToLaunchedInstances: true, }); @@ -640,7 +640,7 @@ export class Cluster extends Resource implements ICluster { continue; } - subnet.node.applyAspect(new Tag(tag, '1')); + Tags.of(subnet).add(tag, '1'); } }; diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 9204f9ff4b475..7db245a814efb 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -4,7 +4,7 @@ import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as ssm from '@aws-cdk/aws-ssm'; -import { CfnOutput, CfnResource, Construct, IResource, Resource, Stack, Tag, Token, Duration } from '@aws-cdk/core'; +import { CfnOutput, CfnResource, Construct, IResource, Resource, Stack, Tags, Token, Duration } from '@aws-cdk/core'; import * as YAML from 'yaml'; import { AwsAuth } from './aws-auth'; import { clusterArnComponents, ClusterResource } from './cluster-resource'; @@ -872,7 +872,7 @@ export class Cluster extends Resource implements ICluster { autoScalingGroup.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerRegistryReadOnly')); // EKS Required Tags - Tag.add(autoScalingGroup, `kubernetes.io/cluster/${this.clusterName}`, 'owned', { + Tags.of(autoScalingGroup).add(`kubernetes.io/cluster/${this.clusterName}`, 'owned', { applyToLaunchedInstances: true, }); @@ -1180,7 +1180,7 @@ export class Cluster extends Resource implements ICluster { continue; } - subnet.node.applyAspect(new Tag(tag, '1')); + Tags.of(subnet).add(tag, '1'); } }; diff --git a/packages/@aws-cdk/aws-eks/lib/legacy-cluster.ts b/packages/@aws-cdk/aws-eks/lib/legacy-cluster.ts index 02708d0dc9d0f..7aec7be0cd693 100644 --- a/packages/@aws-cdk/aws-eks/lib/legacy-cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/legacy-cluster.ts @@ -2,7 +2,7 @@ import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as ssm from '@aws-cdk/aws-ssm'; -import { CfnOutput, Construct, Resource, Stack, Tag, Token } from '@aws-cdk/core'; +import { CfnOutput, Construct, Resource, Stack, Token, Tags } from '@aws-cdk/core'; import { ICluster, ClusterAttributes, KubernetesVersion, NodeType, DefaultCapacityType, EksOptimizedImage, CapacityOptions, MachineImageType, AutoScalingGroupOptions, CommonClusterOptions } from './cluster'; import { clusterArnComponents } from './cluster-resource'; import { CfnCluster, CfnClusterProps } from './eks.generated'; @@ -327,7 +327,7 @@ export class LegacyCluster extends Resource implements ICluster { autoScalingGroup.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerRegistryReadOnly')); // EKS Required Tags - Tag.add(autoScalingGroup, `kubernetes.io/cluster/${this.clusterName}`, 'owned', { + Tags.of(autoScalingGroup).add(`kubernetes.io/cluster/${this.clusterName}`, 'owned', { applyToLaunchedInstances: true, }); @@ -361,7 +361,7 @@ export class LegacyCluster extends Resource implements ICluster { continue; } - subnet.node.applyAspect(new Tag(tag, '1')); + Tags.of(subnet).add(tag, '1'); } }; diff --git a/packages/@aws-cdk/aws-eks/test/test.fargate.ts b/packages/@aws-cdk/aws-eks/test/test.fargate.ts index f490d3f38df10..2787122883b82 100644 --- a/packages/@aws-cdk/aws-eks/test/test.fargate.ts +++ b/packages/@aws-cdk/aws-eks/test/test.fargate.ts @@ -1,7 +1,7 @@ import { expect, haveResource, haveResourceLike, ResourcePart } from '@aws-cdk/assert'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; -import { Stack, Tag } from '@aws-cdk/core'; +import { Stack, Tags } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as eks from '../lib'; @@ -85,8 +85,8 @@ export = { selectors: [{ namespace: 'default' }], }); - Tag.add(stack, 'aspectTag', 'hello'); - Tag.add(cluster, 'propTag', '123'); + Tags.of(stack).add('aspectTag', 'hello'); + Tags.of(cluster).add('propTag', '123'); // THEN expect(stack).to(haveResource('Custom::AWSCDK-EKS-FargateProfile', { diff --git a/packages/@aws-cdk/aws-kms/test/test.key.ts b/packages/@aws-cdk/aws-kms/test/test.key.ts index a428ea1c05f73..bcbaaec7f1c13 100644 --- a/packages/@aws-cdk/aws-kms/test/test.key.ts +++ b/packages/@aws-cdk/aws-kms/test/test.key.ts @@ -8,7 +8,7 @@ import { SynthUtils, } from '@aws-cdk/assert'; import * as iam from '@aws-cdk/aws-iam'; -import { App, CfnOutput, RemovalPolicy, Stack, Tag } from '@aws-cdk/core'; +import { App, CfnOutput, RemovalPolicy, Stack, Tags } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import { Key } from '../lib'; @@ -160,9 +160,9 @@ export = { p.addArnPrincipal('arn'); key.addToResourcePolicy(p); - key.node.applyAspect(new Tag('tag1', 'value1')); - key.node.applyAspect(new Tag('tag2', 'value2')); - key.node.applyAspect(new Tag('tag3', '')); + Tags.of(key).add('tag1', 'value1'); + Tags.of(key).add('tag2', 'value2'); + Tags.of(key).add('tag3', ''); expect(stack).to(exactlyMatchTemplate({ Resources: { diff --git a/packages/@aws-cdk/aws-route53/test/test.hosted-zone.ts b/packages/@aws-cdk/aws-route53/test/test.hosted-zone.ts index 45eaed8e8cafb..37d80a5908a9b 100644 --- a/packages/@aws-cdk/aws-route53/test/test.hosted-zone.ts +++ b/packages/@aws-cdk/aws-route53/test/test.hosted-zone.ts @@ -39,7 +39,7 @@ export = { const hostedZone = new HostedZone(stack, 'HostedZone', { zoneName: 'test.zone', }); - cdk.Tag.add(hostedZone, 'zoneTag', 'inMyZone'); + cdk.Tags.of(hostedZone).add('zoneTag', 'inMyZone'); // THEN expect(stack).toMatch({ diff --git a/packages/@aws-cdk/aws-s3-notifications/test/notifications.test.ts b/packages/@aws-cdk/aws-s3-notifications/test/notifications.test.ts index c919f1473aa4b..8c826e9217204 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/notifications.test.ts +++ b/packages/@aws-cdk/aws-s3-notifications/test/notifications.test.ts @@ -39,7 +39,7 @@ test('when notification are added, a custom resource is provisioned + a lambda h test('when notification are added, you can tag the lambda', () => { const stack = new cdk.Stack(); - stack.node.applyAspect(new cdk.Tag('Lambda', 'AreTagged')); + cdk.Tags.of(stack).add('Lambda', 'AreTagged'); const bucket = new s3.Bucket(stack, 'MyBucket'); diff --git a/packages/@aws-cdk/aws-s3/test/test.aspect.ts b/packages/@aws-cdk/aws-s3/test/test.aspect.ts index ba2049b0f993e..e85e4d243dca0 100644 --- a/packages/@aws-cdk/aws-s3/test/test.aspect.ts +++ b/packages/@aws-cdk/aws-s3/test/test.aspect.ts @@ -11,7 +11,7 @@ export = { new s3.Bucket(stack, 'MyBucket'); // WHEN - stack.node.applyAspect(new BucketVersioningChecker()); + cdk.Aspects.of(stack).add(new BucketVersioningChecker()); // THEN const assembly = SynthUtils.synthesize(stack); @@ -29,7 +29,7 @@ export = { }); // WHEN - stack.node.applyAspect(new BucketVersioningChecker()); + cdk.Aspects.of(stack).add(new BucketVersioningChecker()); // THEN const assembly = SynthUtils.synthesize(stack); diff --git a/packages/@aws-cdk/core/lib/annotations.ts b/packages/@aws-cdk/core/lib/annotations.ts index 8f13e09987035..8ddeedda108a2 100644 --- a/packages/@aws-cdk/core/lib/annotations.ts +++ b/packages/@aws-cdk/core/lib/annotations.ts @@ -1,6 +1,8 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { IConstruct } from './construct-compat'; +const DEPRECATIONS_SYMBOL = Symbol.for('@aws-cdk/core.deprecations'); + /** * Includes API for attaching annotations such as warning messages to constructs. */ @@ -49,6 +51,38 @@ export class Annotations { this.addMessage(cxschema.ArtifactMetadataEntryType.ERROR, message); } + /** + * Adds a deprecation warning for a specific API. + * + * Deprecations will be added only once per construct as a warning and will be + * deduplicated based on the `api`. + * + * If the environment variable `CDK_BLOCK_DEPRECATIONS` is set, this method + * will throw an error instead with the deprecation message. + * + * @param api The API being deprecated in the format `module.Class.property` + * (e.g. `@aws-cdk/core.Construct.node`). + * @param message The deprecation message to display, with information about + * alternatives. + */ + public addDeprecation(api: string, message: string) { + const text = `The API ${api} is deprecated: ${message}. This API will be removed in the next major release`; + + // throw if CDK_BLOCK_DEPRECATIONS is set + if (process.env.CDK_BLOCK_DEPRECATIONS) { + throw new Error(`${this.scope.node.path}: ${text}`); + } + + // de-dup based on api key + const set = this.deprecationsReported; + if (set.has(api)) { + return; + } + + this.addWarning(text); + set.add(api); + } + /** * Adds a message metadata entry to the construct node, to be displayed by the CDK CLI. * @param level The message level @@ -57,4 +91,17 @@ export class Annotations { private addMessage(level: string, message: string) { this.scope.node.addMetadata(level, message); } -} \ No newline at end of file + + /** + * Returns the set of deprecations reported on this construct. + */ + private get deprecationsReported() { + let set = (this.scope as any)[DEPRECATIONS_SYMBOL]; + if (!set) { + set = new Set(); + Object.defineProperty(this.scope, DEPRECATIONS_SYMBOL, { value: set }); + } + + return set; + } +} diff --git a/packages/@aws-cdk/core/lib/construct-compat.ts b/packages/@aws-cdk/core/lib/construct-compat.ts index 306e299016f7c..312ec463a92e6 100644 --- a/packages/@aws-cdk/core/lib/construct-compat.ts +++ b/packages/@aws-cdk/core/lib/construct-compat.ts @@ -447,7 +447,10 @@ export class ConstructNode { * @deprecated This API is going to be removed in the next major version of * the AWS CDK. Please use `Aspects.of(scope).add()` instead. */ - public applyAspect(aspect: IAspect): void { Aspects.of(this.host).add(aspect); } + public applyAspect(aspect: IAspect): void { + Annotations.of(this.host).addDeprecation('@aws-cdk/core.ConstructNode.applyAspect', 'Use "Aspects.of(construct).add(aspect)" instead'); + Aspects.of(this.host).add(aspect); + } /** * All parent scopes of this construct. diff --git a/packages/@aws-cdk/core/lib/index.ts b/packages/@aws-cdk/core/lib/index.ts index 92a6ae2f825ed..2ea4c92f79db4 100644 --- a/packages/@aws-cdk/core/lib/index.ts +++ b/packages/@aws-cdk/core/lib/index.ts @@ -36,6 +36,7 @@ export * from './stack-trace'; export * from './app'; export * from './context-provider'; export * from './environment'; +export * from './annotations'; export * from './runtime'; export * from './secret-value'; diff --git a/packages/@aws-cdk/core/lib/tag-aspect.ts b/packages/@aws-cdk/core/lib/tag-aspect.ts index 3c8fd7b01a6b8..34d9a6dfd4e40 100644 --- a/packages/@aws-cdk/core/lib/tag-aspect.ts +++ b/packages/@aws-cdk/core/lib/tag-aspect.ts @@ -2,6 +2,7 @@ import { IAspect, Aspects } from './aspect'; import { Construct, IConstruct } from './construct-compat'; import { ITaggable, TagManager } from './tag-manager'; +import { Annotations } from './annotations'; /** * Properties for a tag @@ -91,6 +92,7 @@ export class Tag extends TagBase { * @deprecated use `Tags.of(scope).add()` */ public static add(scope: Construct, key: string, value: string, props: TagProps = {}) { + Annotations.of(scope).addDeprecation('@aws-cdk/core.Tag.add(scope,k,v)', 'Use "Tags.of(scope).add(k,v)" instead'); Tags.of(scope).add(key, value, props); } @@ -100,6 +102,7 @@ export class Tag extends TagBase { * @deprecated use `Tags.of(scope).remove()` */ public static remove(scope: Construct, key: string, props: TagProps = {}) { + Annotations.of(scope).addDeprecation('@aws-cdk/core.Tag.remove(scope,k,v)', 'Use "Tags.of(scope).remove(k,v)" instead'); Tags.of(scope).remove(key, props); } diff --git a/packages/@aws-cdk/core/test/test.annotations.ts b/packages/@aws-cdk/core/test/test.annotations.ts new file mode 100644 index 0000000000000..4a193452a95c2 --- /dev/null +++ b/packages/@aws-cdk/core/test/test.annotations.ts @@ -0,0 +1,95 @@ +import { CloudAssembly } from '@aws-cdk/cx-api'; +import { Test } from 'nodeunit'; +import { Construct, App, Stack } from '../lib'; +import { Annotations } from '../lib/annotations'; + +const restore = process.env.CDK_BLOCK_DEPRECATIONS; + +export = { + 'tearDown'(cb: any) { + process.env.CDK_BLOCK_DEPRECATIONS = restore; // restore to the original value + cb(); + }, + + 'addDeprecation() annotates the usage of a deprecated API'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'MyStack'); + const c1 = new Construct(stack, 'Hello'); + + // WHEN + delete process.env.CDK_BLOCK_DEPRECATIONS; + Annotations.of(c1).addDeprecation('@aws-cdk/core.Construct.node', 'use @aws-cdk.Construct.construct instead'); + + // THEN + test.deepEqual(getWarnings(app.synth()), [ + { + path: '/MyStack/Hello', + message: 'The API @aws-cdk/core.Construct.node is deprecated: use @aws-cdk.Construct.construct instead. This API will be removed in the next major release', + }, + ]); + test.done(); + }, + + 'deduplicated per node based on "api"'(test: Test) { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'MyStack1'); + const stack2 = new Stack(app, 'MyStack2'); + const c1 = new Construct(stack1, 'Hello'); + const c2 = new Construct(stack1, 'World'); + const c3 = new Construct(stack2, 'FooBar'); + + // WHEN + delete process.env.CDK_BLOCK_DEPRECATIONS; + Annotations.of(c1).addDeprecation('@aws-cdk/core.Construct.node', 'use @aws-cdk.Construct.construct instead'); + Annotations.of(c2).addDeprecation('@aws-cdk/core.Construct.node', 'use @aws-cdk.Construct.construct instead'); + Annotations.of(c1).addDeprecation('@aws-cdk/core.Construct.node', 'use @aws-cdk.Construct.construct instead'); + Annotations.of(c3).addDeprecation('@aws-cdk/core.Construct.node', 'use @aws-cdk.Construct.construct instead'); + Annotations.of(c1).addDeprecation('@aws-cdk/core.Construct.node', 'use @aws-cdk.Construct.construct instead'); + Annotations.of(c1).addDeprecation('@aws-cdk/core.Construct.node', 'use @aws-cdk.Construct.construct instead'); + + // THEN + test.deepEqual(getWarnings(app.synth()), [ + { + path: '/MyStack1/Hello', + message: 'The API @aws-cdk/core.Construct.node is deprecated: use @aws-cdk.Construct.construct instead. This API will be removed in the next major release', + }, + { + path: '/MyStack1/World', + message: 'The API @aws-cdk/core.Construct.node is deprecated: use @aws-cdk.Construct.construct instead. This API will be removed in the next major release', + }, + { + path: '/MyStack2/FooBar', + message: 'The API @aws-cdk/core.Construct.node is deprecated: use @aws-cdk.Construct.construct instead. This API will be removed in the next major release', + }, + ]); + test.done(); + }, + + 'CDK_BLOCK_DEPRECATIONS will throw if a deprecated API is used'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'MyStack'); + const c1 = new Construct(stack, 'Hello'); + + // THEN + process.env.CDK_BLOCK_DEPRECATIONS = '1'; + test.throws(() => Annotations.of(c1).addDeprecation('foo', 'bar'), /MyStack\/Hello: The API foo is deprecated: bar\. This API will be removed in the next major release/); + test.done(); + }, +}; + +function getWarnings(casm: CloudAssembly) { + const result = new Array<{ path: string, message: string }>(); + for (const stack of Object.values(casm.manifest.artifacts ?? {})) { + for (const [path, md] of Object.entries(stack.metadata ?? {})) { + for (const x of md) { + if (x.type === 'aws:cdk:warning') { + result.push({ path, message: x.data as string }); + } + } + } + } + return result; +} diff --git a/packages/@aws-cdk/pipelines/lib/pipeline.ts b/packages/@aws-cdk/pipelines/lib/pipeline.ts index f20021b34d1a8..6b63c6216b959 100644 --- a/packages/@aws-cdk/pipelines/lib/pipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/pipeline.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as iam from '@aws-cdk/aws-iam'; -import { App, CfnOutput, Construct, PhysicalName, Stack, Stage } from '@aws-cdk/core'; +import { App, CfnOutput, Construct, PhysicalName, Stack, Stage, Aspects } from '@aws-cdk/core'; import { AssetType, DeployCdkStackAction, PublishAssetsAction, UpdatePipelineAction } from './actions'; import { appOf, assemblyBuilderOf } from './private/construct-internals'; import { AddStageOptions, AssetPublishingCommand, CdkStage, StackOutput } from './stage'; @@ -103,7 +103,7 @@ export class CdkPipeline extends Construct { projectName: maybeSuffix(props.pipelineName, '-publish'), }); - this.node.applyAspect({ visit: () => this._assets.removeAssetsStageIfEmpty() }); + Aspects.of(this).add({ visit: () => this._assets.removeAssetsStageIfEmpty() }); } /** diff --git a/packages/@aws-cdk/pipelines/lib/stage.ts b/packages/@aws-cdk/pipelines/lib/stage.ts index dd5aa28c5e50e..e916d8131c7a2 100644 --- a/packages/@aws-cdk/pipelines/lib/stage.ts +++ b/packages/@aws-cdk/pipelines/lib/stage.ts @@ -1,6 +1,6 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as cpactions from '@aws-cdk/aws-codepipeline-actions'; -import { Construct, Stage } from '@aws-cdk/core'; +import { Construct, Stage, Aspects } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { AssetType, DeployCdkStackAction } from './actions'; import { AssetManifestReader, DockerImageManifestEntry, FileManifestEntry } from './private/asset-manifest'; @@ -55,7 +55,7 @@ export class CdkStage extends Construct { this.cloudAssemblyArtifact = props.cloudAssemblyArtifact; this.host = props.host; - this.node.applyAspect({ visit: () => this.prepareStage() }); + Aspects.of(this).add({ visit: () => this.prepareStage() }); } /**