feat(aws): support lambda (#334)
This commit is contained in:
52
vendor/github.com/aws/aws-lambda-go/lambda/entry.go
generated
vendored
Normal file
52
vendor/github.com/aws/aws-lambda-go/lambda/entry.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
|
||||
package lambda
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/rpc"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Start takes a handler, and talks to and internal Lambda endpoint to pass Invoke requests to the handler. If a
|
||||
// handler does not match one of the supported types, the lambda package will respond to new invokes served by in
|
||||
// internal endpoint with an appropriate error message. Start blocks, and does not return after being called.
|
||||
//
|
||||
// Rules:
|
||||
// * handler must be a function
|
||||
// * handler may take between 0 and two arguments.
|
||||
// * If there are two arguments, the first argument must implement "context.Context".
|
||||
// * handler may return between 0 and two arguments.
|
||||
// * If there are two return values, the second argument must implement "error".
|
||||
// * If there is one return value it must implement "error".
|
||||
//
|
||||
// func ()
|
||||
// func () error
|
||||
// func (TIn) error
|
||||
// func () (TOut, error)
|
||||
// func (TIn) (TOut, error)
|
||||
// func (context.Context) error
|
||||
// func (context.Context, TIn) error
|
||||
// func (context.Context) (TOut, error)
|
||||
// func (context.Context, TIn) (TOut, error)
|
||||
//
|
||||
// Where '''TIn''' and '''TOut''' are types compatible with the ''encoding/json'' standard library.
|
||||
// See https://golang.org/pkg/encoding/json/#Unmarshal for how deserialization behaves
|
||||
func Start(handler interface{}) {
|
||||
port := os.Getenv("_LAMBDA_SERVER_PORT")
|
||||
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%s", port))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
wrappedHandler := newHandler(handler)
|
||||
function := new(Function)
|
||||
function.handler = wrappedHandler
|
||||
err = rpc.Register(function)
|
||||
if err != nil {
|
||||
log.Fatal("failed to register handler function")
|
||||
}
|
||||
rpc.Accept(lis)
|
||||
log.Fatal("accept should not have returned")
|
||||
}
|
||||
83
vendor/github.com/aws/aws-lambda-go/lambda/function.go
generated
vendored
Normal file
83
vendor/github.com/aws/aws-lambda-go/lambda/function.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
|
||||
package lambda
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-lambda-go/lambda/messages"
|
||||
"github.com/aws/aws-lambda-go/lambdacontext"
|
||||
)
|
||||
|
||||
type Function struct {
|
||||
handler lambdaHandler
|
||||
}
|
||||
|
||||
func (fn *Function) Ping(req *messages.PingRequest, response *messages.PingResponse) error {
|
||||
*response = messages.PingResponse{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fn *Function) Invoke(req *messages.InvokeRequest, response *messages.InvokeResponse) error {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
panicInfo := getPanicInfo(err)
|
||||
response.Error = &messages.InvokeResponse_Error{
|
||||
Message: panicInfo.Message,
|
||||
Type: getErrorType(err),
|
||||
StackTrace: panicInfo.StackTrace,
|
||||
ShouldExit: true,
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
deadline := time.Unix(req.Deadline.Seconds, req.Deadline.Nanos).UTC()
|
||||
invokeContext, cancel := context.WithDeadline(context.Background(), deadline)
|
||||
defer cancel()
|
||||
|
||||
lc := &lambdacontext.LambdaContext{
|
||||
AwsRequestID: req.RequestId,
|
||||
InvokedFunctionArn: req.InvokedFunctionArn,
|
||||
}
|
||||
if len(req.ClientContext) > 0 {
|
||||
if err := json.Unmarshal(req.ClientContext, &lc.ClientContext); err != nil {
|
||||
response.Error = lambdaErrorResponse(err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
invokeContext = lambdacontext.NewContext(invokeContext, lc)
|
||||
|
||||
invokeContext = context.WithValue(invokeContext, "x-amzn-trace-id", req.XAmznTraceId)
|
||||
|
||||
payload, err := fn.handler.Invoke(invokeContext, req.Payload)
|
||||
if err != nil {
|
||||
response.Error = lambdaErrorResponse(err)
|
||||
return nil
|
||||
}
|
||||
response.Payload = payload
|
||||
return nil
|
||||
}
|
||||
|
||||
func getErrorType(err interface{}) string {
|
||||
errorType := reflect.TypeOf(err)
|
||||
if errorType.Kind() == reflect.Ptr {
|
||||
return errorType.Elem().Name()
|
||||
}
|
||||
return errorType.Name()
|
||||
}
|
||||
|
||||
func lambdaErrorResponse(invokeError error) *messages.InvokeResponse_Error {
|
||||
var errorName string
|
||||
if errorType := reflect.TypeOf(invokeError); errorType.Kind() == reflect.Ptr {
|
||||
errorName = errorType.Elem().Name()
|
||||
} else {
|
||||
errorName = errorType.Name()
|
||||
}
|
||||
return &messages.InvokeResponse_Error{
|
||||
Message: invokeError.Error(),
|
||||
Type: errorName,
|
||||
}
|
||||
}
|
||||
123
vendor/github.com/aws/aws-lambda-go/lambda/handler.go
generated
vendored
Normal file
123
vendor/github.com/aws/aws-lambda-go/lambda/handler.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
|
||||
package lambda
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// lambdaHandler is the generic function type
|
||||
type lambdaHandler func(context.Context, []byte) (interface{}, error)
|
||||
|
||||
// Invoke calls the handler, and serializes the response.
|
||||
// If the underlying handler returned an error, or an error occurs during serialization, error is returned.
|
||||
func (handler lambdaHandler) Invoke(ctx context.Context, payload []byte) ([]byte, error) {
|
||||
response, err := handler(ctx, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responseBytes, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return responseBytes, nil
|
||||
}
|
||||
|
||||
func errorHandler(e error) lambdaHandler {
|
||||
return func(ctx context.Context, event []byte) (interface{}, error) {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
|
||||
func validateArguments(handler reflect.Type) (bool, error) {
|
||||
handlerTakesContext := false
|
||||
if handler.NumIn() > 2 {
|
||||
return false, fmt.Errorf("handlers may not take more than two arguments, but handler takes %d", handler.NumIn())
|
||||
} else if handler.NumIn() > 0 {
|
||||
contextType := reflect.TypeOf((*context.Context)(nil)).Elem()
|
||||
argumentType := handler.In(0)
|
||||
handlerTakesContext = argumentType.Implements(contextType)
|
||||
if handler.NumIn() > 1 && !handlerTakesContext {
|
||||
return false, fmt.Errorf("handler takes two arguments, but the first is not Context. got %s", argumentType.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
return handlerTakesContext, nil
|
||||
}
|
||||
|
||||
func validateReturns(handler reflect.Type) error {
|
||||
errorType := reflect.TypeOf((*error)(nil)).Elem()
|
||||
if handler.NumOut() > 2 {
|
||||
return fmt.Errorf("handler may not return more than two values")
|
||||
} else if handler.NumOut() > 1 {
|
||||
if !handler.Out(1).Implements(errorType) {
|
||||
return fmt.Errorf("handler returns two values, but the second does not implement error")
|
||||
}
|
||||
} else if handler.NumOut() == 1 {
|
||||
if !handler.Out(0).Implements(errorType) {
|
||||
return fmt.Errorf("handler returns a single value, but it does not implement error")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// newHandler Creates the base lambda handler, which will do basic payload unmarshaling before defering to handlerSymbol.
|
||||
// If handlerSymbol is not a valid handler, the returned function will be a handler that just reports the validation error.
|
||||
func newHandler(handlerSymbol interface{}) lambdaHandler {
|
||||
if handlerSymbol == nil {
|
||||
return errorHandler(fmt.Errorf("handler is nil"))
|
||||
}
|
||||
handler := reflect.ValueOf(handlerSymbol)
|
||||
handlerType := reflect.TypeOf(handlerSymbol)
|
||||
if handlerType.Kind() != reflect.Func {
|
||||
return errorHandler(fmt.Errorf("handler kind %s is not %s", handlerType.Kind(), reflect.Func))
|
||||
}
|
||||
|
||||
takesContext, err := validateArguments(handlerType)
|
||||
if err != nil {
|
||||
return errorHandler(err)
|
||||
}
|
||||
|
||||
if err := validateReturns(handlerType); err != nil {
|
||||
return errorHandler(err)
|
||||
}
|
||||
|
||||
return func(ctx context.Context, payload []byte) (interface{}, error) {
|
||||
// construct arguments
|
||||
var args []reflect.Value
|
||||
if takesContext {
|
||||
args = append(args, reflect.ValueOf(ctx))
|
||||
}
|
||||
if (handlerType.NumIn() == 1 && !takesContext) || handlerType.NumIn() == 2 {
|
||||
eventType := handlerType.In(handlerType.NumIn() - 1)
|
||||
event := reflect.New(eventType)
|
||||
|
||||
if err := json.Unmarshal(payload, event.Interface()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args = append(args, event.Elem())
|
||||
}
|
||||
|
||||
response := handler.Call(args)
|
||||
|
||||
// convert return values into (interface{}, error)
|
||||
var err error
|
||||
if len(response) > 0 {
|
||||
if errVal, ok := response[len(response)-1].Interface().(error); ok {
|
||||
err = errVal
|
||||
}
|
||||
}
|
||||
var val interface{}
|
||||
if len(response) > 1 {
|
||||
val = response[0].Interface()
|
||||
}
|
||||
|
||||
return val, err
|
||||
}
|
||||
}
|
||||
43
vendor/github.com/aws/aws-lambda-go/lambda/messages/messages.go
generated
vendored
Normal file
43
vendor/github.com/aws/aws-lambda-go/lambda/messages/messages.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
|
||||
package messages
|
||||
|
||||
type PingRequest struct {
|
||||
}
|
||||
|
||||
type PingResponse struct {
|
||||
}
|
||||
|
||||
type InvokeRequest_Timestamp struct {
|
||||
Seconds int64
|
||||
Nanos int64
|
||||
}
|
||||
|
||||
type InvokeRequest struct {
|
||||
Payload []byte
|
||||
RequestId string
|
||||
XAmznTraceId string
|
||||
Deadline InvokeRequest_Timestamp
|
||||
InvokedFunctionArn string
|
||||
CognitoIdentityId string
|
||||
CognitoIdentityPoolId string
|
||||
ClientContext []byte
|
||||
}
|
||||
|
||||
type InvokeResponse struct {
|
||||
Payload []byte
|
||||
Error *InvokeResponse_Error
|
||||
}
|
||||
|
||||
type InvokeResponse_Error struct {
|
||||
Message string
|
||||
Type string
|
||||
StackTrace []*InvokeResponse_Error_StackFrame
|
||||
ShouldExit bool
|
||||
}
|
||||
|
||||
type InvokeResponse_Error_StackFrame struct {
|
||||
Path string `json:"path"`
|
||||
Line int32 `json:"line"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
99
vendor/github.com/aws/aws-lambda-go/lambda/panic.go
generated
vendored
Normal file
99
vendor/github.com/aws/aws-lambda-go/lambda/panic.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
|
||||
package lambda
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-lambda-go/lambda/messages"
|
||||
)
|
||||
|
||||
type panicInfo struct {
|
||||
Message string // Value passed to panic call, converted to string
|
||||
StackTrace []*messages.InvokeResponse_Error_StackFrame // Stack trace of the panic
|
||||
}
|
||||
|
||||
func getPanicInfo(value interface{}) panicInfo {
|
||||
message := getPanicMessage(value)
|
||||
stack := getPanicStack()
|
||||
|
||||
return panicInfo{Message: message, StackTrace: stack}
|
||||
}
|
||||
|
||||
func getPanicMessage(value interface{}) string {
|
||||
return fmt.Sprintf("%v", value)
|
||||
}
|
||||
|
||||
var defaultErrorFrameCount = 32
|
||||
|
||||
func getPanicStack() []*messages.InvokeResponse_Error_StackFrame {
|
||||
s := make([]uintptr, defaultErrorFrameCount)
|
||||
const framesToHide = 3 // this (getPanicStack) -> getPanicInfo -> handler defer func
|
||||
n := runtime.Callers(framesToHide, s)
|
||||
if n == 0 {
|
||||
return make([]*messages.InvokeResponse_Error_StackFrame, 0)
|
||||
}
|
||||
|
||||
s = s[:n]
|
||||
|
||||
return convertStack(s)
|
||||
}
|
||||
|
||||
func convertStack(s []uintptr) []*messages.InvokeResponse_Error_StackFrame {
|
||||
var converted []*messages.InvokeResponse_Error_StackFrame
|
||||
frames := runtime.CallersFrames(s)
|
||||
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
|
||||
formattedFrame := formatFrame(frame)
|
||||
converted = append(converted, formattedFrame)
|
||||
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func formatFrame(inputFrame runtime.Frame) *messages.InvokeResponse_Error_StackFrame {
|
||||
path := inputFrame.File
|
||||
line := int32(inputFrame.Line)
|
||||
label := inputFrame.Function
|
||||
|
||||
// Strip GOPATH from path by counting the number of seperators in label & path
|
||||
//
|
||||
// For example given this:
|
||||
// GOPATH = /home/user
|
||||
// path = /home/user/src/pkg/sub/file.go
|
||||
// label = pkg/sub.Type.Method
|
||||
//
|
||||
// We want to set:
|
||||
// path = pkg/sub/file.go
|
||||
// label = Type.Method
|
||||
|
||||
i := len(path)
|
||||
for n, g := 0, strings.Count(label, "/")+2; n < g; n++ {
|
||||
i = strings.LastIndex(path[:i], "/")
|
||||
if i == -1 {
|
||||
// Something went wrong and path has less seperators than we expected
|
||||
// Abort and leave i as -1 to counteract the +1 below
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
path = path[i+1:] // Trim the initial /
|
||||
|
||||
// Strip the path from the function name as it's already in the path
|
||||
label = label[strings.LastIndex(label, "/")+1:]
|
||||
// Likewise strip the package name
|
||||
label = label[strings.Index(label, ".")+1:]
|
||||
|
||||
return &messages.InvokeResponse_Error_StackFrame{
|
||||
Path: path,
|
||||
Line: line,
|
||||
Label: label,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user