Skip to content
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

feat(cloudformation-include): throw ValidationError instead of untyped Errors #33391

Merged
Merged
Show file tree
Hide file tree
Changes from 2 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 packages/aws-cdk-lib/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const enableNoThrowDefaultErrorIn = [
'aws-s3objectlambda',
'aws-s3outposts',
'aws-s3tables',
'cloudformation-include',
];
baseConfig.overrides.push({
files: enableNoThrowDefaultErrorIn.map(m => `./${m}/lib/**`),
Expand Down
70 changes: 36 additions & 34 deletions packages/aws-cdk-lib/cloudformation-include/lib/cfn-include.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,14 @@ export class CfnInclude extends core.CfnElement {

for (const logicalId of this.dehydratedResources) {
if (!Object.keys(this.template.Resources).includes(logicalId)) {
throw new Error(`Logical ID '${logicalId}' was specified in 'dehydratedResources', but does not belong to a resource in the template.`);
throw new core.ValidationError(`Logical ID '${logicalId}' was specified in 'dehydratedResources', but does not belong to a resource in the template.`, this);
}
}

// check if all user specified parameter values exist in the template
for (const logicalId of Object.keys(this.parametersToReplace)) {
if (!(logicalId in (this.template.Parameters || {}))) {
throw new Error(`Parameter with logical ID '${logicalId}' was not found in the template`);
throw new core.ValidationError(`Parameter with logical ID '${logicalId}' was not found in the template`, this);
}
}

Expand Down Expand Up @@ -182,7 +182,7 @@ export class CfnInclude extends core.CfnElement {
// verify that all nestedStacks have been instantiated
for (const nestedStackId of Object.keys(props.loadNestedStacks || {})) {
if (!(nestedStackId in this.resources)) {
throw new Error(`Nested Stack with logical ID '${nestedStackId}' was not found in the template`);
throw new core.ValidationError(`Nested Stack with logical ID '${nestedStackId}' was not found in the template`, this);
}
}

Expand Down Expand Up @@ -217,7 +217,7 @@ export class CfnInclude extends core.CfnElement {
public getResource(logicalId: string): core.CfnResource {
const ret = this.resources[logicalId];
if (!ret) {
throw new Error(`Resource with logical ID '${logicalId}' was not found in the template`);
throw new core.ValidationError(`Resource with logical ID '${logicalId}' was not found in the template`, this);
}
return ret;
}
Expand All @@ -235,7 +235,7 @@ export class CfnInclude extends core.CfnElement {
public getCondition(conditionName: string): core.CfnCondition {
const ret = this.conditions[conditionName];
if (!ret) {
throw new Error(`Condition with name '${conditionName}' was not found in the template`);
throw new core.ValidationError(`Condition with name '${conditionName}' was not found in the template`, this);
}
return ret;
}
Expand All @@ -253,7 +253,7 @@ export class CfnInclude extends core.CfnElement {
public getParameter(parameterName: string): core.CfnParameter {
const ret = this.parameters[parameterName];
if (!ret) {
throw new Error(`Parameter with name '${parameterName}' was not found in the template`);
throw new core.ValidationError(`Parameter with name '${parameterName}' was not found in the template`, this);
}
return ret;
}
Expand All @@ -270,7 +270,7 @@ export class CfnInclude extends core.CfnElement {
public getMapping(mappingName: string): core.CfnMapping {
const ret = this.mappings[mappingName];
if (!ret) {
throw new Error(`Mapping with name '${mappingName}' was not found in the template`);
throw new core.ValidationError(`Mapping with name '${mappingName}' was not found in the template`, this);
}
return ret;
}
Expand All @@ -288,7 +288,7 @@ export class CfnInclude extends core.CfnElement {
public getOutput(logicalId: string): core.CfnOutput {
const ret = this.outputs[logicalId];
if (!ret) {
throw new Error(`Output with logical ID '${logicalId}' was not found in the template`);
throw new core.ValidationError(`Output with logical ID '${logicalId}' was not found in the template`, this);
}
return ret;
}
Expand All @@ -306,7 +306,7 @@ export class CfnInclude extends core.CfnElement {
public getRule(ruleName: string): core.CfnRule {
const ret = this.rules[ruleName];
if (!ret) {
throw new Error(`Rule with name '${ruleName}' was not found in the template`);
throw new core.ValidationError(`Rule with name '${ruleName}' was not found in the template`, this);
}
return ret;
}
Expand All @@ -324,7 +324,7 @@ export class CfnInclude extends core.CfnElement {
public getHook(hookLogicalId: string): core.CfnHook {
const ret = this.hooks[hookLogicalId];
if (!ret) {
throw new Error(`Hook with logical ID '${hookLogicalId}' was not found in the template`);
throw new core.ValidationError(`Hook with logical ID '${hookLogicalId}' was not found in the template`, this);
}
return ret;
}
Expand All @@ -340,12 +340,12 @@ export class CfnInclude extends core.CfnElement {
public getNestedStack(logicalId: string): IncludedNestedStack {
if (!this.nestedStacks[logicalId]) {
if (!this.template.Resources[logicalId]) {
throw new Error(`Nested Stack with logical ID '${logicalId}' was not found in the template`);
throw new core.ValidationError(`Nested Stack with logical ID '${logicalId}' was not found in the template`, this);
} else if (this.template.Resources[logicalId].Type !== 'AWS::CloudFormation::Stack') {
throw new Error(`Resource with logical ID '${logicalId}' is not a CloudFormation Stack`);
throw new core.ValidationError(`Resource with logical ID '${logicalId}' is not a CloudFormation Stack`, this);
} else {
throw new Error(`Nested Stack '${logicalId}' was not included in the parent template. ` +
'To retrieve an included nested stack, it must be specified either in the `loadNestedStacks` property, or through the `loadNestedStack` method');
throw new core.ValidationError(`Nested Stack '${logicalId}' was not included in the parent template. ` +
'To retrieve an included nested stack, it must be specified either in the `loadNestedStacks` property, or through the `loadNestedStack` method', this);
}
}
return this.nestedStacks[logicalId];
Expand All @@ -364,11 +364,11 @@ export class CfnInclude extends core.CfnElement {
*/
public loadNestedStack(logicalId: string, nestedStackProps: CfnIncludeProps): IncludedNestedStack {
if (logicalId in this.nestedStacks) {
throw new Error(`Nested Stack '${logicalId}' was already included in its parent template`);
throw new core.ValidationError(`Nested Stack '${logicalId}' was already included in its parent template`, this);
}
const cfnStack = this.resources[logicalId];
if (!cfnStack) {
throw new Error(`Nested Stack with logical ID '${logicalId}' was not found in the template`);
throw new core.ValidationError(`Nested Stack with logical ID '${logicalId}' was not found in the template`, this);
}
if (cfnStack instanceof core.CfnStack) {
// delete the old CfnStack child - one will be created by the NestedStack object
Expand All @@ -381,7 +381,7 @@ export class CfnInclude extends core.CfnElement {
this.getOrCreateResource(logicalId);
return this.nestedStacks[logicalId];
} else {
throw new Error(`Nested Stack with logical ID '${logicalId}' is not an AWS::CloudFormation::Stack resource`);
throw new core.ValidationError(`Nested Stack with logical ID '${logicalId}' is not an AWS::CloudFormation::Stack resource`, this);
}
}

Expand Down Expand Up @@ -429,12 +429,13 @@ export class CfnInclude extends core.CfnElement {
}

private createMapping(mappingName: string): void {
const self = this;
const cfnParser = new cfn_parse.CfnParser({
finder: {
findCondition() { throw new Error('Referring to Conditions in Mapping definitions is not allowed'); },
findMapping() { throw new Error('Referring to other Mappings in Mapping definitions is not allowed'); },
findRefTarget() { throw new Error('Using Ref expressions in Mapping definitions is not allowed'); },
findResource() { throw new Error('Using GetAtt expressions in Mapping definitions is not allowed'); },
findCondition() { throw new core.ValidationError('Referring to Conditions in Mapping definitions is not allowed', self); },
findMapping() { throw new core.ValidationError('Referring to other Mappings in Mapping definitions is not allowed', self); },
findRefTarget() { throw new core.ValidationError('Using Ref expressions in Mapping definitions is not allowed', self); },
findResource() { throw new core.ValidationError('Using GetAtt expressions in Mapping definitions is not allowed', self); },
},
parameters: {},
});
Expand All @@ -450,12 +451,13 @@ export class CfnInclude extends core.CfnElement {
return;
}

const self = this;
const expression = new cfn_parse.CfnParser({
finder: {
findResource() { throw new Error('Using GetAtt expressions in Parameter definitions is not allowed'); },
findRefTarget() { throw new Error('Using Ref expressions in Parameter definitions is not allowed'); },
findCondition() { throw new Error('Referring to Conditions in Parameter definitions is not allowed'); },
findMapping() { throw new Error('Referring to Mappings in Parameter definitions is not allowed'); },
findResource() { throw new core.ValidationError('Using GetAtt expressions in Parameter definitions is not allowed', self); },
findRefTarget() { throw new core.ValidationError('Using Ref expressions in Parameter definitions is not allowed', self); },
findCondition() { throw new core.ValidationError('Referring to Conditions in Parameter definitions is not allowed', self); },
findMapping() { throw new core.ValidationError('Referring to Mappings in Parameter definitions is not allowed', self); },
},
parameters: {},
}).parseValue(this.template.Parameters[logicalId]);
Expand Down Expand Up @@ -485,7 +487,7 @@ export class CfnInclude extends core.CfnElement {
// only parameters can be referenced in Rules
return self.parameters[refTarget];
},
findResource() { throw new Error('Using GetAtt expressions in Rule definitions is not allowed'); },
findResource() { throw new core.ValidationError('Using GetAtt expressions in Rule definitions is not allowed', self); },
findCondition(conditionName: string): core.CfnCondition | undefined {
return self.conditions[conditionName];
},
Expand Down Expand Up @@ -575,8 +577,8 @@ export class CfnInclude extends core.CfnElement {
return self.getCondition(outputAttributes.Condition);
}

throw new Error(`Output with name '${logicalId}' refers to a Condition with name ` +
`'${outputAttributes.Condition}' which was not found in this template`);
throw new core.ValidationError(`Output with name '${logicalId}' refers to a Condition with name ` +
`'${outputAttributes.Condition}' which was not found in this template`, this);
})(),
});

Expand All @@ -592,7 +594,7 @@ export class CfnInclude extends core.CfnElement {
const self = this;
const cfnParser = new cfn_parse.CfnParser({
finder: {
findResource() { throw new Error('Using GetAtt in Condition definitions is not allowed'); },
findResource() { throw new core.ValidationError('Using GetAtt in Condition definitions is not allowed', self); },
findRefTarget(elementName: string): core.CfnElement | undefined {
// only Parameters can be referenced in the 'Conditions' section
return self.parameters[elementName];
Expand Down Expand Up @@ -626,7 +628,7 @@ export class CfnInclude extends core.CfnElement {
cycleChain = cycleChain.concat([logicalId]);
if (cycleChain.length !== new Set(cycleChain).size) {
if (!this.allowCyclicalReferences) {
throw new Error(`Found a cycle between resources in the template: ${cycleChain.join(' depends on ')}`);
throw new core.ValidationError(`Found a cycle between resources in the template: ${cycleChain.join(' depends on ')}`, this);
}
// only allow one placeholder per logical id
if (this.logicalIdToPlaceholderMap.get(logicalId)) {
Expand Down Expand Up @@ -683,7 +685,7 @@ export class CfnInclude extends core.CfnElement {
const resourceAttributes: any = this.template.Resources[logicalId];
let l1Instance: core.CfnResource;
if (this.nestedStacksToInclude[logicalId] && this.dehydratedResources.includes(logicalId)) {
throw new Error(`nested stack '${logicalId}' was marked as dehydrated - nested stacks cannot be dehydrated`);
throw new core.ValidationError(`nested stack '${logicalId}' was marked as dehydrated - nested stacks cannot be dehydrated`, this);
} else if (this.nestedStacksToInclude[logicalId]) {
l1Instance = this.createNestedStack(logicalId, cfnParser);
} else if (this.dehydratedResources.includes(logicalId)) {
Expand Down Expand Up @@ -758,13 +760,13 @@ export class CfnInclude extends core.CfnElement {
const nestedStackAttributes = templateResources[nestedStackId] || {};

if (nestedStackAttributes.Type !== 'AWS::CloudFormation::Stack') {
throw new Error(`Nested Stack with logical ID '${nestedStackId}' is not an AWS::CloudFormation::Stack resource`);
throw new core.ValidationError(`Nested Stack with logical ID '${nestedStackId}' is not an AWS::CloudFormation::Stack resource`, this);
}
if (nestedStackAttributes.CreationPolicy) {
throw new Error('CreationPolicy is not supported by the AWS::CloudFormation::Stack resource');
throw new core.ValidationError('CreationPolicy is not supported by the AWS::CloudFormation::Stack resource', this);
}
if (nestedStackAttributes.UpdatePolicy) {
throw new Error('UpdatePolicy is not supported by the AWS::CloudFormation::Stack resource');
throw new core.ValidationError('UpdatePolicy is not supported by the AWS::CloudFormation::Stack resource', this);
}

const nestedStackProps = cfnParser.parseValue(nestedStackAttributes.Properties);
Expand Down