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 }