From 8dea0febac225d2d51cd4e87df307b43a3b3ae42 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 4 Sep 2020 15:50:09 +0200 Subject: [PATCH 01/15] fix(cli): metadata not recorded in templates >50k The CDK Metadata Resource is added by the CLI, and only added to the in-memory representation of the template. This in-memory representation is only used in the `CreateChangeSet` call if the full template body is <=50kB. If the template is larger than this size, the template file that's on disk will be uploaded to the bootstrap S3 bucket and referenced. However, that template will not have had the metadata resource injected and so the stack will not be registered. This PR: - Provisionally fixes this issue by flushing the in-memory modifications back out to disk. - Also adds the metadata resources to stacks found in nested Cloud Assemblies, such as the stacks deployed via CDK Pipelines. There is some jank around new-style synthesis stacks: the protocol specifies that those stacks have synthesized their templates as assets, and those will not (necessarily) be processed by the CLI, although it might coincidentally work if the files are the same. The proper fix to this whole mess is to have the framework inject the metadata resource at synthesis time. That fix is forthcoming, but this current one is a good stopgap until that time. --- .../lib/artifacts/cloudformation-artifact.ts | 9 +- .../@aws-cdk/cx-api/lib/cloud-assembly.ts | 21 +++- .../aws-cdk/lib/api/cxapp/cloud-executable.ts | 118 ++++++++++++------ packages/aws-cdk/test/integ/cli/app/app.js | 10 ++ .../aws-cdk/test/integ/cli/cli.integtest.ts | 19 +++ 5 files changed, 130 insertions(+), 47 deletions(-) diff --git a/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts b/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts index 66a39b250ca1c..167d589753b63 100644 --- a/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts +++ b/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts @@ -114,12 +114,19 @@ export class CloudFormationStackArtifact extends CloudArtifact { this.originalName = this.stackName; } + /** + * Full path to the template file + */ + public get templateFullPath() { + return path.join(this.assembly.directory, this.templateFile); + } + /** * The CloudFormation template for this stack. */ public get template(): any { if (this._template === undefined) { - this._template = JSON.parse(fs.readFileSync(path.join(this.assembly.directory, this.templateFile), 'utf-8')); + this._template = JSON.parse(fs.readFileSync(this.templateFullPath, 'utf-8')); } return this._template; } diff --git a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts index 6ab4b595ae36b..e988c4e6f0a31 100644 --- a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts +++ b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts @@ -172,13 +172,22 @@ export class CloudAssembly { * @returns all the CloudFormation stack artifacts that are included in this assembly. */ public get stacks(): CloudFormationStackArtifact[] { - const result = new Array(); - for (const a of this.artifacts) { - if (a instanceof CloudFormationStackArtifact) { - result.push(a); - } + return this.artifacts.filter(isCloudFormationStackArtifact); + + function isCloudFormationStackArtifact(x: any): x is CloudFormationStackArtifact { + return x instanceof CloudFormationStackArtifact; + } + } + + /** + * @returns all the nested assembly artifacts in this assembly + */ + public get nestedAssemblies(): NestedCloudAssemblyArtifact[] { + return this.artifacts.filter(isNestedCloudAssemblyArtifact); + + function isNestedCloudAssemblyArtifact(x: any): x is NestedCloudAssemblyArtifact { + return x instanceof NestedCloudAssemblyArtifact; } - return result; } private validateDeps() { diff --git a/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts b/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts index 9a6bd42593b44..f66893dd8aa19 100644 --- a/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts +++ b/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts @@ -1,3 +1,4 @@ +import { promises as fs } from 'fs'; import * as cxapi from '@aws-cdk/cx-api'; import { RegionInfo } from '@aws-cdk/region-info'; import * as contextproviders from '../../context-providers'; @@ -89,54 +90,77 @@ export class CloudExecutable { } } - if (trackVersions && assembly.runtime) { - const modules = formatModules(assembly.runtime); - for (const stack of assembly.stacks) { - if (!stack.template.Resources) { - stack.template.Resources = {}; - } - const resourcePresent = stack.environment.region === cxapi.UNKNOWN_REGION - || RegionInfo.get(stack.environment.region).cdkMetadataResourceAvailable; - if (resourcePresent) { - if (!stack.template.Resources.CDKMetadata) { - stack.template.Resources.CDKMetadata = { - Type: 'AWS::CDK::Metadata', - Properties: { - Modules: modules, - }, - }; - if (stack.environment.region === cxapi.UNKNOWN_REGION) { - stack.template.Conditions = stack.template.Conditions || {}; - const condName = 'CDKMetadataAvailable'; - if (!stack.template.Conditions[condName]) { - stack.template.Conditions[condName] = _makeCdkMetadataAvailableCondition(); - stack.template.Resources.CDKMetadata.Condition = condName; - } else { - warning(`The stack ${stack.id} already includes a ${condName} condition`); - } - } - } else { - warning(`The stack ${stack.id} already includes a CDKMetadata resource`); - } - } - } + if (trackVersions) { + // @deprecated(v2): this should honestly not be done here. The framework + // should (and will, shortly) synthesize this information directly into + // the template. However, in order to support old framework versions + // that don't synthesize this info yet, we can only remove this code + // once we break backwards compatibility. + await this.addMetadataResource(assembly); } return new CloudAssembly(assembly); + } + } + + /** + * Modify the templates in the assembly in-place to add metadata resource declarations + */ + private async addMetadataResource(rootAssembly: cxapi.CloudAssembly) { + if (!rootAssembly.runtime) { return; } + + const modules = formatModules(rootAssembly.runtime); + await processAssembly(rootAssembly); - function formatModules(runtime: cxapi.RuntimeInfo): string { - const modules = new Array(); + async function processAssembly(assembly: cxapi.CloudAssembly) { + for (const stack of assembly.stacks) { + await processStack(stack); + } + // eslint-disable-next-line no-console + console.log(assembly.nestedAssemblies); + for (const nested of assembly.nestedAssemblies) { + await processAssembly(nested.nestedAssembly); + } + } - // inject toolkit version to list of modules - // eslint-disable-next-line @typescript-eslint/no-require-imports - const toolkitVersion = require('../../../package.json').version; - modules.push(`aws-cdk=${toolkitVersion}`); + async function processStack(stack: cxapi.CloudFormationStackArtifact) { + const resourcePresent = stack.environment.region === cxapi.UNKNOWN_REGION + || RegionInfo.get(stack.environment.region).cdkMetadataResourceAvailable; + if (!resourcePresent) { return; } - for (const key of Object.keys(runtime.libraries).sort()) { - modules.push(`${key}=${runtime.libraries[key]}`); + if (!stack.template.Resources) { + stack.template.Resources = {}; + } + if (stack.template.Resources.CDKMetadata) { + warning(`The stack ${stack.id} already includes a CDKMetadata resource`); + return; + } + + stack.template.Resources.CDKMetadata = { + Type: 'AWS::CDK::Metadata', + Properties: { + Modules: modules, + }, + }; + + if (stack.environment.region === cxapi.UNKNOWN_REGION) { + stack.template.Conditions = stack.template.Conditions || {}; + const condName = 'CDKMetadataAvailable'; + if (!stack.template.Conditions[condName]) { + stack.template.Conditions[condName] = _makeCdkMetadataAvailableCondition(); + stack.template.Resources.CDKMetadata.Condition = condName; + } else { + warning(`The stack ${stack.id} already includes a ${condName} condition`); } - return modules.join(','); } + + // The template has changed in-memory, but the file on disk remains unchanged so far. + // The CLI *might* later on deploy the in-memory version (if it's <50kB) or use the + // on-disk version (if it's >50kB). + // + // Be sure to flush the changes we just made back to disk. The on-disk format is always + // JSON. + await fs.writeFile(stack.templateFullPath, JSON.stringify(stack.template, undefined, 2), { encoding: 'utf-8' }); } } } @@ -185,4 +209,18 @@ function _inGroupsOf(array: T[], maxGroup: number): T[][] { result.push(array.slice(i, i + maxGroup)); } return result; +} + +function formatModules(runtime: cxapi.RuntimeInfo): string { + const modules = new Array(); + + // inject toolkit version to list of modules + // eslint-disable-next-line @typescript-eslint/no-require-imports + const toolkitVersion = require('../../../package.json').version; + modules.push(`aws-cdk=${toolkitVersion}`); + + for (const key of Object.keys(runtime.libraries).sort()) { + modules.push(`${key}=${runtime.libraries[key]}`); + } + return modules.join(','); } \ No newline at end of file diff --git a/packages/aws-cdk/test/integ/cli/app/app.js b/packages/aws-cdk/test/integ/cli/app/app.js index ab5efffbded80..175b658949fc4 100644 --- a/packages/aws-cdk/test/integ/cli/app/app.js +++ b/packages/aws-cdk/test/integ/cli/app/app.js @@ -239,6 +239,14 @@ class ConditionalResourceStack extends cdk.Stack { } } +class SomeStage extends cdk.Stage { + constructor(parent, id, props) { + super(parent, id, props); + + new YourStack(this, 'StackInStage'); + } +} + const app = new cdk.App(); const defaultEnv = { @@ -286,4 +294,6 @@ new YourStack(app, `${stackPrefix}-termination-protection`, { terminationProtection: process.env.TERMINATION_PROTECTION !== 'FALSE' ? true : false, }); +new SomeStage(app, `${stackPrefix}-stage`); + app.synth(); diff --git a/packages/aws-cdk/test/integ/cli/cli.integtest.ts b/packages/aws-cdk/test/integ/cli/cli.integtest.ts index e030f27799dc4..43a882b5e5950 100644 --- a/packages/aws-cdk/test/integ/cli/cli.integtest.ts +++ b/packages/aws-cdk/test/integ/cli/cli.integtest.ts @@ -679,6 +679,25 @@ integTest('generating and loading assembly', async () => { await cdkDeploy('docker-with-custom-file', { options: ['-a', '.'], cwd: asmOutputDir }); }); +integTest('templates on disk contain metadata resource, also in nested assemblies', async () => { + // Synth first, and switch on version reporting because cdk.json is disabling it + await cdk(['synth', '--version-reporting=true']); + + // Load template from disk from root assembly + const templateContents = await shell(['cat', 'cdk.out/*-lambda.template.json'], { + cwd: INTEG_TEST_DIR, + }); + + expect(JSON.parse(templateContents).Resources.CDKMetadata).toBeTruthy(); + + // Load template from nested assembly + const nestedTemplateContents = await shell(['cat', 'cdk.out/assembly-*-stage/*-stage-StackInStage.template.json'], { + cwd: INTEG_TEST_DIR, + }); + + expect(JSON.parse(nestedTemplateContents).Resources.CDKMetadata).toBeTruthy(); +}); + async function listChildren(parent: string, pred: (x: string) => Promise) { const ret = new Array(); for (const child of await fs.readdir(parent, { encoding: 'utf-8' })) { From 250fecbfcbdc7f4ff4e7ebca60ba3f1ecc533411 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 4 Sep 2020 16:15:38 +0200 Subject: [PATCH 02/15] Lint --- packages/@aws-cdk/cx-api/lib/cloud-assembly.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts index e988c4e6f0a31..8155d8a2c33ca 100644 --- a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts +++ b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts @@ -180,7 +180,7 @@ export class CloudAssembly { } /** - * @returns all the nested assembly artifacts in this assembly + * The nested assembly artifacts in this assembly */ public get nestedAssemblies(): NestedCloudAssemblyArtifact[] { return this.artifacts.filter(isNestedCloudAssemblyArtifact); From e8d16732d4f998047798f624b3bfce9738c62457 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 4 Sep 2020 17:15:11 +0200 Subject: [PATCH 03/15] Remove console statement --- packages/aws-cdk/lib/api/cxapp/cloud-executable.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts b/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts index f66893dd8aa19..0c7f0584fd48c 100644 --- a/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts +++ b/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts @@ -116,8 +116,6 @@ export class CloudExecutable { for (const stack of assembly.stacks) { await processStack(stack); } - // eslint-disable-next-line no-console - console.log(assembly.nestedAssemblies); for (const nested of assembly.nestedAssemblies) { await processAssembly(nested.nestedAssembly); } From 2986948c297de81dacb25c5df52ca227bd2db6e8 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 4 Sep 2020 17:56:36 +0200 Subject: [PATCH 04/15] fix(core): generate metadata resource in framework Better for new style synthesis --- packages/@aws-cdk/core/lib/cfn-fn.ts | 29 ++++++-- .../core/lib/private/metadata-resource.ts | 71 +++++++++++++++++++ .../@aws-cdk/core/lib/private/synthesis.ts | 4 +- packages/@aws-cdk/core/lib/stack.ts | 11 ++- packages/@aws-cdk/core/lib/stage.ts | 3 - packages/@aws-cdk/core/package.json | 4 +- .../@aws-cdk/cx-api/lib/cloud-assembly.ts | 2 + 7 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 packages/@aws-cdk/core/lib/private/metadata-resource.ts diff --git a/packages/@aws-cdk/core/lib/cfn-fn.ts b/packages/@aws-cdk/core/lib/cfn-fn.ts index d2967779b08b3..c0faf78e8b6cf 100644 --- a/packages/@aws-cdk/core/lib/cfn-fn.ts +++ b/packages/@aws-cdk/core/lib/cfn-fn.ts @@ -187,12 +187,18 @@ export class Fn { * Returns true if all the specified conditions evaluate to true, or returns * false if any one of the conditions evaluates to false. ``Fn::And`` acts as * an AND operator. The minimum number of conditions that you can include is - * 2, and the maximum is 10. + * 1. * @param conditions conditions to AND * @returns an FnCondition token */ public static conditionAnd(...conditions: ICfnConditionExpression[]): ICfnConditionExpression { - return new FnAnd(...conditions); + if (conditions.length === 0) { + throw new Error('Fn.conditionAnd() needs at least one argument'); + } + if (conditions.length === 1) { + return conditions[0]; + } + return Fn.conditionAnd(..._inGroupsOf(conditions, 10).map(group => new FnAnd(...group))); } /** @@ -240,12 +246,18 @@ export class Fn { * Returns true if any one of the specified conditions evaluate to true, or * returns false if all of the conditions evaluates to false. ``Fn::Or`` acts * as an OR operator. The minimum number of conditions that you can include is - * 2, and the maximum is 10. + * 1. * @param conditions conditions that evaluates to true or false. * @returns an FnCondition token */ public static conditionOr(...conditions: ICfnConditionExpression[]): ICfnConditionExpression { - return new FnOr(...conditions); + if (conditions.length === 0) { + throw new Error('Fn.conditionOr() needs at least one argument'); + } + if (conditions.length === 1) { + return conditions[0]; + } + return Fn.conditionOr(..._inGroupsOf(conditions, 10).map(group => new FnOr(...group))); } /** @@ -739,3 +751,12 @@ class FnJoin implements IResolvable { return minimalCloudFormationJoin(this.delimiter, resolvedValues); } } + + +function _inGroupsOf(array: T[], maxGroup: number): T[][] { + const result = new Array(); + for (let i = 0; i < array.length; i += maxGroup) { + result.push(array.slice(i, i + maxGroup)); + } + return result; +} diff --git a/packages/@aws-cdk/core/lib/private/metadata-resource.ts b/packages/@aws-cdk/core/lib/private/metadata-resource.ts new file mode 100644 index 0000000000000..f96365a2210cd --- /dev/null +++ b/packages/@aws-cdk/core/lib/private/metadata-resource.ts @@ -0,0 +1,71 @@ +import * as cxapi from '@aws-cdk/cx-api'; +import { RegionInfo } from '@aws-cdk/region-info'; +import { CfnCondition } from '../cfn-condition'; +import { Fn } from '../cfn-fn'; +import { Aws } from '../cfn-pseudo'; +import { CfnResource } from '../cfn-resource'; +import { Construct } from '../construct-compat'; +import { Lazy } from '../lazy'; +import { Stack } from '../stack'; +import { Token } from '../token'; +import { collectRuntimeInformation } from './runtime-info'; + +/** + * Construct that will render the metadata resource + */ +export class MetadataResource extends Construct { + /** + * Calculate the modules property + */ + private static modulesProperty(): string { + return formatModules(collectRuntimeInformation()); + } + + constructor(scope: Stack, id: string) { + super(scope, id); + + const metadataServiceExists = Token.isUnresolved(scope.region) || RegionInfo.get(scope.region).cdkMetadataResourceAvailable; + if (metadataServiceExists) { + const resource = new CfnResource(this, 'Default', { + type: 'AWS::CDK::Metadata', + properties: { + Modules: Lazy.stringValue({ produce: () => MetadataResource.modulesProperty() }), + }, + }); + + // In case we don't actually know the region, add a condition to determine it at deploy time + if (Token.isUnresolved(scope.region)) { + const condition = new CfnCondition(this, 'Condition', { + expression: makeCdkMetadataAvailableCondition(), + }); + + // To not cause undue template changes + condition.overrideLogicalId('CDKMetadataAvailable'); + + resource.cfnOptions.condition = condition; + } + } + } +} + +function makeCdkMetadataAvailableCondition() { + return Fn.conditionOr(...RegionInfo.regions + .filter(ri => ri.cdkMetadataResourceAvailable) + .map(ri => Fn.conditionEquals(Aws.REGION, ri.name))); +} + +function formatModules(runtime: cxapi.RuntimeInfo): string { + const modules = new Array(); + + // inject toolkit version to list of modules + // eslint-disable-next-line @typescript-eslint/no-require-imports + const cliVersion = process.env[cxapi.CLI_VERSION_ENV]; + if (cliVersion) { + modules.push(`aws-cdk=${cliVersion}`); + } + + for (const key of Object.keys(runtime.libraries).sort()) { + modules.push(`${key}=${runtime.libraries[key]}`); + } + return modules.join(','); +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index b4d2368ae6c99..2281a098e272f 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -35,9 +35,7 @@ export function synthesize(root: IConstruct, options: SynthesisOptions = { }): c // stacks to add themselves to the synthesized cloud assembly. synthesizeTree(root, builder); - return builder.buildAssembly({ - runtimeInfo: options.runtimeInfo, - }); + return builder.buildAssembly(); } /** diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index bfe60415f862d..20a5ee6280927 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -368,6 +368,11 @@ export class Stack extends Construct implements ITaggable { this.templateFile = `${this.artifactId}.template.json`; + // Add metadata resource, but only on top-level stacks and unless disabled + if (!this.parentStack && !this.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING)) { + new MetadataResource(this, 'CDKMetadata'); + } + this.synthesizer = props.synthesizer ?? (newStyleSynthesisContext ? new DefaultStackSynthesizer() : new LegacyStackSynthesizer()); @@ -720,10 +725,11 @@ export class Stack extends Construct implements ITaggable { // this right now, so some parts still happen here. const builder = session.assembly; + const template = this._toCloudFormation(); + // write the CloudFormation template as a JSON file const outPath = path.join(builder.outdir, this.templateFile); - const text = JSON.stringify(this._toCloudFormation(), undefined, 2); - fs.writeFileSync(outPath, text); + fs.writeFileSync(outPath, JSON.stringify(template, undefined, 2)); for (const ctx of this._missingContext) { builder.addMissing(ctx); @@ -1100,6 +1106,7 @@ import { Stage } from './stage'; import { ITaggable, TagManager } from './tag-manager'; import { Token } from './token'; import { FileSystem } from './fs'; +import { MetadataResource } from './private/metadata-resource'; interface StackDependency { stack: Stack; diff --git a/packages/@aws-cdk/core/lib/stage.ts b/packages/@aws-cdk/core/lib/stage.ts index b6f6a1f985d19..1f46616209b55 100644 --- a/packages/@aws-cdk/core/lib/stage.ts +++ b/packages/@aws-cdk/core/lib/stage.ts @@ -1,7 +1,6 @@ import * as cxapi from '@aws-cdk/cx-api'; import { Construct, IConstruct } from './construct-compat'; import { Environment } from './environment'; -import { collectRuntimeInformation } from './private/runtime-info'; import { synthesize } from './private/synthesis'; /** @@ -171,10 +170,8 @@ export class Stage extends Construct { */ public synth(options: StageSynthesisOptions = { }): cxapi.CloudAssembly { if (!this.assembly || options.force) { - const runtimeInfo = this.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING) ? undefined : collectRuntimeInformation(); this.assembly = synthesize(this, { skipValidation: options.skipValidation, - runtimeInfo, }); } diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index e71ef1f3f3841..cc8e5962d1dce 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -169,6 +169,7 @@ "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cx-api": "0.0.0", + "@aws-cdk/region-info": "0.0.0", "constructs": "^3.0.4", "fs-extra": "^9.0.1", "minimatch": "^3.0.4" @@ -181,7 +182,8 @@ "peerDependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cx-api": "0.0.0", - "constructs": "^3.0.4" + "constructs": "^3.0.4", + "@aws-cdk/region-info": "0.0.0" }, "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" diff --git a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts index 8155d8a2c33ca..05210ed891eed 100644 --- a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts +++ b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts @@ -369,6 +369,8 @@ export interface AssemblyBuildOptions { /** * Include the specified runtime information (module versions) in manifest. * @default - if this option is not specified, runtime info will not be included + * @deprecated All template modifications that should result from this should + * have already been inserted into the template. */ readonly runtimeInfo?: RuntimeInfo; } From d34489e242f9a12a09c3ea6fc518d63c4bb6a4f5 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 4 Sep 2020 17:56:36 +0200 Subject: [PATCH 05/15] fix(core): generate metadata resource in framework Better for new style synthesis --- packages/@aws-cdk/core/lib/cfn-fn.ts | 29 +++++++- .../core/lib/private/metadata-resource.ts | 74 +++++++++++++++++++ .../@aws-cdk/core/lib/private/synthesis.ts | 4 +- packages/@aws-cdk/core/lib/stack.ts | 11 ++- packages/@aws-cdk/core/lib/stage.ts | 3 - packages/@aws-cdk/core/package.json | 4 +- .../@aws-cdk/cx-api/lib/cloud-assembly.ts | 2 + 7 files changed, 114 insertions(+), 13 deletions(-) create mode 100644 packages/@aws-cdk/core/lib/private/metadata-resource.ts diff --git a/packages/@aws-cdk/core/lib/cfn-fn.ts b/packages/@aws-cdk/core/lib/cfn-fn.ts index d2967779b08b3..c0faf78e8b6cf 100644 --- a/packages/@aws-cdk/core/lib/cfn-fn.ts +++ b/packages/@aws-cdk/core/lib/cfn-fn.ts @@ -187,12 +187,18 @@ export class Fn { * Returns true if all the specified conditions evaluate to true, or returns * false if any one of the conditions evaluates to false. ``Fn::And`` acts as * an AND operator. The minimum number of conditions that you can include is - * 2, and the maximum is 10. + * 1. * @param conditions conditions to AND * @returns an FnCondition token */ public static conditionAnd(...conditions: ICfnConditionExpression[]): ICfnConditionExpression { - return new FnAnd(...conditions); + if (conditions.length === 0) { + throw new Error('Fn.conditionAnd() needs at least one argument'); + } + if (conditions.length === 1) { + return conditions[0]; + } + return Fn.conditionAnd(..._inGroupsOf(conditions, 10).map(group => new FnAnd(...group))); } /** @@ -240,12 +246,18 @@ export class Fn { * Returns true if any one of the specified conditions evaluate to true, or * returns false if all of the conditions evaluates to false. ``Fn::Or`` acts * as an OR operator. The minimum number of conditions that you can include is - * 2, and the maximum is 10. + * 1. * @param conditions conditions that evaluates to true or false. * @returns an FnCondition token */ public static conditionOr(...conditions: ICfnConditionExpression[]): ICfnConditionExpression { - return new FnOr(...conditions); + if (conditions.length === 0) { + throw new Error('Fn.conditionOr() needs at least one argument'); + } + if (conditions.length === 1) { + return conditions[0]; + } + return Fn.conditionOr(..._inGroupsOf(conditions, 10).map(group => new FnOr(...group))); } /** @@ -739,3 +751,12 @@ class FnJoin implements IResolvable { return minimalCloudFormationJoin(this.delimiter, resolvedValues); } } + + +function _inGroupsOf(array: T[], maxGroup: number): T[][] { + const result = new Array(); + for (let i = 0; i < array.length; i += maxGroup) { + result.push(array.slice(i, i + maxGroup)); + } + return result; +} diff --git a/packages/@aws-cdk/core/lib/private/metadata-resource.ts b/packages/@aws-cdk/core/lib/private/metadata-resource.ts new file mode 100644 index 0000000000000..0e8db7a31e6bd --- /dev/null +++ b/packages/@aws-cdk/core/lib/private/metadata-resource.ts @@ -0,0 +1,74 @@ +import * as cxapi from '@aws-cdk/cx-api'; +import { RegionInfo } from '@aws-cdk/region-info'; +import { CfnCondition } from '../cfn-condition'; +import { Fn } from '../cfn-fn'; +import { Aws } from '../cfn-pseudo'; +import { CfnResource } from '../cfn-resource'; +import { Construct } from '../construct-compat'; +import { Lazy } from '../lazy'; +import { Stack } from '../stack'; +import { Token } from '../token'; +import { collectRuntimeInformation } from './runtime-info'; + +/** + * Construct that will render the metadata resource + */ +export class MetadataResource extends Construct { + /** + * Calculate the modules property + */ + private static modulesProperty(): string { + Array.isArray(formatModules); + Array.isArray(collectRuntimeInformation); + // return formatModules(collectRuntimeInformation()); + return ''; + } + + constructor(scope: Stack, id: string) { + super(scope, id); + + const metadataServiceExists = Token.isUnresolved(scope.region) || RegionInfo.get(scope.region).cdkMetadataResourceAvailable; + if (metadataServiceExists) { + const resource = new CfnResource(this, 'Default', { + type: 'AWS::CDK::Metadata', + properties: { + Modules: Lazy.stringValue({ produce: () => MetadataResource.modulesProperty() }), + }, + }); + + // In case we don't actually know the region, add a condition to determine it at deploy time + if (Token.isUnresolved(scope.region)) { + const condition = new CfnCondition(this, 'Condition', { + expression: makeCdkMetadataAvailableCondition(), + }); + + // To not cause undue template changes + condition.overrideLogicalId('CDKMetadataAvailable'); + + resource.cfnOptions.condition = condition; + } + } + } +} + +function makeCdkMetadataAvailableCondition() { + return Fn.conditionOr(...RegionInfo.regions + .filter(ri => ri.cdkMetadataResourceAvailable) + .map(ri => Fn.conditionEquals(Aws.REGION, ri.name))); +} + +function formatModules(runtime: cxapi.RuntimeInfo): string { + const modules = new Array(); + + // inject toolkit version to list of modules + // eslint-disable-next-line @typescript-eslint/no-require-imports + const cliVersion = process.env[cxapi.CLI_VERSION_ENV]; + if (cliVersion) { + modules.push(`aws-cdk=${cliVersion}`); + } + + for (const key of Object.keys(runtime.libraries).sort()) { + modules.push(`${key}=${runtime.libraries[key]}`); + } + return modules.join(','); +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index b4d2368ae6c99..2281a098e272f 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -35,9 +35,7 @@ export function synthesize(root: IConstruct, options: SynthesisOptions = { }): c // stacks to add themselves to the synthesized cloud assembly. synthesizeTree(root, builder); - return builder.buildAssembly({ - runtimeInfo: options.runtimeInfo, - }); + return builder.buildAssembly(); } /** diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index bfe60415f862d..20a5ee6280927 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -368,6 +368,11 @@ export class Stack extends Construct implements ITaggable { this.templateFile = `${this.artifactId}.template.json`; + // Add metadata resource, but only on top-level stacks and unless disabled + if (!this.parentStack && !this.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING)) { + new MetadataResource(this, 'CDKMetadata'); + } + this.synthesizer = props.synthesizer ?? (newStyleSynthesisContext ? new DefaultStackSynthesizer() : new LegacyStackSynthesizer()); @@ -720,10 +725,11 @@ export class Stack extends Construct implements ITaggable { // this right now, so some parts still happen here. const builder = session.assembly; + const template = this._toCloudFormation(); + // write the CloudFormation template as a JSON file const outPath = path.join(builder.outdir, this.templateFile); - const text = JSON.stringify(this._toCloudFormation(), undefined, 2); - fs.writeFileSync(outPath, text); + fs.writeFileSync(outPath, JSON.stringify(template, undefined, 2)); for (const ctx of this._missingContext) { builder.addMissing(ctx); @@ -1100,6 +1106,7 @@ import { Stage } from './stage'; import { ITaggable, TagManager } from './tag-manager'; import { Token } from './token'; import { FileSystem } from './fs'; +import { MetadataResource } from './private/metadata-resource'; interface StackDependency { stack: Stack; diff --git a/packages/@aws-cdk/core/lib/stage.ts b/packages/@aws-cdk/core/lib/stage.ts index b6f6a1f985d19..1f46616209b55 100644 --- a/packages/@aws-cdk/core/lib/stage.ts +++ b/packages/@aws-cdk/core/lib/stage.ts @@ -1,7 +1,6 @@ import * as cxapi from '@aws-cdk/cx-api'; import { Construct, IConstruct } from './construct-compat'; import { Environment } from './environment'; -import { collectRuntimeInformation } from './private/runtime-info'; import { synthesize } from './private/synthesis'; /** @@ -171,10 +170,8 @@ export class Stage extends Construct { */ public synth(options: StageSynthesisOptions = { }): cxapi.CloudAssembly { if (!this.assembly || options.force) { - const runtimeInfo = this.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING) ? undefined : collectRuntimeInformation(); this.assembly = synthesize(this, { skipValidation: options.skipValidation, - runtimeInfo, }); } diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index e71ef1f3f3841..cc8e5962d1dce 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -169,6 +169,7 @@ "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cx-api": "0.0.0", + "@aws-cdk/region-info": "0.0.0", "constructs": "^3.0.4", "fs-extra": "^9.0.1", "minimatch": "^3.0.4" @@ -181,7 +182,8 @@ "peerDependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cx-api": "0.0.0", - "constructs": "^3.0.4" + "constructs": "^3.0.4", + "@aws-cdk/region-info": "0.0.0" }, "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" diff --git a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts index 8155d8a2c33ca..05210ed891eed 100644 --- a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts +++ b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts @@ -369,6 +369,8 @@ export interface AssemblyBuildOptions { /** * Include the specified runtime information (module versions) in manifest. * @default - if this option is not specified, runtime info will not be included + * @deprecated All template modifications that should result from this should + * have already been inserted into the template. */ readonly runtimeInfo?: RuntimeInfo; } From c62f0bc4409467e906f676d941b4ec2160f4587f Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 8 Sep 2020 11:13:51 +0200 Subject: [PATCH 06/15] WIP --- .../core/lib/private/metadata-resource.ts | 4 ++-- .../@aws-cdk/core/lib/private/synthesis.ts | 21 +++++++++++++++++++ packages/@aws-cdk/core/lib/stack.ts | 6 ------ .../@aws-cdk/core/test/test.runtime-info.ts | 19 ++--------------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/@aws-cdk/core/lib/private/metadata-resource.ts b/packages/@aws-cdk/core/lib/private/metadata-resource.ts index 666a9c9d3be72..c23b726665b9f 100644 --- a/packages/@aws-cdk/core/lib/private/metadata-resource.ts +++ b/packages/@aws-cdk/core/lib/private/metadata-resource.ts @@ -31,8 +31,8 @@ export class MetadataResource extends Construct { return this._modulesPropertyCache; } - constructor(scope: Stack, id: string) { - super(scope, id); + constructor(scope: Stack) { + super(scope, 'CDKMetadata'); const metadataServiceExists = Token.isUnresolved(scope.region) || RegionInfo.get(scope.region).cdkMetadataResourceAvailable; if (metadataServiceExists) { diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index 2281a098e272f..ab2e59b32f228 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -5,6 +5,7 @@ import { Aspects, IAspect } from '../aspect'; import { Construct, IConstruct, SynthesisOptions, ValidationError } from '../construct-compat'; import { Stack } from '../stack'; import { Stage, StageSynthesisOptions } from '../stage'; +import { MetadataResource } from './metadata-resource'; import { prepareApp } from './prepare-app'; import { TreeMetadata } from './tree-metadata'; @@ -14,6 +15,8 @@ export function synthesize(root: IConstruct, options: SynthesisOptions = { }): c invokeAspects(root); + injectMetadataResources(root); + // This is mostly here for legacy purposes as the framework itself does not use prepare anymore. prepareTree(root); @@ -108,6 +111,24 @@ function prepareTree(root: IConstruct) { visit(root, 'post', construct => construct.onPrepare()); } +/** + * Find all stacks and add Metadata Resources to all of them + * + * There is no good generic place to do this. Can't do it in the constructor + * (because adding a child construct makes it impossible to set context on the + * node), and the generic prepare phase is deprecated. + * + * Stop at Assembly boundaries. + */ +function injectMetadataResources(root: IConstruct) { + visit(root, 'post', construct => { + // Only on top-level stacks and unless disabled + if (!Stack.isStack(construct) || construct.parentStack || construct.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING)) { return; } + + new MetadataResource(construct); + }); +} + /** * Synthesize children in post-order into the given builder * diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 20a5ee6280927..f325d40347b5e 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -368,11 +368,6 @@ export class Stack extends Construct implements ITaggable { this.templateFile = `${this.artifactId}.template.json`; - // Add metadata resource, but only on top-level stacks and unless disabled - if (!this.parentStack && !this.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING)) { - new MetadataResource(this, 'CDKMetadata'); - } - this.synthesizer = props.synthesizer ?? (newStyleSynthesisContext ? new DefaultStackSynthesizer() : new LegacyStackSynthesizer()); @@ -1106,7 +1101,6 @@ import { Stage } from './stage'; import { ITaggable, TagManager } from './tag-manager'; import { Token } from './token'; import { FileSystem } from './fs'; -import { MetadataResource } from './private/metadata-resource'; interface StackDependency { stack: Stack; diff --git a/packages/@aws-cdk/core/test/test.runtime-info.ts b/packages/@aws-cdk/core/test/test.runtime-info.ts index 762348378d5ab..46d8e42f85048 100644 --- a/packages/@aws-cdk/core/test/test.runtime-info.ts +++ b/packages/@aws-cdk/core/test/test.runtime-info.ts @@ -21,14 +21,7 @@ export = { const runtimeInfo = collectRuntimeInformation(); // eslint-disable-next-line @typescript-eslint/no-require-imports - const version = require('../package.json').version; - test.deepEqual(runtimeInfo.libraries, { - '@aws-cdk/core': version, - '@aws-cdk/cx-api': version, - '@aws-cdk/cloud-assembly-schema': version, - '@aws-solutions-konstruk/foo': mockVersion, - 'jsii-runtime': `node.js/${process.version}`, - }); + test.deepEqual(runtimeInfo.libraries['@aws-solutions-konstruk/foo'], mockVersion); test.done(); }, @@ -48,15 +41,7 @@ export = { const runtimeInfo = collectRuntimeInformation(); // eslint-disable-next-line @typescript-eslint/no-require-imports - const version = require('../package.json').version; - test.deepEqual(runtimeInfo.libraries, { - '@aws-cdk/core': version, - '@aws-cdk/cx-api': version, - '@aws-cdk/cloud-assembly-schema': version, - '@aws-solutions-konstruk/foo': mockVersion, // picks up the module from the other test. - 'aws-rfdk': mockVersion, - 'jsii-runtime': `node.js/${process.version}`, - }); + test.deepEqual(runtimeInfo.libraries['aws-rfdk'], mockVersion); test.done(); }, From 80773c842a36d0d0923eb1cde55e85fe4ee1ad18 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 11 Sep 2020 09:44:02 +0200 Subject: [PATCH 07/15] Fix tests --- .../core/lib/private/metadata-resource.ts | 18 ++- .../@aws-cdk/core/lib/private/synthesis.ts | 25 +++- packages/@aws-cdk/core/test/test.app.ts | 129 ++++++++++++------ packages/@aws-cdk/core/test/test.rule.ts | 2 +- 4 files changed, 127 insertions(+), 47 deletions(-) diff --git a/packages/@aws-cdk/core/lib/private/metadata-resource.ts b/packages/@aws-cdk/core/lib/private/metadata-resource.ts index c23b726665b9f..07db0085b8027 100644 --- a/packages/@aws-cdk/core/lib/private/metadata-resource.ts +++ b/packages/@aws-cdk/core/lib/private/metadata-resource.ts @@ -14,10 +14,22 @@ import { collectRuntimeInformation } from './runtime-info'; * Construct that will render the metadata resource */ export class MetadataResource extends Construct { + /** + * Clear the modules cache + * + * The next time the MetadataResource is rendered, it will do a lookup of the + * modules from the NodeJS module cache again. + * + * Used only for unit tests. + */ + public static clearModulesCache() { + this._modulesPropertyCache = undefined; + } + /** * Cached version of the _modulesProperty() accessor * - * No point in calculating this fairly-expensive list more than once. + * No point in calculating this fairly expensive list more than once. */ private static _modulesPropertyCache?: string; @@ -31,8 +43,8 @@ export class MetadataResource extends Construct { return this._modulesPropertyCache; } - constructor(scope: Stack) { - super(scope, 'CDKMetadata'); + constructor(scope: Stack, id: string) { + super(scope, id); const metadataServiceExists = Token.isUnresolved(scope.region) || RegionInfo.get(scope.region).cdkMetadataResourceAvailable; if (metadataServiceExists) { diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index ab2e59b32f228..8aed44fcff419 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -118,14 +118,31 @@ function prepareTree(root: IConstruct) { * (because adding a child construct makes it impossible to set context on the * node), and the generic prepare phase is deprecated. * + * Only do this on [parent] stacks (not nested stacks), don't do this when + * disabled by the user. + * + * Also, only when running via the CLI. If we do it unconditionally, + * all unit tests everywhere are going to break massively. I've spent a day + * fixing our own, but downstream users would be affected just as badly. + * * Stop at Assembly boundaries. */ function injectMetadataResources(root: IConstruct) { visit(root, 'post', construct => { - // Only on top-level stacks and unless disabled - if (!Stack.isStack(construct) || construct.parentStack || construct.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING)) { return; } - - new MetadataResource(construct); + // Only on top-level stacks + if (!Stack.isStack(construct) + || construct.parentStack + // Unless disabled + || construct.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING) + // While running via CLI + || !process.env[cxapi.CLI_VERSION_ENV]) { return; } + + // Because of https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/assert/lib/synth-utils.ts#L74 + // synthesize() may be called more than once on a stack in unit tests, and the below would break + // if we execute it a second time. Guard against the constructs already existing. + if (construct.node.tryFindChild('CDKMetadata')) { return; } + + new MetadataResource(construct, 'CDKMetadata'); }); } diff --git a/packages/@aws-cdk/core/test/test.app.ts b/packages/@aws-cdk/core/test/test.app.ts index 3feeae07829a6..d9de3703658dd 100644 --- a/packages/@aws-cdk/core/test/test.app.ts +++ b/packages/@aws-cdk/core/test/test.app.ts @@ -3,6 +3,7 @@ import * as cxapi from '@aws-cdk/cx-api'; import { Test } from 'nodeunit'; import { CfnResource, Construct, Stack, StackProps } from '../lib'; import { Annotations } from '../lib/annotations'; +import { MetadataResource } from '../lib/private/metadata-resource'; import { App, AppProps } from '../lib/app'; function withApp(props: AppProps, block: (app: App) => void): cxapi.CloudAssembly { @@ -245,65 +246,88 @@ export = { test.done(); }, - 'runtime library versions disabled'(test: Test) { - const context: any = {}; - context[cxapi.DISABLE_VERSION_REPORTING] = true; - - const assembly = withApp(context, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); + /** + * Runtime library versions are now synthesized into the Stack templates directly + * + * The are not emitted into Cloud Assembly metadata anymore + */ + 'runtime library versions are not emitted in asm anymore'(test: Test) { + withCliVersion(() => { + const context: any = {}; + + const assembly = withApp(context, app => { + const stack = new Stack(app, 'stack1'); + new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); + }); + + test.deepEqual(assembly.runtime, { libraries: {} }); }); - - test.deepEqual(assembly.runtime, { libraries: {} }); test.done(); }, 'runtime library versions'(test: Test) { - const response = withApp({ runtimeInfo: true }, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); + MetadataResource.clearModulesCache(); + + withCliVersion(() => { + const response = withApp({ runtimeInfo: true }, app => { + const stack = new Stack(app, 'stack1'); + new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); + }); + + const stackTemplate = response.getStackByName('stack1').template; + const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); + + // eslint-disable-next-line @typescript-eslint/no-require-imports + const version = require('../package.json').version; + test.deepEqual(libs['@aws-cdk/core'], version); + test.deepEqual(libs['@aws-cdk/cx-api'], version); + test.deepEqual(libs['jsii-runtime'], `node.js/${process.version}`); }); - const libs = (response.runtime && response.runtime.libraries) || {}; - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const version = require('../package.json').version; - test.deepEqual(libs['@aws-cdk/core'], version); - test.deepEqual(libs['@aws-cdk/cx-api'], version); - test.deepEqual(libs['jsii-runtime'], `node.js/${process.version}`); test.done(); }, 'jsii-runtime version loaded from JSII_AGENT'(test: Test) { process.env.JSII_AGENT = 'Java/1.2.3.4'; + MetadataResource.clearModulesCache(); - const response = withApp({ runtimeInfo: true }, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); - }); + withCliVersion(() => { + const response = withApp({ runtimeInfo: true }, app => { + const stack = new Stack(app, 'stack1'); + new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); + }); - const libs = (response.runtime && response.runtime.libraries) || {}; - test.deepEqual(libs['jsii-runtime'], 'Java/1.2.3.4'); + const stackTemplate = response.getStackByName('stack1').template; + const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); + + test.deepEqual(libs['jsii-runtime'], 'Java/1.2.3.4'); + }); delete process.env.JSII_AGENT; test.done(); }, 'version reporting includes only @aws-cdk, aws-cdk and jsii libraries'(test: Test) { - const response = withApp({ runtimeInfo: true }, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); - }); - - const libs = (response.runtime && response.runtime.libraries) || {}; - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const version = require('../package.json').version; - test.deepEqual(libs, { - '@aws-cdk/core': version, - '@aws-cdk/cx-api': version, - '@aws-cdk/cloud-assembly-schema': version, - 'jsii-runtime': `node.js/${process.version}`, + MetadataResource.clearModulesCache(); + withCliVersion(() => { + const response = withApp({ runtimeInfo: true }, app => { + const stack = new Stack(app, 'stack1'); + new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); + }); + + const stackTemplate = response.getStackByName('stack1').template; + const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); + + // eslint-disable-next-line @typescript-eslint/no-require-imports + const version = require('../package.json').version; + test.deepEqual(libs, { + 'aws-cdk': '1.2.3', + '@aws-cdk/core': version, + '@aws-cdk/cx-api': version, + '@aws-cdk/region-info': version, + '@aws-cdk/cloud-assembly-schema': version, + 'jsii-runtime': `node.js/${process.version}`, + }); }); test.done(); @@ -385,3 +409,30 @@ class MyConstruct extends Construct { new CfnResource(this, 'r2', { type: 'ResourceType2', properties: { FromContext: this.node.tryGetContext('ctx1') } }); } } + +function parseModules(x?: string): Record { + if (x === undefined) { return {}; } + + const ret: Record = {}; + for (const clause of x.split(',')) { + const [key, value] = clause.split('='); + if (key !== undefined && value !== undefined) { + ret[key] = value; + } + } + return ret; +} + +/** + * Set the CLI_VERSION_ENV environment variable + * + * This is necessary to get the Stack to emit the metadata resource + */ +function withCliVersion(block: () => A): A { + process.env[cxapi.CLI_VERSION_ENV] = '1.2.3'; + try { + return block(); + } finally { + delete process.env[cxapi.CLI_VERSION_ENV]; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/test/test.rule.ts b/packages/@aws-cdk/core/test/test.rule.ts index 62ef0611c9a38..8385183333855 100644 --- a/packages/@aws-cdk/core/test/test.rule.ts +++ b/packages/@aws-cdk/core/test/test.rule.ts @@ -19,7 +19,7 @@ export = { AssertDescription: 'lhs equals rhs', }, { - Assert: { 'Fn::Not': [{ 'Fn::And': [{ 'Fn::Contains': [['hello', 'world'], 'world'] }] }] }, + Assert: { 'Fn::Not': [{ 'Fn::Contains': [['hello', 'world'], 'world'] }] }, AssertDescription: 'some assertion', }, ], From 30ae434332eab2f904c1735fe75e903fc4716ea8 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 11 Sep 2020 10:09:12 +0200 Subject: [PATCH 08/15] Humans can't mere --- .../aws-cdk/test/integ/cli/cli.integtest.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/packages/aws-cdk/test/integ/cli/cli.integtest.ts b/packages/aws-cdk/test/integ/cli/cli.integtest.ts index 0b02e95e5f599..d203c0f66e605 100644 --- a/packages/aws-cdk/test/integ/cli/cli.integtest.ts +++ b/packages/aws-cdk/test/integ/cli/cli.integtest.ts @@ -682,25 +682,6 @@ integTest('templates on disk contain metadata resource, also in nested assemblie expect(JSON.parse(nestedTemplateContents).Resources.CDKMetadata).toBeTruthy(); })); -integTest('templates on disk contain metadata resource, also in nested assemblies', async () => { - // Synth first, and switch on version reporting because cdk.json is disabling it - await cdk(['synth', '--version-reporting=true']); - - // Load template from disk from root assembly - const templateContents = await shell(['cat', 'cdk.out/*-lambda.template.json'], { - cwd: INTEG_TEST_DIR, - }); - - expect(JSON.parse(templateContents).Resources.CDKMetadata).toBeTruthy(); - - // Load template from nested assembly - const nestedTemplateContents = await shell(['cat', 'cdk.out/assembly-*-stage/*-stage-StackInStage.template.json'], { - cwd: INTEG_TEST_DIR, - }); - - expect(JSON.parse(nestedTemplateContents).Resources.CDKMetadata).toBeTruthy(); -}); - async function listChildren(parent: string, pred: (x: string) => Promise) { const ret = new Array(); for (const child of await fs.readdir(parent, { encoding: 'utf-8' })) { From b815752226c96d5ef355c9c7aeec53684c6d3cb0 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 22 Sep 2020 10:46:07 +0200 Subject: [PATCH 09/15] Reverse default value of flags --- .../schema/cloud-assembly.version.json | 2 +- packages/@aws-cdk/core/lib/app.ts | 19 +++++++++++---- .../core/lib/private/metadata-resource.ts | 1 - .../@aws-cdk/core/lib/private/synthesis.ts | 24 +++++++++---------- packages/@aws-cdk/core/lib/stack.ts | 22 +++++++++++++++++ packages/@aws-cdk/core/test/test.app.ts | 2 +- packages/@aws-cdk/core/test/util.ts | 11 +-------- packages/@aws-cdk/cx-api/lib/app.ts | 7 ++++++ packages/aws-cdk/lib/api/cxapp/exec.ts | 10 +++----- 9 files changed, 61 insertions(+), 37 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json index 78d33700c0698..2211f30276a5e 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json @@ -1 +1 @@ -{"version":"5.0.0"} +{"version":"6.0.0"} diff --git a/packages/@aws-cdk/core/lib/app.ts b/packages/@aws-cdk/core/lib/app.ts index cff51ff197ec6..a5532dac5687d 100644 --- a/packages/@aws-cdk/core/lib/app.ts +++ b/packages/@aws-cdk/core/lib/app.ts @@ -35,11 +35,20 @@ export interface AppProps { readonly stackTraces?: boolean; /** - * Include runtime versioning information in cloud assembly manifest - * @default true runtime info is included unless `aws:cdk:disable-runtime-info` is set in the context. + * Include runtime versioning information in the Stacks of this app + * + * @depreacted use `versionReporting` instead + * @default Value of 'aws:cdk:version-reporting' context key */ readonly runtimeInfo?: boolean; + /** + * Include runtime versioning information in the Stacks of this app + * + * @default Value of 'aws:cdk:version-reporting' context key + */ + readonly versionReporting?: boolean; + /** * Additional context values for the application. * @@ -101,8 +110,10 @@ export class App extends Stage { this.node.setContext(cxapi.DISABLE_METADATA_STACK_TRACE, true); } - if (props.runtimeInfo === false) { - this.node.setContext(cxapi.DISABLE_VERSION_REPORTING, true); + const versionReporting = props.versionReporting ?? props.runtimeInfo; + + if (versionReporting !== undefined) { + this.node.setContext(cxapi.VERSION_REPORTING_ENABLED_CONTEXT, versionReporting); } const autoSynth = props.autoSynth !== undefined ? props.autoSynth : cxapi.OUTDIR_ENV in process.env; diff --git a/packages/@aws-cdk/core/lib/private/metadata-resource.ts b/packages/@aws-cdk/core/lib/private/metadata-resource.ts index 07db0085b8027..09813ccb87faa 100644 --- a/packages/@aws-cdk/core/lib/private/metadata-resource.ts +++ b/packages/@aws-cdk/core/lib/private/metadata-resource.ts @@ -80,7 +80,6 @@ function formatModules(runtime: cxapi.RuntimeInfo): string { const modules = new Array(); // inject toolkit version to list of modules - // eslint-disable-next-line @typescript-eslint/no-require-imports const cliVersion = process.env[cxapi.CLI_VERSION_ENV]; if (cliVersion) { modules.push(`aws-cdk=${cliVersion}`); diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index 5cb2782f6831a..93a5ccf009daf 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -130,19 +130,17 @@ function prepareTree(root: IConstruct) { function injectMetadataResources(root: IConstruct) { visit(root, 'post', construct => { // Only on top-level stacks - if (!Stack.isStack(construct) - || construct.parentStack - // Unless disabled - || construct.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING) - // While running via CLI - || !process.env[cxapi.CLI_VERSION_ENV]) { return; } - - // Because of https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/assert/lib/synth-utils.ts#L74 - // synthesize() may be called more than once on a stack in unit tests, and the below would break - // if we execute it a second time. Guard against the constructs already existing. - if (construct.node.tryFindChild('CDKMetadata')) { return; } - - new MetadataResource(construct, 'CDKMetadata'); + if (!Stack.isStack(construct)) { return; } + + if (construct._versionReportingEnabled) { + // Because of https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/assert/lib/synth-utils.ts#L74 + // synthesize() may be called more than once on a stack in unit tests, and the below would break + // if we execute it a second time. Guard against the constructs already existing. + const CDKMetadata = 'CDKMetadata'; + if (construct.node.tryFindChild(CDKMetadata)) { return; } + + new MetadataResource(construct, CDKMetadata); + } }); } diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 87a6e8e0cc8e6..988bbb44d9f53 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -126,6 +126,14 @@ export interface StackProps { * @default false */ readonly terminationProtection?: boolean; + + /** + * Include runtime versioning information in this Stack + * + * @default `versionReporting setting of containing `App`, or value of + * 'aws:cdk:version-reporting' context key + */ + readonly versionReporting?: boolean; } /** @@ -279,6 +287,16 @@ export class Stack extends Construct implements ITaggable { */ public readonly synthesizer: IStackSynthesizer; + /** + * Whether version reporting is enabled for this stack + * + * Controls whether the CDK Metadata resource is injected + * + * @internal + */ + public readonly _versionReportingEnabled: boolean; + + /** * Logical ID generation strategy */ @@ -368,6 +386,10 @@ export class Stack extends Construct implements ITaggable { this.templateFile = `${this.artifactId}.template.json`; + // Not for nested stacks + this._versionReportingEnabled = (props.versionReporting ?? this.node.tryGetContext(cxapi.VERSION_REPORTING_ENABLED_CONTEXT)) + && !this.nestedStackParent; + this.synthesizer = props.synthesizer ?? (newStyleSynthesisContext ? new DefaultStackSynthesizer() : new LegacyStackSynthesizer()); diff --git a/packages/@aws-cdk/core/test/test.app.ts b/packages/@aws-cdk/core/test/test.app.ts index d9de3703658dd..35e16e0523d26 100644 --- a/packages/@aws-cdk/core/test/test.app.ts +++ b/packages/@aws-cdk/core/test/test.app.ts @@ -3,8 +3,8 @@ import * as cxapi from '@aws-cdk/cx-api'; import { Test } from 'nodeunit'; import { CfnResource, Construct, Stack, StackProps } from '../lib'; import { Annotations } from '../lib/annotations'; -import { MetadataResource } from '../lib/private/metadata-resource'; import { App, AppProps } from '../lib/app'; +import { MetadataResource } from '../lib/private/metadata-resource'; function withApp(props: AppProps, block: (app: App) => void): cxapi.CloudAssembly { const app = new App({ diff --git a/packages/@aws-cdk/core/test/util.ts b/packages/@aws-cdk/core/test/util.ts index ba499aee5480b..8378d5ad26a3e 100644 --- a/packages/@aws-cdk/core/test/util.ts +++ b/packages/@aws-cdk/core/test/util.ts @@ -1,15 +1,6 @@ -import * as cxapi from '@aws-cdk/cx-api'; -import { Stack, Construct, StackProps, App } from '../lib'; +import { Stack } from '../lib'; import { synthesize } from '../lib/private/synthesis'; -export class TestStack extends Stack { - constructor(scope: Construct, id: string, props?: StackProps) { - super(scope ?? new App({ - context: { [cxapi.DISABLE_VERSION_REPORTING]: true }, - }), id, props); - } -} - export function toCloudFormation(stack: Stack): any { return synthesize(stack, { skipValidation: true }).getStackByName(stack.stackName).template; } diff --git a/packages/@aws-cdk/cx-api/lib/app.ts b/packages/@aws-cdk/cx-api/lib/app.ts index d135e9cf40f07..8e2dd3b2d8b04 100644 --- a/packages/@aws-cdk/cx-api/lib/app.ts +++ b/packages/@aws-cdk/cx-api/lib/app.ts @@ -12,9 +12,16 @@ export const PATH_METADATA_ENABLE_CONTEXT = 'aws:cdk:enable-path-metadata'; /** * Disable the collection and reporting of version information. + * + * @deprecated Use ENABLE_VERSION_REPORTING instead */ export const DISABLE_VERSION_REPORTING = 'aws:cdk:disable-version-reporting'; +/** + * Enable the collection and reporting of version information. + */ +export const VERSION_REPORTING_ENABLED_CONTEXT = 'aws:cdk:version-reporting'; + /** * If this is set, asset staging is disabled. This means that assets will not be copied to * the output directory and will be referenced with absolute source paths. diff --git a/packages/aws-cdk/lib/api/cxapp/exec.ts b/packages/aws-cdk/lib/api/cxapp/exec.ts index 597a236ec1b3c..c2302509e025d 100644 --- a/packages/aws-cdk/lib/api/cxapp/exec.ts +++ b/packages/aws-cdk/lib/api/cxapp/exec.ts @@ -16,25 +16,21 @@ export async function execProgram(aws: SdkProvider, config: Configuration): Prom await populateDefaultEnvironmentIfNeeded(aws, env); const pathMetadata: boolean = config.settings.get(['pathMetadata']) ?? true; - if (pathMetadata) { context[cxapi.PATH_METADATA_ENABLE_CONTEXT] = true; } const assetMetadata: boolean = config.settings.get(['assetMetadata']) ?? true; - if (assetMetadata) { context[cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT] = true; } const versionReporting: boolean = config.settings.get(['versionReporting']) ?? true; - - if (!versionReporting) { - context[cxapi.DISABLE_VERSION_REPORTING] = true; - } + if (versionReporting) { context[cxapi.VERSION_REPORTING_ENABLED_CONTEXT] = true; } + // We need to keep on doing this for framework version from before this flag was deprecated. + if (!versionReporting) { context[cxapi.DISABLE_VERSION_REPORTING] = true; } const stagingEnabled = config.settings.get(['staging']) ?? true; - if (!stagingEnabled) { context[cxapi.DISABLE_ASSET_STAGING_CONTEXT] = true; } From 96c1879127989d69b9a720106074bada3aae8f3e Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 22 Sep 2020 11:17:23 +0200 Subject: [PATCH 10/15] Tiny changes --- packages/@aws-cdk/core/lib/cfn-fn.ts | 1 - .../@aws-cdk/core/lib/private/synthesis.ts | 17 ++--- packages/@aws-cdk/core/lib/stack.ts | 1 - packages/@aws-cdk/core/test/test.app.ts | 75 +++++++++++-------- packages/@aws-cdk/core/test/test.stack.ts | 18 +++++ packages/@aws-cdk/core/test/test.stage.ts | 2 +- packages/@aws-cdk/pipelines/test/testutil.ts | 1 - 7 files changed, 68 insertions(+), 47 deletions(-) diff --git a/packages/@aws-cdk/core/lib/cfn-fn.ts b/packages/@aws-cdk/core/lib/cfn-fn.ts index c0faf78e8b6cf..f1683c18afa60 100644 --- a/packages/@aws-cdk/core/lib/cfn-fn.ts +++ b/packages/@aws-cdk/core/lib/cfn-fn.ts @@ -752,7 +752,6 @@ class FnJoin implements IResolvable { } } - function _inGroupsOf(array: T[], maxGroup: number): T[][] { const result = new Array(); for (let i = 0; i < array.length; i += maxGroup) { diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index 93a5ccf009daf..c8243ec03c763 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -129,18 +129,15 @@ function prepareTree(root: IConstruct) { */ function injectMetadataResources(root: IConstruct) { visit(root, 'post', construct => { - // Only on top-level stacks - if (!Stack.isStack(construct)) { return; } + if (!Stack.isStack(construct) || !construct._versionReportingEnabled) { return; } - if (construct._versionReportingEnabled) { - // Because of https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/assert/lib/synth-utils.ts#L74 - // synthesize() may be called more than once on a stack in unit tests, and the below would break - // if we execute it a second time. Guard against the constructs already existing. - const CDKMetadata = 'CDKMetadata'; - if (construct.node.tryFindChild(CDKMetadata)) { return; } + // Because of https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/assert/lib/synth-utils.ts#L74 + // synthesize() may be called more than once on a stack in unit tests, and the below would break + // if we execute it a second time. Guard against the constructs already existing. + const CDKMetadata = 'CDKMetadata'; + if (construct.node.tryFindChild(CDKMetadata)) { return; } - new MetadataResource(construct, CDKMetadata); - } + new MetadataResource(construct, CDKMetadata); }); } diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 988bbb44d9f53..66056bc70220d 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -296,7 +296,6 @@ export class Stack extends Construct implements ITaggable { */ public readonly _versionReportingEnabled: boolean; - /** * Logical ID generation strategy */ diff --git a/packages/@aws-cdk/core/test/test.app.ts b/packages/@aws-cdk/core/test/test.app.ts index 35e16e0523d26..d5fb7f333a710 100644 --- a/packages/@aws-cdk/core/test/test.app.ts +++ b/packages/@aws-cdk/core/test/test.app.ts @@ -8,7 +8,6 @@ import { MetadataResource } from '../lib/private/metadata-resource'; function withApp(props: AppProps, block: (app: App) => void): cxapi.CloudAssembly { const app = new App({ - runtimeInfo: false, stackTraces: false, ...props, }); @@ -252,24 +251,40 @@ export = { * The are not emitted into Cloud Assembly metadata anymore */ 'runtime library versions are not emitted in asm anymore'(test: Test) { - withCliVersion(() => { - const context: any = {}; + const assembly = withApp({ versionReporting: true }, app => { + const stack = new Stack(app, 'stack1'); + new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); + }); - const assembly = withApp(context, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); - }); + test.deepEqual(assembly.runtime, { libraries: {} }); + test.done(); + }, - test.deepEqual(assembly.runtime, { libraries: {} }); + 'runtime library versions'(test: Test) { + MetadataResource.clearModulesCache(); + + const response = withApp({ versionReporting: true }, app => { + const stack = new Stack(app, 'stack1'); + new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); }); + + const stackTemplate = response.getStackByName('stack1').template; + const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); + + // eslint-disable-next-line @typescript-eslint/no-require-imports + const version = require('../package.json').version; + test.deepEqual(libs['@aws-cdk/core'], version); + test.deepEqual(libs['@aws-cdk/cx-api'], version); + test.deepEqual(libs['jsii-runtime'], `node.js/${process.version}`); + test.done(); }, - 'runtime library versions'(test: Test) { + 'CDK version'(test: Test) { MetadataResource.clearModulesCache(); withCliVersion(() => { - const response = withApp({ runtimeInfo: true }, app => { + const response = withApp({ versionReporting: true }, app => { const stack = new Stack(app, 'stack1'); new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); }); @@ -278,10 +293,7 @@ export = { const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); // eslint-disable-next-line @typescript-eslint/no-require-imports - const version = require('../package.json').version; - test.deepEqual(libs['@aws-cdk/core'], version); - test.deepEqual(libs['@aws-cdk/cx-api'], version); - test.deepEqual(libs['jsii-runtime'], `node.js/${process.version}`); + test.deepEqual(libs['aws-cdk'], '1.2.3'); }); test.done(); @@ -292,7 +304,7 @@ export = { MetadataResource.clearModulesCache(); withCliVersion(() => { - const response = withApp({ runtimeInfo: true }, app => { + const response = withApp({ versionReporting: true }, app => { const stack = new Stack(app, 'stack1'); new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); }); @@ -309,27 +321,24 @@ export = { 'version reporting includes only @aws-cdk, aws-cdk and jsii libraries'(test: Test) { MetadataResource.clearModulesCache(); - withCliVersion(() => { - const response = withApp({ runtimeInfo: true }, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); - }); - const stackTemplate = response.getStackByName('stack1').template; - const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const version = require('../package.json').version; - test.deepEqual(libs, { - 'aws-cdk': '1.2.3', - '@aws-cdk/core': version, - '@aws-cdk/cx-api': version, - '@aws-cdk/region-info': version, - '@aws-cdk/cloud-assembly-schema': version, - 'jsii-runtime': `node.js/${process.version}`, - }); + const response = withApp({ versionReporting: true }, app => { + const stack = new Stack(app, 'stack1'); + new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); }); + const stackTemplate = response.getStackByName('stack1').template; + const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); + const libNames = Object.keys(libs).sort(); + + test.deepEqual(libNames, [ + '@aws-cdk/cloud-assembly-schema', + '@aws-cdk/core', + '@aws-cdk/cx-api', + '@aws-cdk/region-info', + 'jsii-runtime', + ]); + test.done(); }, diff --git a/packages/@aws-cdk/core/test/test.stack.ts b/packages/@aws-cdk/core/test/test.stack.ts index a27a992b5fdfd..d1a9cf10026f1 100644 --- a/packages/@aws-cdk/core/test/test.stack.ts +++ b/packages/@aws-cdk/core/test/test.stack.ts @@ -975,6 +975,24 @@ export = { test.done(); }, + + 'version reporting can be configured on the app'(test: Test) { + const app = new App({ versionReporting: true }); + test.ok(new Stack(app, 'Stack')._versionReportingEnabled); + test.done(); + }, + + 'version reporting can be configured with context'(test: Test) { + const app = new App({ context: { 'aws:cdk:version-reporting': true } }); + test.ok(new Stack(app, 'Stack')._versionReportingEnabled); + test.done(); + }, + + 'version reporting can be configured on the stack'(test: Test) { + const app = new App(); + test.ok(new Stack(app, 'Stack', { versionReporting: true })._versionReportingEnabled); + test.done(); + }, }; class StackWithPostProcessor extends Stack { diff --git a/packages/@aws-cdk/core/test/test.stage.ts b/packages/@aws-cdk/core/test/test.stage.ts index f816458c6199e..52fe1c14c4d21 100644 --- a/packages/@aws-cdk/core/test/test.stage.ts +++ b/packages/@aws-cdk/core/test/test.stage.ts @@ -209,7 +209,7 @@ export = { 'Assemblies can be deeply nested'(test: Test) { // GIVEN - const app = new App({ runtimeInfo: false, treeMetadata: false }); + const app = new App({ treeMetadata: false }); const level1 = new Stage(app, 'StageLevel1'); const level2 = new Stage(level1, 'StageLevel2'); diff --git a/packages/@aws-cdk/pipelines/test/testutil.ts b/packages/@aws-cdk/pipelines/test/testutil.ts index beb6e0180fa87..0e235003fc925 100644 --- a/packages/@aws-cdk/pipelines/test/testutil.ts +++ b/packages/@aws-cdk/pipelines/test/testutil.ts @@ -20,7 +20,6 @@ export class TestApp extends App { }, stackTraces: false, autoSynth: false, - runtimeInfo: false, treeMetadata: false, ...props, }); From 1eadb6d28e299addc6e1a64ddff12c6113474ca5 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 22 Sep 2020 12:58:27 +0200 Subject: [PATCH 11/15] Update packages/@aws-cdk/core/lib/app.ts Co-authored-by: Nick Lynch --- packages/@aws-cdk/core/lib/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/core/lib/app.ts b/packages/@aws-cdk/core/lib/app.ts index a5532dac5687d..f4df159a37f87 100644 --- a/packages/@aws-cdk/core/lib/app.ts +++ b/packages/@aws-cdk/core/lib/app.ts @@ -37,7 +37,7 @@ export interface AppProps { /** * Include runtime versioning information in the Stacks of this app * - * @depreacted use `versionReporting` instead + * @deprecated use `versionReporting` instead * @default Value of 'aws:cdk:version-reporting' context key */ readonly runtimeInfo?: boolean; From 6c0119c27e85d14ef4beeba8ba204f626386b847 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 22 Sep 2020 12:58:35 +0200 Subject: [PATCH 12/15] Update packages/@aws-cdk/core/lib/stack.ts Co-authored-by: Nick Lynch --- packages/@aws-cdk/core/lib/stack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 66056bc70220d..daa82b2492241 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -130,7 +130,7 @@ export interface StackProps { /** * Include runtime versioning information in this Stack * - * @default `versionReporting setting of containing `App`, or value of + * @default `versionReporting` setting of containing `App`, or value of * 'aws:cdk:version-reporting' context key */ readonly versionReporting?: boolean; From fc7229d480a837e7d1c7d21d7f1bc9c02dc448d4 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 22 Sep 2020 12:58:43 +0200 Subject: [PATCH 13/15] Update packages/@aws-cdk/cx-api/lib/app.ts Co-authored-by: Nick Lynch --- packages/@aws-cdk/cx-api/lib/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/cx-api/lib/app.ts b/packages/@aws-cdk/cx-api/lib/app.ts index 8e2dd3b2d8b04..c8bf984a2d944 100644 --- a/packages/@aws-cdk/cx-api/lib/app.ts +++ b/packages/@aws-cdk/cx-api/lib/app.ts @@ -13,7 +13,7 @@ export const PATH_METADATA_ENABLE_CONTEXT = 'aws:cdk:enable-path-metadata'; /** * Disable the collection and reporting of version information. * - * @deprecated Use ENABLE_VERSION_REPORTING instead + * @deprecated Use VERSION_REPORTING_ENABLED_CONTEXT instead */ export const DISABLE_VERSION_REPORTING = 'aws:cdk:disable-version-reporting'; From 183026f3b11aed10161654aa49d7ed670678dd72 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 28 Sep 2020 15:37:53 +0200 Subject: [PATCH 14/15] Rename "version reporting" -> "analytics reporting" --- packages/@aws-cdk/core/lib/app.ts | 8 ++++---- packages/@aws-cdk/core/lib/stack.ts | 6 +++--- packages/@aws-cdk/core/test/test.synthesis.ts | 7 +------ packages/@aws-cdk/cx-api/lib/app.ts | 9 +-------- packages/aws-cdk/lib/api/cxapp/exec.ts | 4 ++-- 5 files changed, 11 insertions(+), 23 deletions(-) diff --git a/packages/@aws-cdk/core/lib/app.ts b/packages/@aws-cdk/core/lib/app.ts index f4df159a37f87..eb095f801ee4f 100644 --- a/packages/@aws-cdk/core/lib/app.ts +++ b/packages/@aws-cdk/core/lib/app.ts @@ -47,7 +47,7 @@ export interface AppProps { * * @default Value of 'aws:cdk:version-reporting' context key */ - readonly versionReporting?: boolean; + readonly analyticsReporting?: boolean; /** * Additional context values for the application. @@ -110,10 +110,10 @@ export class App extends Stage { this.node.setContext(cxapi.DISABLE_METADATA_STACK_TRACE, true); } - const versionReporting = props.versionReporting ?? props.runtimeInfo; + const analyticsReporting = props.analyticsReporting ?? props.runtimeInfo; - if (versionReporting !== undefined) { - this.node.setContext(cxapi.VERSION_REPORTING_ENABLED_CONTEXT, versionReporting); + if (analyticsReporting !== undefined) { + this.node.setContext(cxapi.ANALYTICS_REPORTING_ENABLED_CONTEXT, analyticsReporting); } const autoSynth = props.autoSynth !== undefined ? props.autoSynth : cxapi.OUTDIR_ENV in process.env; diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index daa82b2492241..5309195ef40fe 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -130,10 +130,10 @@ export interface StackProps { /** * Include runtime versioning information in this Stack * - * @default `versionReporting` setting of containing `App`, or value of + * @default `analyticsReporting` setting of containing `App`, or value of * 'aws:cdk:version-reporting' context key */ - readonly versionReporting?: boolean; + readonly analyticsReporting?: boolean; } /** @@ -386,7 +386,7 @@ export class Stack extends Construct implements ITaggable { this.templateFile = `${this.artifactId}.template.json`; // Not for nested stacks - this._versionReportingEnabled = (props.versionReporting ?? this.node.tryGetContext(cxapi.VERSION_REPORTING_ENABLED_CONTEXT)) + this._versionReportingEnabled = (props.analyticsReporting ?? this.node.tryGetContext(cxapi.ANALYTICS_REPORTING_ENABLED_CONTEXT)) && !this.nestedStackParent; this.synthesizer = props.synthesizer ?? (newStyleSynthesisContext diff --git a/packages/@aws-cdk/core/test/test.synthesis.ts b/packages/@aws-cdk/core/test/test.synthesis.ts index 02b96b733d3c1..3d8c0fad35cf1 100644 --- a/packages/@aws-cdk/core/test/test.synthesis.ts +++ b/packages/@aws-cdk/core/test/test.synthesis.ts @@ -2,16 +2,11 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import * as cxapi from '@aws-cdk/cx-api'; import { Test } from 'nodeunit'; import * as cdk from '../lib'; function createModernApp() { - return new cdk.App({ - context: { - [cxapi.DISABLE_VERSION_REPORTING]: 'true', // for test reproducibility - }, - }); + return new cdk.App(); } export = { diff --git a/packages/@aws-cdk/cx-api/lib/app.ts b/packages/@aws-cdk/cx-api/lib/app.ts index c8bf984a2d944..4f9f583d4d309 100644 --- a/packages/@aws-cdk/cx-api/lib/app.ts +++ b/packages/@aws-cdk/cx-api/lib/app.ts @@ -10,17 +10,10 @@ */ export const PATH_METADATA_ENABLE_CONTEXT = 'aws:cdk:enable-path-metadata'; -/** - * Disable the collection and reporting of version information. - * - * @deprecated Use VERSION_REPORTING_ENABLED_CONTEXT instead - */ -export const DISABLE_VERSION_REPORTING = 'aws:cdk:disable-version-reporting'; - /** * Enable the collection and reporting of version information. */ -export const VERSION_REPORTING_ENABLED_CONTEXT = 'aws:cdk:version-reporting'; +export const ANALYTICS_REPORTING_ENABLED_CONTEXT = 'aws:cdk:version-reporting'; /** * If this is set, asset staging is disabled. This means that assets will not be copied to diff --git a/packages/aws-cdk/lib/api/cxapp/exec.ts b/packages/aws-cdk/lib/api/cxapp/exec.ts index c2302509e025d..4d236e725721d 100644 --- a/packages/aws-cdk/lib/api/cxapp/exec.ts +++ b/packages/aws-cdk/lib/api/cxapp/exec.ts @@ -26,9 +26,9 @@ export async function execProgram(aws: SdkProvider, config: Configuration): Prom } const versionReporting: boolean = config.settings.get(['versionReporting']) ?? true; - if (versionReporting) { context[cxapi.VERSION_REPORTING_ENABLED_CONTEXT] = true; } + if (versionReporting) { context[cxapi.ANALYTICS_REPORTING_ENABLED_CONTEXT] = true; } // We need to keep on doing this for framework version from before this flag was deprecated. - if (!versionReporting) { context[cxapi.DISABLE_VERSION_REPORTING] = true; } + if (!versionReporting) { context['aws:cdk:disable-version-reporting'] = true; } const stagingEnabled = config.settings.get(['staging']) ?? true; if (!stagingEnabled) { From 71b85362317f84d3372567411cf175665fdb7590 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 28 Sep 2020 17:43:09 +0200 Subject: [PATCH 15/15] Carry rename through in more places --- packages/@aws-cdk/core/test/test.app.ts | 10 +++++----- packages/@aws-cdk/core/test/test.stack.ts | 4 ++-- packages/aws-cdk/lib/settings.ts | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/core/test/test.app.ts b/packages/@aws-cdk/core/test/test.app.ts index d5fb7f333a710..ac14bcff7e620 100644 --- a/packages/@aws-cdk/core/test/test.app.ts +++ b/packages/@aws-cdk/core/test/test.app.ts @@ -251,7 +251,7 @@ export = { * The are not emitted into Cloud Assembly metadata anymore */ 'runtime library versions are not emitted in asm anymore'(test: Test) { - const assembly = withApp({ versionReporting: true }, app => { + const assembly = withApp({ analyticsReporting: true }, app => { const stack = new Stack(app, 'stack1'); new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); }); @@ -263,7 +263,7 @@ export = { 'runtime library versions'(test: Test) { MetadataResource.clearModulesCache(); - const response = withApp({ versionReporting: true }, app => { + const response = withApp({ analyticsReporting: true }, app => { const stack = new Stack(app, 'stack1'); new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); }); @@ -284,7 +284,7 @@ export = { MetadataResource.clearModulesCache(); withCliVersion(() => { - const response = withApp({ versionReporting: true }, app => { + const response = withApp({ analyticsReporting: true }, app => { const stack = new Stack(app, 'stack1'); new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); }); @@ -304,7 +304,7 @@ export = { MetadataResource.clearModulesCache(); withCliVersion(() => { - const response = withApp({ versionReporting: true }, app => { + const response = withApp({ analyticsReporting: true }, app => { const stack = new Stack(app, 'stack1'); new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); }); @@ -322,7 +322,7 @@ export = { 'version reporting includes only @aws-cdk, aws-cdk and jsii libraries'(test: Test) { MetadataResource.clearModulesCache(); - const response = withApp({ versionReporting: true }, app => { + const response = withApp({ analyticsReporting: true }, app => { const stack = new Stack(app, 'stack1'); new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); }); diff --git a/packages/@aws-cdk/core/test/test.stack.ts b/packages/@aws-cdk/core/test/test.stack.ts index d1a9cf10026f1..1501f91d1fd34 100644 --- a/packages/@aws-cdk/core/test/test.stack.ts +++ b/packages/@aws-cdk/core/test/test.stack.ts @@ -977,7 +977,7 @@ export = { }, 'version reporting can be configured on the app'(test: Test) { - const app = new App({ versionReporting: true }); + const app = new App({ analyticsReporting: true }); test.ok(new Stack(app, 'Stack')._versionReportingEnabled); test.done(); }, @@ -990,7 +990,7 @@ export = { 'version reporting can be configured on the stack'(test: Test) { const app = new App(); - test.ok(new Stack(app, 'Stack', { versionReporting: true })._versionReportingEnabled); + test.ok(new Stack(app, 'Stack', { analyticsReporting: true })._versionReportingEnabled); test.done(); }, }; diff --git a/packages/aws-cdk/lib/settings.ts b/packages/aws-cdk/lib/settings.ts index 1cd74d42e858c..c03583b9dd607 100644 --- a/packages/aws-cdk/lib/settings.ts +++ b/packages/aws-cdk/lib/settings.ts @@ -28,7 +28,7 @@ export class Configuration { public context = new Context(); public readonly defaultConfig = new Settings({ - versionReporting: true, + analyticsReporting: true, pathMetadata: true, output: 'cdk.out', }); @@ -201,7 +201,7 @@ export class Settings { bucketName: argv.bootstrapBucketName, kmsKeyId: argv.bootstrapKmsKeyId, }, - versionReporting: argv.versionReporting, + analyticsReporting: argv.versionReporting, staging: argv.staging, output: argv.output, progress: argv.progress,