diff --git a/command/base/command.go b/command/base/command.go index 40c578eea..2741ea2cb 100644 --- a/command/base/command.go +++ b/command/base/command.go @@ -62,6 +62,12 @@ func (c *Command) HTTPDatacenter() string { return c.datacenter.String() } +func (c *Command) HTTPStale() bool { + var stale bool + c.stale.Merge(&stale) + return stale +} + // httpFlagsClient is the list of flags that apply to HTTP connections. func (c *Command) httpFlagsClient(f *flag.FlagSet) *flag.FlagSet { if f == nil { @@ -134,6 +140,9 @@ func (c *Command) Parse(args []string) error { // Help returns the help for this flagSet. func (c *Command) Help() string { + if c.flagSet == nil { + return "" + } return c.helpFlagsFor(c.flagSet) } diff --git a/command/kv_command.go b/command/kv_command.go index 28bd71a94..5e6e0461c 100644 --- a/command/kv_command.go +++ b/command/kv_command.go @@ -3,13 +3,14 @@ package command import ( "strings" + "github.com/hashicorp/consul/command/base" "github.com/mitchellh/cli" ) // KVCommand is a Command implementation that just shows help for // the subcommands nested below it. type KVCommand struct { - Ui cli.Ui + base.Command } func (c *KVCommand) Run(args []string) int { diff --git a/command/kv_delete.go b/command/kv_delete.go index 85b30b4c8..839d2aa4b 100644 --- a/command/kv_delete.go +++ b/command/kv_delete.go @@ -1,18 +1,17 @@ package command import ( - "flag" "fmt" "strings" "github.com/hashicorp/consul/api" - "github.com/mitchellh/cli" + "github.com/hashicorp/consul/command/base" ) // KVDeleteCommand is a Command implementation that is used to delete a key or // prefix of keys from the key-value store. type KVDeleteCommand struct { - Ui cli.Ui + base.Command } func (c *KVDeleteCommand) Help() string { @@ -33,40 +32,30 @@ Usage: consul kv delete [options] KEY_OR_PREFIX This will delete the keys named "foo", "food", and "foo/bar/zip" if they existed. -` + apiOptsText + ` +` + c.Command.Help() -KV Delete Options: - - -cas Perform a Check-And-Set operation. Specifying this - value also requires the -modify-index flag to be set. - The default value is false. - - -modify-index= Unsigned integer representing the ModifyIndex of the - key. This is used in combination with the -cas flag. - - -recurse Recursively delete all keys with the path. The default - value is false. -` return strings.TrimSpace(helpText) } func (c *KVDeleteCommand) Run(args []string) int { - cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError) - cmdFlags.Usage = func() { c.Ui.Output(c.Help()) } - datacenter := cmdFlags.String("datacenter", "", "") - token := cmdFlags.String("token", "", "") - cas := cmdFlags.Bool("cas", false, "") - modifyIndex := cmdFlags.Uint64("modify-index", 0, "") - recurse := cmdFlags.Bool("recurse", false, "") - httpAddr := HTTPAddrFlag(cmdFlags) - if err := cmdFlags.Parse(args); err != nil { + f := c.Command.NewFlagSet(c) + cas := f.Bool("cas", false, + "Perform a Check-And-Set operation. Specifying this value also requires "+ + "the -modify-index flag to be set. The default value is false.") + modifyIndex := f.Uint64("modify-index", 0, + "Unsigned integer representing the ModifyIndex of the key. This is "+ + "used in combination with the -cas flag.") + recurse := f.Bool("recurse", false, + "Recursively delete all keys with the path. The default value is false.") + + if err := c.Command.Parse(args); err != nil { return 1 } key := "" // Check for arg validation - args = cmdFlags.Args() + args = f.Args() switch len(args) { case 0: key = "" @@ -109,22 +98,15 @@ func (c *KVDeleteCommand) Run(args []string) int { } // Create and test the HTTP client - conf := api.DefaultConfig() - conf.Address = *httpAddr - conf.Token = *token - client, err := api.NewClient(conf) + client, err := c.Command.HTTPClient() if err != nil { c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) return 1 } - wo := &api.WriteOptions{ - Datacenter: *datacenter, - } - switch { case *recurse: - if _, err := client.KV().DeleteTree(key, wo); err != nil { + if _, err := client.KV().DeleteTree(key, nil); err != nil { c.Ui.Error(fmt.Sprintf("Error! Did not delete prefix %s: %s", key, err)) return 1 } @@ -137,7 +119,7 @@ func (c *KVDeleteCommand) Run(args []string) int { ModifyIndex: *modifyIndex, } - success, _, err := client.KV().DeleteCAS(pair, wo) + success, _, err := client.KV().DeleteCAS(pair, nil) if err != nil { c.Ui.Error(fmt.Sprintf("Error! Did not delete key %s: %s", key, err)) return 1 @@ -150,7 +132,7 @@ func (c *KVDeleteCommand) Run(args []string) int { c.Ui.Info(fmt.Sprintf("Success! Deleted key: %s", key)) return 0 default: - if _, err := client.KV().Delete(key, wo); err != nil { + if _, err := client.KV().Delete(key, nil); err != nil { c.Ui.Error(fmt.Sprintf("Error deleting key %s: %s", key, err)) return 1 } diff --git a/command/kv_delete_test.go b/command/kv_delete_test.go index ac8572aad..518492daa 100644 --- a/command/kv_delete_test.go +++ b/command/kv_delete_test.go @@ -6,9 +6,20 @@ import ( "testing" "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/command/base" "github.com/mitchellh/cli" ) +func testKVDeleteCommand(t *testing.T) (*cli.MockUi, *KVDeleteCommand) { + ui := new(cli.MockUi) + return ui, &KVDeleteCommand{ + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, + } +} + func TestKVDeleteCommand_implements(t *testing.T) { var _ cli.Command = &KVDeleteCommand{} } @@ -18,8 +29,7 @@ func TestKVDeleteCommand_noTabs(t *testing.T) { } func TestKVDeleteCommand_Validation(t *testing.T) { - ui := new(cli.MockUi) - c := &KVDeleteCommand{Ui: ui} + ui, c := testKVDeleteCommand(t) cases := map[string]struct { args []string @@ -73,8 +83,7 @@ func TestKVDeleteCommand_Run(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVDeleteCommand{Ui: ui} + ui, c := testKVDeleteCommand(t) pair := &api.KVPair{ Key: "foo", @@ -109,8 +118,7 @@ func TestKVDeleteCommand_Recurse(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVDeleteCommand{Ui: ui} + ui, c := testKVDeleteCommand(t) keys := []string{"foo/a", "foo/b", "food"} @@ -152,8 +160,7 @@ func TestKVDeleteCommand_CAS(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVDeleteCommand{Ui: ui} + ui, c := testKVDeleteCommand(t) pair := &api.KVPair{ Key: "foo", diff --git a/command/kv_export.go b/command/kv_export.go index 77d7f4d46..bb1482d26 100644 --- a/command/kv_export.go +++ b/command/kv_export.go @@ -3,18 +3,17 @@ package command import ( "encoding/base64" "encoding/json" - "flag" "fmt" "strings" "github.com/hashicorp/consul/api" - "github.com/mitchellh/cli" + "github.com/hashicorp/consul/command/base" ) // KVExportCommand is a Command implementation that is used to export // a KV tree as JSON type KVExportCommand struct { - Ui cli.Ui + base.Command } func (c *KVExportCommand) Synopsis() string { @@ -33,29 +32,20 @@ Usage: consul kv export [KEY_OR_PREFIX] For a full list of options and examples, please see the Consul documentation. -` + apiOptsText + ` +` + c.Command.Help() -KV Export Options: - - None. -` return strings.TrimSpace(helpText) } func (c *KVExportCommand) Run(args []string) int { - cmdFlags := flag.NewFlagSet("export", flag.ContinueOnError) - - datacenter := cmdFlags.String("datacenter", "", "") - token := cmdFlags.String("token", "", "") - stale := cmdFlags.Bool("stale", false, "") - httpAddr := HTTPAddrFlag(cmdFlags) - if err := cmdFlags.Parse(args); err != nil { + f := c.Command.NewFlagSet(c) + if err := c.Command.Parse(args); err != nil { return 1 } key := "" // Check for arg validation - args = cmdFlags.Args() + args = f.Args() switch len(args) { case 0: key = "" @@ -74,18 +64,14 @@ func (c *KVExportCommand) Run(args []string) int { } // Create and test the HTTP client - conf := api.DefaultConfig() - conf.Address = *httpAddr - conf.Token = *token - client, err := api.NewClient(conf) + client, err := c.Command.HTTPClient() if err != nil { c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) return 1 } pairs, _, err := client.KV().List(key, &api.QueryOptions{ - Datacenter: *datacenter, - AllowStale: *stale, + AllowStale: c.Command.HTTPStale(), }) if err != nil { c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err)) diff --git a/command/kv_export_test.go b/command/kv_export_test.go index 9fa64982d..51dbda822 100644 --- a/command/kv_export_test.go +++ b/command/kv_export_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/command/base" "github.com/mitchellh/cli" ) @@ -15,7 +16,12 @@ func TestKVExportCommand_Run(t *testing.T) { waitForLeader(t, srv.httpAddr) ui := new(cli.MockUi) - c := &KVExportCommand{Ui: ui} + c := KVExportCommand{ + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, + } keys := map[string]string{ "foo/a": "a", diff --git a/command/kv_get.go b/command/kv_get.go index c49d983b4..dc509b243 100644 --- a/command/kv_get.go +++ b/command/kv_get.go @@ -3,20 +3,19 @@ package command import ( "bytes" "encoding/base64" - "flag" "fmt" "io" "strings" "text/tabwriter" "github.com/hashicorp/consul/api" - "github.com/mitchellh/cli" + "github.com/hashicorp/consul/command/base" ) // KVGetCommand is a Command implementation that is used to fetch the value of // a key from the key-value store. type KVGetCommand struct { - Ui cli.Ui + base.Command } func (c *KVGetCommand) Help() string { @@ -51,54 +50,39 @@ Usage: consul kv get [options] [KEY_OR_PREFIX] For a full list of options and examples, please see the Consul documentation. -` + apiOptsText + ` +` + c.Command.Help() -KV Get Options: - - -base64 Base64 encode the value. The default value is false. - - -detailed Provide additional metadata about the key in addition - to the value such as the ModifyIndex and any flags - that may have been set on the key. The default value - is false. - - -keys List keys which start with the given prefix, but not - their values. This is especially useful if you only - need the key names themselves. This option is commonly - combined with the -separator option. The default value - is false. - - -recurse Recursively look at all keys prefixed with the given - path. The default value is false. - - -separator= String to use as a separator between keys. The default - value is "/", but this option is only taken into - account when paired with the -keys flag. - -` return strings.TrimSpace(helpText) } func (c *KVGetCommand) Run(args []string) int { - cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError) - cmdFlags.Usage = func() { c.Ui.Output(c.Help()) } - datacenter := cmdFlags.String("datacenter", "", "") - token := cmdFlags.String("token", "", "") - stale := cmdFlags.Bool("stale", false, "") - detailed := cmdFlags.Bool("detailed", false, "") - keys := cmdFlags.Bool("keys", false, "") - base64encode := cmdFlags.Bool("base64", false, "") - recurse := cmdFlags.Bool("recurse", false, "") - separator := cmdFlags.String("separator", "/", "") - httpAddr := HTTPAddrFlag(cmdFlags) - if err := cmdFlags.Parse(args); err != nil { + f := c.Command.NewFlagSet(c) + detailed := f.Bool("detailed", false, + "Provide additional metadata about the key in addition to the value such "+ + "as the ModifyIndex and any flags that may have been set on the key. "+ + "The default value is false.") + keys := f.Bool("keys", false, + "List keys which start with the given prefix, but not their values. "+ + "This is especially useful if you only need the key names themselves. "+ + "This option is commonly combined with the -separator option. The default "+ + "value is false.") + base64encode := f.Bool("base64", false, + "Base64 encode the value. The default value is false.") + recurse := f.Bool("recurse", false, + "Recursively look at all keys prefixed with the given path. The default "+ + "value is false.") + separator := f.String("separator", "/", + "String to use as a separator between keys. The default value is \"/\", "+ + "but this option is only taken into account when paired with the -keys flag.") + + if err := c.Command.Parse(args); err != nil { return 1 } key := "" // Check for arg validation - args = cmdFlags.Args() + args = f.Args() switch len(args) { case 0: key = "" @@ -124,10 +108,7 @@ func (c *KVGetCommand) Run(args []string) int { } // Create and test the HTTP client - conf := api.DefaultConfig() - conf.Address = *httpAddr - conf.Token = *token - client, err := api.NewClient(conf) + client, err := c.Command.HTTPClient() if err != nil { c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) return 1 @@ -136,8 +117,7 @@ func (c *KVGetCommand) Run(args []string) int { switch { case *keys: keys, _, err := client.KV().Keys(key, *separator, &api.QueryOptions{ - Datacenter: *datacenter, - AllowStale: *stale, + AllowStale: c.Command.HTTPStale(), }) if err != nil { c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err)) @@ -151,8 +131,7 @@ func (c *KVGetCommand) Run(args []string) int { return 0 case *recurse: pairs, _, err := client.KV().List(key, &api.QueryOptions{ - Datacenter: *datacenter, - AllowStale: *stale, + AllowStale: c.Command.HTTPStale(), }) if err != nil { c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err)) @@ -184,8 +163,7 @@ func (c *KVGetCommand) Run(args []string) int { return 0 default: pair, _, err := client.KV().Get(key, &api.QueryOptions{ - Datacenter: *datacenter, - AllowStale: *stale, + AllowStale: c.Command.HTTPStale(), }) if err != nil { c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err)) diff --git a/command/kv_get_test.go b/command/kv_get_test.go index 979df1adc..773edd95a 100644 --- a/command/kv_get_test.go +++ b/command/kv_get_test.go @@ -6,9 +6,20 @@ import ( "testing" "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/command/base" "github.com/mitchellh/cli" ) +func testKVGetCommand(t *testing.T) (*cli.MockUi, *KVGetCommand) { + ui := new(cli.MockUi) + return ui, &KVGetCommand{ + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, + } +} + func TestKVGetCommand_implements(t *testing.T) { var _ cli.Command = &KVGetCommand{} } @@ -18,8 +29,7 @@ func TestKVGetCommand_noTabs(t *testing.T) { } func TestKVGetCommand_Validation(t *testing.T) { - ui := new(cli.MockUi) - c := &KVGetCommand{Ui: ui} + ui, c := testKVGetCommand(t) cases := map[string]struct { args []string @@ -61,8 +71,7 @@ func TestKVGetCommand_Run(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVGetCommand{Ui: ui} + ui, c := testKVGetCommand(t) pair := &api.KVPair{ Key: "foo", @@ -94,8 +103,7 @@ func TestKVGetCommand_Missing(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVGetCommand{Ui: ui} + _, c := testKVGetCommand(t) args := []string{ "-http-addr=" + srv.httpAddr, @@ -113,8 +121,7 @@ func TestKVGetCommand_Empty(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVGetCommand{Ui: ui} + ui, c := testKVGetCommand(t) pair := &api.KVPair{ Key: "empty", @@ -141,8 +148,7 @@ func TestKVGetCommand_Detailed(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVGetCommand{Ui: ui} + ui, c := testKVGetCommand(t) pair := &api.KVPair{ Key: "foo", @@ -184,8 +190,7 @@ func TestKVGetCommand_Keys(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVGetCommand{Ui: ui} + ui, c := testKVGetCommand(t) keys := []string{"foo/bar", "foo/baz", "foo/zip"} for _, key := range keys { @@ -218,8 +223,7 @@ func TestKVGetCommand_Recurse(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVGetCommand{Ui: ui} + ui, c := testKVGetCommand(t) keys := map[string]string{ "foo/a": "a", @@ -257,8 +261,7 @@ func TestKVGetCommand_RecurseBase64(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVGetCommand{Ui: ui} + ui, c := testKVGetCommand(t) keys := map[string]string{ "foo/a": "Hello World 1", @@ -297,8 +300,7 @@ func TestKVGetCommand_DetailedBase64(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVGetCommand{Ui: ui} + ui, c := testKVGetCommand(t) pair := &api.KVPair{ Key: "foo", diff --git a/command/kv_import.go b/command/kv_import.go index c23606744..cf8986bef 100644 --- a/command/kv_import.go +++ b/command/kv_import.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "encoding/json" "errors" - "flag" "fmt" "io" "io/ioutil" @@ -13,13 +12,13 @@ import ( "strings" "github.com/hashicorp/consul/api" - "github.com/mitchellh/cli" + "github.com/hashicorp/consul/command/base" ) // KVImportCommand is a Command implementation that is used to import // a KV tree stored as JSON type KVImportCommand struct { - Ui cli.Ui + base.Command // testStdin is the input for testing. testStdin io.Reader @@ -50,27 +49,20 @@ Usage: consul kv import [DATA] For a full list of options and examples, please see the Consul documentation. -` + apiOptsText + ` +` + c.Command.Help() -KV Import Options: - - None. -` return strings.TrimSpace(helpText) } func (c *KVImportCommand) Run(args []string) int { - cmdFlags := flag.NewFlagSet("import", flag.ContinueOnError) + f := c.Command.NewFlagSet(c) - datacenter := cmdFlags.String("datacenter", "", "") - token := cmdFlags.String("token", "", "") - httpAddr := HTTPAddrFlag(cmdFlags) - if err := cmdFlags.Parse(args); err != nil { + if err := c.Command.Parse(args); err != nil { return 1 } // Check for arg validation - args = cmdFlags.Args() + args = f.Args() data, err := c.dataFromArgs(args) if err != nil { c.Ui.Error(fmt.Sprintf("Error! %s", err)) @@ -78,10 +70,7 @@ func (c *KVImportCommand) Run(args []string) int { } // Create and test the HTTP client - conf := api.DefaultConfig() - conf.Address = *httpAddr - conf.Token = *token - client, err := api.NewClient(conf) + client, err := c.Command.HTTPClient() if err != nil { c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) return 1 @@ -106,12 +95,7 @@ func (c *KVImportCommand) Run(args []string) int { Value: value, } - wo := &api.WriteOptions{ - Datacenter: *datacenter, - Token: *token, - } - - if _, err := client.KV().Put(pair, wo); err != nil { + if _, err := client.KV().Put(pair, nil); err != nil { c.Ui.Error(fmt.Sprintf("Error! Failed writing data for key %s: %s", pair.Key, err)) return 1 } diff --git a/command/kv_import_test.go b/command/kv_import_test.go index 817cd788f..9f8326ad3 100644 --- a/command/kv_import_test.go +++ b/command/kv_import_test.go @@ -4,6 +4,7 @@ import ( "strings" "testing" + "github.com/hashicorp/consul/command/base" "github.com/mitchellh/cli" ) @@ -27,7 +28,10 @@ func TestKVImportCommand_Run(t *testing.T) { ui := new(cli.MockUi) c := &KVImportCommand{ - Ui: ui, + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, testStdin: strings.NewReader(json), } diff --git a/command/kv_put.go b/command/kv_put.go index 5e5b333b1..7bbb56c25 100644 --- a/command/kv_put.go +++ b/command/kv_put.go @@ -3,7 +3,6 @@ package command import ( "bytes" "encoding/base64" - "flag" "fmt" "io" "io/ioutil" @@ -11,13 +10,13 @@ import ( "strings" "github.com/hashicorp/consul/api" - "github.com/mitchellh/cli" + "github.com/hashicorp/consul/command/base" ) // KVPutCommand is a Command implementation that is used to write data to the // key-value store. type KVPutCommand struct { - Ui cli.Ui + base.Command // testStdin is the input for testing. testStdin io.Reader @@ -57,62 +56,45 @@ Usage: consul kv put [options] KEY [DATA] Additional flags and more advanced use cases are detailed below. -` + apiOptsText + ` +` + c.Command.Help() -KV Put Options: - - -acquire Obtain a lock on the key. If the key does not exist, - this operation will create the key and obtain the - lock. The session must already exist and be specified - via the -session flag. The default value is false. - - -base64 Treat the data as base 64 encoded. The default value - is false. - - -cas Perform a Check-And-Set operation. Specifying this - value also requires the -modify-index flag to be set. - The default value is false. - - -flags= Unsigned integer value to assign to this key-value - pair. This value is not read by Consul, so clients can - use this value however makes sense for their use case. - The default value is 0 (no flags). - - -modify-index= Unsigned integer representing the ModifyIndex of the - key. This is used in combination with the -cas flag. - - -release Forfeit the lock on the key at the given path. This - requires the -session flag to be set. The key must be - held by the session in order to be unlocked. The - default value is false. - - -session= User-defined identifer for this session as a string. - This is commonly used with the -acquire and -release - operations to build robust locking, but it can be set - on any key. The default value is empty (no session). -` return strings.TrimSpace(helpText) } func (c *KVPutCommand) Run(args []string) int { - cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError) - cmdFlags.Usage = func() { c.Ui.Output(c.Help()) } - httpAddr := HTTPAddrFlag(cmdFlags) - datacenter := cmdFlags.String("datacenter", "", "") - token := cmdFlags.String("token", "", "") - cas := cmdFlags.Bool("cas", false, "") - flags := cmdFlags.Uint64("flags", 0, "") - base64encoded := cmdFlags.Bool("base64", false, "") - modifyIndex := cmdFlags.Uint64("modify-index", 0, "") - session := cmdFlags.String("session", "", "") - acquire := cmdFlags.Bool("acquire", false, "") - release := cmdFlags.Bool("release", false, "") - if err := cmdFlags.Parse(args); err != nil { + f := c.Command.NewFlagSet(c) + cas := f.Bool("cas", false, + "Perform a Check-And-Set operation. Specifying this value also "+ + "requires the -modify-index flag to be set. The default value "+ + "is false.") + flags := f.Uint64("flags", 0, + "Unsigned integer value to assign to this key-value pair. This "+ + "value is not read by Consul, so clients can use this value however "+ + "makes sense for their use case. The default value is 0 (no flags).") + base64encoded := f.Bool("base64", false, + "Treat the data as base 64 encoded. The default value is false.") + modifyIndex := f.Uint64("modify-index", 0, + "Unsigned integer representing the ModifyIndex of the key. This is "+ + "used in combination with the -cas flag.") + session := f.String("session", "", + "User-defined identifer for this session as a string. This is commonly "+ + "used with the -acquire and -release operations to build robust locking, "+ + "but it can be set on any key. The default value is empty (no session).") + acquire := f.Bool("acquire", false, + "Obtain a lock on the key. If the key does not exist, this operation "+ + "will create the key and obtain the lock. The session must already "+ + "exist and be specified via the -session flag. The default value is false.") + release := f.Bool("release", false, + "Forfeit the lock on the key at the given path. This requires the "+ + "-session flag to be set. The key must be held by the session in order to "+ + "be unlocked. The default value is false.") + + if err := c.Command.Parse(args); err != nil { return 1 } // Check for arg validation - args = cmdFlags.Args() + args = f.Args() key, data, err := c.dataFromArgs(args) if err != nil { c.Ui.Error(fmt.Sprintf("Error! %s", err)) @@ -140,10 +122,7 @@ func (c *KVPutCommand) Run(args []string) int { } // Create and test the HTTP client - conf := api.DefaultConfig() - conf.Address = *httpAddr - conf.Token = *token - client, err := api.NewClient(conf) + client, err := c.Command.HTTPClient() if err != nil { c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) return 1 @@ -157,14 +136,9 @@ func (c *KVPutCommand) Run(args []string) int { Session: *session, } - wo := &api.WriteOptions{ - Datacenter: *datacenter, - Token: *token, - } - switch { case *cas: - ok, _, err := client.KV().CAS(pair, wo) + ok, _, err := client.KV().CAS(pair, nil) if err != nil { c.Ui.Error(fmt.Sprintf("Error! Did not write to %s: %s", key, err)) return 1 @@ -177,7 +151,7 @@ func (c *KVPutCommand) Run(args []string) int { c.Ui.Info(fmt.Sprintf("Success! Data written to: %s", key)) return 0 case *acquire: - ok, _, err := client.KV().Acquire(pair, wo) + ok, _, err := client.KV().Acquire(pair, nil) if err != nil { c.Ui.Error(fmt.Sprintf("Error! Failed writing data: %s", err)) return 1 @@ -190,7 +164,7 @@ func (c *KVPutCommand) Run(args []string) int { c.Ui.Info(fmt.Sprintf("Success! Lock acquired on: %s", key)) return 0 case *release: - ok, _, err := client.KV().Release(pair, wo) + ok, _, err := client.KV().Release(pair, nil) if err != nil { c.Ui.Error(fmt.Sprintf("Error! Failed writing data: %s", key)) return 1 @@ -203,7 +177,7 @@ func (c *KVPutCommand) Run(args []string) int { c.Ui.Info(fmt.Sprintf("Success! Lock released on: %s", key)) return 0 default: - if _, err := client.KV().Put(pair, wo); err != nil { + if _, err := client.KV().Put(pair, nil); err != nil { c.Ui.Error(fmt.Sprintf("Error! Failed writing data: %s", err)) return 1 } diff --git a/command/kv_put_test.go b/command/kv_put_test.go index f691719b1..66a442c06 100644 --- a/command/kv_put_test.go +++ b/command/kv_put_test.go @@ -11,20 +11,30 @@ import ( "testing" "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/command/base" "github.com/mitchellh/cli" ) +func testKVPutCommand(t *testing.T) (*cli.MockUi, *KVPutCommand) { + ui := new(cli.MockUi) + return ui, &KVPutCommand{ + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, + } +} + func TestKVPutCommand_implements(t *testing.T) { var _ cli.Command = &KVPutCommand{} } func TestKVPutCommand_noTabs(t *testing.T) { - assertNoTabs(t, new(KVPutCommand)) + assertNoTabs(t, new(KVDeleteCommand)) } func TestKVPutCommand_Validation(t *testing.T) { - ui := new(cli.MockUi) - c := &KVPutCommand{Ui: ui} + ui, c := testKVPutCommand(t) cases := map[string]struct { args []string @@ -78,8 +88,7 @@ func TestKVPutCommand_Run(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVPutCommand{Ui: ui} + ui, c := testKVPutCommand(t) args := []string{ "-http-addr=" + srv.httpAddr, @@ -106,8 +115,7 @@ func TestKVPutCommand_RunEmptyDataQuoted(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVPutCommand{Ui: ui} + ui, c := testKVPutCommand(t) args := []string{ "-http-addr=" + srv.httpAddr, @@ -134,8 +142,7 @@ func TestKVPutCommand_RunBase64(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVPutCommand{Ui: ui} + ui, c := testKVPutCommand(t) const encodedString = "aGVsbG8gd29ybGQK" @@ -170,8 +177,7 @@ func TestKVPutCommand_File(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVPutCommand{Ui: ui} + ui, c := testKVPutCommand(t) f, err := ioutil.TempFile("", "kv-put-command-file") if err != nil { @@ -203,8 +209,7 @@ func TestKVPutCommand_File(t *testing.T) { } func TestKVPutCommand_FileNoExist(t *testing.T) { - ui := new(cli.MockUi) - c := &KVPutCommand{Ui: ui} + ui, c := testKVPutCommand(t) args := []string{ "foo", "@/nope/definitely/not-a-real-file.txt", @@ -228,11 +233,8 @@ func TestKVPutCommand_Stdin(t *testing.T) { stdinR, stdinW := io.Pipe() - ui := new(cli.MockUi) - c := &KVPutCommand{ - Ui: ui, - testStdin: stdinR, - } + ui, c := testKVPutCommand(t) + c.testStdin = stdinR go func() { stdinW.Write([]byte("bar")) @@ -264,8 +266,7 @@ func TestKVPutCommand_NegativeVal(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVPutCommand{Ui: ui} + ui, c := testKVPutCommand(t) args := []string{ "-http-addr=" + srv.httpAddr, @@ -292,8 +293,7 @@ func TestKVPutCommand_Flags(t *testing.T) { defer srv.Shutdown() waitForLeader(t, srv.httpAddr) - ui := new(cli.MockUi) - c := &KVPutCommand{Ui: ui} + ui, c := testKVPutCommand(t) args := []string{ "-http-addr=" + srv.httpAddr, @@ -330,8 +330,7 @@ func TestKVPutCommand_CAS(t *testing.T) { t.Fatalf("err: %#v", err) } - ui := new(cli.MockUi) - c := &KVPutCommand{Ui: ui} + ui, c := testKVPutCommand(t) args := []string{ "-http-addr=" + srv.httpAddr, diff --git a/commands.go b/commands.go index 5ad8f5f36..6d4ca3e23 100644 --- a/commands.go +++ b/commands.go @@ -105,37 +105,55 @@ func init() { "kv": func() (cli.Command, error) { return &command.KVCommand{ - Ui: ui, + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetNone, + }, }, nil }, "kv delete": func() (cli.Command, error) { return &command.KVDeleteCommand{ - Ui: ui, + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, }, nil }, "kv get": func() (cli.Command, error) { return &command.KVGetCommand{ - Ui: ui, + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, }, nil }, "kv put": func() (cli.Command, error) { return &command.KVPutCommand{ - Ui: ui, + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, }, nil }, "kv export": func() (cli.Command, error) { return &command.KVExportCommand{ - Ui: ui, + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, }, nil }, "kv import": func() (cli.Command, error) { return &command.KVImportCommand{ - Ui: ui, + Command: base.Command{ + Ui: ui, + Flags: base.FlagSetHTTP, + }, }, nil },