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  }