diff --git a/cmd/local/local.go b/cmd/local/local.go index 381f0725b..4cc15db0f 100644 --- a/cmd/local/local.go +++ b/cmd/local/local.go @@ -61,11 +61,7 @@ func NewCommand() *cobra.Command { localCmd.Flags().StringVar(&metaphorBranch, "metaphor-branch", "main", "metaphro application branch") localCmd.Flags().StringVar(&gitOpsBranch, "gitops-branch", "main", "version/branch used on git clone - former: version-gitops flag") localCmd.Flags().StringVar(&gitOpsRepo, "gitops-repo", "gitops", "") - localCmd.Flags().BoolVar(&enableConsole, "enable-console", true, "If hand-off screen will be presented on a browser UI") - // todo: - //initCmd.Flags().StringP("config", "c", "", "File to be imported to bootstrap configs") - //viper.BindPFlag("config.file", currentCommand.Flags().Lookup("config-load")) return localCmd } diff --git a/cmd/local/prerun.go b/cmd/local/prerun.go index 504eb4714..47534eecf 100644 --- a/cmd/local/prerun.go +++ b/cmd/local/prerun.go @@ -80,7 +80,7 @@ func validateLocal(cmd *cobra.Command, args []string) error { // convert available disk size to GB format availableDiskSize := float64(free) / humanize.GByte - if availableDiskSize > pkg.MinimumAvailableDiskSize { + if availableDiskSize < pkg.MinimumAvailableDiskSize { return fmt.Errorf( "there is not enough space to proceed with the installation, a minimum of %d GB is required to proceed", pkg.MinimumAvailableDiskSize, diff --git a/cmd/root.go b/cmd/root.go index ff8b74736..cdc88133b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "github.com/kubefirst/kubefirst/cmd/local" + "github.com/kubefirst/kubefirst/configs" "os" "github.com/kubefirst/kubefirst/internal/progressPrinter" @@ -16,8 +17,11 @@ var rootCmd = &cobra.Command{ Long: `kubefirst management cluster installer provisions an open source application delivery platform in under an hour. checkout the docs at docs.kubefirst.io.`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + // wire viper config for flags for all commands + return configs.InitializeViperConfig(cmd) + }, Run: func(cmd *cobra.Command, args []string) { - //log.Println(viper.Get("name")) fmt.Println("To learn more about kubefirst, run:") fmt.Println(" kubefirst help") }, @@ -38,11 +42,5 @@ func Execute() { func init() { cobra.OnInitialize() - - // Cobra also supports local flags, which will only run, when this action is called directly. - rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - - // todo: temporary, move it into CLI tree rootCmd.AddCommand(local.NewCommand()) - } diff --git a/configs/viperConfig.go b/configs/viperConfig.go new file mode 100644 index 000000000..04a4c6a2f --- /dev/null +++ b/configs/viperConfig.go @@ -0,0 +1,99 @@ +package configs + +// code from: https://github.com/carolynvs/stingoftheviper + +import ( + "fmt" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" + "strings" +) + +// it follows Viper precedence: +// 1. explicit call to Set +// 2. flag +// 3. env +// 4. config +// 5. key/value store +// 6. default + +// the input file that is able to provide is called kubefirst.yaml, and should be at the root folder, where the user has +// it's Kubefirst binary. Following the flag name convention is enough to have a Kubefirst config file. + +// FLAGS VARIABLE +// example loading values from flags: +// go run . command-name --admin-email user@example.com + +// ENVIRONMENT VARIABLE +// example loading environment variables: +// export KUBEFIRST_CLOUD=k3d +// command line commands loads the values from the environment variable and override the command flag. + +// YAML +// example of a YAML Kubefirst file: +// admin-email: user@example.com +// cloud: k3d +// command line commands loads the value from the kubefirst.yaml and override the command flags. + +const ( + // The name of our config file, without the file extension because viper supports many different config file languages. + defaultConfigFilename = "kubefirst" + + // The environment variable prefix of all environment variables bound to our command line flags. + // For example, --number is bound to STING_NUMBER. + envPrefix = "KUBEFIRST" +) + +func InitializeViperConfig(cmd *cobra.Command) error { + v := viper.New() + + // Set the base name of the config file, without the file extension. + v.SetConfigName(defaultConfigFilename) + v.SetConfigType("yaml") + + // Set as many paths as you like where viper should look for the + // config file. We are only looking in the current working directory. + v.AddConfigPath(".") + + // Attempt to read the config file, gracefully ignoring errors + // caused by a config file not being found. Return an error + // if we cannot parse the config file. + if err := v.ReadInConfig(); err != nil { + // It's okay if there isn't a config file + if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + return err + } + } + + // When we bind flags to environment variables expect that the + // environment variables are prefixed, e.g. a flag like --number + // binds to an environment variable STING_NUMBER. This helps + // avoid conflicts. + v.SetEnvPrefix(envPrefix) + + // Bind to environment variables + // Works great for simple config names, but needs help for names + // like --favorite-color which we fix in the bindFlags function + v.AutomaticEnv() + + // Bind the current command's flags to viper + bindFlags(cmd, v) + + return nil +} + +// Bind each cobra flag to its associated viper configuration (config file and environment variable) +func bindFlags(cmd *cobra.Command, v *viper.Viper) { + cmd.Flags().VisitAll(func(f *pflag.Flag) { + // Environment variables can't have dashes in them, so bind them to their equivalent + // keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR + v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + + // Apply the viper config value to the flag when the flag is not set and viper has a value + if !f.Changed && v.IsSet(f.Name) { + val := v.Get(f.Name) + cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)) + } + }) +}