Skip to content

Commit

Permalink
fix(cli): handle attributes of AWS::Events::EventBus when hotswapping (
Browse files Browse the repository at this point in the history
…#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*
  • Loading branch information
skinny85 committed Feb 9, 2022
1 parent 1a7e3e2 commit a30a32a
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 1 deletion.
30 changes: 29 additions & 1 deletion packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts
Expand Up @@ -300,7 +300,12 @@ export class EvaluateCloudFormationTemplate {
}

private getResourceTypeArnPartOfResource(resource: AWS.CloudFormation.StackResourceSummary): string {
return resource.ResourceType.split('::')[2].toLowerCase();
const resourceType = resource.ResourceType;
const specialCaseResourceType = RESOURCE_TYPE_SPECIAL_NAMES[resourceType]?.resourceType;
return specialCaseResourceType
? specialCaseResourceType
// this is the default case
: resourceType.split('::')[2].toLowerCase();
}
}

Expand All @@ -313,12 +318,30 @@ interface ArnParts {
readonly resourceName: string;
}

/**
* Usually, we deduce the names of the service and the resource type used to format the ARN from the CloudFormation resource type.
* For a CFN type like AWS::Service::ResourceType, the second segment becomes the service name, and the third the resource type
* (after converting both of them to lowercase).
* However, some resource types break this simple convention, and we need to special-case them.
* This map is for storing those cases.
*/
const RESOURCE_TYPE_SPECIAL_NAMES: { [type: string]: { resourceType: string } } = {
'AWS::Events::EventBus': {
resourceType: 'event-bus',
},
};

const RESOURCE_TYPE_ATTRIBUTES_FORMATS: { [type: string]: { [attribute: string]: (parts: ArnParts) => string } } = {
'AWS::IAM::Role': { Arn: iamArnFmt },
'AWS::IAM::User': { Arn: iamArnFmt },
'AWS::IAM::Group': { Arn: iamArnFmt },
'AWS::S3::Bucket': { Arn: s3ArnFmt },
'AWS::Lambda::Function': { Arn: stdColonResourceArnFmt },
'AWS::Events::EventBus': {
Arn: stdSlashResourceArnFmt,
// the name attribute of the EventBus is the same as the Ref
Name: parts => parts.resourceName,
},
};

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

function stdSlashResourceArnFmt(parts: ArnParts): string {
// this is a standard format for ARNs like: arn:aws:service:region:account:resourceType/resourceName
return `arn:${parts.partition}:${parts.service}:${parts.region}:${parts.account}:${parts.resourceType}/${parts.resourceName}`;
}

interface Intrinsic {
readonly name: string;
readonly args: any;
Expand Down
Expand Up @@ -482,3 +482,79 @@ test("will not perform a hotswap deployment if it doesn't know how to handle a s
hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact),
).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");
});

test('knows how to handle attributes of the AWS::Events::EventBus resource', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
EventBus: {
Type: 'AWS::Events::EventBus',
Properties: {
Name: 'my-event-bus',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': ['', [
'{"EventBus1Arn":"',
{ 'Fn::GetAtt': ['EventBus', 'Arn'] },
'","EventBus1Name":"',
{ 'Fn::GetAtt': ['EventBus', 'Name'] },
'","EventBus1Ref":"',
{ Ref: 'EventBus' },
'"}',
]],
},
StateMachineName: 'my-machine',
},
},
},
});
setup.pushStackResourceSummaries(
setup.stackSummaryOf('EventBus', 'AWS::Events::EventBus', 'my-event-bus'),
);
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
EventBus: {
Type: 'AWS::Events::EventBus',
Properties: {
Name: 'my-event-bus',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': ['', [
'{"EventBus2Arn":"',
{ 'Fn::GetAtt': ['EventBus', 'Arn'] },
'","EventBus2Name":"',
{ 'Fn::GetAtt': ['EventBus', 'Name'] },
'","EventBus2Ref":"',
{ Ref: 'EventBus' },
'"}',
]],
},
StateMachineName: 'my-machine',
},
},
},
},
});

// THEN
const result = await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact);

expect(result).not.toBeUndefined();
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine',
definition: JSON.stringify({
EventBus2Arn: 'arn:aws:events:here:123456789012:event-bus/my-event-bus',
EventBus2Name: 'my-event-bus',
EventBus2Ref: 'my-event-bus',
}),
});
});

0 comments on commit a30a32a

Please sign in to comment.