Skip to content

Commit 8fccea7

Browse files
authored
chore: correctly filter failed cfn events for displaying error messages (#11815)
* chore: do not filter AWS::CloudFormation::Stack type failure events if they have valid error message * Update packages/amplify-provider-awscloudformation/src/aws-utils/aws-cfn.js
1 parent 5b3adcd commit 8fccea7

File tree

2 files changed

+105
-2
lines changed

2 files changed

+105
-2
lines changed

packages/amplify-provider-awscloudformation/src/__tests__/aws-utils/aws-cfn.test.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,96 @@ describe('CloudFormation', () => {
6060
}
6161
cfn.progressBar.stop();
6262
});
63+
64+
describe('filterFailedStackEvents', () => {
65+
test('that it does not filter stack events that are in eventToCategories map', async () => {
66+
const eventsWithFailure = [
67+
{
68+
StackId: 'testStackId1',
69+
LogicalResourceId: 'testLogicalResourceId1',
70+
ResourceType: 'AWS::IAM::Role',
71+
ResourceStatusReason: 'Some valid reason',
72+
},
73+
{
74+
StackId: 'testStackId2',
75+
LogicalResourceId: 'testLogicalResourceId2',
76+
ResourceType: 'AWS::IAM::Role',
77+
ResourceStatusReason: 'Some valid reason',
78+
},
79+
];
80+
81+
const eventMap = {
82+
rootResources: [],
83+
eventToCategories: new Map(),
84+
categories: [],
85+
};
86+
// Only testLogicalResourceId1 is in the eventToCategories Map
87+
eventMap.eventToCategories.set('testLogicalResourceId1', 'testLogicalResourceId1-value');
88+
const cfn = await new CloudFormation();
89+
cfn.eventMap = eventMap;
90+
const filteredEvents = cfn.filterFailedStackEvents(eventsWithFailure);
91+
92+
// Only testStackId1 event should be returned since that's the only one in eventToCategories map
93+
expect(filteredEvents).toEqual(eventsWithFailure.filter(e => e.StackId == 'testStackId1'));
94+
});
95+
96+
test('that it filters stack events with cascade failure reasons', async () => {
97+
const eventsWithFailure = [
98+
{
99+
StackId: 'testStackId1',
100+
LogicalResourceId: 'testLogicalResourceId1',
101+
ResourceType: 'AWS::IAM::Role',
102+
ResourceStatusReason: 'Resource creation cancelled',
103+
},
104+
];
105+
106+
const eventMap = {
107+
rootResources: [],
108+
eventToCategories: new Map(),
109+
categories: [],
110+
};
111+
eventMap.eventToCategories.set('testLogicalResourceId1', 'testLogicalResourceId1-value');
112+
const cfn = await new CloudFormation();
113+
cfn.eventMap = eventMap;
114+
const filteredEvents = cfn.filterFailedStackEvents(eventsWithFailure);
115+
expect(filteredEvents).toEqual([]); // empty array
116+
});
117+
118+
test('that it only filters resource of type AWS::CloudFormation::Stack with generic error message', async () => {
119+
const eventsWithFailure = [
120+
{
121+
StackId: 'testStackId1',
122+
LogicalResourceId: 'testLogicalResourceId1',
123+
ResourceType: 'AWS::CloudFormation::Stack',
124+
ResourceStatusReason: 'The following resource(s) failed to create: [LambdaExecutionRole]. ',
125+
},
126+
{
127+
StackId: 'testStackId2',
128+
LogicalResourceId: 'testLogicalResourceId2',
129+
ResourceType: 'AWS::CloudFormation::Stack',
130+
ResourceStatusReason: 'Some valid stack failure message',
131+
},
132+
];
133+
134+
const eventMap = {
135+
rootResources: [
136+
{
137+
category: 'resourceCategory1',
138+
key: 'testLogicalResourceId1',
139+
},
140+
{
141+
category: 'resourceCategory2',
142+
key: 'testLogicalResourceId2',
143+
},
144+
],
145+
categories: [],
146+
};
147+
const cfn = await new CloudFormation();
148+
cfn.eventMap = eventMap;
149+
const filteredEvents = cfn.filterFailedStackEvents(eventsWithFailure);
150+
151+
// Should not filter testStackId1 event that has a specific error message
152+
expect(filteredEvents).toEqual(eventsWithFailure.filter(e => e.StackId == 'testStackId2'));
153+
});
154+
});
63155
});

packages/amplify-provider-awscloudformation/src/aws-utils/aws-cfn.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ const CNF_ERROR_STATUS = ['CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED'];
3030

3131
// These are cascade failures caused because of a root failure. Safe to ignore
3232
const RESOURCE_CASCADE_FAIL_REASONS = ['Resource creation cancelled', 'Resource update cancelled'];
33+
34+
const STACK_RESOURCE_FILTER_FAIL_REASON = 'The following resource(s) failed';
3335
class CloudFormation {
3436
constructor(context, userAgentAction, options = {}, eventMap = {}) {
3537
return (async () => {
@@ -177,8 +179,17 @@ class CloudFormation {
177179
*/
178180
filterFailedStackEvents(eventsWithFailure) {
179181
return eventsWithFailure
180-
.filter(stack => stack.ResourceType !== 'AWS::CloudFormation::Stack')
181-
.filter(stack => this.eventMap['eventToCategories'].has(stack.LogicalResourceId))
182+
.filter(
183+
stack =>
184+
stack.ResourceType !== 'AWS::CloudFormation::Stack' ||
185+
(stack.ResourceStatusReason && !stack.ResourceStatusReason.includes(STACK_RESOURCE_FILTER_FAIL_REASON)),
186+
)
187+
.filter(
188+
stack =>
189+
(this.eventMap['eventToCategories'] && this.eventMap['eventToCategories'].has(stack.LogicalResourceId)) ||
190+
(this.eventMap['rootResources'] &&
191+
this.eventMap['rootResources'].some(resource => resource.key === stack.LogicalResourceId)),
192+
)
182193
.filter(stack => !RESOURCE_CASCADE_FAIL_REASONS.includes(stack.ResourceStatusReason));
183194
}
184195

0 commit comments

Comments
 (0)