Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { format } from 'node:util';
import { Writable } from 'stream';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
import {
type DescribeChangeSetOutput,
type TemplateDiff,
fullDiff,
formatSecurityChanges,
Expand Down Expand Up @@ -75,14 +74,10 @@ interface DiffFormatterProps {
readonly ioHelper: IoHelper;

/**
* The old/current state of the stack.
* The relevant information for the Template that is being diffed.
* Includes the old/current state of the stack as well as the new state.
*/
readonly oldTemplate: any;

/**
* The new/target state of the stack.
*/
readonly newTemplate: cxapi.CloudFormationStackArtifact;
readonly templateInfo: TemplateInfo;
}

/**
Expand All @@ -93,18 +88,6 @@ interface FormatSecurityDiffOptions {
* The approval level of the security diff
*/
readonly requireApproval: RequireApproval;

/**
* The name of the Stack.
*/
readonly stackName?: string;

/**
* The changeSet for the Stack.
*
* @default undefined
*/
readonly changeSet?: DescribeChangeSetOutput;
}

/**
Expand All @@ -131,30 +114,57 @@ interface FormatStackDiffOptions {
* @default false
*/
readonly quiet?: boolean;
}

interface ReusableStackDiffOptions extends FormatStackDiffOptions {
readonly ioDefaultHelper: IoDefaultMessages;
}

/**
* Information on a template's old/new state
* that is used for diff.
*/
export interface TemplateInfo {
/**
* The name of the stack
* The old/existing template
*/
readonly stackName?: string;
readonly oldTemplate: any;

/**
* The new template
*/
readonly newTemplate: cxapi.CloudFormationStackArtifact;

/**
* A CloudFormation ChangeSet to help the diff operation.
* Probably created via `createDiffChangeSet`.
*
* @default undefined
*/
readonly changeSet?: any;

/**
* The name of the stack
*
* @default undefined
*/
readonly changeSet?: DescribeChangeSetOutput;
readonly stackName?: string;

/**
* Whether or not there are any imported resources
*
* @default false
*/
readonly isImport?: boolean;

/**
* @default undefined
* Any nested stacks included in the template
*
* @default {}
*/
readonly nestedStackTemplates?: { [nestedStackLogicalId: string]: NestedStackTemplates };
}

interface ReusableStackDiffOptions extends Omit<FormatStackDiffOptions, 'stackName' | 'nestedStackTemplates'> {
readonly ioDefaultHelper: IoDefaultMessages;
readonly nestedStacks?: {
[nestedStackLogicalId: string]: NestedStackTemplates;
};
}

