Skip to content

Commit 4fcc061

Browse files
committed
refactor(cli): resource-import uses IoHost
1 parent 92a9a73 commit 4fcc061

File tree

11 files changed

+314
-84
lines changed

11 files changed

+314
-84
lines changed

packages/@aws-cdk/toolkit/lib/actions/deploy/index.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,6 @@ export interface BaseDeployOptions {
115115
*/
116116
readonly stacks?: StackSelector;
117117

118-
/**
119-
* @deprecated set on toolkit
120-
* Name of the toolkit stack to use/deploy
121-
*
122-
* @default CDKToolkit
123-
*/
124-
readonly toolkitStackName?: string;
125-
126118
/**
127119
* Role to pass to CloudFormation for deployment
128120
*/

packages/@aws-cdk/toolkit/lib/api/aws-cdk.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export { Deployments, type SuccessfulDeployStackResult } from '../../../../aws-c
77
export { Settings } from '../../../../aws-cdk/lib/api/settings';
88
export { tagsForStack } from '../../../../aws-cdk/lib/api/tags';
99
export { DEFAULT_TOOLKIT_STACK_NAME } from '../../../../aws-cdk/lib/api/toolkit-info';
10+
export { ResourceMigrator } from '../../../../aws-cdk/lib/api/resource-import';
1011

1112
// Context Providers
1213
export * as contextproviders from '../../../../aws-cdk/lib/context-providers';
@@ -19,7 +20,6 @@ export { formatTime } from '../../../../aws-cdk/lib/api/util/string-manipulation
1920

2021
// @todo Not yet API probably should be
2122
export { formatErrorMessage } from '../../../../aws-cdk/lib/util/error';
22-
export { ResourceMigrator } from '../../../../aws-cdk/lib/migrator';
2323
export { obscureTemplate, serializeStructure } from '../../../../aws-cdk/lib/serialize';
2424
export { loadTree, some } from '../../../../aws-cdk/lib/tree';
2525
export { splitBySize } from '../../../../aws-cdk/lib/util';

packages/@aws-cdk/toolkit/lib/api/io/private/codes.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@ export const CODES = {
1515
// 2: List
1616
CDK_TOOLKIT_I2901: 'Provides details on the selected stacks and their dependencies',
1717

18+
// 3: Import & Migrate
19+
CDK_TOOLKIT_E3900: 'Resource import failed',
20+
1821
// 4: Diff
1922

20-
// 5: Deploy
23+
// 5: Deploy & Watch
2124
CDK_TOOLKIT_I5000: 'Provides deployment times',
2225
CDK_TOOLKIT_I5001: 'Provides total time in deploy action, including synth and rollback',
26+
CDK_TOOLKIT_I5002: 'Provides time for resource migration',
2327
CDK_TOOLKIT_I5031: 'Informs about any log groups that are traced as part of the deployment',
2428
CDK_TOOLKIT_I5050: 'Confirm rollback during deployment',
2529
CDK_TOOLKIT_I5060: 'Confirm deploy security sensitive changes',

packages/@aws-cdk/toolkit/lib/toolkit/toolkit.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { CachedCloudAssemblySource, IdentityCloudAssemblySource, StackAssembly,
1919
import { ALL_STACKS, CloudAssemblySourceBuilder } from '../api/cloud-assembly/private';
2020
import { ToolkitError } from '../api/errors';
2121
import { IIoHost, IoMessageCode, IoMessageLevel } from '../api/io';
22-
import { asSdkLogger, withAction, Timer, confirm, error, highlight, info, success, warn, ActionAwareIoHost, debug, result, withoutEmojis, withoutColor, withTrimmedWhitespace } from '../api/io/private';
22+
import { asSdkLogger, withAction, Timer, confirm, error, info, success, warn, ActionAwareIoHost, debug, result, withoutEmojis, withoutColor, withTrimmedWhitespace } from '../api/io/private';
2323

2424
/**
2525
* The current action being performed by the CLI. 'none' represents the absence of an action.
@@ -256,11 +256,12 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
256256
}
257257

258258
const deployments = await this.deploymentsForAction('deploy');
259+
const migrator = new ResourceMigrator({ deployments, ioHost, action });
259260

260-
const migrator = new ResourceMigrator({
261-
deployments,
261+
await migrator.tryMigrateResources(stackCollection, {
262+
toolkitStackName: this.toolkitStackName,
263+
...options,
262264
});
263-
await migrator.tryMigrateResources(stackCollection, options);
264265

265266
const requireApproval = options.requireApproval ?? RequireApproval.NEVER;
266267

@@ -275,7 +276,6 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
275276
}
276277

277278
const stacks = stackCollection.stackArtifacts;
278-
279279
const stackOutputs: { [key: string]: any } = {};
280280
const outputsFile = options.outputsFile;
281281

@@ -303,28 +303,31 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab
303303
const deployStack = async (stackNode: StackNode) => {
304304
const stack = stackNode.stack;
305305
if (stackCollection.stackCount !== 1) {
306-
await ioHost.notify(highlight(stack.displayName));
306+
await ioHost.notify(info(chalk.bold(stack.displayName)));
307307
}
308308

309309
if (!stack.environment) {
310-
// eslint-disable-next-line max-len
311310
throw new ToolkitError(
312311
`Stack ${stack.displayName} does not define an environment, and AWS credentials could not be obtained from standard locations or no region was configured.`,
313312
);
314313
}
315314

315+
// The generated stack has no resources
316316
if (Object.keys(stack.template.Resources || {}).length === 0) {
317-
// The generated stack has no resources
318-
if (!(await deployments.stackExists({ stack }))) {
319-
await ioHost.notify(warn(`${chalk.bold(stack.displayName)}: stack has no resources, skipping deployment.`));
320-
} else {
321-
await ioHost.notify(warn(`${chalk.bold(stack.displayName)}: stack has no resources, deleting existing stack.`));
322-
await this._destroy(assembly, 'deploy', {
323-
stacks: { patterns: [stack.hierarchicalId], strategy: StackSelectionStrategy.PATTERN_MUST_MATCH_SINGLE },
324-
roleArn: options.roleArn,
325-
ci: options.ci,
326-
});
317+
// stack is empty and doesn't exist => do nothing
318+
const stackExists = await deployments.stackExists({ stack });
319+
if (!stackExists) {
320+
return ioHost.notify(warn(`${chalk.bold(stack.displayName)}: stack has no resources, skipping deployment.`));
327321
}
322+
323+
// stack is empty, but exists => delete
324+
await ioHost.notify(warn(`${chalk.bold(stack.displayName)}: stack has no resources, deleting existing stack.`));
325+
await this._destroy(assembly, 'deploy', {
326+
stacks: { patterns: [stack.hierarchicalId], strategy: StackSelectionStrategy.PATTERN_MUST_MATCH_SINGLE },
327+
roleArn: options.roleArn,
328+
ci: options.ci,
329+
});
330+
328331
return;
329332
}
330333

packages/aws-cdk/lib/import.ts renamed to packages/aws-cdk/lib/api/resource-import/importer.ts

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,71 @@
1-
import { DeployOptions } from '@aws-cdk/cloud-assembly-schema';
1+
import { format } from 'util';
22
import * as cfnDiff from '@aws-cdk/cloudformation-diff';
33
import { ResourceDifference } from '@aws-cdk/cloudformation-diff';
44
import * as cxapi from '@aws-cdk/cx-api';
55
import * as chalk from 'chalk';
66
import * as fs from 'fs-extra';
77
import * as promptly from 'promptly';
8-
import { assertIsSuccessfulDeployStackResult, Deployments, DeploymentMethod, ResourceIdentifierProperties, ResourcesToImport } from './api/deployments';
9-
import { Tag } from './api/tags';
10-
import { StackActivityProgress } from './api/util/cloudformation/stack-activity-monitor';
11-
import { error, info, success, warning } from './logging';
12-
import { ToolkitError } from './toolkit/error';
13-
14-
export interface ImportDeploymentOptions extends DeployOptions {
15-
deploymentMethod?: DeploymentMethod;
16-
progress?: StackActivityProgress;
17-
tags?: Tag[];
8+
import { error, info, warn } from '../../cli/messages';
9+
import { IIoHost, ToolkitAction } from '../../toolkit/cli-io-host';
10+
import { ToolkitError } from '../../toolkit/error';
11+
import { assertIsSuccessfulDeployStackResult, Deployments, DeploymentMethod, ResourceIdentifierProperties, ResourcesToImport } from '../deployments';
12+
import { Tag } from '../tags';
13+
import { StackActivityProgress } from '../util/cloudformation/stack-activity-monitor';
14+
15+
export interface ResourceImporterProps {
16+
deployments: Deployments;
17+
ioHost: IIoHost;
18+
action: ToolkitAction;
19+
}
20+
21+
export interface ImportDeploymentOptions {
22+
/**
23+
* Name of the toolkit stack to use/deploy
24+
*
25+
* @default CDKToolkit
26+
*/
27+
readonly toolkitStackName: string;
28+
29+
/**
30+
* Role to pass to CloudFormation for deployment
31+
*
32+
* @default - Default stack role
33+
*/
34+
readonly roleArn?: string;
35+
36+
/**
37+
* Deployment method
38+
*/
39+
readonly deploymentMethod?: DeploymentMethod;
40+
41+
/**
42+
* Stack tags (pass through to CloudFormation)
43+
*/
44+
readonly tags?: Tag[];
45+
46+
/**
47+
* Use previous values for unspecified parameters
48+
*
49+
* If not set, all parameters must be specified for every deployment.
50+
*
51+
* @default true
52+
*/
53+
readonly usePreviousParameters?: boolean;
54+
55+
/**
56+
* Display mode for stack deployment progress.
57+
*
58+
* @default - StackActivityProgress.Bar - stack events will be displayed for
59+
* the resource currently being deployed.
60+
*/
61+
readonly progress?: StackActivityProgress;
62+
63+
/**
64+
* Rollback failed deployments
65+
*
66+
* @default true
67+
*/
68+
readonly rollback?: boolean;
1869
}
1970

2071
/**
@@ -61,9 +112,20 @@ export type ResourceMap = { [logicalResource: string]: ResourceIdentifierPropert
61112
export class ResourceImporter {
62113
private _currentTemplate: any;
63114

115+
private readonly stack: cxapi.CloudFormationStackArtifact;
116+
private readonly cfn: Deployments;
117+
private readonly ioHost: IIoHost;
118+
private readonly action: ToolkitAction;
119+
64120
constructor(
65-
private readonly stack: cxapi.CloudFormationStackArtifact,
66-
private readonly cfn: Deployments) { }
121+
stack: cxapi.CloudFormationStackArtifact,
122+
props: ResourceImporterProps,
123+
) {
124+
this.stack = stack;
125+
this.cfn = props.deployments;
126+
this.ioHost = props.ioHost;
127+
this.action = props.action;
128+
}
67129

68130
/**
69131
* Ask the user for resources to import
@@ -96,19 +158,19 @@ export class ResourceImporter {
96158
const descr = this.describeResource(resource.logicalId);
97159
const idProps = contents[resource.logicalId];
98160
if (idProps) {
99-
info('%s: importing using %s', chalk.blue(descr), chalk.blue(fmtdict(idProps)));
161+
await this.ioHost.notify(info(this.action, format('%s: importing using %s', chalk.blue(descr), chalk.blue(fmtdict(idProps)))));
100162

101163
ret.importResources.push(resource);
102164
ret.resourceMap[resource.logicalId] = idProps;
103165
delete contents[resource.logicalId];
104166
} else {
105-
info('%s: skipping', chalk.blue(descr));
167+
await this.ioHost.notify(info(this.action, format('%s: skipping', chalk.blue(descr))));
106168
}
107169
}
108170

109171
const unknown = Object.keys(contents);
110172
if (unknown.length > 0) {
111-
warning(`Unrecognized resource identifiers in mapping file: ${unknown.join(', ')}`);
173+
await this.ioHost.notify(warn(this.action, `Unrecognized resource identifiers in mapping file: ${unknown.join(', ')}`));
112174
}
113175

114176
return ret;
@@ -158,9 +220,9 @@ export class ResourceImporter {
158220
? ' ✅ %s (no changes)'
159221
: ' ✅ %s';
160222

161-
success('\n' + message, this.stack.displayName);
223+
await this.ioHost.notify(info(this.action, '\n' + chalk.green(format(message, this.stack.displayName))));
162224
} catch (e) {
163-
error('\n ❌ %s failed: %s', chalk.bold(this.stack.displayName), e);
225+
await this.ioHost.notify(error(this.action, format('\n ❌ %s failed: %s', chalk.bold(this.stack.displayName), e), 'CDK_TOOLKIT_E3900'));
164226
throw e;
165227
}
166228
}
@@ -189,7 +251,7 @@ export class ResourceImporter {
189251
const offendingResources = nonAdditions.map(([logId, _]) => this.describeResource(logId));
190252

191253
if (allowNonAdditions) {
192-
warning(`Ignoring updated/deleted resources (--force): ${offendingResources.join(', ')}`);
254+
await this.ioHost.notify(warn(this.action, `Ignoring updated/deleted resources (--force): ${offendingResources.join(', ')}`));
193255
} else {
194256
throw new ToolkitError('No resource updates or deletes are allowed on import operation. Make sure to resolve pending changes ' +
195257
`to existing resources, before attempting an import. Updated/deleted resources: ${offendingResources.join(', ')} (--force to override)`);
@@ -277,7 +339,7 @@ export class ResourceImporter {
277339
// Skip resources that do not support importing
278340
const resourceType = chg.resourceDiff.newResourceType;
279341
if (resourceType === undefined || !(resourceType in resourceIdentifiers)) {
280-
warning(`${resourceName}: unsupported resource type ${resourceType}, skipping import.`);
342+
await this.ioHost.notify(warn(this.action, `${resourceName}: unsupported resource type ${resourceType}, skipping import.`));
281343
return undefined;
282344
}
283345

@@ -303,7 +365,7 @@ export class ResourceImporter {
303365

304366
// If we got here and the user rejected any available identifiers, then apparently they don't want the resource at all
305367
if (satisfiedPropSets.length > 0) {
306-
info(chalk.grey(`Skipping import of ${resourceName}`));
368+
await this.ioHost.notify(info(this.action, chalk.grey(`Skipping import of ${resourceName}`)));
307369
return undefined;
308370
}
309371

@@ -321,7 +383,7 @@ export class ResourceImporter {
321383

322384
// Do the input loop here
323385
if (preamble) {
324-
info(preamble);
386+
await this.ioHost.notify(info(this.action, preamble));
325387
}
326388
for (const idProps of idPropSets) {
327389
const input: Record<string, string> = {};
@@ -356,7 +418,7 @@ export class ResourceImporter {
356418
}
357419
}
358420

359-
info(chalk.grey(`Skipping import of ${resourceName}`));
421+
await this.ioHost.notify(info(this.action, chalk.grey(`Skipping import of ${resourceName}`)));
360422
return undefined;
361423
}
362424

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './importer';
2+
export * from './migrator';

0 commit comments

Comments
 (0)