Skip to content

Commit 31b9f5c

Browse files
committed
fix version
1 parent dc90375 commit 31b9f5c

File tree

5 files changed

+62
-6
lines changed

5 files changed

+62
-6
lines changed

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ jobs:
1818

1919
- name: golangci-golint
2020
run: |
21-
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.52.2
21+
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.52.1
2222
./bin/golangci-lint run -v ./...
2323

lambda/handler.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type handlerOptions struct {
2828
jsonResponseIndentValue string
2929
enableSIGTERM bool
3030
sigtermCallbacks []func()
31+
setupFuncs []func() error
3132
}
3233

3334
type Option func(*handlerOptions)
@@ -102,6 +103,15 @@ func WithEnableSIGTERM(callbacks ...func()) Option {
102103
})
103104
}
104105

106+
// WithSetup enables capturing of errors or panics that occur before the function is ready to handle invokes.
107+
// The provided functions will be run a single time, in order, before the runtime reports itself ready to recieve invokes.
108+
// If any of the provided functions returns an error, or panics, the error will be serialized and reported to the Runtime API.
109+
func WithSetup(funcs ...func() error) Option {
110+
return Option(func(h *handlerOptions) {
111+
h.setupFuncs = append(h.setupFuncs, funcs...)
112+
})
113+
}
114+
105115
// handlerTakesContext returns whether the handler takes a context.Context as its first argument.
106116
func handlerTakesContext(handler reflect.Type) (bool, error) {
107117
switch handler.NumIn() {

lambda/invoke_loop.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ func unixMS(ms int64) time.Time {
3131
func startRuntimeAPILoop(api string, handler Handler) error {
3232
client := newRuntimeAPIClient(api)
3333
h := newHandler(handler)
34+
35+
if err := handleSetup(client, h); err != nil {
36+
return err
37+
}
3438
for {
3539
invoke, err := client.next()
3640
if err != nil {
@@ -42,6 +46,21 @@ func startRuntimeAPILoop(api string, handler Handler) error {
4246
}
4347
}
4448

49+
// handleSetup returns an error if any of the handler's optional setup functions return and error or panic
50+
func handleSetup(client *runtimeAPIClient, handler *handlerOptions) error {
51+
for _, setup := range handler.setupFuncs {
52+
if setupErr := callSetupFunc(setup); setupErr != nil {
53+
errorPayload := safeMarshal(setupErr)
54+
log.Printf("%s", errorPayload)
55+
if err := client.initError(bytes.NewReader(errorPayload), contentTypeJSON); err != nil {
56+
return fmt.Errorf("unexpected error occurred when sending the setup error to the API: %v", err)
57+
}
58+
return fmt.Errorf("setting up the handler function resulted in an error, the process should exit")
59+
}
60+
}
61+
return nil
62+
}
63+
4564
// handleInvoke returns an error if the function panics, or some other non-recoverable error occurred
4665
func handleInvoke(invoke *invoke, handler *handlerOptions) error {
4766
// set the deadline
@@ -110,6 +129,18 @@ func reportFailure(invoke *invoke, invokeErr *messages.InvokeResponse_Error) err
110129
return nil
111130
}
112131

132+
func callSetupFunc(f func() error) (setupErr *messages.InvokeResponse_Error) {
133+
defer func() {
134+
if err := recover(); err != nil {
135+
setupErr = lambdaPanicResponse(err)
136+
}
137+
}()
138+
if err := f(); err != nil {
139+
return lambdaErrorResponse(err)
140+
}
141+
return nil
142+
}
143+
113144
func callBytesHandlerFunc(ctx context.Context, payload []byte, handler handlerFunc) (response io.Reader, invokeErr *messages.InvokeResponse_Error) {
114145
defer func() {
115146
if err := recover(); err != nil {

lambda/rpc_function.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,19 @@ func init() {
3333
}
3434

3535
func startFunctionRPC(port string, handler Handler) error {
36+
rpcFunction := NewFunction(handler)
37+
if len(rpcFunction.handler.setupFuncs) > 0 {
38+
runtimeAPIClient := newRuntimeAPIClient(os.Getenv("AWS_LAMBDA_RUNTIME_API"))
39+
if err := handleSetup(runtimeAPIClient, rpcFunction.handler); err != nil {
40+
return err
41+
}
42+
}
43+
3644
lis, err := net.Listen("tcp", "localhost:"+port)
3745
if err != nil {
3846
log.Fatal(err)
3947
}
40-
err = rpc.Register(NewFunction(handler))
48+
err = rpc.Register(rpcFunction)
4149
if err != nil {
4250
log.Fatal("failed to register handler function")
4351
}

lambda/runtime_api_client.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,18 @@ func newRuntimeAPIClient(address string) *runtimeAPIClient {
3737
client := &http.Client{
3838
Timeout: 0, // connections to the runtime API are never expected to time out
3939
}
40-
endpoint := "http://" + address + "/" + apiVersion + "/runtime/invocation/"
40+
endpoint := "http://" + address + "/" + apiVersion + "/runtime"
4141
userAgent := "aws-lambda-go/" + runtime.Version()
4242
return &runtimeAPIClient{endpoint, userAgent, client, bytes.NewBuffer(nil)}
4343
}
4444

45+
// initError connects to the Runtime API and reports that a failure occured during initialization.
46+
// Note: After calling this function, the caller should call os.Exit()
47+
func (c *runtimeAPIClient) initError(body io.Reader, contentType string) error {
48+
url := c.baseURL + "/init/error"
49+
return c.post(url, body, contentType)
50+
}
51+
4552
type invoke struct {
4653
id string
4754
payload []byte
@@ -53,7 +60,7 @@ type invoke struct {
5360
// Notes:
5461
// - An invoke is not complete until next() is called again!
5562
func (i *invoke) success(body io.Reader, contentType string) error {
56-
url := i.client.baseURL + i.id + "/response"
63+
url := i.client.baseURL + "/invocation/" + i.id + "/response"
5764
return i.client.post(url, body, contentType)
5865
}
5966

@@ -63,14 +70,14 @@ func (i *invoke) success(body io.Reader, contentType string) error {
6370
// - A Lambda Function continues to be re-used for future invokes even after a failure.
6471
// If the error is fatal (panic, unrecoverable state), exit the process immediately after calling failure()
6572
func (i *invoke) failure(body io.Reader, contentType string) error {
66-
url := i.client.baseURL + i.id + "/error"
73+
url := i.client.baseURL + "/invocation/" + i.id + "/error"
6774
return i.client.post(url, body, contentType)
6875
}
6976

7077
// next connects to the Runtime API and waits for a new invoke Request to be available.
7178
// Note: After a call to Done() or Error() has been made, a call to next() will complete the in-flight invoke.
7279
func (c *runtimeAPIClient) next() (*invoke, error) {
73-
url := c.baseURL + "next"
80+
url := c.baseURL + "/invocation/next"
7481
req, err := http.NewRequest(http.MethodGet, url, nil)
7582
if err != nil {
7683
return nil, fmt.Errorf("failed to construct GET request to %s: %v", url, err)

0 commit comments

Comments
 (0)