cli/cli.go (125 lines of code) (raw):
package cli
/*
Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
*/
import (
"strconv"
"time"
"github.com/facebookincubator/go2chef/util/temp"
"github.com/facebookincubator/go2chef"
"github.com/facebookincubator/go2chef/plugin/logger/stdlib"
"github.com/spf13/pflag"
)
var (
// DefaultConfigSource sets the go2chef CLI default configuration source type
DefaultConfigSource = "go2chef.config_source.local"
// DefaultLogLevel sets the go2chef CLI default logging level
DefaultLogLevel = go2chef.LogLevelDebug
logger go2chef.Logger
)
func init() {
}
// Go2ChefCLI is the CLI entry point for go2chef
type Go2ChefCLI struct {
flags *pflag.FlagSet
configSourceName string
logLevel string
logDebugLevel int
preserveTemp bool
}
// Option defines the interface for CLI option functions
type Option func(cli *Go2ChefCLI)
// WithFlagSet is an Option to set a custom FlagSet
func WithFlagSet(set *pflag.FlagSet) Option {
return func(cli *Go2ChefCLI) {
cli.flags = set
}
}
// NewGo2ChefCLI configures a Go2ChefCLI instance
func NewGo2ChefCLI(opts ...Option) *Go2ChefCLI {
cli := &Go2ChefCLI{
flags: pflag.NewFlagSet("go2chef", pflag.ExitOnError),
}
for _, opt := range opts {
opt(cli)
}
logLevel, err := go2chef.LogLevelToString(DefaultLogLevel)
if err != nil {
panic("invalid go2chef.cli.DefaultLogLevel compiled in")
}
cli.flags.StringVarP(&cli.configSourceName, "config-source", "C", DefaultConfigSource, "name of the configuration source to use")
cli.flags.StringVarP(&cli.logLevel, "log-level", "l", logLevel, "log level")
cli.flags.BoolVar(&cli.preserveTemp, "preserve-temp", false, "preserve temporary directories from this run")
return cli
}
// Run kicks off the execution of go2chef
func (g *Go2ChefCLI) Run(argv []string) int {
// Set early config flags and parse. As we build our
// own pflag.FlagSet plugins using pflag.*Var() functions
// won't be able to pollute this.
go2chef.InitializeConfigSourceFlags(g.flags)
if err := g.flags.Parse(argv); err != nil {
return 1
}
// Pull in early log level config from flags
logLevel, err := go2chef.StringToLogLevel(g.logLevel)
if err != nil {
go2chef.EarlyLogger.Printf("--log-level/-l value `%s` is invalid: %s", g.logLevel, err)
return 1
}
// Add stdlib early logger
early := stdlib.NewFromLogger(go2chef.EarlyLogger, logLevel, g.logDebugLevel)
// Load actual configuration
cfg, err := go2chef.GetConfig(g.configSourceName, early)
if err != nil {
early.Errorf("config error: %s", err)
return 1
}
// Wire up central logging
go2chef.InitGlobalLogger(cfg.Loggers)
logger = go2chef.GetGlobalLogger()
logger.WriteEvent(&go2chef.Event{
Event: "LOGGING_INITIALIZED",
Component: "go2chef.cli",
})
defer temp.Cleanup(g.preserveTemp)
defer go2chef.ShutdownGlobalLogger()
all_start := time.Now()
for i, step := range cfg.Steps {
start := time.Now()
eventStartStep(i, step.Name(), step.Type())
if err := step.Download(); err != nil {
eventFailStep(i, err, step.Name(), step.Type())
return 1
}
if err := step.Execute(); err != nil {
eventFailStep(i, err, step.Name(), step.Type())
return 1
}
elapsed := int(time.Since(start).Seconds())
eventFinishStep(i, elapsed, step.Name(), step.Type())
}
all_elapsed := int(time.Since(all_start).Seconds())
eventFinishAllSteps(len(cfg.Steps), all_elapsed)
return 0
}
func eventStartStep(idx int, step_name, step_type string) {
logger.WriteEvent(&go2chef.Event{
Event: "STEP_" + strconv.Itoa(idx) + "_START " + step_type + ":" + "'" + step_name + "'",
Component: "go2chef.cli",
})
}
func eventFailStep(idx int, err error, step_name, step_type string) {
logger.WriteEvent(&go2chef.Event{
Event: "STEP_" + strconv.Itoa(idx) + "_FAILURE " + step_type + ":" + "'" + step_name + "'",
Component: "go2chef.cli",
Message: err.Error(),
})
}
func eventFinishStep(idx, elapsed int, step_name, step_type string) {
logger.WriteEvent(&go2chef.Event{
Event: "STEP_" + strconv.Itoa(idx) + "_COMPLETE " + step_type + ":" + "'" + step_name + "'",
Component: "go2chef.cli",
Message: "completed successfully in " + strconv.Itoa(elapsed) + " second(s)",
ExtraFields: (&go2chef.ExtraLoggingFields{
StepName: step_name,
StepType: step_type,
ElapsedTime: elapsed,
}),
})
}
func eventFinishAllSteps(steps int, elapsed int) {
logger.WriteEvent(&go2chef.Event{
Event: "ALL_STEPS_COMPLETE",
Component: "go2chef.cli",
Message: strconv.Itoa(steps) + " step(s) completed successfully in " + strconv.Itoa(elapsed) + " second(s)",
ExtraFields: (&go2chef.ExtraLoggingFields{
StepName: "All_Steps",
StepCount: steps,
ElapsedTime: elapsed,
}),
})
}