command/write

This commit is contained in:
Mitchell Hashimoto
2015-03-15 20:35:33 -07:00
parent 80798a3869
commit 8093f94c65
5 changed files with 107 additions and 52 deletions

View File

@@ -3,49 +3,25 @@ package api
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"strings"
"github.com/mitchellh/mapstructure"
) )
// Secret is the structure returned for every secret within Vault. // Secret is the structure returned for every secret within Vault.
type Secret struct { type Secret struct {
VaultId string `mapstructure:"vault_id"` VaultId string `json:"vault_id"`
Renewable bool Renewable bool
LeaseDuration int `mapstructure:"lease_duration"` LeaseDuration int `json:"lease_duration"`
LeaseDurationMax int `mapstructure:"lease_duration_max"` LeaseDurationMax int `json:"lease_duration_max"`
Data map[string]interface{} `mapstructure:"data"` Data map[string]interface{} `json:"data"`
} }
// ParseSecret is used to parse a secret value from JSON from an io.Reader. // ParseSecret is used to parse a secret value from JSON from an io.Reader.
func ParseSecret(r io.Reader) (*Secret, error) { func ParseSecret(r io.Reader) (*Secret, error) {
// First decode the JSON into a map[string]interface{} // First decode the JSON into a map[string]interface{}
var raw map[string]interface{} var secret Secret
dec := json.NewDecoder(r) dec := json.NewDecoder(r)
if err := dec.Decode(&raw); err != nil { if err := dec.Decode(&secret); err != nil {
return nil, err return nil, err
} }
// Use mapstructure to get as much as possible return &secret, nil
var result Secret
var metadata mapstructure.Metadata
mdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Metadata: &metadata,
Result: &result,
})
if err != nil {
return nil, err
}
if err := mdec.Decode(raw); err != nil {
return nil, err
}
// Delete the keys we decoded from the raw value, then set that
// raw value as the resulting data.
for _, k := range metadata.Keys {
delete(raw, strings.ToLower(k))
}
result.Data = raw
return &result, nil
} }

View File

@@ -4,13 +4,13 @@ import (
"strings" "strings"
) )
// GetCommand is a Command that gets data from the Vault. // ReadCommand is a Command that gets data from the Vault.
type GetCommand struct { type ReadCommand struct {
Meta Meta
} }
func (c *GetCommand) Run(args []string) int { func (c *ReadCommand) Run(args []string) int {
flags := c.Meta.FlagSet("put", FlagSetDefault) flags := c.Meta.FlagSet("read", FlagSetDefault)
flags.Usage = func() { c.Ui.Error(c.Help()) } flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil { if err := flags.Parse(args); err != nil {
return 1 return 1
@@ -19,11 +19,11 @@ func (c *GetCommand) Run(args []string) int {
return 0 return 0
} }
func (c *GetCommand) Synopsis() string { func (c *ReadCommand) Synopsis() string {
return "Get data or secrets from Vault" return "Read data or secrets from Vault"
} }
func (c *GetCommand) Help() string { func (c *ReadCommand) Help() string {
helpText := ` helpText := `
Usage: vault get [options] path Usage: vault get [options] path

View File

@@ -1,35 +1,66 @@
package command package command
import ( import (
"fmt"
"strings" "strings"
) )
// PutCommand is a Command that puts data into the Vault. // DefaultDataKey is the key used in the write as a default for data.
type PutCommand struct { const DefaultDataKey = "value"
// WriteCommand is a Command that puts data into the Vault.
type WriteCommand struct {
Meta Meta
} }
func (c *PutCommand) Run(args []string) int { func (c *WriteCommand) Run(args []string) int {
flags := c.Meta.FlagSet("put", FlagSetDefault) flags := c.Meta.FlagSet("write", FlagSetDefault)
flags.Usage = func() { c.Ui.Error(c.Help()) } flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil { if err := flags.Parse(args); err != nil {
return 1 return 1
} }
args = flags.Args()
if len(args) != 2 {
c.Ui.Error("write expects two arguments")
flags.Usage()
return 1
}
path := args[0]
if path[0] == '/' {
path = path[1:]
}
data := map[string]interface{}{DefaultDataKey: args[1]}
client, err := c.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing client: %s", err))
return 2
}
if err := client.Logical().Write(path, data); err != nil {
c.Ui.Error(fmt.Sprintf(
"Error writing data to %s: %s", path, err))
return 1
}
c.Ui.Output(fmt.Sprintf("Success! Data written to: %s", path))
return 0 return 0
} }
func (c *PutCommand) Synopsis() string { func (c *WriteCommand) Synopsis() string {
return "Put secrets or configuration into Vault" return "Write secrets or configuration into Vault"
} }
func (c *PutCommand) Help() string { func (c *WriteCommand) Help() string {
helpText := ` helpText := `
Usage: vault put [options] path data Usage: vault write [options] path data
Write data (secrets or configuration) into Vault. Write data (secrets or configuration) into Vault.
Put sends data into Vault at the given path. The behavior of the write 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 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 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 (configuration), but writing to "consul/foo" will write a value directly
@@ -37,7 +68,10 @@ Usage: vault put [options] path data
you're using for more information on key structure. you're using for more information on key structure.
If data is "-" then the data will be ready from stdin. To write a literal If data is "-" then the data will be ready from stdin. To write a literal
"-", you'll have to pipe that value in from stdin. "-", you'll have to pipe that value in from stdin. To write data from a
file, pipe the file contents in via stdin and set data to "-".
If data is a string, it will be sent with the key of "value".
General Options: General Options:

45
command/write_test.go Normal file
View File

@@ -0,0 +1,45 @@
package command
import (
"testing"
"github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
func TestWrite(t *testing.T) {
core, _ := vault.TestCoreUnsealed(t)
ln, addr := http.TestServer(t, core)
defer ln.Close()
ui := new(cli.MockUi)
c := &WriteCommand{
Meta: Meta{
Ui: ui,
},
}
args := []string{
"-address", addr,
"secret/foo",
"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[DefaultDataKey] != "bar" {
t.Fatalf("bad: %#v", resp)
}
}

View File

@@ -24,14 +24,14 @@ func init() {
}, nil }, nil
}, },
"get": func() (cli.Command, error) { "read": func() (cli.Command, error) {
return &command.GetCommand{ return &command.ReadCommand{
Meta: meta, Meta: meta,
}, nil }, nil
}, },
"put": func() (cli.Command, error) { "write": func() (cli.Command, error) {
return &command.PutCommand{ return &command.WriteCommand{
Meta: meta, Meta: meta,
}, nil }, nil
}, },