github.com/mweagle/Sparta@v1.15.0/sparta_main_build.go (about) 1 // +build !lambdabinary 2 3 package sparta 4 5 import ( 6 "fmt" 7 "log" 8 "os" 9 "time" 10 11 validator "gopkg.in/go-playground/validator.v9" 12 13 "github.com/pkg/errors" 14 "github.com/sirupsen/logrus" 15 "github.com/spf13/cobra" 16 ) 17 18 func platformLogSysInfo(lambdaFunc string, logger *logrus.Logger) { 19 // NOP 20 } 21 22 // RegisterCodePipelineEnvironment is part of a CodePipeline deployment 23 // and defines the environments available for deployment. Environments 24 // are defined the `environmentName`. The values defined in the 25 // environmentVariables are made available to each service as 26 // environment variables. The environment key will be transformed into 27 // a configuration file for a CodePipeline CloudFormation action: 28 // TemplateConfiguration: !Sub "TemplateSource::${environmentName}". 29 func RegisterCodePipelineEnvironment(environmentName string, 30 environmentVariables map[string]string) error { 31 if _, exists := codePipelineEnvironments[environmentName]; exists { 32 return errors.Errorf("Environment (%s) has already been defined", environmentName) 33 } 34 codePipelineEnvironments[environmentName] = environmentVariables 35 return nil 36 } 37 38 // NewLoggerWithFormatter returns a logger with the given formatter. If formatter 39 // is nil, a TTY-aware formatter is used 40 func NewLoggerWithFormatter(level string, formatter logrus.Formatter) (*logrus.Logger, error) { 41 logger := logrus.New() 42 // If there is an environment override, use that 43 envLogLevel := os.Getenv(envVarLogLevel) 44 if envLogLevel != "" { 45 level = envLogLevel 46 } 47 48 logLevel, err := logrus.ParseLevel(level) 49 if err != nil { 50 return nil, err 51 } 52 53 logger.Level = logLevel 54 if nil != formatter { 55 logger.Formatter = formatter 56 } 57 logger.Out = os.Stdout 58 return logger, nil 59 } 60 61 // Main defines the primary handler for transforming an application into a Sparta package. The 62 // serviceName is used to uniquely identify your service within a region and will 63 // be used for subsequent updates. For provisioning, ensure that you've 64 // properly configured AWS credentials for the golang SDK. 65 // See http://docs.aws.amazon.com/sdk-for-go/api/aws/defaults.html#DefaultChainCredentials-constant 66 // for more information. 67 func Main(serviceName string, serviceDescription string, lambdaAWSInfos []*LambdaAWSInfo, api APIGateway, site *S3Site) error { 68 return MainEx(serviceName, 69 serviceDescription, 70 lambdaAWSInfos, 71 api, 72 site, 73 nil, 74 false) 75 } 76 77 // MainEx provides an "extended" Main that supports customizing the standard Sparta 78 // workflow via the `workflowHooks` parameter. 79 func MainEx(serviceName string, 80 serviceDescription string, 81 lambdaAWSInfos []*LambdaAWSInfo, 82 api APIGateway, 83 site *S3Site, 84 workflowHooks *WorkflowHooks, 85 useCGO bool) error { 86 87 ////////////////////////////////////////////////////////////////////////////// 88 // cmdRoot defines the root, non-executable command 89 CommandLineOptions.Root.Short = fmt.Sprintf("%s - Sparta v.%s powered AWS Lambda Microservice", 90 serviceName, 91 SpartaVersion) 92 CommandLineOptions.Root.Long = serviceDescription 93 CommandLineOptions.Root.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { 94 // Save the ServiceName in case a custom command wants it 95 OptionsGlobal.ServiceName = serviceName 96 OptionsGlobal.ServiceDescription = serviceDescription 97 98 validateErr := validate.Struct(OptionsGlobal) 99 if nil != validateErr { 100 return validateErr 101 } 102 103 // Format? 104 // Running in AWS? 105 disableColors := OptionsGlobal.DisableColors || isRunningInAWS() 106 var formatter logrus.Formatter 107 switch OptionsGlobal.LogFormat { 108 case "text", "txt": 109 formatter = &logrus.TextFormatter{ 110 DisableColors: disableColors, 111 FullTimestamp: OptionsGlobal.TimeStamps, 112 } 113 case "json": 114 formatter = &logrus.JSONFormatter{} 115 disableColors = true 116 } 117 logger, loggerErr := NewLoggerWithFormatter(OptionsGlobal.LogLevel, formatter) 118 if nil != loggerErr { 119 return loggerErr 120 } 121 122 // This is a NOP, but makes megacheck happy b/c it doesn't know about 123 // build flags 124 platformLogSysInfo("", logger) 125 OptionsGlobal.Logger = logger 126 welcomeMessage := fmt.Sprintf("Service: %s", serviceName) 127 128 // Header information... 129 displayPrettyHeader(headerDivider, disableColors, logger) 130 // Metadata about the build... 131 logger.WithFields(logrus.Fields{ 132 "Option": cmd.Name(), 133 "UTC": (time.Now().UTC().Format(time.RFC3339)), 134 "LinkFlags": OptionsGlobal.LinkerFlags, 135 }).Info(welcomeMessage) 136 logger.Info(headerDivider) 137 138 return nil 139 } 140 141 ////////////////////////////////////////////////////////////////////////////// 142 // Version 143 CommandLineOptions.Root.AddCommand(CommandLineOptions.Version) 144 145 ////////////////////////////////////////////////////////////////////////////// 146 // Provision 147 CommandLineOptions.Provision.PreRunE = func(cmd *cobra.Command, args []string) error { 148 validateErr := validate.Struct(optionsProvision) 149 150 OptionsGlobal.Logger.WithFields(logrus.Fields{ 151 "validateErr": validateErr, 152 "optionsProvision": optionsProvision, 153 }).Debug("Provision validation results") 154 return validateErr 155 } 156 157 if nil == CommandLineOptions.Provision.RunE { 158 CommandLineOptions.Provision.RunE = func(cmd *cobra.Command, args []string) error { 159 buildID, buildIDErr := provisionBuildID(optionsProvision.BuildID, OptionsGlobal.Logger) 160 if nil != buildIDErr { 161 return buildIDErr 162 } 163 // Save the BuildID 164 StampedBuildID = buildID 165 return Provision(OptionsGlobal.Noop, 166 serviceName, 167 serviceDescription, 168 lambdaAWSInfos, 169 api, 170 site, 171 optionsProvision.S3Bucket, 172 useCGO, 173 optionsProvision.InPlace, 174 buildID, 175 optionsProvision.PipelineTrigger, 176 OptionsGlobal.BuildTags, 177 OptionsGlobal.LinkerFlags, 178 nil, 179 workflowHooks, 180 OptionsGlobal.Logger) 181 } 182 } 183 CommandLineOptions.Root.AddCommand(CommandLineOptions.Provision) 184 185 ////////////////////////////////////////////////////////////////////////////// 186 // Delete 187 CommandLineOptions.Delete.RunE = func(cmd *cobra.Command, args []string) error { 188 return Delete(serviceName, OptionsGlobal.Logger) 189 } 190 191 CommandLineOptions.Root.AddCommand(CommandLineOptions.Delete) 192 193 ////////////////////////////////////////////////////////////////////////////// 194 // Execute 195 if nil == CommandLineOptions.Execute.RunE { 196 CommandLineOptions.Execute.RunE = func(cmd *cobra.Command, args []string) error { 197 198 OptionsGlobal.Logger.Formatter = new(logrus.JSONFormatter) 199 // Ensure the discovery service is initialized 200 initializeDiscovery(OptionsGlobal.Logger) 201 202 return Execute(serviceName, 203 lambdaAWSInfos, 204 OptionsGlobal.Logger) 205 } 206 } 207 CommandLineOptions.Root.AddCommand(CommandLineOptions.Execute) 208 209 ////////////////////////////////////////////////////////////////////////////// 210 // Describe 211 if nil == CommandLineOptions.Describe.RunE { 212 CommandLineOptions.Describe.RunE = func(cmd *cobra.Command, args []string) error { 213 validateErr := validate.Struct(optionsDescribe) 214 if nil != validateErr { 215 return validateErr 216 } 217 218 fileWriter, fileWriterErr := os.Create(optionsDescribe.OutputFile) 219 if fileWriterErr != nil { 220 return fileWriterErr 221 } 222 defer func() { 223 closeErr := fileWriter.Close() 224 if closeErr != nil { 225 OptionsGlobal.Logger.WithFields(logrus.Fields{ 226 "error": closeErr, 227 }).Warn("Failed to close describe output writer") 228 } 229 }() 230 231 describeErr := Describe(serviceName, 232 serviceDescription, 233 lambdaAWSInfos, 234 api, 235 site, 236 optionsDescribe.S3Bucket, 237 OptionsGlobal.BuildTags, 238 OptionsGlobal.LinkerFlags, 239 fileWriter, 240 workflowHooks, 241 OptionsGlobal.Logger) 242 243 if describeErr == nil { 244 describeErr = fileWriter.Sync() 245 } 246 return describeErr 247 } 248 } 249 CommandLineOptions.Root.AddCommand(CommandLineOptions.Describe) 250 251 ////////////////////////////////////////////////////////////////////////////// 252 // Explore 253 if nil == CommandLineOptions.Explore.RunE { 254 CommandLineOptions.Explore.RunE = func(cmd *cobra.Command, args []string) error { 255 validateErr := validate.Struct(optionsExplore) 256 if nil != validateErr { 257 return validateErr 258 } 259 260 return ExploreWithInputFilter(serviceName, 261 serviceDescription, 262 lambdaAWSInfos, 263 api, 264 site, 265 optionsExplore.InputExtensions, 266 optionsDescribe.S3Bucket, 267 OptionsGlobal.BuildTags, 268 OptionsGlobal.LinkerFlags, 269 OptionsGlobal.Logger) 270 } 271 } 272 CommandLineOptions.Root.AddCommand(CommandLineOptions.Explore) 273 274 ////////////////////////////////////////////////////////////////////////////// 275 // Profile 276 if nil == CommandLineOptions.Profile.RunE { 277 CommandLineOptions.Profile.RunE = func(cmd *cobra.Command, args []string) error { 278 validateErr := validate.Struct(optionsProfile) 279 if nil != validateErr { 280 return validateErr 281 } 282 return Profile(serviceName, 283 serviceDescription, 284 optionsProfile.S3Bucket, 285 optionsProfile.Port, 286 OptionsGlobal.Logger) 287 } 288 } 289 CommandLineOptions.Root.AddCommand(CommandLineOptions.Profile) 290 291 ////////////////////////////////////////////////////////////////////////////// 292 // Status 293 if nil == CommandLineOptions.Status.RunE { 294 CommandLineOptions.Status.RunE = func(cmd *cobra.Command, args []string) error { 295 validateErr := validate.Struct(optionsStatus) 296 if nil != validateErr { 297 return validateErr 298 } 299 return Status(serviceName, 300 serviceDescription, 301 optionsStatus.Redact, 302 OptionsGlobal.Logger) 303 } 304 } 305 CommandLineOptions.Root.AddCommand(CommandLineOptions.Status) 306 307 // Run it! 308 executedCmd, executeErr := CommandLineOptions.Root.ExecuteC() 309 if executeErr != nil { 310 if OptionsGlobal.Logger == nil { 311 newLogger, newLoggerErr := NewLogger("info") 312 if newLoggerErr != nil { 313 fmt.Printf("Failed to create new logger: %v", newLoggerErr) 314 newLogger = logrus.New() 315 } 316 OptionsGlobal.Logger = newLogger 317 } 318 if OptionsGlobal.Logger != nil { 319 validationErr, validationErrOk := executeErr.(validator.ValidationErrors) 320 if validationErrOk { 321 for _, eachError := range validationErr { 322 OptionsGlobal.Logger.Error(eachError) 323 } 324 // Only show the usage if there were input validation errors 325 if executedCmd != nil { 326 usageErr := executedCmd.Usage() 327 if usageErr != nil { 328 OptionsGlobal.Logger.Error(usageErr) 329 } 330 } 331 } else { 332 OptionsGlobal.Logger.Error(executeErr) 333 } 334 } else { 335 log.Printf("ERROR: %s", executeErr) 336 } 337 } 338 339 // Cleanup, if for some reason the caller wants to re-execute later... 340 CommandLineOptions.Root.PersistentPreRunE = nil 341 return executeErr 342 }