github.com/mweagle/Sparta@v1.15.0/execute.go (about) 1 package sparta 2 3 import ( 4 "context" 5 "reflect" 6 "regexp" 7 "strings" 8 9 "github.com/pkg/errors" 10 ) 11 12 var ( 13 reSplitCustomType = regexp.MustCompile(`\:+`) 14 15 reSplitFunctionName = regexp.MustCompile(`\W+`) 16 ) 17 18 const ( 19 // LogFieldRequestID is the fieldname in the context-scoped logger 20 // that includes the AWS assigned request ID 21 LogFieldRequestID = "reqID" 22 // LogFieldARN is the InvokedFunctionArn value 23 LogFieldARN = "arn" 24 // LogFieldBuildID is the Build ID stamped into the binary exposing 25 // the lambda functions 26 LogFieldBuildID = "buildID" 27 // LogFieldInstanceID is a unique identifier for a given container instance 28 LogFieldInstanceID = "instanceID" 29 ) 30 31 const functionNameDelimiter = "_" 32 33 // awsLambdaFunctionName returns the name of the function, which 34 // is set in the CloudFormation template that is published 35 // into the container as `AWS_LAMBDA_FUNCTION_NAME`. Rather 36 // than publish custom vars which are editable in the Console, 37 // tunneling this value through allows Sparta to leverage the 38 // built in env vars. 39 func awsLambdaInternalName(internalFunctionName string) string { 40 41 var internalNameParts []string 42 43 // If this is something that implements something else, trim the 44 // leading * 45 internalFunctionName = strings.TrimPrefix(internalFunctionName, "*") 46 customTypeParts := reSplitCustomType.Split(internalFunctionName, -1) 47 if len(customTypeParts) > 1 { 48 internalNameParts = []string{customTypeParts[len(customTypeParts)-1]} 49 } else { 50 internalNameParts = reSplitFunctionName.Split(internalFunctionName, -1) 51 } 52 return strings.Join(internalNameParts, functionNameDelimiter) 53 } 54 55 func validateArguments(handler reflect.Type) error { 56 57 if handler.NumIn() > 2 { 58 return errors.Errorf("handlers may not take more than two arguments, but handler takes %d", handler.NumIn()) 59 } else if handler.NumIn() > 0 { 60 contextType := reflect.TypeOf((*context.Context)(nil)).Elem() 61 argumentType := handler.In(0) 62 handlerTakesContext := argumentType.Implements(contextType) 63 if handler.NumIn() > 1 && !handlerTakesContext { 64 return errors.Errorf("handler takes two arguments, but the first is not Context. got %s", argumentType.Kind()) 65 } 66 } 67 return nil 68 } 69 func validateReturns(handler reflect.Type) error { 70 errorType := reflect.TypeOf((*error)(nil)).Elem() 71 if handler.NumOut() > 2 { 72 return errors.Errorf("handler may not return more than two values") 73 } else if handler.NumOut() > 1 { 74 if !handler.Out(1).Implements(errorType) { 75 return errors.Errorf("handler returns two values, but the second does not implement error") 76 } 77 } else { 78 if !handler.Out(0).Implements(errorType) { 79 return errors.Errorf("handler returns a single value, but it does not implement error") 80 } 81 } 82 return nil 83 } 84 85 func ensureValidSignature(lambdaName string, handlerSymbol interface{}) error { 86 handlerType := reflect.TypeOf(handlerSymbol) 87 if handlerType == nil { 88 return errors.Errorf("Failed to confirm function type: %#v", handlerSymbol) 89 } 90 if handlerType.Kind() != reflect.Func { 91 return errors.Errorf("Lambda function (%s) is a %s type, not a %s type", 92 lambdaName, 93 handlerType.Kind(), 94 reflect.Func) 95 } 96 argumentErr := validateArguments(handlerType) 97 if argumentErr != nil { 98 return errors.Errorf("Lambda function (%s) has invalid formal arguments: %s", 99 lambdaName, 100 argumentErr) 101 } 102 returnsErr := validateReturns(handlerType) 103 if returnsErr != nil { 104 return errors.Errorf("Lambda function (%s) has invalid returns: %s", 105 lambdaName, 106 returnsErr) 107 } 108 return nil 109 }