diff --git a/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts b/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts index 6fc24758d3dce..5c0f1a2c2a289 100644 --- a/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts +++ b/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts @@ -1,10 +1,7 @@ -import { promises as fs } from 'fs'; import * as cxapi from '@aws-cdk/cx-api'; -import { RegionInfo } from '@aws-cdk/region-info'; -import * as semver from 'semver'; import { CloudAssembly } from './cloud-assembly'; import * as contextproviders from '../../context-providers'; -import { debug, warning } from '../../logging'; +import { debug } from '../../logging'; import { Configuration } from '../../settings'; import { ToolkitError } from '../../toolkit/error'; import { SdkProvider } from '../aws-auth'; @@ -14,13 +11,6 @@ import { SdkProvider } from '../aws-auth'; */ export type Synthesizer = (aws: SdkProvider, config: Configuration) => Promise; -/** - * The Cloud Assembly schema version where the framework started to generate analytics itself - * - * See https://github.com/aws/aws-cdk/pull/10306 - */ -const TEMPLATE_INCLUDES_ANALYTICS_SCHEMA_VERSION = '6.0.0'; - export interface CloudExecutableProps { /** * Application configuration (settings and context) @@ -69,8 +59,6 @@ export class CloudExecutable { } private async doSynthesize(): Promise { - const trackVersions: boolean = this.props.configuration.settings.get(['versionReporting']); - // We may need to run the cloud executable multiple times in order to satisfy all missing context // (When the executable runs, it will tell us about context it wants to use // but it missing. We'll then look up the context and run the executable again, and @@ -113,76 +101,10 @@ export class CloudExecutable { } } - if (trackVersions && !semver.gte(assembly.version, TEMPLATE_INCLUDES_ANALYTICS_SCHEMA_VERSION)) { - // @deprecate(v2): the framework now manages its own analytics. For - // Cloud Assemblies *older* than when we introduced this feature, have - // the CLI add it. Otherwise, do nothing. - 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); - - async function processAssembly(assembly: cxapi.CloudAssembly) { - for (const stack of assembly.stacks) { - await processStack(stack); - } - for (const nested of assembly.nestedAssemblies) { - await processAssembly(nested.nestedAssembly); - } - } - - async function processStack(stack: cxapi.CloudFormationStackArtifact) { - const resourcePresent = stack.environment.region === cxapi.UNKNOWN_REGION - || RegionInfo.get(stack.environment.region).cdkMetadataResourceAvailable; - if (!resourcePresent) { return; } - - if (!stack.template.Resources) { - stack.template.Resources = {}; - } - if (stack.template.Resources.CDKMetadata) { - // Already added by framework, this is expected. - 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`); - } - } - - // 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' }); - } - } - private get canLookup() { return !!(this.props.configuration.settings.get(['lookups']) ?? true); } @@ -202,48 +124,3 @@ function setsEqual(a: Set, b: Set) { } return true; } - -function _makeCdkMetadataAvailableCondition() { - return _fnOr(RegionInfo.regions - .filter(ri => ri.cdkMetadataResourceAvailable) - .map(ri => ({ 'Fn::Equals': [{ Ref: 'AWS::Region' }, ri.name] }))); -} - -/** - * This takes a bunch of operands and crafts an `Fn::Or` for those. Funny thing is `Fn::Or` requires - * at least 2 operands and at most 10 operands, so we have to... do this. - */ -function _fnOr(operands: any[]): any { - if (operands.length === 0) { - throw new ToolkitError('Cannot build `Fn::Or` with zero operands!'); - } - if (operands.length === 1) { - return operands[0]; - } - if (operands.length <= 10) { - return { 'Fn::Or': operands }; - } - return _fnOr(_inGroupsOf(operands, 10).map(group => _fnOr(group))); -} - -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; -} - -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(','); -} diff --git a/packages/aws-cdk/test/api/cloud-executable.test.ts b/packages/aws-cdk/test/api/cloud-executable.test.ts index 2da2e5652b9f7..796ea71819495 100644 --- a/packages/aws-cdk/test/api/cloud-executable.test.ts +++ b/packages/aws-cdk/test/api/cloud-executable.test.ts @@ -1,69 +1,10 @@ /* eslint-disable import/order */ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import * as cxapi from '@aws-cdk/cx-api'; import { DefaultSelection } from '../../lib/api/cxapp/cloud-assembly'; import { registerContextProvider } from '../../lib/context-providers'; import { MockCloudExecutable } from '../util'; -// Apps on this version of the cxschema don't emit their own metadata resources -// yet, so rely on the CLI to add the Metadata resource in. -const SCHEMA_VERSION_THAT_DOESNT_INCLUDE_METADATA_ITSELF = '2.0.0'; - describe('AWS::CDK::Metadata', () => { - test('is generated for relocatable stacks from old frameworks', async () => { - const cx = await testCloudExecutable({ - env: `aws://${cxapi.UNKNOWN_ACCOUNT}/${cxapi.UNKNOWN_REGION}`, - versionReporting: true, - schemaVersion: SCHEMA_VERSION_THAT_DOESNT_INCLUDE_METADATA_ITSELF, - }); - const cxasm = await cx.synthesize(); - - const result = cxasm.stackById('withouterrors').firstStack; - const metadata = result.template.Resources && result.template.Resources.CDKMetadata; - expect(metadata).toEqual({ - Type: 'AWS::CDK::Metadata', - Properties: { - // eslint-disable-next-line @typescript-eslint/no-require-imports - Modules: `${require('../../package.json').name}=${require('../../package.json').version}`, - }, - Condition: 'CDKMetadataAvailable', - }); - - expect(result.template.Conditions?.CDKMetadataAvailable).toBeDefined(); - }); - - test('is generated for stacks in supported regions from old frameworks', async () => { - const cx = await testCloudExecutable({ - env: 'aws://012345678912/us-east-1', - versionReporting: true, - schemaVersion: SCHEMA_VERSION_THAT_DOESNT_INCLUDE_METADATA_ITSELF, - }); - const cxasm = await cx.synthesize(); - - const result = cxasm.stackById('withouterrors').firstStack; - const metadata = result.template.Resources && result.template.Resources.CDKMetadata; - expect(metadata).toEqual({ - Type: 'AWS::CDK::Metadata', - Properties: { - // eslint-disable-next-line @typescript-eslint/no-require-imports - Modules: `${require('../../package.json').name}=${require('../../package.json').version}`, - }, - }); - }); - - test('is not generated for stacks in unsupported regions from old frameworks', async () => { - const cx = await testCloudExecutable({ - env: 'aws://012345678912/bermuda-triangle-1337', - versionReporting: true, - schemaVersion: SCHEMA_VERSION_THAT_DOESNT_INCLUDE_METADATA_ITSELF, - }); - const cxasm = await cx.synthesize(); - - const result = cxasm.stackById('withouterrors').firstStack; - const metadata = result.template.Resources && result.template.Resources.CDKMetadata; - expect(metadata).toBeUndefined(); - }); - test('is not generated for new frameworks', async () => { const cx = await testCloudExecutable({ env: 'aws://012345678912/us-east-1',