Skip to content

Commit a30a32a

Browse files
authored
fix(cli): handle attributes of AWS::Events::EventBus when hotswapping (#18834)
Without them, customers referencing an `AWS::Events::EventBus` in their StepFunctions StateMachine definition would get this message: ``` Could not perform a hotswap deployment, because the CloudFormation template could not be resolved: We don't support attributes of the 'AWS::Events::EventBus' resource. This is a CDK limitation. Please report it at https://github.com/aws/aws-cdk/issues/new/choose Falling back to doing a full deployment ``` when hotswapping. Fixes #18831 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 1a7e3e2 commit a30a32a

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,12 @@ export class EvaluateCloudFormationTemplate {
300300
}
301301

302302
private getResourceTypeArnPartOfResource(resource: AWS.CloudFormation.StackResourceSummary): string {
303-
return resource.ResourceType.split('::')[2].toLowerCase();
303+
const resourceType = resource.ResourceType;
304+
const specialCaseResourceType = RESOURCE_TYPE_SPECIAL_NAMES[resourceType]?.resourceType;
305+
return specialCaseResourceType
306+
? specialCaseResourceType
307+
// this is the default case
308+
: resourceType.split('::')[2].toLowerCase();
304309
}
305310
}
306311

@@ -313,12 +318,30 @@ interface ArnParts {
313318
readonly resourceName: string;
314319
}
315320

321+
/**
322+
* Usually, we deduce the names of the service and the resource type used to format the ARN from the CloudFormation resource type.
323+
* For a CFN type like AWS::Service::ResourceType, the second segment becomes the service name, and the third the resource type
324+
* (after converting both of them to lowercase).
325+
* However, some resource types break this simple convention, and we need to special-case them.
326+
* This map is for storing those cases.
327+
*/
328+
const RESOURCE_TYPE_SPECIAL_NAMES: { [type: string]: { resourceType: string } } = {
329+
'AWS::Events::EventBus': {
330+
resourceType: 'event-bus',
331+
},
332+
};
333+
316334
const RESOURCE_TYPE_ATTRIBUTES_FORMATS: { [type: string]: { [attribute: string]: (parts: ArnParts) => string } } = {
317335
'AWS::IAM::Role': { Arn: iamArnFmt },
318336
'AWS::IAM::User': { Arn: iamArnFmt },
319337
'AWS::IAM::Group': { Arn: iamArnFmt },
320338
'AWS::S3::Bucket': { Arn: s3ArnFmt },
321339
'AWS::Lambda::Function': { Arn: stdColonResourceArnFmt },
340+
'AWS::Events::EventBus': {
341+
Arn: stdSlashResourceArnFmt,
342+
// the name attribute of the EventBus is the same as the Ref
343+
Name: parts => parts.resourceName,
344+
},
322345
};
323346

324347
function iamArnFmt(parts: ArnParts): string {
@@ -336,6 +359,11 @@ function stdColonResourceArnFmt(parts: ArnParts): string {
336359
return `arn:${parts.partition}:${parts.service}:${parts.region}:${parts.account}:${parts.resourceType}:${parts.resourceName}`;
337360
}
338361

362+
function stdSlashResourceArnFmt(parts: ArnParts): string {
363+
// this is a standard format for ARNs like: arn:aws:service:region:account:resourceType/resourceName
364+
return `arn:${parts.partition}:${parts.service}:${parts.region}:${parts.account}:${parts.resourceType}/${parts.resourceName}`;
365+
}
366+
339367
interface Intrinsic {
340368
readonly name: string;
341369
readonly args: any;

packages/aws-cdk/test/api/hotswap/state-machine-hotswap-deployments.test.ts

+76
Original file line numberDiff line numberDiff line change
@@ -482,3 +482,79 @@ test("will not perform a hotswap deployment if it doesn't know how to handle a s
482482
hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact),
483483
).rejects.toThrow("We don't support the 'UnknownAttribute' attribute of the 'AWS::S3::Bucket' resource. This is a CDK limitation. Please report it at https://github.com/aws/aws-cdk/issues/new/choose");
484484
});
485+
486+
test('knows how to handle attributes of the AWS::Events::EventBus resource', async () => {
487+
// GIVEN
488+
setup.setCurrentCfnStackTemplate({
489+
Resources: {
490+
EventBus: {
491+
Type: 'AWS::Events::EventBus',
492+
Properties: {
493+
Name: 'my-event-bus',
494+
},
495+
},
496+
Machine: {
497+
Type: 'AWS::StepFunctions::StateMachine',
498+
Properties: {
499+
DefinitionString: {
500+
'Fn::Join': ['', [
501+
'{"EventBus1Arn":"',
502+
{ 'Fn::GetAtt': ['EventBus', 'Arn'] },
503+
'","EventBus1Name":"',
504+
{ 'Fn::GetAtt': ['EventBus', 'Name'] },
505+
'","EventBus1Ref":"',
506+
{ Ref: 'EventBus' },
507+
'"}',
508+
]],
509+
},
510+
StateMachineName: 'my-machine',
511+
},
512+
},
513+
},
514+
});
515+
setup.pushStackResourceSummaries(
516+
setup.stackSummaryOf('EventBus', 'AWS::Events::EventBus', 'my-event-bus'),
517+
);
518+
const cdkStackArtifact = setup.cdkStackArtifactOf({
519+
template: {
520+
Resources: {
521+
EventBus: {
522+
Type: 'AWS::Events::EventBus',
523+
Properties: {
524+
Name: 'my-event-bus',
525+
},
526+
},
527+
Machine: {
528+
Type: 'AWS::StepFunctions::StateMachine',
529+
Properties: {
530+
DefinitionString: {
531+
'Fn::Join': ['', [
532+
'{"EventBus2Arn":"',
533+
{ 'Fn::GetAtt': ['EventBus', 'Arn'] },
534+
'","EventBus2Name":"',
535+
{ 'Fn::GetAtt': ['EventBus', 'Name'] },
536+
'","EventBus2Ref":"',
537+
{ Ref: 'EventBus' },
538+
'"}',
539+
]],
540+
},
541+
StateMachineName: 'my-machine',
542+
},
543+
},
544+
},
545+
},
546+
});
547+
548+
// THEN
549+
const result = await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact);
550+
551+
expect(result).not.toBeUndefined();
552+
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
553+
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine',
554+
definition: JSON.stringify({
555+
EventBus2Arn: 'arn:aws:events:here:123456789012:event-bus/my-event-bus',
556+
EventBus2Name: 'my-event-bus',
557+
EventBus2Ref: 'my-event-bus',
558+
}),
559+
});
560+
});

0 commit comments

Comments
 (0)