Skip to content

Commit

Permalink
cli: Add -base64 option to consul kv get
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jen20 committed Jan 4, 2017
1 parent 406a868 commit b79296a
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 6 deletions.
24 changes: 18 additions & 6 deletions command/kv_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package command

import (
"bytes"
"encoding/base64"
"flag"
"fmt"
"io"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
Expand All @@ -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))
}
}
}

Expand All @@ -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
}
Expand All @@ -209,18 +217,22 @@ 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)
fmt.Fprintf(tw, "Key\t%s\n", pair.Key)
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()
}
89 changes: 89 additions & 0 deletions command/kv_get_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package command

import (
"encoding/base64"
"strings"
"testing"

Expand Down Expand Up @@ -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)
}
}
2 changes: 2 additions & 0 deletions website/source/docs/commands/kv/get.html.markdown.erb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit b79296a

Please sign in to comment.