From b79296ac703b17fc856a2cd6f91f34eece2a8fd8 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Wed, 4 Jan 2017 15:39:19 -0600 Subject: [PATCH] cli: Add -base64 option to `consul kv get` This commit adds a `-base64` option to the `consul kv get` command, which base 64 encodes the output such that it can be processed by terminal tools in the event that the data is binary. The flag defaults to false. --- command/kv_get.go | 24 +++-- command/kv_get_test.go | 89 +++++++++++++++++++ .../docs/commands/kv/get.html.markdown.erb | 2 + 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/command/kv_get.go b/command/kv_get.go index 1a54b37769e7..c49d983b4801 100644 --- a/command/kv_get.go +++ b/command/kv_get.go @@ -2,6 +2,7 @@ package command import ( "bytes" + "encoding/base64" "flag" "fmt" "io" @@ -54,6 +55,8 @@ Usage: consul kv get [options] [KEY_OR_PREFIX] 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 @@ -84,6 +87,7 @@ func (c *KVGetCommand) Run(args []string) int { 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) @@ -158,7 +162,7 @@ func (c *KVGetCommand) Run(args []string) int { for i, pair := range pairs { if *detailed { var b bytes.Buffer - if err := prettyKVPair(&b, pair); err != nil { + if err := prettyKVPair(&b, pair, *base64encode); err != nil { c.Ui.Error(fmt.Sprintf("Error rendering KV pair: %s", err)) return 1 } @@ -169,7 +173,11 @@ func (c *KVGetCommand) Run(args []string) int { c.Ui.Info("") } } else { - c.Ui.Info(fmt.Sprintf("%s:%s", pair.Key, pair.Value)) + if *base64encode { + c.Ui.Info(fmt.Sprintf("%s:%s", pair.Key, base64.StdEncoding.EncodeToString(pair.Value))) + } else { + c.Ui.Info(fmt.Sprintf("%s:%s", pair.Key, pair.Value)) + } } } @@ -191,7 +199,7 @@ func (c *KVGetCommand) Run(args []string) int { if *detailed { var b bytes.Buffer - if err := prettyKVPair(&b, pair); err != nil { + if err := prettyKVPair(&b, pair, *base64encode); err != nil { c.Ui.Error(fmt.Sprintf("Error rendering KV pair: %s", err)) return 1 } @@ -209,7 +217,7 @@ func (c *KVGetCommand) Synopsis() string { return "Retrieves or lists data from the KV store" } -func prettyKVPair(w io.Writer, pair *api.KVPair) error { +func prettyKVPair(w io.Writer, pair *api.KVPair, base64EncodeValue bool) error { tw := tabwriter.NewWriter(w, 0, 2, 6, ' ', 0) fmt.Fprintf(tw, "CreateIndex\t%d\n", pair.CreateIndex) fmt.Fprintf(tw, "Flags\t%d\n", pair.Flags) @@ -217,10 +225,14 @@ func prettyKVPair(w io.Writer, pair *api.KVPair) error { fmt.Fprintf(tw, "LockIndex\t%d\n", pair.LockIndex) fmt.Fprintf(tw, "ModifyIndex\t%d\n", pair.ModifyIndex) if pair.Session == "" { - fmt.Fprintf(tw, "Session\t-\n") + fmt.Fprint(tw, "Session\t-\n") } else { fmt.Fprintf(tw, "Session\t%s\n", pair.Session) } - fmt.Fprintf(tw, "Value\t%s", pair.Value) + if base64EncodeValue { + fmt.Fprintf(tw, "Value\t%s", base64.StdEncoding.EncodeToString(pair.Value)) + } else { + fmt.Fprintf(tw, "Value\t%s", pair.Value) + } return tw.Flush() } diff --git a/command/kv_get_test.go b/command/kv_get_test.go index 815b4d72d15c..979df1adc650 100644 --- a/command/kv_get_test.go +++ b/command/kv_get_test.go @@ -1,6 +1,7 @@ package command import ( + "encoding/base64" "strings" "testing" @@ -250,3 +251,91 @@ func TestKVGetCommand_Recurse(t *testing.T) { } } } + +func TestKVGetCommand_RecurseBase64(t *testing.T) { + srv, client := testAgentWithAPIClient(t) + defer srv.Shutdown() + waitForLeader(t, srv.httpAddr) + + ui := new(cli.MockUi) + c := &KVGetCommand{Ui: ui} + + keys := map[string]string{ + "foo/a": "Hello World 1", + "foo/b": "Hello World 2", + "foo/c": "Hello World 3", + } + for k, v := range keys { + pair := &api.KVPair{Key: k, Value: []byte(v)} + if _, err := client.KV().Put(pair, nil); err != nil { + t.Fatalf("err: %#v", err) + } + } + + args := []string{ + "-http-addr=" + srv.httpAddr, + "-recurse", + "-base64", + "foo", + } + + code := c.Run(args) + if code != 0 { + t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) + } + + output := ui.OutputWriter.String() + for key, value := range keys { + if !strings.Contains(output, key+":"+base64.StdEncoding.EncodeToString([]byte(value))) { + t.Fatalf("bad %#v missing %q", output, key) + } + } +} + +func TestKVGetCommand_DetailedBase64(t *testing.T) { + srv, client := testAgentWithAPIClient(t) + defer srv.Shutdown() + waitForLeader(t, srv.httpAddr) + + ui := new(cli.MockUi) + c := &KVGetCommand{Ui: ui} + + pair := &api.KVPair{ + Key: "foo", + Value: []byte("bar"), + } + _, err := client.KV().Put(pair, nil) + if err != nil { + t.Fatalf("err: %#v", err) + } + + args := []string{ + "-http-addr=" + srv.httpAddr, + "-detailed", + "-base64", + "foo", + } + + code := c.Run(args) + if code != 0 { + t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) + } + + output := ui.OutputWriter.String() + for _, key := range []string{ + "CreateIndex", + "LockIndex", + "ModifyIndex", + "Flags", + "Session", + "Value", + } { + if !strings.Contains(output, key) { + t.Fatalf("bad %#v, missing %q", output, key) + } + } + + if !strings.Contains(output, base64.StdEncoding.EncodeToString([]byte("bar"))) { + t.Fatalf("bad %#v, value is not base64 encoded", output) + } +} diff --git a/website/source/docs/commands/kv/get.html.markdown.erb b/website/source/docs/commands/kv/get.html.markdown.erb index 4b316cb3190e..49c9b6ca33fd 100644 --- a/website/source/docs/commands/kv/get.html.markdown.erb +++ b/website/source/docs/commands/kv/get.html.markdown.erb @@ -24,6 +24,8 @@ Usage: `consul kv get [options] [KEY_OR_PREFIX]` #### KV Get Options +* `-base64` - Base 64 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.