Skip to content

feat: refactor execution #674

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,7 @@ const toolkitLib = configureProject(
'cdk-from-cfn',
'chalk@^4',
'chokidar@^3',
'fast-deep-equal',
'fs-extra@^9',
'glob',
'minimatch',
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/cloudformation-diff/lib/mappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export function formatAmbiguousMappings(
formatter.print('Detected ambiguities:');
formatter.print(tables.join('\n\n'));
formatter.print(' ');
formatter.print(chalk.yellow('Please provide an override file to resolve these ambiguous mappings.'));
formatter.print(' ');

function renderTable([removed, added]: [string[], string[]]) {
return formatTable([['', 'Resource'], renderRemoval(removed), renderAddition(added)], undefined);
Expand Down
4 changes: 4 additions & 0 deletions packages/@aws-cdk/toolkit-lib/.projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/@aws-cdk/toolkit-lib/.projen/tasks.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/@aws-cdk/toolkit-lib/docs/message-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ Please let us know by [opening an issue](https://github.com/aws/aws-cdk-cli/issu
| `CDK_TOOLKIT_E7900` | Stack deletion failed | `error` | {@link ErrorPayload} |
| `CDK_TOOLKIT_E8900` | Stack refactor failed | `error` | {@link ErrorPayload} |
| `CDK_TOOLKIT_I8900` | Refactor result | `result` | {@link RefactorResult} |
| `CDK_TOOLKIT_I8910` | Confirm refactor | `info` | {@link ConfirmationRequest} |
| `CDK_TOOLKIT_W8010` | Refactor execution not yet supported | `warn` | n/a |
| `CDK_TOOLKIT_I9000` | Provides bootstrap times | `info` | {@link Duration} |
| `CDK_TOOLKIT_I9100` | Bootstrap progress | `info` | {@link BootstrapEnvironmentProgress} |
Expand Down
21 changes: 21 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/actions/refactor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,27 @@ export interface RefactorOptions {
* A list of names of additional deployed stacks to be included in the comparison.
*/
additionalStackNames?: string[];

/**
* List of patterns for filtering local stacks. If no patterns are passed,
* then all stacks, except the bootstrap stacks are considered. If you want
* to consider all stacks (including bootstrap stacks), pass the wildcard
* '*'.
*/
localStacks?: string[];

/**
* List of patterns for filtering deployed stacks. If no patterns are passed,
* then all stacks, except the bootstrap stacks are considered. If you want
* to consider all stacks (including bootstrap stacks), pass the wildcard
* '*'.
*/
deployedStacks?: string[];

/**
* Whether to do the refactor without prompting the user for confirmation.
*/
force?: boolean;
}

export interface MappingGroup {
Expand Down
40 changes: 40 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/api/aws-auth/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import type {
CreateGeneratedTemplateCommandOutput,
CreateStackCommandInput,
CreateStackCommandOutput,
CreateStackRefactorCommandInput,
DeleteChangeSetCommandInput,
DeleteChangeSetCommandOutput,
DeleteGeneratedTemplateCommandInput,
Expand Down Expand Up @@ -97,6 +98,9 @@ import type {
DetectStackResourceDriftCommandInput,
DetectStackResourceDriftCommandOutput,
DescribeStackResourceDriftsCommandInput,
ExecuteStackRefactorCommandInput,
DescribeStackRefactorCommandInput,
CreateStackRefactorCommandOutput, ExecuteStackRefactorCommandOutput,
} from '@aws-sdk/client-cloudformation';
import {
paginateListStacks,
Expand All @@ -105,6 +109,7 @@ import {
CreateChangeSetCommand,
CreateGeneratedTemplateCommand,
CreateStackCommand,
CreateStackRefactorCommand,
DeleteChangeSetCommand,
DeleteGeneratedTemplateCommand,
DeleteStackCommand,
Expand All @@ -115,6 +120,7 @@ import {
DescribeStackResourcesCommand,
DescribeStacksCommand,
ExecuteChangeSetCommand,
ExecuteStackRefactorCommand,
GetGeneratedTemplateCommand,
GetTemplateCommand,
GetTemplateSummaryCommand,
Expand All @@ -132,6 +138,8 @@ import {
DescribeStackResourceDriftsCommand,
DetectStackDriftCommand,
DetectStackResourceDriftCommand,
waitUntilStackRefactorCreateComplete,
waitUntilStackRefactorExecuteComplete,
} from '@aws-sdk/client-cloudformation';
import type {
FilterLogEventsCommandInput,
Expand Down Expand Up @@ -460,6 +468,10 @@ export interface ICloudFormationClient {
describeStackEvents(input: DescribeStackEventsCommandInput): Promise<DescribeStackEventsCommandOutput>;
listStackResources(input: ListStackResourcesCommandInput): Promise<StackResourceSummary[]>;
paginatedListStacks(input: ListStacksCommandInput): Promise<StackSummary[]>;
createStackRefactor(input: CreateStackRefactorCommandInput): Promise<CreateStackRefactorCommandOutput>;
executeStackRefactor(input: ExecuteStackRefactorCommandInput): Promise<ExecuteStackRefactorCommandOutput>;
waitUntilStackRefactorCreateComplete(input: DescribeStackRefactorCommandInput): Promise<WaiterResult>;
waitUntilStackRefactorExecuteComplete(input: DescribeStackRefactorCommandInput): Promise<WaiterResult>;
}

export interface ICloudWatchLogsClient {
Expand Down Expand Up @@ -766,6 +778,34 @@ export class SDK {
}
return stackResources;
},
createStackRefactor: (input: CreateStackRefactorCommandInput): Promise<CreateStackRefactorCommandOutput> => {
return client.send(new CreateStackRefactorCommand(input));
},
executeStackRefactor: (input: ExecuteStackRefactorCommandInput): Promise<ExecuteStackRefactorCommandOutput> => {
return client.send(new ExecuteStackRefactorCommand(input));
},
waitUntilStackRefactorCreateComplete: (input: DescribeStackRefactorCommandInput): Promise<WaiterResult> => {
return waitUntilStackRefactorCreateComplete(
{
client,
maxWaitTime: 600,
minDelay: 6,
maxDelay: 6,
},
input,
);
},
waitUntilStackRefactorExecuteComplete: (input: DescribeStackRefactorCommandInput): Promise<WaiterResult> => {
return waitUntilStackRefactorExecuteComplete(
{
client,
maxWaitTime: 600,
minDelay: 6,
maxDelay: 6,
},
input,
);
},
};
}

Expand Down
17 changes: 16 additions & 1 deletion packages/@aws-cdk/toolkit-lib/lib/api/io/private/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@ import type { StackRollbackProgress } from '../../../payloads/rollback';
import type { MfaTokenRequest, SdkTrace } from '../../../payloads/sdk';
import type { StackActivity, StackMonitoringControlEvent } from '../../../payloads/stack-activity';
import type { StackSelectionDetails } from '../../../payloads/synth';
import type { AssemblyData, ConfirmationRequest, ContextProviderMessageSource, Duration, ErrorPayload, SingleStack, StackAndAssemblyData } from '../../../payloads/types';
import type {
AssemblyData,
ConfirmationRequest,
ContextProviderMessageSource,
DataRequest,
Duration,
ErrorPayload,
SingleStack,
StackAndAssemblyData,
} from '../../../payloads/types';
import type { FileWatchEvent, WatchSettings } from '../../../payloads/watch';

/**
Expand Down Expand Up @@ -381,6 +390,12 @@ export const IO = {
interface: 'RefactorResult',
}),

CDK_TOOLKIT_I8910: make.question<DataRequest>({
code: 'CDK_TOOLKIT_I8910',
description: 'Confirm refactor',
interface: 'ConfirmationRequest',
}),

CDK_TOOLKIT_W8010: make.warn({
code: 'CDK_TOOLKIT_W8010',
description: 'Refactor execution not yet supported',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { TypedMapping } from '@aws-cdk/cloudformation-diff';
import type * as cxapi from '@aws-cdk/cx-api';
import type { ResourceMapping as CfnResourceMapping } from '@aws-sdk/client-cloudformation';

export interface CloudFormationResource {
Type: string;
Expand All @@ -13,6 +14,8 @@ export interface CloudFormationTemplate {
[logicalId: string]: CloudFormationResource;
};
Outputs?: Record<string, any>;
Rules?: Record<string, any>;
Parameters?: Record<string, any>;
}

export interface CloudFormationStack {
Expand Down Expand Up @@ -54,6 +57,10 @@ export class ResourceLocation {
public equalTo(other: ResourceLocation): boolean {
return this.logicalResourceId === other.logicalResourceId && this.stack.stackName === other.stack.stackName;
}

public get stackName(): string {
return this.stack.stackName;
}
}

/**
Expand All @@ -72,4 +79,17 @@ export class ResourceMapping {
destinationPath: this.destination.toPath(),
};
}

public toCloudFormation(): CfnResourceMapping {
return {
Source: {
StackName: this.source.stack.stackName,
LogicalResourceId: this.source.logicalResourceId,
},
Destination: {
StackName: this.destination.stack.stackName,
LogicalResourceId: this.destination.logicalResourceId,
},
};
}
}
56 changes: 56 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/api/refactoring/context.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import type { Environment } from '@aws-cdk/cx-api';
import type { StackDefinition } from '@aws-sdk/client-cloudformation';
import type { CloudFormationStack } from './cloudformation';
import { ResourceLocation, ResourceMapping } from './cloudformation';
import type { GraphDirection } from './digest';
import { computeResourceDigests } from './digest';
import { ToolkitError } from '../../toolkit/toolkit-error';
import { equalSets } from '../../util/sets';
import type { SDK } from '../aws-auth/sdk';
import type { SdkProvider } from '../aws-auth/sdk-provider';
import { EnvironmentResourcesRegistry } from '../environment';
import type { IoHelper } from '../io/private';
import { Mode } from '../plugin';

/**
* Represents a set of possible moves of a resource from one location
Expand Down Expand Up @@ -51,6 +57,56 @@ export class RefactoringContext {
public get mappings(): ResourceMapping[] {
return this._mappings;
}

public async execute(stackDefinitions: StackDefinition[], sdkProvider: SdkProvider, ioHelper: IoHelper): Promise<void> {
if (this.mappings.length === 0) {
return;
}

const sdk = (await sdkProvider.forEnvironment(this.environment, Mode.ForWriting)).sdk;

await this.checkBootstrapVersion(sdk, ioHelper);

const cfn = sdk.cloudFormation();
const mappings = this.mappings;

const input = {
EnableStackCreation: true,
ResourceMappings: mappings.map((m) => m.toCloudFormation()),
StackDefinitions: stackDefinitions,
};
const refactor = await cfn.createStackRefactor(input);

await cfn.waitUntilStackRefactorCreateComplete({
StackRefactorId: refactor.StackRefactorId,
});

await cfn.executeStackRefactor({
StackRefactorId: refactor.StackRefactorId,
});

await cfn.waitUntilStackRefactorExecuteComplete({
StackRefactorId: refactor.StackRefactorId,
});
}

private async checkBootstrapVersion(sdk: SDK, ioHelper: IoHelper) {
const environmentResourcesRegistry = new EnvironmentResourcesRegistry();
const envResources = environmentResourcesRegistry.for(this.environment, sdk, ioHelper);
let bootstrapVersion: number | undefined = undefined;
try {
// Try to get the bootstrap version
bootstrapVersion = (await envResources.lookupToolkit()).version;
} catch (e) {
// But if we can't, keep going. Maybe we can still succeed.
}
if (bootstrapVersion != null && bootstrapVersion < 28) {
const environment = `aws://${this.environment.account}/${this.environment.region}`;
throw new ToolkitError(
`The CDK toolkit stack in environment ${environment} doesn't support refactoring. Please run 'cdk bootstrap ${environment}' to update it.`,
);
}
}
}

/**
Expand Down
6 changes: 6 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/api/refactoring/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ interface StackGroup {
deployedStacks: CloudFormationStack[];
}

interface StackGroup {
environment: cxapi.Environment;
localStacks: CloudFormationStack[];
deployedStacks: CloudFormationStack[];
}

export async function usePrescribedMappings(
mappingGroups: MappingGroup[],
sdkProvider: SdkProvider,
Expand Down
Loading