diff options
author | Martin Polden <mpolden@mpolden.no> | 2021-08-27 09:15:47 +0200 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2021-08-27 10:06:14 +0200 |
commit | f733b31e93cb1c430313d8344b5c084102d5f72b (patch) | |
tree | 36750fd2f44efa6533c9cae1e7c9852321605467 /client | |
parent | 4e5b62fedee054dc2b06d2fd82e722009a72f3c0 (diff) |
vespa config get/set
Diffstat (limited to 'client')
-rw-r--r-- | client/go/cmd/command_tester.go | 18 | ||||
-rw-r--r-- | client/go/cmd/config.go | 138 | ||||
-rw-r--r-- | client/go/cmd/config_test.go | 17 | ||||
-rw-r--r-- | client/go/cmd/root.go | 7 | ||||
-rw-r--r-- | client/go/cmd/target.go | 31 |
5 files changed, 168 insertions, 43 deletions
diff --git a/client/go/cmd/command_tester.go b/client/go/cmd/command_tester.go index 6078167df40..cbd169dfb4b 100644 --- a/client/go/cmd/command_tester.go +++ b/client/go/cmd/command_tester.go @@ -9,20 +9,32 @@ import ( "io/ioutil" "log" "net/http" + "os" "strconv" "testing" "time" + "github.com/logrusorgru/aurora" + "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/vespa-engine/vespa/util" ) func executeCommand(t *testing.T, client *mockHttpClient, args []string, moreArgs []string) (standardout string) { - util.ActiveHttpClient = client + if client != nil { + util.ActiveHttpClient = client + } + + // Never print colors in tests + color = aurora.NewAurora(false) + + // Use a separate config dir for each test + os.Setenv("VESPA_CLI_HOME", t.TempDir()) + viper.Reset() // Reset config in case tests manipulate it - // Reset - persistent flags in Cobra persists over tests + // Reset to default target - persistent flags in Cobra persists over tests log.SetOutput(bytes.NewBufferString("")) - rootCmd.SetArgs([]string{"status", "-t", ""}) + rootCmd.SetArgs([]string{"status", "-t", "local"}) rootCmd.Execute() b := bytes.NewBufferString("") diff --git a/client/go/cmd/config.go b/client/go/cmd/config.go index 15f6e623a33..6a0b11545e5 100644 --- a/client/go/cmd/config.go +++ b/client/go/cmd/config.go @@ -5,61 +5,147 @@ package cmd import ( + "fmt" "log" "os" "path/filepath" + "strings" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/vespa-engine/vespa/util" +) + +const ( + configName = "config" + configType = "yaml" ) func init() { rootCmd.AddCommand(configCmd) + configCmd.AddCommand(setConfigCmd) + configCmd.AddCommand(getConfigCmd) } var configCmd = &cobra.Command{ Use: "config", Short: "Configure the Vespa command", - Long: `TODO`, + Long: "Get and set options for Vespa commands. This is an alternative to always specifying flags", Run: func(cmd *cobra.Command, args []string) { + // Root command does nothing + cmd.Help() + os.Exit(1) }, } -func readConfig() { - // TODO: Make ~/.vespa a directory so that we can put other files there as well - home, err := os.UserHomeDir() - configName := ".vespa" - configType := "yaml" +var setConfigCmd = &cobra.Command{ + Use: "set option value", + Short: "Set a configuration option.", + Example: "vespa config set target cloud", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + if err := setOption(args[0], args[1]); err != nil { + log.Print(err) + } else { + writeConfig() + } + }, +} +var getConfigCmd = &cobra.Command{ + Use: "get option", + Short: "Get a configuration option", + Example: "vespa config get target", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + option := args[0] + value, err := getOption(option) + if err != nil { + value = color.Faint("<unset>").String() + } else { + value = color.Cyan(value).String() + } + log.Printf("%s = %s", option, value) + }, +} + +func configDir(application string) (string, error) { + home := os.Getenv("VESPA_CLI_HOME") + if home == "" { + var err error + home, err = os.UserHomeDir() + if err != nil { + return "", err + } + } + return filepath.Join(home, ".vespa", application), nil +} + +func bindFlagToConfig(option string, command *cobra.Command) { + viper.BindPFlag(option, command.PersistentFlags().Lookup(option)) +} + +func readConfig() { + configDir, err := configDir("") cobra.CheckErr(err) - viper.AddConfigPath(home) - viper.SetConfigType(configType) + viper.SetConfigName(configName) + viper.SetConfigType(configType) + viper.AddConfigPath(configDir) viper.AutomaticEnv() + viper.BindPFlag("target", deployCmd.PersistentFlags().Lookup("target")) + + err = viper.ReadInConfig() + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + return // Fine + } + cobra.CheckErr(err) +} + +func getOption(option string) (string, error) { + value := viper.GetString(option) + if value == "" { + return "", fmt.Errorf("no such option: %q", option) + } + return value, nil +} - viper.ReadInConfig() +func setOption(option, value string) error { + if option != "target" { + return fmt.Errorf("invalid option: %q", option) + } + switch value { + case "local", "cloud": + viper.Set(option, value) + return nil + } + if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { + viper.Set(option, value) + return nil + } + return fmt.Errorf("invalid value for option %q: %q", option, value) } -// WIP: Not used yet func writeConfig() { - //viper.BindPFlag("container-target", rootCmd.PersistentFlags().Lookup("container-target")) - //viper.SetDefault("container-target", "http://127.0.0.1:8080") - - home, _ := os.UserHomeDir() - configName := ".vespa" - configType := "yaml" - - // Viper bug: WriteConfig() will not create the file if missing - configPath := filepath.Join(home, configName+"."+configType) - _, statErr := os.Stat(configPath) - if !os.IsExist(statErr) { - if _, createErr := os.Create(configPath); createErr != nil { - log.Printf("Warning: Can not remember flag parameters: %s", color.Red(createErr)) + configDir, err := configDir("") + if err != nil { + log.Fatalf("Could not decide config directory: %s", color.Red(err)) + } + + if !util.PathExists(configDir) { + if err := os.MkdirAll(configDir, 0700); err != nil { + log.Fatalf("Could not create %s: %s", color.Cyan(configDir), color.Red(err)) + } + } + + configFile := filepath.Join(configDir, configName+"."+configType) + if !util.PathExists(configFile) { + if _, err := os.Create(configFile); err != nil { + log.Fatalf("Could not create %s: %s", color.Cyan(configFile), color.Red(err)) } } - writeErr := viper.WriteConfig() - if writeErr != nil { - log.Printf("Could not write config: %s", color.Red(writeErr)) + if err := viper.WriteConfig(); err != nil { + log.Printf("Could not write config: %s", color.Red(err)) } } diff --git a/client/go/cmd/config_test.go b/client/go/cmd/config_test.go new file mode 100644 index 00000000000..b51e7f012e5 --- /dev/null +++ b/client/go/cmd/config_test.go @@ -0,0 +1,17 @@ +package cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConfig(t *testing.T) { + assert.Equal(t, "invalid option: \"foo\"\n", executeCommand(t, nil, []string{"config", "set", "foo", "bar"}, nil)) + assert.Equal(t, "foo = <unset>\n", executeCommand(t, nil, []string{"config", "get", "foo"}, nil)) + assert.Equal(t, "target = local\n", executeCommand(t, nil, []string{"config", "get", "target"}, nil)) + assert.Equal(t, "", executeCommand(t, nil, []string{"config", "set", "target", "cloud"}, nil)) + assert.Equal(t, "", executeCommand(t, nil, []string{"config", "set", "target", "local"}, nil)) + assert.Equal(t, "", executeCommand(t, nil, []string{"config", "set", "target", "http://127.0.0.1:8080"}, nil)) + assert.Equal(t, "", executeCommand(t, nil, []string{"config", "set", "target", "https://127.0.0.1"}, nil)) +} diff --git a/client/go/cmd/root.go b/client/go/cmd/root.go index 8c7ab2da12b..89dff2ccf85 100644 --- a/client/go/cmd/root.go +++ b/client/go/cmd/root.go @@ -15,10 +15,9 @@ import ( ) var ( - // flags + // global flags // TODO: add timeout flag // TODO: add flag to show http request made - targetArgument string rootCmd = &cobra.Command{ Use: "vespa", @@ -41,9 +40,5 @@ func init() { cobra.OnInitialize(readConfig) } -func addTargetFlag(command *cobra.Command) { - command.PersistentFlags().StringVarP(&targetArgument, "target", "t", "local", "The name or URL of the recipient of this command") -} - // Execute executes the root command. func Execute() error { return rootCmd.Execute() } diff --git a/client/go/cmd/target.go b/client/go/cmd/target.go index 09e63bc1d3b..79d3d6f32e8 100644 --- a/client/go/cmd/target.go +++ b/client/go/cmd/target.go @@ -7,8 +7,14 @@ package cmd import ( "log" "strings" + + "github.com/spf13/cobra" ) +const flagName = "target" + +var targetArgument string + type target struct { deploy string query string @@ -23,6 +29,11 @@ const ( documentContext context = 2 ) +func addTargetFlag(command *cobra.Command) { + command.PersistentFlags().StringVarP(&targetArgument, flagName, "t", "local", "The name or URL of the recipient of this command") + bindFlagToConfig(flagName, command) +} + func deployTarget() string { return getTarget(deployContext).deploy } @@ -36,27 +47,31 @@ func documentTarget() string { } func getTarget(targetContext context) *target { - if strings.HasPrefix(targetArgument, "http") { + targetValue, err := getOption(flagName) + if err != nil { + log.Fatalf("a valid target must be specified") + } + if strings.HasPrefix(targetValue, "http") { // TODO: Add default ports if missing switch targetContext { case deployContext: return &target{ - deploy: targetArgument, + deploy: targetValue, } case queryContext: return &target{ - query: targetArgument, + query: targetValue, } case documentContext: return &target{ - document: targetArgument, + document: targetValue, } } } // Otherwise, target is a name - if targetArgument == "" || targetArgument == "local" { + if targetValue == "" || targetValue == "local" { return &target{ deploy: "http://127.0.0.1:19071", query: "http://127.0.0.1:8080", @@ -64,10 +79,10 @@ func getTarget(targetContext context) *target { } } - if targetArgument == "cloud" { - return nil // TODO + if targetValue == "cloud" { + panic("cloud target is not implemented") } - log.Printf("Unknown target '%s': Use %s, %s or an URL", color.Red(targetArgument), color.Cyan("local"), color.Cyan("cloud")) + log.Printf("Unknown target '%s': Use %s, %s or an URL", color.Red(targetValue), color.Cyan("local"), color.Cyan("cloud")) return nil } |