forked from aws/aws-lambda-runtime-interface-emulator
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexit.go
More file actions
142 lines (115 loc) · 4.84 KB
/
Copy pathexit.go
File metadata and controls
142 lines (115 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package rapid
import (
"fmt"
"os"
"go.amzn.com/lambda/appctx"
"go.amzn.com/lambda/extensions"
"go.amzn.com/lambda/fatalerror"
"go.amzn.com/lambda/interop"
"go.amzn.com/lambda/rapi/model"
log "github.com/sirupsen/logrus"
)
func checkInteropError(format string, err error) {
if err == interop.ErrInvalidInvokeID || err == interop.ErrResponseSent {
log.Warnf(format, err)
} else {
log.Panicf(format, err)
}
}
func trySendDefaultErrorResponse(interopServer interop.Server, invokeID string, errorType fatalerror.ErrorType, err error) {
resp := model.ErrorResponse{
ErrorType: string(errorType),
ErrorMessage: fmt.Sprintf("Error: %v", err),
}
if invokeID != "" {
resp.ErrorMessage = fmt.Sprintf("RequestId: %s Error: %v", invokeID, err)
}
if err := interopServer.SendErrorResponse(invokeID, resp.AsInteropError()); err != nil {
checkInteropError("Failed to send default error response: %s", err)
}
}
func reportErrorAndExit(appCtx appctx.ApplicationContext, execCtx *rapidContext, invokeID string, interopServer interop.Server, err error, correlationID string) {
// This function maintains compatibility of exit sequence behaviour
// with Sandbox Factory in the absence of extensions
errorType, found := appctx.LoadFirstFatalError(appCtx)
if !found {
errorType = fatalerror.Unknown
}
// NOTE this check will prevent us from sending FAULT message in case
// response (positive or negative) has already been sent. This is done
// to maintain legacy behavior of RAPID.
// ALSO NOTE, this works in case of positive response because this will
// be followed by RAPID exit.
if !interopServer.IsResponseSent() {
trySendDefaultErrorResponse(interopServer, invokeID, errorType, err)
}
if err := interopServer.CommitResponse(); err != nil {
checkInteropError("Failed to commit error response: %s", err)
}
doneMsg := &interop.Done{
WaitForExit: true,
RuntimeRelease: appctx.GetRuntimeRelease(appCtx),
CorrelationID: correlationID, // required for standalone mode
}
if execCtx.telemetryAPIEnabled {
doneMsg.LogsAPIMetrics = execCtx.telemetryService.FlushMetrics()
}
if err := interopServer.SendDone(doneMsg); err != nil {
checkInteropError("Failed to send DONE during exit: %s", err)
}
os.Exit(1)
}
func reportErrorAndRequestReset(appCtx appctx.ApplicationContext, execCtx *rapidContext, invokeID string, interopServer interop.Server, err error, correlationID string) {
errorType, found := appctx.LoadFirstFatalError(appCtx)
if !found {
errorType = fatalerror.Unknown
}
trySendDefaultErrorResponse(interopServer, invokeID, errorType, err)
if err := interopServer.CommitResponse(); err != nil {
checkInteropError("Failed to commit error response: %s", err)
}
doneFailMsg := &interop.DoneFail{
ErrorType: string(errorType),
RuntimeRelease: appctx.GetRuntimeRelease(appCtx),
CorrelationID: correlationID, // required for standalone mode
NumActiveExtensions: execCtx.registrationService.CountAgents(),
}
if execCtx.telemetryAPIEnabled {
doneFailMsg.LogsAPIMetrics = execCtx.telemetryService.FlushMetrics()
}
if err := interopServer.SendDoneFail(doneFailMsg); err != nil {
checkInteropError("Failed to send DONEFAIL: %s", err)
}
}
func handleError(appCtx appctx.ApplicationContext, execCtx *rapidContext, invokeID string, interopServer interop.Server, err error, correlationID string) {
if err == errResetReceived {
// errResetReceived is returned when execution flow was interrupted by the Reset message,
// hence this error deserves special handling and we yield to main receive loop to handle it
return
}
reportErrorAndRequestReset(appCtx, execCtx, invokeID, interopServer, err, correlationID)
}
func handleInitError(appCtx appctx.ApplicationContext, execCtx *rapidContext, invokeID string, interopServer interop.Server, err error, correlationID string) {
if execCtx.standaloneMode {
handleError(appCtx, execCtx, invokeID, interopServer, err, correlationID)
return
}
if !execCtx.HasActiveExtensions() {
// we don't expect Slicer to send RESET during INIT, that's why we Exit here
reportErrorAndExit(appCtx, execCtx, invokeID, interopServer, err, correlationID)
}
handleError(appCtx, execCtx, invokeID, interopServer, err, correlationID)
}
func handleInvokeError(appCtx appctx.ApplicationContext, execCtx *rapidContext, invokeID string, interopServer interop.Server, err error, correlationID string) {
if execCtx.standaloneMode {
handleError(appCtx, execCtx, invokeID, interopServer, err, correlationID)
return
}
// Invoke with extensions disabled maintains behaviour parity with pre-extensions rapid
if !extensions.AreEnabled() {
reportErrorAndExit(appCtx, execCtx, invokeID, interopServer, err, correlationID)
}
handleError(appCtx, execCtx, invokeID, interopServer, err, correlationID)
}