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.
This commit is contained in:
James Nugent 2017-01-04 15:39:19 -06:00
parent 406a868277
commit b79296ac70
3 changed files with 109 additions and 6 deletions

View File

@ -2,6 +2,7 @@ package command
import ( import (
"bytes" "bytes"
"encoding/base64"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@ -54,6 +55,8 @@ Usage: consul kv get [options] [KEY_OR_PREFIX]
KV Get Options: KV Get Options:
-base64 Base64 encode the value. The default value is false.
-detailed Provide additional metadata about the key in addition -detailed Provide additional metadata about the key in addition
to the value such as the ModifyIndex and any flags to the value such as the ModifyIndex and any flags
that may have been set on the key. The default value 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, "") stale := cmdFlags.Bool("stale", false, "")
detailed := cmdFlags.Bool("detailed", false, "") detailed := cmdFlags.Bool("detailed", false, "")
keys := cmdFlags.Bool("keys", false, "") keys := cmdFlags.Bool("keys", false, "")
base64encode := cmdFlags.Bool("base64", false, "")
recurse := cmdFlags.Bool("recurse", false, "") recurse := cmdFlags.Bool("recurse", false, "")
separator := cmdFlags.String("separator", "/", "") separator := cmdFlags.String("separator", "/", "")
httpAddr := HTTPAddrFlag(cmdFlags) httpAddr := HTTPAddrFlag(cmdFlags)
@ -158,7 +162,7 @@ func (c *KVGetCommand) Run(args []string) int {
for i, pair := range pairs { for i, pair := range pairs {
if *detailed { if *detailed {
var b bytes.Buffer 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)) c.Ui.Error(fmt.Sprintf("Error rendering KV pair: %s", err))
return 1 return 1
} }
@ -169,7 +173,11 @@ func (c *KVGetCommand) Run(args []string) int {
c.Ui.Info("") c.Ui.Info("")
} }
} else { } 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 { if *detailed {
var b bytes.Buffer 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)) c.Ui.Error(fmt.Sprintf("Error rendering KV pair: %s", err))
return 1 return 1
} }
@ -209,7 +217,7 @@ func (c *KVGetCommand) Synopsis() string {
return "Retrieves or lists data from the KV store" 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) tw := tabwriter.NewWriter(w, 0, 2, 6, ' ', 0)
fmt.Fprintf(tw, "CreateIndex\t%d\n", pair.CreateIndex) fmt.Fprintf(tw, "CreateIndex\t%d\n", pair.CreateIndex)
fmt.Fprintf(tw, "Flags\t%d\n", pair.Flags) 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, "LockIndex\t%d\n", pair.LockIndex)
fmt.Fprintf(tw, "ModifyIndex\t%d\n", pair.ModifyIndex) fmt.Fprintf(tw, "ModifyIndex\t%d\n", pair.ModifyIndex)
if pair.Session == "" { if pair.Session == "" {
fmt.Fprintf(tw, "Session\t-\n") fmt.Fprint(tw, "Session\t-\n")
} else { } else {
fmt.Fprintf(tw, "Session\t%s\n", pair.Session) 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() return tw.Flush()
} }

View File

@ -1,6 +1,7 @@
package command package command
import ( import (
"encoding/base64"
"strings" "strings"
"testing" "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)
}
}

View File

@ -24,6 +24,8 @@ Usage: `consul kv get [options] [KEY_OR_PREFIX]`
#### KV Get Options #### 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 * `-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. value such as the ModifyIndex and any flags that may have been set on the key.
The default value is false. The default value is false.