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  }