Update write command
This commit is contained in:
parent
f7c9fe6d20
commit
f7782df97e
222
command/write.go
222
command/write.go
|
@ -6,149 +6,145 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/helper/kv-builder"
|
||||
"github.com/hashicorp/vault/meta"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
// Ensure we are implementing the right interfaces.
|
||||
var _ cli.Command = (*WriteCommand)(nil)
|
||||
var _ cli.CommandAutocomplete = (*WriteCommand)(nil)
|
||||
|
||||
// WriteCommand is a Command that puts data into the Vault.
|
||||
type WriteCommand struct {
|
||||
meta.Meta
|
||||
*BaseCommand
|
||||
|
||||
// The fields below can be overwritten for tests
|
||||
testStdin io.Reader
|
||||
flagForce bool
|
||||
|
||||
testStdin io.Reader // for tests
|
||||
}
|
||||
|
||||
func (c *WriteCommand) Synopsis() string {
|
||||
return "Writes data, configuration, and secrets"
|
||||
}
|
||||
|
||||
func (c *WriteCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: vault write [options] PATH [DATA K=V...]
|
||||
|
||||
Writes data to Vault at the given path. The data can be credentials, secrets,
|
||||
configuration, or arbitrary data. The specific behavior of this command is
|
||||
determined at the backend mounted at the path.
|
||||
|
||||
Data is specified as "key=value" pairs. If the value begins with an "@", then
|
||||
it is loaded from a file. If the value is "-", Vault will read the value from
|
||||
stdin.
|
||||
|
||||
Persist data in the static secret backend:
|
||||
|
||||
$ vault write secret/my-secret foo=bar
|
||||
|
||||
Create a new encryption key in the transit backend:
|
||||
|
||||
$ vault write -f transit/keys/my-key
|
||||
|
||||
Upload an AWS IAM policy from a file on disk:
|
||||
|
||||
$ vault write aws/roles/ops policy=@policy.json
|
||||
|
||||
Configure access to Consul by providing an access token:
|
||||
|
||||
$ echo $MY_TOKEN | vault write consul/config/access token=-
|
||||
|
||||
For a full list of examples and paths, please see the documentation that
|
||||
corresponds to the secret backend in use.
|
||||
|
||||
` + c.Flags().Help()
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *WriteCommand) Flags() *FlagSets {
|
||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputField | FlagSetOutputFormat)
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
f.BoolVar(&BoolVar{
|
||||
Name: "force",
|
||||
Aliases: []string{"f"},
|
||||
Target: &c.flagForce,
|
||||
Default: false,
|
||||
EnvVar: "",
|
||||
Completion: complete.PredictNothing,
|
||||
Usage: "Allow the operation to continue with no key=value pairs. This " +
|
||||
"allows writing to keys that do not need or expect data.",
|
||||
})
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *WriteCommand) AutocompleteArgs() complete.Predictor {
|
||||
// Return an anything predictor here. Without a way to access help
|
||||
// information, we don't know what paths we could write to.
|
||||
return complete.PredictAnything
|
||||
}
|
||||
|
||||
func (c *WriteCommand) AutocompleteFlags() complete.Flags {
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *WriteCommand) Run(args []string) int {
|
||||
var field, format string
|
||||
var force bool
|
||||
flags := c.Meta.FlagSet("write", meta.FlagSetDefault)
|
||||
flags.StringVar(&format, "format", "table", "")
|
||||
flags.StringVar(&field, "field", "", "")
|
||||
flags.BoolVar(&force, "force", false, "")
|
||||
flags.BoolVar(&force, "f", false, "")
|
||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := flags.Parse(args); err != nil {
|
||||
f := c.Flags()
|
||||
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
args = flags.Args()
|
||||
if len(args) < 1 {
|
||||
c.Ui.Error("write requires a path")
|
||||
flags.Usage()
|
||||
return 1
|
||||
}
|
||||
|
||||
if len(args) < 2 && !force {
|
||||
c.Ui.Error("write expects at least two arguments; use -f to perform the write anyways")
|
||||
flags.Usage()
|
||||
return 1
|
||||
}
|
||||
|
||||
path := args[0]
|
||||
if path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
|
||||
data, err := c.parseData(args[1:])
|
||||
path, kvs, err := extractPath(f.Args())
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error loading data: %s", err))
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
if len(kvs) == 0 && !c.flagForce {
|
||||
c.UI.Error("Missing DATA! Specify at least one K=V pair or use -force.")
|
||||
return 1
|
||||
}
|
||||
|
||||
// Pull our fake stdin if needed
|
||||
stdin := (io.Reader)(os.Stdin)
|
||||
if c.testStdin != nil {
|
||||
stdin = c.testStdin
|
||||
}
|
||||
|
||||
data, err := parseArgsData(stdin, kvs)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Failed to parse K=V data: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error initializing client: %s", err))
|
||||
c.UI.Error(err.Error())
|
||||
return 2
|
||||
}
|
||||
|
||||
secret, err := client.Logical().Write(path, data)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error writing data to %s: %s", path, err))
|
||||
return 1
|
||||
c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err))
|
||||
return 2
|
||||
}
|
||||
|
||||
if secret == nil {
|
||||
// Don't output anything if people aren't using the "human" output
|
||||
if format == "table" {
|
||||
c.Ui.Output(fmt.Sprintf("Success! Data written to: %s", path))
|
||||
// Don't output anything unless using the "table" format
|
||||
if c.flagFormat == "table" {
|
||||
c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Handle single field output
|
||||
if field != "" {
|
||||
return PrintRawField(c.Ui, secret, field)
|
||||
if c.flagField != "" {
|
||||
return PrintRawField(c.UI, secret, c.flagField)
|
||||
}
|
||||
|
||||
return OutputSecret(c.Ui, format, secret)
|
||||
}
|
||||
|
||||
func (c *WriteCommand) parseData(args []string) (map[string]interface{}, error) {
|
||||
var stdin io.Reader = os.Stdin
|
||||
if c.testStdin != nil {
|
||||
stdin = c.testStdin
|
||||
}
|
||||
|
||||
builder := &kvbuilder.Builder{Stdin: stdin}
|
||||
if err := builder.Add(args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return builder.Map(), nil
|
||||
}
|
||||
|
||||
func (c *WriteCommand) Synopsis() string {
|
||||
return "Write secrets or configuration into Vault"
|
||||
}
|
||||
|
||||
func (c *WriteCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: vault write [options] path [data]
|
||||
|
||||
Write data (secrets or configuration) into Vault.
|
||||
|
||||
Write sends data into Vault at the given path. The behavior of the write is
|
||||
determined by the backend at the given path. For example, writing to
|
||||
"aws/policy/ops" will create an "ops" IAM policy for the AWS backend
|
||||
(configuration), but writing to "consul/foo" will write a value directly into
|
||||
Consul at that key. Check the documentation of the logical backend you're
|
||||
using for more information on key structure.
|
||||
|
||||
Data is sent via additional arguments in "key=value" pairs. If value begins
|
||||
with an "@", then it is loaded from a file. Write expects data in the file to
|
||||
be in JSON format. If you want to start the value with a literal "@", then
|
||||
prefix the "@" with a slash: "\@".
|
||||
|
||||
General Options:
|
||||
` + meta.GeneralOptionsUsage() + `
|
||||
Write Options:
|
||||
|
||||
-f | -force Force the write to continue without any data values
|
||||
specified. This allows writing to keys that do not
|
||||
need or expect any fields to be specified.
|
||||
|
||||
-format=table The format for output. By default it is a whitespace-
|
||||
delimited table. This can also be json or yaml.
|
||||
|
||||
-field=field If included, the raw value of the specified field
|
||||
will be output raw to stdout.
|
||||
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *WriteCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictNothing
|
||||
}
|
||||
|
||||
func (c *WriteCommand) AutocompleteFlags() complete.Flags {
|
||||
return complete.Flags{
|
||||
"-force": complete.PredictNothing,
|
||||
"-format": predictFormat,
|
||||
"-field": complete.PredictNothing,
|
||||
}
|
||||
return OutputSecret(c.UI, c.flagFormat, secret)
|
||||
}
|
||||
|
|
|
@ -2,271 +2,246 @@ package command
|
|||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/http"
|
||||
"github.com/hashicorp/vault/meta"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := http.TestServer(t, core)
|
||||
defer ln.Close()
|
||||
func testWriteCommand(tb testing.TB) (*cli.MockUi, *WriteCommand) {
|
||||
tb.Helper()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &WriteCommand{
|
||||
Meta: meta.Meta{
|
||||
ClientToken: token,
|
||||
Ui: ui,
|
||||
ui := cli.NewMockUi()
|
||||
return ui, &WriteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-address", addr,
|
||||
"secret/foo",
|
||||
"value=bar",
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
resp, err := client.Logical().Read("secret/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if resp.Data["value"] != "bar" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite_arbitrary(t *testing.T) {
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := http.TestServer(t, core)
|
||||
defer ln.Close()
|
||||
func TestWriteCommand_Run(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
stdinR, stdinW := io.Pipe()
|
||||
ui := new(cli.MockUi)
|
||||
c := &WriteCommand{
|
||||
Meta: meta.Meta{
|
||||
ClientToken: token,
|
||||
Ui: ui,
|
||||
cases := []struct {
|
||||
name string
|
||||
args []string
|
||||
out string
|
||||
code int
|
||||
}{
|
||||
{
|
||||
"empty_path",
|
||||
nil,
|
||||
"Missing PATH!",
|
||||
1,
|
||||
},
|
||||
|
||||
testStdin: stdinR,
|
||||
}
|
||||
|
||||
go func() {
|
||||
stdinW.Write([]byte(`{"foo":"bar"}`))
|
||||
stdinW.Close()
|
||||
}()
|
||||
|
||||
args := []string{
|
||||
"-address", addr,
|
||||
"secret/foo",
|
||||
"-",
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
resp, err := client.Logical().Read("secret/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if resp.Data["foo"] != "bar" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite_escaped(t *testing.T) {
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := http.TestServer(t, core)
|
||||
defer ln.Close()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &WriteCommand{
|
||||
Meta: meta.Meta{
|
||||
ClientToken: token,
|
||||
Ui: ui,
|
||||
{
|
||||
"empty_kvs",
|
||||
[]string{"secret/write/foo"},
|
||||
"Missing DATA!",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"force_kvs",
|
||||
[]string{"-force", "auth/token/create"},
|
||||
"token",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"force_f_kvs",
|
||||
[]string{"-f", "auth/token/create"},
|
||||
"token",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"kvs_no_value",
|
||||
[]string{"secret/write/foo", "foo"},
|
||||
"Failed to parse K=V data",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"single_value",
|
||||
[]string{"secret/write/foo", "foo=bar"},
|
||||
"Success!",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"multi_value",
|
||||
[]string{"secret/write/foo", "foo=bar", "zip=zap"},
|
||||
"Success!",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"field",
|
||||
[]string{
|
||||
"-field", "token_renewable",
|
||||
"auth/token/create", "display_name=foo",
|
||||
},
|
||||
"false",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"field_not_found",
|
||||
[]string{
|
||||
"-field", "not-a-real-field",
|
||||
"auth/token/create", "display_name=foo",
|
||||
},
|
||||
"not present in secret",
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-address", addr,
|
||||
"secret/foo",
|
||||
"value=\\@bar",
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, closer := testVaultServer(t)
|
||||
defer closer()
|
||||
|
||||
ui, cmd := testWriteCommand(t)
|
||||
cmd.client = client
|
||||
|
||||
code := cmd.Run(tc.args)
|
||||
if code != tc.code {
|
||||
t.Errorf("expected %d to be %d", code, tc.code)
|
||||
}
|
||||
|
||||
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
|
||||
if !strings.Contains(combined, tc.out) {
|
||||
t.Errorf("expected %q to contain %q", combined, tc.out)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
t.Run("stdin_full", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
resp, err := client.Logical().Read("secret/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
client, closer := testVaultServer(t)
|
||||
defer closer()
|
||||
|
||||
if resp.Data["value"] != "@bar" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite_file(t *testing.T) {
|
||||
tf, err := ioutil.TempFile("", "vault")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
tf.Write([]byte(`{"foo":"bar"}`))
|
||||
tf.Close()
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := http.TestServer(t, core)
|
||||
defer ln.Close()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &WriteCommand{
|
||||
Meta: meta.Meta{
|
||||
ClientToken: token,
|
||||
Ui: ui,
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-address", addr,
|
||||
"secret/foo",
|
||||
"@" + tf.Name(),
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
resp, err := client.Logical().Read("secret/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if resp.Data["foo"] != "bar" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite_fileValue(t *testing.T) {
|
||||
tf, err := ioutil.TempFile("", "vault")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
tf.Write([]byte("foo"))
|
||||
tf.Close()
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := http.TestServer(t, core)
|
||||
defer ln.Close()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &WriteCommand{
|
||||
Meta: meta.Meta{
|
||||
ClientToken: token,
|
||||
Ui: ui,
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-address", addr,
|
||||
"secret/foo",
|
||||
"value=@" + tf.Name(),
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
resp, err := client.Logical().Read("secret/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if resp.Data["value"] != "foo" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite_Output(t *testing.T) {
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := http.TestServer(t, core)
|
||||
defer ln.Close()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &WriteCommand{
|
||||
Meta: meta.Meta{
|
||||
ClientToken: token,
|
||||
Ui: ui,
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-address", addr,
|
||||
"auth/token/create",
|
||||
"display_name=foo",
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
if !strings.Contains(ui.OutputWriter.String(), "Key") {
|
||||
t.Fatalf("bad: %s", ui.OutputWriter.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite_force(t *testing.T) {
|
||||
core, _, token := vault.TestCoreUnsealed(t)
|
||||
ln, addr := http.TestServer(t, core)
|
||||
defer ln.Close()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
c := &WriteCommand{
|
||||
Meta: meta.Meta{
|
||||
ClientToken: token,
|
||||
Ui: ui,
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-address", addr,
|
||||
"-force",
|
||||
"sys/rotate",
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
stdinR, stdinW := io.Pipe()
|
||||
go func() {
|
||||
stdinW.Write([]byte(`{"foo":"bar"}`))
|
||||
stdinW.Close()
|
||||
}()
|
||||
|
||||
_, cmd := testWriteCommand(t)
|
||||
cmd.client = client
|
||||
cmd.testStdin = stdinR
|
||||
|
||||
code := cmd.Run([]string{
|
||||
"secret/write/stdin_full", "-",
|
||||
})
|
||||
if code != 0 {
|
||||
t.Fatalf("expected 0 to be %d", code)
|
||||
}
|
||||
|
||||
secret, err := client.Logical().Read("secret/write/stdin_full")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret == nil || secret.Data == nil {
|
||||
t.Fatal("expected secret to have data")
|
||||
}
|
||||
if exp, act := "bar", secret.Data["foo"].(string); exp != act {
|
||||
t.Errorf("expected %q to be %q", act, exp)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("stdin_value", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, closer := testVaultServer(t)
|
||||
defer closer()
|
||||
|
||||
stdinR, stdinW := io.Pipe()
|
||||
go func() {
|
||||
stdinW.Write([]byte("bar"))
|
||||
stdinW.Close()
|
||||
}()
|
||||
|
||||
_, cmd := testWriteCommand(t)
|
||||
cmd.client = client
|
||||
cmd.testStdin = stdinR
|
||||
|
||||
code := cmd.Run([]string{
|
||||
"secret/write/stdin_value", "foo=-",
|
||||
})
|
||||
if code != 0 {
|
||||
t.Fatalf("expected 0 to be %d", code)
|
||||
}
|
||||
|
||||
secret, err := client.Logical().Read("secret/write/stdin_value")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret == nil || secret.Data == nil {
|
||||
t.Fatal("expected secret to have data")
|
||||
}
|
||||
if exp, act := "bar", secret.Data["foo"].(string); exp != act {
|
||||
t.Errorf("expected %q to be %q", act, exp)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("integration", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, closer := testVaultServer(t)
|
||||
defer closer()
|
||||
|
||||
_, cmd := testWriteCommand(t)
|
||||
cmd.client = client
|
||||
|
||||
code := cmd.Run([]string{
|
||||
"secret/write/integration", "foo=bar", "zip=zap",
|
||||
})
|
||||
if code != 0 {
|
||||
t.Fatalf("expected 0 to be %d", code)
|
||||
}
|
||||
|
||||
secret, err := client.Logical().Read("secret/write/integration")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret == nil || secret.Data == nil {
|
||||
t.Fatal("expected secret to have data")
|
||||
}
|
||||
if exp, act := "bar", secret.Data["foo"].(string); exp != act {
|
||||
t.Errorf("expected %q to be %q", act, exp)
|
||||
}
|
||||
if exp, act := "zap", secret.Data["zip"].(string); exp != act {
|
||||
t.Errorf("expected %q to be %q", act, exp)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("communication_failure", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, closer := testVaultServerBad(t)
|
||||
defer closer()
|
||||
|
||||
ui, cmd := testWriteCommand(t)
|
||||
cmd.client = client
|
||||
|
||||
code := cmd.Run([]string{
|
||||
"foo/bar", "a=b",
|
||||
})
|
||||
if exp := 2; code != exp {
|
||||
t.Errorf("expected %d to be %d", code, exp)
|
||||
}
|
||||
|
||||
expected := "Error writing data to foo/bar: "
|
||||
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
|
||||
if !strings.Contains(combined, expected) {
|
||||
t.Errorf("expected %q to contain %q", combined, expected)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("no_tabs", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, cmd := testWriteCommand(t)
|
||||
assertNoTabs(t, cmd)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue