diff --git a/src/constants/__mocks__/library-info.ts b/src/constants/__mocks__/library-info.ts index 30a151a3f..ba9ed1892 100644 --- a/src/constants/__mocks__/library-info.ts +++ b/src/constants/__mocks__/library-info.ts @@ -3,6 +3,6 @@ export const LibraryInfo = { }; export const TrackingTag = { - Key: "X-Gu-CDK-Version", + Key: "gu:cdk:version", Value: LibraryInfo.VERSION, }; diff --git a/src/constants/library-info.ts b/src/constants/library-info.ts index bd36c1d43..d4b5d06c7 100644 --- a/src/constants/library-info.ts +++ b/src/constants/library-info.ts @@ -7,6 +7,6 @@ export const LibraryInfo = { }; export const TrackingTag = { - Key: "X-Gu-CDK-Version", + Key: "gu:cdk:version", Value: LibraryInfo.VERSION, }; diff --git a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap index a469d9bd7..06e0909d0 100644 --- a/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap +++ b/src/constructs/cloudwatch/__snapshots__/lambda-alarms.test.ts.snap @@ -43,6 +43,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -55,10 +59,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], "Timeout": 30, }, @@ -97,6 +97,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -109,10 +113,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", diff --git a/src/constructs/core/stack.test.ts b/src/constructs/core/stack.test.ts index fea9c49b1..d384a01f2 100644 --- a/src/constructs/core/stack.test.ts +++ b/src/constructs/core/stack.test.ts @@ -3,7 +3,7 @@ import "@aws-cdk/assert/jest"; import { SynthUtils } from "@aws-cdk/assert"; import { Role, ServicePrincipal } from "@aws-cdk/aws-iam"; import { App } from "@aws-cdk/core"; -import { simpleGuStackForTesting } from "../../../test/utils"; +import { alphabeticalTags, simpleGuStackForTesting } from "../../../test/utils"; import type { SynthedStack } from "../../../test/utils"; import { Stage, Stages } from "../../constants"; import { TrackingTag } from "../../constants/library-info"; @@ -38,7 +38,7 @@ describe("The GuStack construct", () => { }); expect(stack).toHaveResource("AWS::IAM::Role", { - Tags: [ + Tags: alphabeticalTags([ { Key: "App", Value: "MyApp", @@ -56,7 +56,7 @@ describe("The GuStack construct", () => { }, }, TrackingTag, - ], + ]), }); }); diff --git a/src/constructs/core/stack.ts b/src/constructs/core/stack.ts index 26a3177b6..13506c8ae 100644 --- a/src/constructs/core/stack.ts +++ b/src/constructs/core/stack.ts @@ -58,12 +58,17 @@ export class GuStack extends Stack { /** * A helper function to add a tag to all resources in a stack. + * + * Note: tags will be listed in alphabetical order during synthesis. + * * @param key the tag name * @param value the value of the tag * @param applyToLaunchedInstances whether or not to apply the tag to instances launched in an ASG. * @protected */ protected addTag(key: string, value: string, applyToLaunchedInstances: boolean = true): void { + // TODO add validation for `key` and `value` + // see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html Tags.of(this).add(key, value, { applyToLaunchedInstances }); } diff --git a/src/constructs/iam/policies/__snapshots__/ses.test.ts.snap b/src/constructs/iam/policies/__snapshots__/ses.test.ts.snap index dc9c426f5..70dbbf77f 100644 --- a/src/constructs/iam/policies/__snapshots__/ses.test.ts.snap +++ b/src/constructs/iam/policies/__snapshots__/ses.test.ts.snap @@ -92,6 +92,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -104,10 +108,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", diff --git a/src/constructs/iam/roles/__snapshots__/instance-role.test.ts.snap b/src/constructs/iam/roles/__snapshots__/instance-role.test.ts.snap index ed36e8263..92cd6284a 100644 --- a/src/constructs/iam/roles/__snapshots__/instance-role.test.ts.snap +++ b/src/constructs/iam/roles/__snapshots__/instance-role.test.ts.snap @@ -133,6 +133,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -145,10 +149,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", @@ -396,6 +396,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -408,10 +412,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", @@ -612,6 +612,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -624,10 +628,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", diff --git a/src/constructs/lambda/__snapshots__/lambda.test.ts.snap b/src/constructs/lambda/__snapshots__/lambda.test.ts.snap index 8e1e5fa53..53b06190f 100644 --- a/src/constructs/lambda/__snapshots__/lambda.test.ts.snap +++ b/src/constructs/lambda/__snapshots__/lambda.test.ts.snap @@ -97,6 +97,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -109,10 +113,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], "Timeout": 30, }, @@ -151,6 +151,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -163,10 +167,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", @@ -230,6 +230,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -242,10 +246,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::ApiGateway::RestApi", @@ -418,6 +418,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -430,15 +434,11 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", }, - "lambdaapi2Deployment4ECB937340ba28821c4d87686bb600192997b7b2": Object { + "lambdaapi2Deployment4ECB93739afe50d503afeac32f7e1b1edd2caf70": Object { "DependsOn": Array [ "lambdaapi2proxyANY3F8E1D36", "lambdaapi2proxyE68FDFBC", @@ -455,7 +455,7 @@ Object { "lambdaapi2DeploymentStageprodCC9E3016": Object { "Properties": Object { "DeploymentId": Object { - "Ref": "lambdaapi2Deployment4ECB937340ba28821c4d87686bb600192997b7b2", + "Ref": "lambdaapi2Deployment4ECB93739afe50d503afeac32f7e1b1edd2caf70", }, "RestApiId": Object { "Ref": "lambdaapi239350ECC", @@ -466,6 +466,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -478,10 +482,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::ApiGateway::Stage", @@ -763,6 +763,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -775,10 +779,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::ApiGateway::RestApi", @@ -816,6 +816,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -828,15 +832,11 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", }, - "lambdaapiDeployment8E95B58515e203156872c143be11756a3c89fbba": Object { + "lambdaapiDeployment8E95B5854a7d40530a7080bd25491376ccd199af": Object { "DependsOn": Array [ "lambdaapiproxyANYA94E968A", "lambdaapiproxyB573C729", @@ -853,7 +853,7 @@ Object { "lambdaapiDeploymentStageprod9598BC2F": Object { "Properties": Object { "DeploymentId": Object { - "Ref": "lambdaapiDeployment8E95B58515e203156872c143be11756a3c89fbba", + "Ref": "lambdaapiDeployment8E95B5854a7d40530a7080bd25491376ccd199af", }, "RestApiId": Object { "Ref": "lambdaapiC1812993", @@ -864,6 +864,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -876,10 +880,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::ApiGateway::Stage", diff --git a/src/constructs/rds/instance.test.ts b/src/constructs/rds/instance.test.ts index 2c571a335..77d920934 100644 --- a/src/constructs/rds/instance.test.ts +++ b/src/constructs/rds/instance.test.ts @@ -3,7 +3,7 @@ import { SynthUtils } from "@aws-cdk/assert/lib/synth-utils"; import { Vpc } from "@aws-cdk/aws-ec2"; import { DatabaseInstanceEngine, ParameterGroup, PostgresEngineVersion } from "@aws-cdk/aws-rds"; import { Stack } from "@aws-cdk/core"; -import { simpleGuStackForTesting } from "../../../test/utils"; +import { alphabeticalTags, simpleGuStackForTesting } from "../../../test/utils"; import type { SynthedStack } from "../../../test/utils"; import { TrackingTag } from "../../constants/library-info"; import { GuDatabaseInstance } from "./instance"; @@ -84,7 +84,7 @@ describe("The GuDatabaseInstance class", () => { Parameters: { max_connections: "100", }, - Tags: [ + Tags: alphabeticalTags([ { Key: "App", Value: "testing", @@ -102,7 +102,7 @@ describe("The GuDatabaseInstance class", () => { }, }, TrackingTag, - ], + ]), }); }); diff --git a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap index 8220a71d2..a72f27870 100644 --- a/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap +++ b/src/patterns/__snapshots__/scheduled-lambda.test.ts.snap @@ -44,6 +44,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -56,10 +60,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], "Timeout": 30, }, @@ -148,6 +148,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -160,10 +164,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", diff --git a/src/patterns/__snapshots__/sns-lambda.test.ts.snap b/src/patterns/__snapshots__/sns-lambda.test.ts.snap index 7846a1ef6..e328867c4 100644 --- a/src/patterns/__snapshots__/sns-lambda.test.ts.snap +++ b/src/patterns/__snapshots__/sns-lambda.test.ts.snap @@ -36,6 +36,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -48,10 +52,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::SNS::Topic", @@ -81,6 +81,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -93,10 +97,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], "Timeout": 30, }, @@ -201,6 +201,10 @@ Object { "Key": "App", "Value": "testing", }, + Object { + "Key": "gu:cdk:version", + "Value": "TEST", + }, Object { "Key": "Stack", "Value": Object { @@ -213,10 +217,6 @@ Object { "Ref": "Stage", }, }, - Object { - "Key": "X-Gu-CDK-Version", - "Value": "TEST", - }, ], }, "Type": "AWS::IAM::Role", diff --git a/test/utils/alphabetical-tags.ts b/test/utils/alphabetical-tags.ts new file mode 100644 index 000000000..e6eeab077 --- /dev/null +++ b/test/utils/alphabetical-tags.ts @@ -0,0 +1,16 @@ +interface Tag { + Key: string; + Value: unknown; +} + +export function alphabeticalTags(tags: Tag[]): Tag[] { + return [...tags].sort((first, second) => { + if (first.Key.toLowerCase() < second.Key.toLowerCase()) { + return -1; + } + if (first.Key.toLowerCase() > second.Key.toLowerCase()) { + return 1; + } + return 0; + }); +} diff --git a/test/utils/index.ts b/test/utils/index.ts index ebf1109c3..84ffeb780 100644 --- a/test/utils/index.ts +++ b/test/utils/index.ts @@ -1,3 +1,4 @@ export * from "./simple-gu-stack"; export * from "./synthed-stack"; export * from "./attach-policy-to-test-role"; +export * from "./alphabetical-tags";