Skip to content

Commit

Permalink
feat(toolkit): by default hide AWS::CDK::Metadata from "cdk diff" (#1186
Browse files Browse the repository at this point in the history
)

`DifferenceCollection#filter` returns a new collection with changes filtered.

Changed `DifferenceCollection#count` to do a lazy calculation since now
`changes` is mutable.

The toolkit switch `cdk diff --strict` disables this behavior.

Fixes #465
  • Loading branch information
Elad Ben-Israel committed Nov 18, 2018
1 parent b3a5cb3 commit ef0017a
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 34 deletions.
78 changes: 49 additions & 29 deletions packages/@aws-cdk/cloudformation-diff/lib/diff/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,60 @@ import { deepEqual } from './util';

/** Semantic differences between two CloudFormation templates. */
export class TemplateDiff implements ITemplateDiff {
public readonly awsTemplateFormatVersion?: Difference<string>;
public readonly description?: Difference<string>;
public readonly transform?: Difference<string>;
public readonly conditions: DifferenceCollection<Condition, ConditionDifference>;
public readonly mappings: DifferenceCollection<Mapping, MappingDifference>;
public readonly metadata: DifferenceCollection<Metadata, MetadataDifference>;
public readonly outputs: DifferenceCollection<Output, OutputDifference>;
public readonly parameters: DifferenceCollection<Parameter, ParameterDifference>;
public readonly resources: DifferenceCollection<Resource, ResourceDifference>;
public awsTemplateFormatVersion?: Difference<string>;
public description?: Difference<string>;
public transform?: Difference<string>;
public conditions: DifferenceCollection<Condition, ConditionDifference>;
public mappings: DifferenceCollection<Mapping, MappingDifference>;
public metadata: DifferenceCollection<Metadata, MetadataDifference>;
public outputs: DifferenceCollection<Output, OutputDifference>;
public parameters: DifferenceCollection<Parameter, ParameterDifference>;
public resources: DifferenceCollection<Resource, ResourceDifference>;
/** The differences in unknown/unexpected parts of the template */
public readonly unknown: DifferenceCollection<any, Difference<any>>;

public readonly count: number;
public unknown: DifferenceCollection<any, Difference<any>>;

constructor(args: ITemplateDiff) {
let count = 0;
if (args.awsTemplateFormatVersion !== undefined) {
this.awsTemplateFormatVersion = args.awsTemplateFormatVersion;
count += 1;
}
if (args.description !== undefined) {
this.description = args.description;
count += 1;
}
if (args.transform !== undefined) {
this.transform = args.transform;
count += 1;
}

this.conditions = args.conditions || new DifferenceCollection({});
count += this.conditions.count;

this.mappings = args.mappings || new DifferenceCollection({});
count += this.mappings.count;

this.metadata = args.metadata || new DifferenceCollection({});
count += this.metadata.count;

this.outputs = args.outputs || new DifferenceCollection({});
count += this.outputs.count;

this.parameters = args.parameters || new DifferenceCollection({});
count += this.parameters.count;

this.resources = args.resources || new DifferenceCollection({});
count += this.resources.count;

this.unknown = args.unknown || new DifferenceCollection({});
}

public get count() {
let count = 0;

if (this.awsTemplateFormatVersion !== undefined) {
count += 1;
}
if (this.description !== undefined) {
count += 1;
}
if (this.transform !== undefined) {
count += 1;
}

count += this.conditions.count;
count += this.mappings.count;
count += this.metadata.count;
count += this.outputs.count;
count += this.parameters.count;
count += this.resources.count;
count += this.unknown.count;

this.count = count;
return count;
}

public get isEmpty(): boolean {
Expand Down Expand Up @@ -117,6 +120,23 @@ export class DifferenceCollection<V, T extends Difference<V>> {
return Object.keys(this.changes);
}

/**
* Returns a new TemplateDiff which only contains changes for which `predicate`
* returns `true`.
*/
public filter(predicate: (diff: T | undefined) => boolean): DifferenceCollection<V, T> {
const newChanges: { [logicalId: string]: T | undefined } = { };
for (const id of Object.keys(this.changes)) {
const diff = this.changes[id];

if (predicate(diff)) {
newChanges[id] = diff;
}
}

return new DifferenceCollection<V, T>(newChanges);
}

public forEach(cb: (logicalId: string, change: T) => any): void {
for (const logicalId of this.logicalIds) {
cb(logicalId, this.changes[logicalId]!);
Expand Down
9 changes: 5 additions & 4 deletions packages/aws-cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ async function parseCommandLineArguments() {
.command('destroy [STACKS..]', 'Destroy the stack(s) named STACKS', yargs => yargs
.option('force', { type: 'boolean', alias: 'f', desc: 'Do not ask for confirmation before destroying the stacks' }))
.command('diff [STACK]', 'Compares the specified stack with the deployed stack or a local template file', yargs => yargs
.option('template', { type: 'string', desc: 'the path to the CloudFormation template to compare with' }))
.option('template', { type: 'string', desc: 'the path to the CloudFormation template to compare with' })
.option('strict', { type: 'boolean', desc: 'do not filter out AWS::CDK::Metadata resources', default: false }))
.command('metadata [STACK]', 'Returns all metadata associated with this stack')
.command('init [TEMPLATE]', 'Create a new, empty CDK project from a template. Invoked without TEMPLATE, the app template will be used.', yargs => yargs
.option('language', { type: 'string', alias: 'l', desc: 'the language to be used for the new project (default can be configured in ~/.cdk.json)', choices: initTemplateLanuages })
Expand Down Expand Up @@ -187,7 +188,7 @@ async function initCommandLine() {
return await cliList({ long: args.long });

case 'diff':
return await diffStack(await findStack(args.STACK), args.template);
return await diffStack(await findStack(args.STACK), args.template, args.strict);

case 'bootstrap':
return await cliBootstrap(args.ENVIRONMENTS, toolkitStackName, args.roleArn);
Expand Down Expand Up @@ -394,10 +395,10 @@ async function initCommandLine() {
}
}

async function diffStack(stackName: string, templatePath?: string): Promise<number> {
async function diffStack(stackName: string, templatePath: string | undefined, strict: boolean): Promise<number> {
const stack = await appStacks.synthesizeStack(stackName);
const currentTemplate = await readCurrentTemplate(stack, templatePath);
if (printStackDiff(currentTemplate, stack) === 0) {
if (printStackDiff(currentTemplate, stack, strict) === 0) {
return 0;
} else {
return 1;
Expand Down
13 changes: 12 additions & 1 deletion packages/aws-cdk/lib/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,19 @@ import { print } from './logging';
*
* @returns the count of differences that were rendered.
*/
export function printStackDiff(oldTemplate: any, newTemplate: cxapi.SynthesizedStack): number {
export function printStackDiff(oldTemplate: any, newTemplate: cxapi.SynthesizedStack, strict: boolean): number {
const diff = cfnDiff.diffTemplate(oldTemplate, newTemplate.template);

// filter out 'AWS::CDK::Metadata' resources from the template
if (diff.resources && !strict) {
diff.resources = diff.resources.filter(change => {
if (!change) { return true; }
if (change.newResourceType === 'AWS::CDK::Metadata') { return false; }
if (change.oldResourceType === 'AWS::CDK::Metadata') { return false; }
return true;
});
}

if (!diff.isEmpty) {
cfnDiff.formatDifferences(process.stderr, diff);
} else {
Expand Down

0 comments on commit ef0017a

Please sign in to comment.