experimenting with cascading config

2 changes unrelated to cascading config:

1. using cobra.Command.RunE instead of just Run - similar behavior to log + exit,
but simpler. Also prints error message, then usage, then error again which is
nice.

2. getting serveCmd.Flags() once, and re-using it. More readable methinks.

For cascading config, I'm trying to set things up so that we can define the
flags once, and have them work from a config file, environment variable, or
command line. I also don't want references to viper scattered throughout our
code, so I want to set the config at startup, and not touch it from then on.

The idea here is to iterate through the set up command line flags, and then set
their values from viper which knows about the commmand line, the environment,
and the config file, and will pick the right one. The command line flags were
set pointing to the correct config values in the pilosa object, so the right
values will be set automatically.
This commit is contained in:
Matt Jaffee 2017-03-09 19:44:07 -06:00
parent 371665f459
commit e1a582ad2c

View file

@ -6,9 +6,11 @@ import (
"os"
"os/signal"
"runtime/pprof"
"strings"
"time"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"
"github.com/pilosa/pilosa/server"
@ -24,22 +26,22 @@ var serveCmd = &cobra.Command{
It will load existing data from the configured
directory, and start listening client connections
on the configured port.`,
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
setupViper(cmd.Flags())
fmt.Println(viper.AllSettings())
serve.Server.Handler.Version = Version
fmt.Fprintf(serve.Stderr, "Pilosa %s, build time %s\n", Version, BuildTime)
// Parse command line arguments.
if err := serve.SetupConfig(args); err != nil {
fmt.Fprintln(serve.Stderr, err)
os.Exit(2)
return err
}
// Start CPU profiling.
if serve.CPUProfile != "" {
f, err := os.Create(serve.CPUProfile)
if err != nil {
fmt.Fprintf(serve.Stderr, "create cpu profile: %v", err)
os.Exit(1)
return fmt.Errorf("create cpu profile: %v", err)
}
defer f.Close()
@ -54,8 +56,7 @@ on the configured port.`,
// Execute the program.
if err := serve.Run(); err != nil {
fmt.Fprintln(serve.Stderr, err)
os.Exit(1)
return err
}
// First SIGKILL causes server to shut down gracefully.
@ -68,22 +69,50 @@ on the configured port.`,
go func() { <-c; os.Exit(1) }()
if err := serve.Close(); err != nil {
fmt.Fprintln(serve.Stderr, err)
os.Exit(1)
return err
}
return nil
},
}
func init() {
serveCmd.Flags().StringVarP(&serve.ConfigPath, "config", "c", "", "Configuration file to read from")
serveCmd.Flags().StringVarP(&serve.CPUProfile, "cpuprofile", "", "", "Where to store CPU profile")
serveCmd.Flags().DurationVarP(&serve.CPUTime, "cputime", "", 30*time.Second, "CPU profile duration")
flags := serveCmd.Flags()
err := viper.BindPFlags(serveCmd.Flags())
if err != nil {
log.Fatalf("Error binding server flags: %v", err)
}
flags.StringVarP(&serve.ConfigPath, "config", "c", "", "Configuration file to read from")
flags.StringVarP(&serve.CPUProfile, "cpu-profile", "", "", "Where to store CPU profile")
flags.DurationVarP(&serve.CPUTime, "cpu-time", "", 30*time.Second, "CPU profile duration")
RootCmd.AddCommand(serveCmd)
}
func setupViper(flags *flag.FlagSet) {
// add cmd line flag def to viper
err := viper.BindPFlags(flags)
if err != nil {
log.Fatalf("Error binding server flags: %v", err)
}
// add env to viper
viper.SetEnvPrefix("PILOSA")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
viper.AutomaticEnv()
c := viper.GetString("config")
// add config file to viper
if c != "" {
viper.AddConfigPath(c)
err := viper.ReadInConfig()
if err != nil {
log.Printf("Couldn't read config from '%s'", c)
}
}
flags.VisitAll(func(f *flag.Flag) {
log.Printf("Now visiting: %v with value '%s'", f.Name, f.Value)
value := viper.GetString(f.Name)
log.Printf("Setting to value: '%v'", value)
err := f.Value.Set(value)
if err != nil {
log.Printf("Error setting %s to '%s': %v", f.Name, value, err)
}
})
}