diff --git a/command/read.go b/command/read.go index 62604da5a..8f94bcba8 100644 --- a/command/read.go +++ b/command/read.go @@ -12,8 +12,10 @@ type ReadCommand struct { func (c *ReadCommand) Run(args []string) int { var format string + var field string flags := c.Meta.FlagSet("read", FlagSetDefault) flags.StringVar(&format, "format", "table", "") + flags.StringVar(&field, "field", "", "") flags.Usage = func() { c.Ui.Error(c.Help()) } if err := flags.Parse(args); err != nil { return 1 @@ -46,6 +48,18 @@ func (c *ReadCommand) Run(args []string) int { return 1 } + // Handle single field output + if field != "" { + if val, ok := secret.Data[field]; ok { + c.Ui.Output(val.(string)) + return 0 + } else { + c.Ui.Error(fmt.Sprintf( + "Field %s not present in secret", field)) + return 1 + } + } + return OutputSecret(c.Ui, format, secret) } @@ -84,6 +98,9 @@ Read Options: -format=table The format for output. By default it is a whitespace- delimited table. This can also be json. + -field=field If included, the raw value of the specified field + will be output raw to stdout. + ` return strings.TrimSpace(helpText) } diff --git a/command/read_test.go b/command/read_test.go index 548f7e96e..ac46bf5ce 100644 --- a/command/read_test.go +++ b/command/read_test.go @@ -51,3 +51,86 @@ func TestRead_notFound(t *testing.T) { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } } + +func TestRead_field(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := http.TestServer(t, core) + defer ln.Close() + + ui := new(cli.MockUi) + c := &ReadCommand{ + Meta: Meta{ + ClientToken: token, + Ui: ui, + }, + } + + args := []string{ + "-address", addr, + "-field", "value", + "secret/foo", + } + + // Run once so the client is setup, ignore errors + c.Run(args) + + // Get the client so we can write data + client, err := c.Client() + if err != nil { + t.Fatalf("err: %s", err) + } + + data := map[string]interface{}{"value": "bar"} + if _, err := client.Logical().Write("secret/foo", data); err != nil { + t.Fatalf("err: %s", err) + } + + // Run the read + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + output := ui.OutputWriter.String() + if output != "bar\n" { + t.Fatalf("unexpectd output:\n%s", output) + } +} + +func TestRead_field_notFound(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := http.TestServer(t, core) + defer ln.Close() + + ui := new(cli.MockUi) + c := &ReadCommand{ + Meta: Meta{ + ClientToken: token, + Ui: ui, + }, + } + + args := []string{ + "-address", addr, + "-field", "nope", + "secret/foo", + } + + // Run once so the client is setup, ignore errors + c.Run(args) + + // Get the client so we can write data + client, err := c.Client() + if err != nil { + t.Fatalf("err: %s", err) + } + + data := map[string]interface{}{"value": "bar"} + if _, err := client.Logical().Write("secret/foo", data); err != nil { + t.Fatalf("err: %s", err) + } + + // Run the read + if code := c.Run(args); code != 1 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +}