Skip to content

Commit

Permalink
feat(core): add option to suppress indentation in templates (#25892)
Browse files Browse the repository at this point in the history
Fixes #18694, #8712 

This change adds an option to suppress indentation in CloudFormation template files. Suppressing indentation will reduce the file size of templates.

Indentation can be set by enabling for specific Stacks using the new `suppressTemplateIndentation` property in `StackProps`, or globally using the new `@aws-cdk/core:suppressTemplateIndentation` context key.

This PR provides additional template size reduction beyond the indentation change in  #19656.

@rix0rrr @mackalex @PatMyron 

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jesterhazy authored Jun 15, 2023
1 parent 97d2fab commit b705956
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 2 deletions.
14 changes: 14 additions & 0 deletions packages/aws-cdk-lib/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,20 @@ It's possible to synthesize the project with more Resources than the allowed (or

Set the context key `@aws-cdk/core:stackResourceLimit` with the proper value, being 0 for disable the limit of resources.

### Template Indentation

The AWS CloudFormation templates generated by CDK include indentation by default.
Indentation makes the templates more readable, but also increases their size,
and CloudFormation templates cannot exceed 1MB.

It's possible to reduce the size of your templates by suppressing indentation.

To do this for all templates, set the context key `@aws-cdk/core:suppressTemplateIndentation` to `true`.

To do this for a specific stack, add a `suppressTemplateIndentation: true` property to the
stack's `StackProps` parameter. You can also set this property to `false` to override
the context key setting.

## App Context

[Context values](https://docs.aws.amazon.com/cdk/v2/guide/context.html) are key-value pairs that can be associated with an app, stack, or construct.
Expand Down
45 changes: 44 additions & 1 deletion packages/aws-cdk-lib/core/lib/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const MY_STACK_CACHE = Symbol.for('@aws-cdk/core.Stack.myStack');

export const STACK_RESOURCE_LIMIT_CONTEXT = '@aws-cdk/core:stackResourceLimit';

const SUPPRESS_TEMPLATE_INDENTATION_CONTEXT = '@aws-cdk/core:suppressTemplateIndentation';
const TEMPLATE_BODY_MAXIMUM_SIZE = 1_000_000;

const VALID_STACK_NAME_REGEX = /^[A-Za-z][A-Za-z0-9-]*$/;

const MAX_RESOURCES = 500;
Expand Down Expand Up @@ -172,6 +175,18 @@ export interface StackProps {
* @default - no permissions boundary is applied
*/
readonly permissionsBoundary?: PermissionsBoundary;

/**
* Enable this flag to suppress indentation in generated
* CloudFormation templates.
*
* If not specified, the value of the `@aws-cdk/core:suppressTemplateIndentation`
* context key will be used. If that is not specified, then the
* default value `false` will be used.
*
* @default - the value of `@aws-cdk/core:suppressTemplateIndentation`, or `false` if that is not set.
*/
readonly suppressTemplateIndentation?: boolean;
}

/**
Expand Down Expand Up @@ -359,6 +374,18 @@ export class Stack extends Construct implements ITaggable {

private readonly _stackName: string;

/**
* Enable this flag to suppress indentation in generated
* CloudFormation templates.
*
* If not specified, the value of the `@aws-cdk/core:suppressTemplateIndentation`
* context key will be used. If that is not specified, then the
* default value `false` will be used.
*
* @default - the value of `@aws-cdk/core:suppressTemplateIndentation`, or `false` if that is not set.
*/
private readonly _suppressTemplateIndentation: boolean;

/**
* Creates a new stack.
*
Expand All @@ -385,6 +412,7 @@ export class Stack extends Construct implements ITaggable {
this._stackDependencies = { };
this.templateOptions = { };
this._crossRegionReferences = !!props.crossRegionReferences;
this._suppressTemplateIndentation = props.suppressTemplateIndentation ?? this.node.tryGetContext(SUPPRESS_TEMPLATE_INDENTATION_CONTEXT) ?? false;

Object.defineProperty(this, STACK_SYMBOL, { value: true });

Expand Down Expand Up @@ -1047,7 +1075,22 @@ export class Stack extends Construct implements ITaggable {
Annotations.of(this).addInfo(`Number of resources: ${numberOfResources} is approaching allowed maximum of ${this.maxResources}`);
}
}
fs.writeFileSync(outPath, JSON.stringify(template, undefined, 1));

const indent = this._suppressTemplateIndentation ? undefined : 1;
const templateData = JSON.stringify(template, undefined, indent);

if (templateData.length > (TEMPLATE_BODY_MAXIMUM_SIZE * 0.8)) {
const verb = templateData.length > TEMPLATE_BODY_MAXIMUM_SIZE ? 'exceeds' : 'is approaching';
const advice = this._suppressTemplateIndentation ?
'Split resources into multiple stacks to reduce template size' :
'Split resources into multiple stacks or set suppressTemplateIndentation to reduce template size';

const message = `Template size ${verb} limit: ${templateData.length}/${TEMPLATE_BODY_MAXIMUM_SIZE}. ${advice}.`;

Annotations.of(this).addWarning(message);
}

fs.writeFileSync(outPath, templateData);

for (const ctx of this._missingContext) {
if (lookupRoleArn != null) {
Expand Down
44 changes: 44 additions & 0 deletions packages/aws-cdk-lib/core/test/stack.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as fs from 'fs';
import { testDeprecated } from '@aws-cdk/cdk-build-tools';
import { Construct, Node } from 'constructs';
import { toCloudFormation } from './util';
Expand Down Expand Up @@ -2104,6 +2105,49 @@ describe('stack', () => {
});
}).toThrowError('Region of stack environment must be a \'string\' but received \'number\'');
});

test('indent templates when suppressTemplateIndentation is not set', () => {
const app = new App();

const stack = new Stack(app, 'Stack');
new CfnResource(stack, 'MyResource', { type: 'MyResourceType' });

const assembly = app.synth();
const artifact = assembly.getStackArtifact(stack.artifactId);
const templateData = fs.readFileSync(artifact.templateFullPath, 'utf-8');

expect(templateData).toMatch(/^{\n \"Resources\": {\n \"MyResource\": {\n \"Type\": \"MyResourceType\"\n }\n }/);
});

test('do not indent templates when suppressTemplateIndentation is true', () => {
const app = new App();

const stack = new Stack(app, 'Stack', { suppressTemplateIndentation: true });
new CfnResource(stack, 'MyResource', { type: 'MyResourceType' });

const assembly = app.synth();
const artifact = assembly.getStackArtifact(stack.artifactId);
const templateData = fs.readFileSync(artifact.templateFullPath, 'utf-8');

expect(templateData).toMatch(/^{\"Resources\":{\"MyResource\":{\"Type\":\"MyResourceType\"}}/);
});

test('do not indent templates when @aws-cdk/core:suppressTemplateIndentation is true', () => {
const app = new App({
context: {
'@aws-cdk/core:suppressTemplateIndentation': true,
},
});

const stack = new Stack(app, 'Stack');
new CfnResource(stack, 'MyResource', { type: 'MyResourceType' });

const assembly = app.synth();
const artifact = assembly.getStackArtifact(stack.artifactId);
const templateData = fs.readFileSync(artifact.templateFullPath, 'utf-8');

expect(templateData).toMatch(/^{\"Resources\":{\"MyResource\":{\"Type\":\"MyResourceType\"}}/);
});
});

describe('permissions boundary', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,11 @@ test('Policy sizes do not exceed the maximum size', () => {
}
}

Annotations.fromStack(pipelineStack).hasNoWarning('*', Match.anyValue());
// expect template size warning, but no other warnings
const annotations = Annotations.fromStack(pipelineStack);
annotations.hasWarning('*', Match.stringLikeRegexp('^Template size is approaching limit'));
const warnings = annotations.findWarning('*', Match.anyValue());
expect(warnings.length).toEqual(1);
});

test('CodeBuild action role has the right AssumeRolePolicyDocument', () => {
Expand Down

0 comments on commit b705956

Please sign in to comment.