github.com/mweagle/Sparta@v1.15.0/sparta_main_awsbinary.go (about)

     1  // +build lambdabinary
     2  
     3  package sparta
     4  
     5  // Provides NOP implementations for functions that do not need to execute
     6  // in the Lambda context
     7  
     8  import (
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"regexp"
    14  	"runtime"
    15  	"time"
    16  
    17  	"github.com/pkg/errors"
    18  	"github.com/sirupsen/logrus"
    19  	"github.com/spf13/cobra"
    20  )
    21  
    22  var reExtractPlatInfo = regexp.MustCompile(`(\w+)=\"(.*)\"`)
    23  
    24  // Main defines the primary handler for transforming an application into a Sparta package.  The
    25  // serviceName is used to uniquely identify your service within a region and will
    26  // be used for subsequent updates.  For provisioning, ensure that you've
    27  // properly configured AWS credentials for the golang SDK.
    28  // See http://docs.aws.amazon.com/sdk-for-go/api/aws/defaults.html#DefaultChainCredentials-constant
    29  // for more information.
    30  func Main(serviceName string,
    31  	serviceDescription string,
    32  	lambdaAWSInfos []*LambdaAWSInfo,
    33  	api *API,
    34  	site *S3Site) error {
    35  	return MainEx(serviceName,
    36  		serviceDescription,
    37  		lambdaAWSInfos,
    38  		api,
    39  		site,
    40  		nil,
    41  		false)
    42  }
    43  
    44  // MainEx provides an "extended" Main that supports customizing the standard Sparta
    45  // workflow via the `workflowHooks` parameter.
    46  func MainEx(serviceName string,
    47  	serviceDescription string,
    48  	lambdaAWSInfos []*LambdaAWSInfo,
    49  	api APIGateway,
    50  	site *S3Site,
    51  	workflowHooks *WorkflowHooks,
    52  	useCGO bool) error {
    53  
    54  	// It's possible the user attached a custom command to the
    55  	// root command. If there is no command, then just run the
    56  	// Execute command...
    57  	CommandLineOptions.Root.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
    58  		// This can only run in AWS Lambda
    59  		formatter := &logrus.JSONFormatter{}
    60  		mainLogLevel := "info"
    61  		envVarLogLevel := os.Getenv(envVarLogLevel)
    62  		if envVarLogLevel != "" {
    63  			mainLogLevel = envVarLogLevel
    64  		}
    65  		logger, loggerErr := NewLoggerWithFormatter(mainLogLevel, formatter)
    66  		if loggerErr != nil {
    67  			return loggerErr
    68  		}
    69  		if logger == nil {
    70  			return errors.Errorf("Failed to initialize logger instance")
    71  		}
    72  
    73  		welcomeMessage := fmt.Sprintf("Service: %s", StampedServiceName)
    74  		logger.WithFields(logrus.Fields{
    75  			fmt.Sprintf("%sVersion", ProperName): SpartaVersion,
    76  			fmt.Sprintf("%sSHA", ProperName):     SpartaGitHash[0:7],
    77  			"go Version":                         runtime.Version(),
    78  			"BuildID":                            StampedBuildID,
    79  			"UTC":                                (time.Now().UTC().Format(time.RFC3339)),
    80  		}).Info(welcomeMessage)
    81  		OptionsGlobal.ServiceName = StampedServiceName
    82  		OptionsGlobal.Logger = logger
    83  		return nil
    84  	}
    85  	CommandLineOptions.Root.RunE = func(cmd *cobra.Command, args []string) (err error) {
    86  		defer func() {
    87  			if r := recover(); r != nil {
    88  				OptionsGlobal.Logger.Error("Panic recovered: %v", r)
    89  				err = errors.Errorf(fmt.Sprintf("%v", r))
    90  			}
    91  		}()
    92  
    93  		// By default run the Execute command
    94  		err = Execute(StampedServiceName,
    95  			lambdaAWSInfos,
    96  			OptionsGlobal.Logger)
    97  		return err
    98  	}
    99  	return CommandLineOptions.Root.Execute()
   100  }
   101  
   102  // Delete is not available in the AWS Lambda binary
   103  func Delete(serviceName string, logger *logrus.Logger) error {
   104  	logger.Error("Delete() not supported in AWS Lambda binary")
   105  	return errors.New("Delete not supported for this binary")
   106  }
   107  
   108  // Provision is not available in the AWS Lambda binary
   109  func Provision(noop bool,
   110  	serviceName string,
   111  	serviceDescription string,
   112  	lambdaAWSInfos []*LambdaAWSInfo,
   113  	api *API,
   114  	site *S3Site,
   115  	s3Bucket string,
   116  	useCGO bool,
   117  	inplace bool,
   118  	buildID string,
   119  	codePipelineTrigger string,
   120  	buildTags string,
   121  	linkerFlags string,
   122  	writer io.Writer,
   123  	workflowHooks *WorkflowHooks,
   124  	logger *logrus.Logger) error {
   125  	logger.Error("Provision() not supported in AWS Lambda binary")
   126  	return errors.New("Provision not supported for this binary")
   127  }
   128  
   129  // Describe is not available in the AWS Lambda binary
   130  func Describe(serviceName string,
   131  	serviceDescription string,
   132  	lambdaAWSInfos []*LambdaAWSInfo,
   133  	api *API,
   134  	site *S3Site,
   135  	s3BucketName string,
   136  	buildTags string,
   137  	linkerFlags string,
   138  	outputWriter io.Writer,
   139  	workflowHooks *WorkflowHooks,
   140  	logger *logrus.Logger) error {
   141  	logger.Error("Describe() not supported in AWS Lambda binary")
   142  	return errors.New("Describe not supported for this binary")
   143  }
   144  
   145  // Explore is an interactive command that brings up a GUI to test
   146  // lambda functions previously deployed into AWS lambda. It's not supported in the
   147  // AWS binary build
   148  func Explore(serviceName string,
   149  	serviceDescription string,
   150  	lambdaAWSInfos []*LambdaAWSInfo,
   151  	api *API,
   152  	site *S3Site,
   153  	s3BucketName string,
   154  	buildTags string,
   155  	linkerFlags string,
   156  	logger *logrus.Logger) error {
   157  	return errors.New("Explore not supported for this binary")
   158  }
   159  
   160  // Profile is the interactive command used to pull S3 assets locally into /tmp
   161  // and run ppro against the cached profiles
   162  func Profile(serviceName string,
   163  	serviceDescription string,
   164  	s3Bucket string,
   165  	httpPort int,
   166  	logger *logrus.Logger) error {
   167  	return errors.New("Profile not supported for this binary")
   168  }
   169  
   170  // Status is the command that produces a simple status report for a given
   171  // stack
   172  func Status(serviceName string,
   173  	serviceDescription string,
   174  	redact bool,
   175  	logger *logrus.Logger) error {
   176  	return errors.New("Status not supported for this binary")
   177  }
   178  
   179  func platformLogSysInfo(lambdaFunc string, logger *logrus.Logger) {
   180  
   181  	// Setup the files and their respective log levels
   182  	mapFilesToLoggerCall := map[logrus.Level][]string{
   183  		logrus.InfoLevel: {
   184  			"/proc/version",
   185  			"/etc/os-release",
   186  		},
   187  		logrus.DebugLevel: {
   188  			"/proc/cpuinfo",
   189  			"/proc/meminfo",
   190  		},
   191  	}
   192  
   193  	for eachLevel, eachList := range mapFilesToLoggerCall {
   194  		for _, eachEntry := range eachList {
   195  			data, dataErr := ioutil.ReadFile(eachEntry)
   196  			if dataErr == nil && len(data) != 0 {
   197  				loggerFields := make(logrus.Fields, 0)
   198  				loggerFields["filepath"] = eachEntry
   199  				match := reExtractPlatInfo.FindAllStringSubmatch(string(data), -1)
   200  				if match != nil {
   201  					for _, eachMatch := range match {
   202  						loggerFields[eachMatch[1]] = eachMatch[2]
   203  					}
   204  				} else {
   205  					loggerFields["contents"] = string(data)
   206  				}
   207  				logger.WithFields(loggerFields).
   208  					Log(eachLevel, "Host Info")
   209  			} else if dataErr != nil || len(data) <= 0 {
   210  				logger.WithFields(logrus.Fields{
   211  					"filepath": eachEntry,
   212  					"error":    dataErr,
   213  					"length":   len(data),
   214  				}).Warn("Failed to read host info")
   215  			}
   216  		}
   217  	}
   218  }
   219  
   220  // RegisterCodePipelineEnvironment is not available during lambda execution
   221  func RegisterCodePipelineEnvironment(environmentName string, environmentVariables map[string]string) error {
   222  	return nil
   223  }
   224  
   225  // NewLoggerWithFormatter always returns a JSON formatted logger
   226  // that is aware of the environment variable that may have been
   227  // set and carried through to the AWS Lambda execution environment
   228  func NewLoggerWithFormatter(level string, formatter logrus.Formatter) (*logrus.Logger, error) {
   229  
   230  	logger := logrus.New()
   231  	// If there is an environment override, use that
   232  	envLogLevel := os.Getenv(envVarLogLevel)
   233  	if envLogLevel != "" {
   234  		level = envLogLevel
   235  	}
   236  	logLevel, err := logrus.ParseLevel(level)
   237  	if err != nil {
   238  		return nil, err
   239  	}
   240  	logger.Level = logLevel
   241  	// We always use JSON in AWS
   242  	logger.Formatter = &logrus.JSONFormatter{}
   243  
   244  	// TODO - consider writing a buffered logger that only
   245  	// writes output following an error.
   246  	// This was done as part of the XRay interceptor!
   247  	logger.Out = os.Stdout
   248  	return logger, nil
   249  }