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  }