2018-06-27 09:02:49 +01:00
|
|
|
// Copyright (C) 2018 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package process
|
|
|
|
|
|
|
|
import (
|
2018-07-09 23:43:32 +01:00
|
|
|
"context"
|
2018-06-27 09:02:49 +01:00
|
|
|
"flag"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
homedir "github.com/mitchellh/go-homedir"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/pflag"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
)
|
|
|
|
|
2018-07-19 01:09:38 +01:00
|
|
|
// ReadFlags will read in and bind flags for viper and pflag
|
|
|
|
func readFlags() {
|
|
|
|
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
|
|
|
pflag.Parse()
|
|
|
|
err := viper.BindPFlags(pflag.CommandLine)
|
|
|
|
if err != nil {
|
|
|
|
log.Print("error parsing command line flags into viper:", err)
|
2018-06-27 09:02:49 +01:00
|
|
|
}
|
2018-07-19 01:09:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// get default config folder
|
|
|
|
func configPath() string {
|
|
|
|
home, _ := homedir.Dir()
|
|
|
|
return filepath.Join(home, ".storj")
|
|
|
|
}
|
|
|
|
|
|
|
|
// get default config file
|
|
|
|
func defaultConfigFile(name string) string {
|
|
|
|
return filepath.Join(configPath(), name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateConfig() error {
|
|
|
|
err := viper.WriteConfigAs(defaultConfigFile("main.json"))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConfigEnvironment will read in command line flags, set the name of the config file,
|
|
|
|
// then look for configs in the current working directory and in $HOME/.storj
|
|
|
|
func ConfigEnvironment() (*viper.Viper, error) {
|
|
|
|
viper.SetEnvPrefix("storj")
|
|
|
|
viper.AutomaticEnv()
|
|
|
|
viper.SetConfigName("main")
|
|
|
|
viper.AddConfigPath(".")
|
|
|
|
viper.AddConfigPath(configPath())
|
|
|
|
|
|
|
|
// Override default config with a specific config
|
|
|
|
cfgFile := flag.String("config", "", "config file")
|
|
|
|
generate := flag.Bool("generate", false, "generate a default config in ~/.storj")
|
|
|
|
|
|
|
|
// if that file exists, set it as the config instead of reading in from default locations
|
|
|
|
if *cfgFile != "" && fileExists(*cfgFile) {
|
|
|
|
viper.SetConfigFile(*cfgFile)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := viper.ReadInConfig()
|
|
|
|
|
2018-06-27 09:02:49 +01:00
|
|
|
if err != nil {
|
2018-07-19 01:09:38 +01:00
|
|
|
log.Print("could not read config file; defaulting to command line flags for configuration")
|
|
|
|
}
|
|
|
|
|
|
|
|
readFlags()
|
|
|
|
|
|
|
|
if *generate == true {
|
|
|
|
err := generateConfig()
|
|
|
|
if err != nil {
|
|
|
|
log.Print("unable to generate config file.", err)
|
|
|
|
}
|
2018-06-27 09:02:49 +01:00
|
|
|
}
|
2018-07-19 01:09:38 +01:00
|
|
|
|
|
|
|
v := viper.GetViper()
|
|
|
|
return v, nil
|
2018-06-27 09:02:49 +01:00
|
|
|
}
|
|
|
|
|
2018-07-19 01:09:38 +01:00
|
|
|
// check if file exists, handle error correctly if it doesn't
|
2018-07-17 15:12:35 +01:00
|
|
|
func fileExists(path string) bool {
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
log.Fatalf("failed to check for file existence: %v", err)
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2018-06-27 09:02:49 +01:00
|
|
|
// Execute runs a *cobra.Command and sets up Storj-wide process configuration
|
|
|
|
// like a configuration file and logging.
|
|
|
|
func Execute(cmd *cobra.Command) {
|
|
|
|
cobra.OnInitialize(func() {
|
2018-07-19 01:09:38 +01:00
|
|
|
_, err := ConfigEnvironment()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("error configuring environment", err)
|
2018-06-27 09:02:49 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
Must(cmd.Execute())
|
|
|
|
}
|
|
|
|
|
2018-07-09 23:43:32 +01:00
|
|
|
// Main runs a Service
|
2018-07-19 01:09:38 +01:00
|
|
|
func Main(configFn func() (*viper.Viper, error), s ...Service) error {
|
|
|
|
if _, err := configFn(); err != nil {
|
2018-07-16 20:22:34 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-09 23:43:32 +01:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
2018-07-16 20:22:34 +01:00
|
|
|
defer cancel()
|
|
|
|
|
2018-07-09 23:43:32 +01:00
|
|
|
errors := make(chan error, len(s))
|
|
|
|
|
|
|
|
for _, service := range s {
|
|
|
|
go func(ctx context.Context, s Service, ch <-chan error) {
|
|
|
|
errors <- CtxService(s)(&cobra.Command{}, pflag.Args())
|
|
|
|
}(ctx, service, errors)
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil
|
|
|
|
case err := <-errors:
|
|
|
|
return err
|
|
|
|
}
|
2018-06-27 09:02:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Must checks for errors
|
|
|
|
func Must(err error) {
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|