@cumulus/message 9.9.0 → 9.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Build.d.ts.map +1 -1
- package/Build.js.map +1 -1
- package/DeadLetterMessage.d.ts +16 -0
- package/DeadLetterMessage.d.ts.map +1 -0
- package/DeadLetterMessage.js +42 -0
- package/DeadLetterMessage.js.map +1 -0
- package/README.md +75 -36
- package/StepFunctions.d.ts +56 -0
- package/StepFunctions.d.ts.map +1 -1
- package/StepFunctions.js +127 -8
- package/StepFunctions.js.map +1 -1
- package/package.json +7 -7
- package/src/Build.ts +1 -1
- package/src/DeadLetterMessage.ts +54 -0
- package/src/StepFunctions.ts +158 -1
- package/tsconfig.tsbuildinfo +1 -1
package/src/StepFunctions.ts
CHANGED
|
@@ -8,14 +8,41 @@
|
|
|
8
8
|
* const StepFunctions = require('@cumulus/message/StepFunctions');
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { EventBridgeEvent } from 'aws-lambda';
|
|
11
12
|
import { JSONPath } from 'jsonpath-plus';
|
|
13
|
+
import get from 'lodash/get';
|
|
14
|
+
import set from 'lodash/set';
|
|
15
|
+
|
|
16
|
+
import { getExecutionHistory } from '@cumulus/aws-client/StepFunctions';
|
|
17
|
+
import { getStepExitedEvent, getTaskExitedEventOutput } from '@cumulus/common/execution-history';
|
|
18
|
+
import { Message } from '@cumulus/types';
|
|
12
19
|
import * as s3Utils from '@cumulus/aws-client/S3';
|
|
13
20
|
import Logger from '@cumulus/logger';
|
|
14
|
-
|
|
21
|
+
|
|
22
|
+
import { getMessageExecutionArn } from './Executions';
|
|
15
23
|
|
|
16
24
|
const log = new Logger({
|
|
17
25
|
sender: '@cumulus/message/StepFunctions',
|
|
18
26
|
});
|
|
27
|
+
type ExecutionStatus = ('ABORTED' | 'RUNNING' | 'TIMED_OUT' | 'SUCCEEDED' | 'FAILED');
|
|
28
|
+
|
|
29
|
+
type ExecutionStatusToWorkflowStatusMap = {
|
|
30
|
+
[K in ExecutionStatus]: Message.WorkflowStatus;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const executionStatusToWorkflowStatus = (
|
|
34
|
+
executionStatus: ExecutionStatus
|
|
35
|
+
): Message.WorkflowStatus => {
|
|
36
|
+
const statusMap: ExecutionStatusToWorkflowStatusMap = {
|
|
37
|
+
ABORTED: 'failed',
|
|
38
|
+
FAILED: 'failed',
|
|
39
|
+
RUNNING: 'running',
|
|
40
|
+
SUCCEEDED: 'completed',
|
|
41
|
+
TIMED_OUT: 'failed',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return statusMap[executionStatus];
|
|
45
|
+
};
|
|
19
46
|
|
|
20
47
|
/**
|
|
21
48
|
* Given a Step Function event, replace specified key in event with contents
|
|
@@ -91,3 +118,133 @@ export const parseStepMessage = async (
|
|
|
91
118
|
}
|
|
92
119
|
return <Message.CumulusMessage>parsedMessage;
|
|
93
120
|
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Searches the Execution step History for the TaskStateEntered pertaining to
|
|
124
|
+
* the failed task Id. HistoryEvent ids are numbered sequentially, starting at
|
|
125
|
+
* one.
|
|
126
|
+
*
|
|
127
|
+
* @param {HistoryEvent[]} events - Step Function events array
|
|
128
|
+
* @param {HistoryEvent} failedStepEvent - Step Function's failed event.
|
|
129
|
+
* @returns {string} name of the current stepfunction task or 'UnknownFailedStepName'.
|
|
130
|
+
*/
|
|
131
|
+
export const getFailedStepName = (
|
|
132
|
+
events: AWS.StepFunctions.HistoryEvent[],
|
|
133
|
+
failedStepEvent: { id: number }
|
|
134
|
+
) => {
|
|
135
|
+
try {
|
|
136
|
+
const previousEvents = events.slice(0, failedStepEvent.id - 1);
|
|
137
|
+
const startEvents = previousEvents.filter(
|
|
138
|
+
(e) => e.type === 'TaskStateEntered'
|
|
139
|
+
);
|
|
140
|
+
const stateName = startEvents.pop()?.stateEnteredEventDetails?.name;
|
|
141
|
+
if (!stateName) throw new Error('TaskStateEntered Event Object did not have `stateEnteredEventDetails.name`');
|
|
142
|
+
return stateName;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
log.info('Failed to determine a failed stepName from execution events.');
|
|
145
|
+
log.error(error);
|
|
146
|
+
}
|
|
147
|
+
return 'UnknownFailedStepName';
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Finds all failed execution events and returns the last one in the list.
|
|
152
|
+
*
|
|
153
|
+
* @param {Array<HistoryEventList>} events - array of AWS Stepfunction execution HistoryEvents
|
|
154
|
+
* @returns {HistoryEvent[]} - the last lambda or activity that failed in the
|
|
155
|
+
* event array, or an empty array.
|
|
156
|
+
*/
|
|
157
|
+
export const lastFailedEventStep = (
|
|
158
|
+
events: AWS.StepFunctions.HistoryEvent[]
|
|
159
|
+
): AWS.StepFunctions.HistoryEvent | undefined => {
|
|
160
|
+
const failures = events.filter((event) =>
|
|
161
|
+
['LambdaFunctionFailed', 'ActivityFailed'].includes(event.type));
|
|
162
|
+
return failures.pop();
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get message to use for publishing failed execution notifications.
|
|
167
|
+
*
|
|
168
|
+
* Try to get the input to the last failed step in the execution so we can
|
|
169
|
+
* update the status of any granules/PDRs that don't exist in the initial execution
|
|
170
|
+
* input.
|
|
171
|
+
*
|
|
172
|
+
* Falls back to overall execution input.
|
|
173
|
+
*
|
|
174
|
+
* @param {Object} inputCumulusMessage - Workflow execution input message
|
|
175
|
+
* @param {Function} getExecutionHistoryFunction - Testing override for mock/etc of
|
|
176
|
+
* StepFunctions.getExecutionHistory
|
|
177
|
+
* @returns {Object} - CumulusMessage Execution step message or execution input message
|
|
178
|
+
*/
|
|
179
|
+
export const getFailedExecutionMessage = async (
|
|
180
|
+
inputCumulusMessage: Message.CumulusMessage,
|
|
181
|
+
getExecutionHistoryFunction: typeof getExecutionHistory = getExecutionHistory
|
|
182
|
+
) => {
|
|
183
|
+
const amendedCumulusMessage = { ...inputCumulusMessage };
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const executionArn = getMessageExecutionArn(amendedCumulusMessage);
|
|
187
|
+
if (!executionArn) { throw new Error('No execution arn found'); }
|
|
188
|
+
const { events } = await getExecutionHistoryFunction({ executionArn });
|
|
189
|
+
|
|
190
|
+
const lastFailedEvent = lastFailedEventStep(events);
|
|
191
|
+
if (!lastFailedEvent) {
|
|
192
|
+
log.warn(`No failed step events found in execution ${executionArn}`);
|
|
193
|
+
return amendedCumulusMessage;
|
|
194
|
+
}
|
|
195
|
+
const failedExecutionStepName = getFailedStepName(events, lastFailedEvent);
|
|
196
|
+
const failedStepExitedEvent = getStepExitedEvent(events, lastFailedEvent);
|
|
197
|
+
|
|
198
|
+
if (!failedStepExitedEvent) {
|
|
199
|
+
log.info(`Could not retrieve output from last failed step in execution ${executionArn}, falling back to execution input`);
|
|
200
|
+
log.info(`Could not find TaskStateExited event after step ID ${lastFailedEvent.id} for execution ${executionArn}`);
|
|
201
|
+
return {
|
|
202
|
+
...amendedCumulusMessage,
|
|
203
|
+
exception: {
|
|
204
|
+
...lastFailedEvent.activityFailedEventDetails,
|
|
205
|
+
...lastFailedEvent.lambdaFunctionFailedEventDetails,
|
|
206
|
+
failedExecutionStepName,
|
|
207
|
+
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const taskExitedEventOutput = JSON.parse(getTaskExitedEventOutput(failedStepExitedEvent) || '{}');
|
|
212
|
+
taskExitedEventOutput.exception = {
|
|
213
|
+
...taskExitedEventOutput.exception,
|
|
214
|
+
failedExecutionStepName,
|
|
215
|
+
};
|
|
216
|
+
return await parseStepMessage(taskExitedEventOutput, failedExecutionStepName);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
log.error('getFailedExecution failed to retrieve failure:', error);
|
|
219
|
+
return amendedCumulusMessage;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const getCumulusMessageFromExecutionEvent = async (executionEvent: EventBridgeEvent<'Step Functions Execution Status Change', { [key: string]: string }>) => {
|
|
224
|
+
let cumulusMessage;
|
|
225
|
+
if (executionEvent.detail.status === 'RUNNING') {
|
|
226
|
+
cumulusMessage = JSON.parse(executionEvent.detail.input);
|
|
227
|
+
} else if (executionEvent.detail.status === 'SUCCEEDED') {
|
|
228
|
+
cumulusMessage = JSON.parse(executionEvent.detail.output);
|
|
229
|
+
} else {
|
|
230
|
+
const inputMessage = JSON.parse(get(executionEvent, 'detail.input', '{}'));
|
|
231
|
+
cumulusMessage = await getFailedExecutionMessage(inputMessage);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const fullCumulusMessage = (await pullStepFunctionEvent(
|
|
235
|
+
cumulusMessage
|
|
236
|
+
)) as Message.CumulusMessage;
|
|
237
|
+
|
|
238
|
+
const workflowStatus = executionStatusToWorkflowStatus(
|
|
239
|
+
executionEvent.detail.status as ExecutionStatus
|
|
240
|
+
);
|
|
241
|
+
set(fullCumulusMessage, 'meta.status', workflowStatus);
|
|
242
|
+
|
|
243
|
+
set(
|
|
244
|
+
fullCumulusMessage,
|
|
245
|
+
'cumulus_meta.workflow_stop_time',
|
|
246
|
+
executionEvent.detail.stopDate
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return fullCumulusMessage;
|
|
250
|
+
};
|