get.porter.sh/porter@v1.3.0/pkg/cli/config.go (about) 1 package cli 2 3 import ( 4 "fmt" 5 "strings" 6 7 "get.porter.sh/porter/pkg/config" 8 "github.com/spf13/cobra" 9 "github.com/spf13/pflag" 10 "github.com/spf13/viper" 11 ) 12 13 // LoadHierarchicalConfig loads data with the following precedence: 14 // * User set flag Flags (highest) 15 // * Environment variables where --flag is assumed to be PORTER_FLAG 16 // * Config file 17 // * Flag default (lowest) 18 func LoadHierarchicalConfig(cmd *cobra.Command) config.DataStoreLoaderFunc { 19 bindCobraFlagsToViper := func(v *viper.Viper) { 20 // Apply the configuration file value to the flag when the flag is not set 21 flags := cmd.Flags() 22 flags.VisitAll(func(f *pflag.Flag) { 23 viperKey := f.Name 24 25 // Check if a viper key has been explicitly configured 26 if altKey, ok := f.Annotations["viper-key"]; ok { 27 if len(altKey) > 0 { 28 viperKey = altKey[0] 29 } 30 } 31 32 if f.Changed { 33 // Apply the flag to viper 34 v.Set(viperKey, getViperValue(flags, f)) 35 } else if v.IsSet(viperKey) { 36 // Apply viper to the flag 37 val := getFlagValue(v, viperKey) 38 _ = flags.Set(f.Name, val) 39 } 40 }) 41 } 42 return config.LoadFromViper(config.BindViperToEnvironmentVariables, bindCobraFlagsToViper) 43 } 44 45 func getFlagValue(v *viper.Viper, key string) string { 46 val := v.Get(key) 47 48 switch typedValue := val.(type) { 49 case []interface{}: 50 // slice flags should be set using a,b,c not [a,b,c] 51 items := make([]string, len(typedValue)) 52 for i, item := range typedValue { 53 items[i] = fmt.Sprintf("%v", item) 54 } 55 return strings.Join(items, ",") 56 default: 57 return fmt.Sprintf("%v", val) 58 } 59 } 60 61 func getViperValue(flags *pflag.FlagSet, f *pflag.Flag) interface{} { 62 var out interface{} 63 var err error 64 65 // This is not an exhaustive list, if we need more types supported, it'll panic, and then we can add it. 66 flagType := f.Value.Type() 67 switch flagType { 68 case "int": 69 out, err = flags.GetInt(f.Name) 70 case "int64": 71 out, err = flags.GetInt64(f.Name) 72 case "string": 73 out, err = flags.GetString(f.Name) 74 case "bool": 75 out, err = flags.GetBool(f.Name) 76 case "stringSlice": 77 out, err = flags.GetStringSlice(f.Name) 78 case "stringArray": 79 out, err = flags.GetStringArray(f.Name) 80 default: 81 panic(fmt.Errorf("unsupported type for conversion between flag %s and viper configuration: %T", f.Name, flagType)) 82 } 83 84 if err != nil { 85 panic(fmt.Errorf("error parsing config key %s as %T: %w", f.Name, flagType, err)) 86 } 87 88 return out 89 }