|
1 |
| -import * as path from 'path'; |
2 | 1 | import * as cxapi from '@aws-cdk/cx-api';
|
3 | 2 | import { AssetManifest } from 'cdk-assets';
|
4 |
| -import * as fs from 'fs-extra'; |
5 | 3 | import { Tag } from '../cdk-toolkit';
|
6 | 4 | import { debug, warning } from '../logging';
|
7 | 5 | import { publishAssets } from '../util/asset-publishing';
|
8 | 6 | import { Mode } from './aws-auth/credentials';
|
9 | 7 | import { ISDK } from './aws-auth/sdk';
|
10 | 8 | import { SdkProvider } from './aws-auth/sdk-provider';
|
11 | 9 | import { deployStack, DeployStackResult, destroyStack } from './deploy-stack';
|
12 |
| -import { LazyListStackResources, ListStackResources } from './evaluate-cloudformation-template'; |
| 10 | +import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate } from './nested-stack-helpers'; |
13 | 11 | import { ToolkitInfo } from './toolkit-info';
|
14 | 12 | import { CloudFormationStack, Template } from './util/cloudformation';
|
15 | 13 | import { StackActivityProgress } from './util/cloudformation/stack-activity-monitor';
|
@@ -283,21 +281,13 @@ export class CloudFormationDeployments {
|
283 | 281 |
|
284 | 282 | public async readCurrentTemplateWithNestedStacks(rootStackArtifact: cxapi.CloudFormationStackArtifact): Promise<Template> {
|
285 | 283 | const sdk = await this.prepareSdkWithLookupOrDeployRole(rootStackArtifact);
|
286 |
| - const deployedTemplate = await this.readCurrentTemplate(rootStackArtifact, sdk); |
287 |
| - await this.addNestedTemplatesToGeneratedAndDeployedStacks(rootStackArtifact, sdk, { |
288 |
| - generatedTemplate: rootStackArtifact.template, |
289 |
| - deployedTemplate: deployedTemplate, |
290 |
| - deployedStackName: rootStackArtifact.stackName, |
291 |
| - }); |
292 |
| - return deployedTemplate; |
| 284 | + return (await loadCurrentTemplateWithNestedStacks(rootStackArtifact, sdk)).deployedTemplate; |
293 | 285 | }
|
294 | 286 |
|
295 |
| - public async readCurrentTemplate(stackArtifact: cxapi.CloudFormationStackArtifact, sdk?: ISDK): Promise<Template> { |
| 287 | + public async readCurrentTemplate(stackArtifact: cxapi.CloudFormationStackArtifact): Promise<Template> { |
296 | 288 | debug(`Reading existing template for stack ${stackArtifact.displayName}.`);
|
297 |
| - if (!sdk) { |
298 |
| - sdk = await this.prepareSdkWithLookupOrDeployRole(stackArtifact); |
299 |
| - } |
300 |
| - return this.readCurrentStackTemplate(stackArtifact.stackName, sdk); |
| 289 | + const sdk = await this.prepareSdkWithLookupOrDeployRole(stackArtifact); |
| 290 | + return loadCurrentTemplate(stackArtifact, sdk); |
301 | 291 | }
|
302 | 292 |
|
303 | 293 | public async deployStack(options: DeployStackOptions): Promise<DeployStackResult> {
|
@@ -370,83 +360,6 @@ export class CloudFormationDeployments {
|
370 | 360 | return (await this.prepareSdkFor(stackArtifact, undefined, Mode.ForReading)).stackSdk;
|
371 | 361 | }
|
372 | 362 |
|
373 |
| - private async readCurrentStackTemplate(stackName: string, stackSdk: ISDK) : Promise<Template> { |
374 |
| - const cfn = stackSdk.cloudFormation(); |
375 |
| - const stack = await CloudFormationStack.lookup(cfn, stackName); |
376 |
| - return stack.template(); |
377 |
| - } |
378 |
| - |
379 |
| - private async addNestedTemplatesToGeneratedAndDeployedStacks( |
380 |
| - rootStackArtifact: cxapi.CloudFormationStackArtifact, |
381 |
| - sdk: ISDK, |
382 |
| - parentTemplates: StackTemplates, |
383 |
| - ): Promise<void> { |
384 |
| - const listStackResources = parentTemplates.deployedStackName ? new LazyListStackResources(sdk, parentTemplates.deployedStackName) : undefined; |
385 |
| - for (const [nestedStackLogicalId, generatedNestedStackResource] of Object.entries(parentTemplates.generatedTemplate.Resources ?? {})) { |
386 |
| - if (!this.isCdkManagedNestedStack(generatedNestedStackResource)) { |
387 |
| - continue; |
388 |
| - } |
389 |
| - |
390 |
| - const assetPath = generatedNestedStackResource.Metadata['aws:asset:path']; |
391 |
| - const nestedStackTemplates = await this.getNestedStackTemplates(rootStackArtifact, assetPath, nestedStackLogicalId, listStackResources, sdk); |
392 |
| - |
393 |
| - generatedNestedStackResource.Properties.NestedTemplate = nestedStackTemplates.generatedTemplate; |
394 |
| - |
395 |
| - const deployedParentTemplate = parentTemplates.deployedTemplate; |
396 |
| - deployedParentTemplate.Resources = deployedParentTemplate.Resources ?? {}; |
397 |
| - const deployedNestedStackResource = deployedParentTemplate.Resources[nestedStackLogicalId] ?? {}; |
398 |
| - deployedParentTemplate.Resources[nestedStackLogicalId] = deployedNestedStackResource; |
399 |
| - deployedNestedStackResource.Type = deployedNestedStackResource.Type ?? 'AWS::CloudFormation::Stack'; |
400 |
| - deployedNestedStackResource.Properties = deployedNestedStackResource.Properties ?? {}; |
401 |
| - deployedNestedStackResource.Properties.NestedTemplate = nestedStackTemplates.deployedTemplate; |
402 |
| - |
403 |
| - await this.addNestedTemplatesToGeneratedAndDeployedStacks( |
404 |
| - rootStackArtifact, |
405 |
| - sdk, |
406 |
| - nestedStackTemplates, |
407 |
| - ); |
408 |
| - } |
409 |
| - } |
410 |
| - |
411 |
| - private async getNestedStackTemplates( |
412 |
| - rootStackArtifact: cxapi.CloudFormationStackArtifact, nestedTemplateAssetPath: string, nestedStackLogicalId: string, |
413 |
| - listStackResources: ListStackResources | undefined, sdk: ISDK, |
414 |
| - ): Promise<StackTemplates> { |
415 |
| - const nestedTemplatePath = path.join(rootStackArtifact.assembly.directory, nestedTemplateAssetPath); |
416 |
| - |
417 |
| - // CFN generates the nested stack name in the form `ParentStackName-NestedStackLogicalID-SomeHashWeCan'tCompute, |
418 |
| - // the arn is of the form: arn:aws:cloudformation:region:123456789012:stack/NestedStackName/AnotherHashWeDon'tNeed |
419 |
| - // so we get the ARN and manually extract the name. |
420 |
| - const nestedStackArn = await this.getNestedStackArn(nestedStackLogicalId, listStackResources); |
421 |
| - const deployedStackName = nestedStackArn?.slice(nestedStackArn.indexOf('/') + 1, nestedStackArn.lastIndexOf('/')); |
422 |
| - |
423 |
| - return { |
424 |
| - generatedTemplate: JSON.parse(fs.readFileSync(nestedTemplatePath, 'utf-8')), |
425 |
| - deployedTemplate: deployedStackName |
426 |
| - ? await this.readCurrentStackTemplate(deployedStackName, sdk) |
427 |
| - : {}, |
428 |
| - deployedStackName, |
429 |
| - }; |
430 |
| - } |
431 |
| - |
432 |
| - private async getNestedStackArn( |
433 |
| - nestedStackLogicalId: string, listStackResources?: ListStackResources, |
434 |
| - ): Promise<string | undefined> { |
435 |
| - try { |
436 |
| - const stackResources = await listStackResources?.listStackResources(); |
437 |
| - return stackResources?.find(sr => sr.LogicalResourceId === nestedStackLogicalId)?.PhysicalResourceId; |
438 |
| - } catch (e) { |
439 |
| - if (e.message.startsWith('Stack with id ') && e.message.endsWith(' does not exist')) { |
440 |
| - return; |
441 |
| - } |
442 |
| - throw e; |
443 |
| - } |
444 |
| - } |
445 |
| - |
446 |
| - private isCdkManagedNestedStack(stackResource: any): stackResource is NestedStackResource { |
447 |
| - return stackResource.Type === 'AWS::CloudFormation::Stack' && stackResource.Metadata && stackResource.Metadata['aws:asset:path']; |
448 |
| - } |
449 |
| - |
450 | 363 | /**
|
451 | 364 | * Get the environment necessary for touching the given stack
|
452 | 365 | *
|
@@ -528,14 +441,3 @@ export class CloudFormationDeployments {
|
528 | 441 | function isAssetManifestArtifact(art: cxapi.CloudArtifact): art is cxapi.AssetManifestArtifact {
|
529 | 442 | return art instanceof cxapi.AssetManifestArtifact;
|
530 | 443 | }
|
531 |
| - |
532 |
| -interface StackTemplates { |
533 |
| - readonly generatedTemplate: any; |
534 |
| - readonly deployedTemplate: any; |
535 |
| - readonly deployedStackName: string | undefined; |
536 |
| -} |
537 |
| - |
538 |
| -interface NestedStackResource { |
539 |
| - readonly Metadata: { 'aws:asset:path': string }; |
540 |
| - readonly Properties: any; |
541 |
| -} |
0 commit comments