summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-08-27 09:15:47 +0200
committerMartin Polden <mpolden@mpolden.no>2021-08-27 10:06:14 +0200
commitf733b31e93cb1c430313d8344b5c084102d5f72b (patch)
tree36750fd2f44efa6533c9cae1e7c9852321605467 /client
parent4e5b62fedee054dc2b06d2fd82e722009a72f3c0 (diff)
vespa config get/set
Diffstat (limited to 'client')
-rw-r--r--client/go/cmd/command_tester.go18
-rw-r--r--client/go/cmd/config.go138
-rw-r--r--client/go/cmd/config_test.go17
-rw-r--r--client/go/cmd/root.go7
-rw-r--r--client/go/cmd/target.go31
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
}