/**
Expand All @@ -164,22 +174,30 @@ export class DiffFormatter {
private readonly ioHelper: IoHelper;
private readonly oldTemplate: any;
private readonly newTemplate: cxapi.CloudFormationStackArtifact;
private readonly stackName?: string;
private readonly changeSet?: any;
private readonly nestedStacks: { [nestedStackLogicalId: string]: NestedStackTemplates } | undefined;
private readonly isImport: boolean;

constructor(props: DiffFormatterProps) {
this.ioHelper = props.ioHelper;
this.oldTemplate = props.oldTemplate;
this.newTemplate = props.newTemplate;
this.oldTemplate = props.templateInfo.oldTemplate;
this.newTemplate = props.templateInfo.newTemplate;
this.stackName = props.templateInfo.stackName;
this.changeSet = props.templateInfo.changeSet;
this.nestedStacks = props.templateInfo.nestedStacks;
this.isImport = props.templateInfo.isImport ?? false;
}

/**
* Format the stack diff
*/
public formatStackDiff(options: FormatStackDiffOptions): FormatStackDiffOutput {
public formatStackDiff(options: FormatStackDiffOptions = {}): FormatStackDiffOutput {
const ioDefaultHelper = new IoDefaultMessages(this.ioHelper);
return this.formatStackDiffHelper(
this.oldTemplate,
options.stackName,
options.nestedStackTemplates,
this.stackName,
this.nestedStacks,
{
...options,
ioDefaultHelper,
Expand All @@ -193,7 +211,7 @@ export class DiffFormatter {
nestedStackTemplates: { [nestedStackLogicalId: string]: NestedStackTemplates } | undefined,
options: ReusableStackDiffOptions,
) {
let diff = fullDiff(oldTemplate, this.newTemplate.template, options.changeSet, options.isImport);
let diff = fullDiff(oldTemplate, this.newTemplate.template, this.changeSet, this.isImport);

// The stack diff is formatted via `Formatter`, which takes in a stream
// and sends its output directly to that stream. To faciliate use of the
Expand All @@ -211,14 +229,14 @@ export class DiffFormatter {
stream.write(format(`Stack ${chalk.bold(stackName)}\n`));
}

if (!options.quiet && options.isImport) {
if (!options.quiet && this.isImport) {
stream.write('Parameters and rules created during migration do not affect resource configuration.\n');
}

// detect and filter out mangled characters from the diff
if (diff.differenceCount && !options.strict) {
const mangledNewTemplate = JSON.parse(mangleLikeCloudFormation(JSON.stringify(this.newTemplate.template)));
const mangledDiff = fullDiff(this.oldTemplate, mangledNewTemplate, options.changeSet);
const mangledDiff = fullDiff(this.oldTemplate, mangledNewTemplate, this.changeSet);
filteredChangesCount = Math.max(0, diff.differenceCount - mangledDiff.differenceCount);
if (filteredChangesCount > 0) {
diff = mangledDiff;
Expand Down Expand Up @@ -281,7 +299,7 @@ export class DiffFormatter {
public formatSecurityDiff(options: FormatSecurityDiffOptions): FormatSecurityDiffOutput {
const ioDefaultHelper = new IoDefaultMessages(this.ioHelper);

const diff = fullDiff(this.oldTemplate, this.newTemplate.template, options.changeSet);
const diff = fullDiff(this.oldTemplate, this.newTemplate.template, this.changeSet);

if (diffRequiresApproval(diff, options.requireApproval)) {
// The security diff is formatted via `Formatter`, which takes in a stream
Expand All @@ -291,7 +309,7 @@ export class DiffFormatter {
// `formatSecurityDiff` to decide what to do with it.
const stream = new StringWriteStream();

stream.write(format(`Stack ${chalk.bold(options.stackName)}\n`));
stream.write(format(`Stack ${chalk.bold(this.stackName)}\n`));

// eslint-disable-next-line max-len
ioDefaultHelper.warning(`This deployment will make potentially sensitive changes according to your current security approval level (--require-approval ${options.requireApproval}).`);
Expand Down
91 changes: 45 additions & 46 deletions packages/@aws-cdk/tmp-toolkit-helpers/test/api/diff/diff.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,13 @@ describe('formatStackDiff', () => {
// WHEN
const formatter = new DiffFormatter({
ioHelper: mockIoHelper,
oldTemplate: {},
newTemplate: {
template: {},
templateFile: 'template.json',
templateInfo: {
oldTemplate: mockNewTemplate.template,
newTemplate: mockNewTemplate,
stackName: 'test-stack',
findMetadataByType: () => [],
} as any,
});
const result = formatter.formatStackDiff({
strict: false,
context: 3,
quiet: false,
stackName: 'test-stack',
},
});
const result = formatter.formatStackDiff();

// THEN
expect(result.numStacksWithChanges).toBe(0);
Expand All @@ -88,12 +81,13 @@ describe('formatStackDiff', () => {
// WHEN
const formatter = new DiffFormatter({
ioHelper: mockIoHelper,
oldTemplate: {},
newTemplate: mockNewTemplate,
});
const result = formatter.formatStackDiff({
stackName: 'test-stack',
templateInfo: {
oldTemplate: {},
newTemplate: mockNewTemplate,
stackName: 'test-stack',
},
});
const result = formatter.formatStackDiff();

// THEN
expect(result.numStacksWithChanges).toBe(1);
Expand All @@ -110,13 +104,14 @@ describe('formatStackDiff', () => {
// WHEN
const formatter = new DiffFormatter({
ioHelper: mockIoHelper,
oldTemplate: {},
newTemplate: mockNewTemplate,
});
const result = formatter.formatStackDiff({
stackName: 'test-stack',
isImport: true,
templateInfo: {
oldTemplate: {},
newTemplate: mockNewTemplate,
stackName: 'test-stack',
isImport: true,
},
});
const result = formatter.formatStackDiff();

// THEN
expect(result.numStacksWithChanges).toBe(1);
Expand All @@ -132,7 +127,7 @@ describe('formatStackDiff', () => {

test('handles nested stack templates', () => {
// GIVEN
const nestedStackTemplates = {
const nestedStacks = {
NestedStack1: {
deployedTemplate: {},
generatedTemplate: {},
Expand All @@ -151,13 +146,14 @@ describe('formatStackDiff', () => {
// WHEN
const formatter = new DiffFormatter({
ioHelper: mockIoHelper,
oldTemplate: {},
newTemplate: mockNewTemplate,
});
const result = formatter.formatStackDiff({
stackName: 'test-stack',
nestedStackTemplates,
templateInfo: {
oldTemplate: {},
newTemplate: mockNewTemplate,
stackName: 'test-stack',
nestedStacks,
},
});
const result = formatter.formatStackDiff();

// THEN
expect(result.numStacksWithChanges).toBe(3);
Expand Down Expand Up @@ -226,16 +222,13 @@ describe('formatSecurityDiff', () => {
// WHEN
const formatter = new DiffFormatter({
ioHelper: mockIoHelper,
oldTemplate: {},
newTemplate: {
template: {},
templateFile: 'template.json',
templateInfo: {
oldTemplate: mockNewTemplate.template,
newTemplate: mockNewTemplate,
stackName: 'test-stack',
findMetadataByType: () => [],
} as any,
},
});
const result = formatter.formatSecurityDiff({
stackName: 'test-stack',
requireApproval: RequireApproval.BROADENING,
});

Expand All @@ -248,11 +241,13 @@ describe('formatSecurityDiff', () => {
// WHEN
const formatter = new DiffFormatter({
ioHelper: mockIoHelper,
oldTemplate: {},
newTemplate: mockNewTemplate,
templateInfo: {
oldTemplate: {},
newTemplate: mockNewTemplate,
stackName: 'test-stack',
},
});
const result = formatter.formatSecurityDiff({
stackName: 'test-stack',
requireApproval: RequireApproval.BROADENING,
});

Expand Down Expand Up @@ -281,11 +276,13 @@ describe('formatSecurityDiff', () => {
// WHEN
const formatter = new DiffFormatter({
ioHelper: mockIoHelper,
oldTemplate: {},
newTemplate: mockNewTemplate,
templateInfo: {
oldTemplate: {},
newTemplate: mockNewTemplate,
stackName: 'test-stack',
},
});
const result = formatter.formatSecurityDiff({
stackName: 'test-stack',
requireApproval: RequireApproval.ANY_CHANGE,
});

Expand Down Expand Up @@ -317,11 +314,13 @@ describe('formatSecurityDiff', () => {
// WHEN
const formatter = new DiffFormatter({
ioHelper: mockIoHelper,
oldTemplate: {},
newTemplate: mockNewTemplate,
templateInfo: {
oldTemplate: {},
newTemplate: mockNewTemplate,
stackName: 'test-stack',
},
});
const result = formatter.formatSecurityDiff({
stackName: 'test-stack',
requireApproval: RequireApproval.NEVER,
});

Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/toolkit-lib/lib/actions/diff/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { StackSelector } from '../../api/cloud-assembly';
import type { StackSelector } from '../../../lib/api/shared-public';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for this?


export interface CloudFormationDiffOptions {
/**
Expand Down
Loading