improve kv CLI to remove data or custom metadata using kv patch (#18067)

* improve kv CLI to remove data or custom metadata using kv patch

* CL

* adding a comment
This commit is contained in:
Hamid Ghaf 2022-11-21 17:11:36 -05:00 committed by GitHub
parent 1c0b2df8f1
commit 22f51dc6d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 14 deletions

3
changelog/18067.txt Normal file
View file

@ -0,0 +1,3 @@
```release-note:improvement
cli/kv: improve kv CLI to remove data or custom metadata using kv patch
```

View file

@ -20,12 +20,13 @@ var (
type KVMetadataPatchCommand struct { type KVMetadataPatchCommand struct {
*BaseCommand *BaseCommand
flagMaxVersions int flagMaxVersions int
flagCASRequired BoolPtr flagCASRequired BoolPtr
flagDeleteVersionAfter time.Duration flagDeleteVersionAfter time.Duration
flagCustomMetadata map[string]string flagCustomMetadata map[string]string
flagMount string flagRemoveCustomMetadata []string
testStdin io.Reader // for tests flagMount string
testStdin io.Reader // for tests
} }
func (c *KVMetadataPatchCommand) Synopsis() string { func (c *KVMetadataPatchCommand) Synopsis() string {
@ -65,6 +66,10 @@ Usage: vault kv metadata patch [options] KEY
$ vault kv metadata patch -mount=secret -custom-metadata=foo=abc -custom-metadata=bar=123 foo $ vault kv metadata patch -mount=secret -custom-metadata=foo=abc -custom-metadata=bar=123 foo
To remove custom meta data from the corresponding path in the key-value store, kv metadata patch can be used.
$ vault kv metadata patch -mount=secret -remove-custom-metadata=bar foo
Additional flags and more advanced use cases are detailed below. Additional flags and more advanced use cases are detailed below.
` + c.Flags().Help() ` + c.Flags().Help()
@ -111,6 +116,13 @@ func (c *KVMetadataPatchCommand) Flags() *FlagSets {
This can be specified multiple times to add multiple pieces of metadata.`, This can be specified multiple times to add multiple pieces of metadata.`,
}) })
f.StringSliceVar(&StringSliceVar{
Name: "remove-custom-metadata",
Target: &c.flagRemoveCustomMetadata,
Default: []string{},
Usage: "Key to remove from custom metadata. To specify multiple values, specify this flag multiple times.",
})
f.StringVar(&StringVar{ f.StringVar(&StringVar{
Name: "mount", Name: "mount",
Target: &c.flagMount, Target: &c.flagMount,
@ -198,7 +210,7 @@ func (c *KVMetadataPatchCommand) Run(args []string) int {
fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata") fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata")
data := map[string]interface{}{} data := make(map[string]interface{}, 0)
if c.flagMaxVersions >= 0 { if c.flagMaxVersions >= 0 {
data["max_versions"] = c.flagMaxVersions data["max_versions"] = c.flagMaxVersions
@ -212,10 +224,19 @@ func (c *KVMetadataPatchCommand) Run(args []string) int {
data["delete_version_after"] = c.flagDeleteVersionAfter.String() data["delete_version_after"] = c.flagDeleteVersionAfter.String()
} }
if len(c.flagCustomMetadata) > 0 { customMetadata := make(map[string]interface{})
data["custom_metadata"] = c.flagCustomMetadata
for key, value := range c.flagCustomMetadata {
customMetadata[key] = value
} }
for _, key := range c.flagRemoveCustomMetadata {
// A null in a JSON merge patch payload will remove the associated key
customMetadata[key] = nil
}
data["custom_metadata"] = customMetadata
secret, err := client.Logical().JSONMergePatch(context.Background(), fullPath, data) secret, err := client.Logical().JSONMergePatch(context.Background(), fullPath, data)
if err != nil { if err != nil {
c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err)) c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err))

View file

@ -122,6 +122,29 @@ func TestKvMetadataPatchCommand_Flags(t *testing.T) {
}, },
}, },
}, },
{
"remove-custom_metadata",
[]string{"-custom-metadata=baz=ghi", "-remove-custom-metadata=foo"},
"Success!",
0,
map[string]interface{}{
"custom_metadata": map[string]interface{}{
"bar": "def",
"baz": "ghi",
},
},
},
{
"remove-custom_metadata-multiple",
[]string{"-custom-metadata=baz=ghi", "-remove-custom-metadata=foo", "-remove-custom-metadata=bar"},
"Success!",
0,
map[string]interface{}{
"custom_metadata": map[string]interface{}{
"baz": "ghi",
},
},
},
{ {
"delete_version_after_success", "delete_version_after_success",
[]string{"-delete-version-after=5s"}, []string{"-delete-version-after=5s"},

View file

@ -21,10 +21,11 @@ var (
type KVPatchCommand struct { type KVPatchCommand struct {
*BaseCommand *BaseCommand
flagCAS int flagCAS int
flagMethod string flagMethod string
flagMount string flagMount string
testStdin io.Reader // for tests testStdin io.Reader // for tests
flagRemoveData []string
} }
func (c *KVPatchCommand) Synopsis() string { func (c *KVPatchCommand) Synopsis() string {
@ -76,6 +77,10 @@ Usage: vault kv patch [options] KEY [DATA]
$ vault kv patch -mount=secret -method=rw foo bar=baz $ vault kv patch -mount=secret -method=rw foo bar=baz
To remove data from the corresponding path in the key-value store, kv patch can be used.
$ vault kv patch -mount=secret -remove-data=bar foo
Additional flags and more advanced use cases are detailed below. Additional flags and more advanced use cases are detailed below.
` + c.Flags().Help() ` + c.Flags().Help()
@ -117,6 +122,13 @@ func (c *KVPatchCommand) Flags() *FlagSets {
v2 secrets.`, v2 secrets.`,
}) })
f.StringSliceVar(&StringSliceVar{
Name: "remove-data",
Target: &c.flagRemoveData,
Default: []string{},
Usage: "Key to remove from data. To specify multiple values, specify this flag multiple times.",
})
return set return set
} }
@ -147,7 +159,7 @@ func (c *KVPatchCommand) Run(args []string) int {
case len(args) < 1: case len(args) < 1:
c.UI.Error(fmt.Sprintf("Not enough arguments (expected >1, got %d)", len(args))) c.UI.Error(fmt.Sprintf("Not enough arguments (expected >1, got %d)", len(args)))
return 1 return 1
case len(args) == 1: case len(c.flagRemoveData) == 0 && len(args) == 1:
c.UI.Error("Must supply data") c.UI.Error("Must supply data")
return 1 return 1
} }
@ -211,6 +223,16 @@ func (c *KVPatchCommand) Run(args []string) int {
return 2 return 2
} }
// collecting data to be removed
if newData == nil {
newData = make(map[string]interface{})
}
for _, key := range c.flagRemoveData {
// A null in a JSON merge patch payload will remove the associated key
newData[key] = nil
}
// Check the method and behave accordingly // Check the method and behave accordingly
var secret *api.Secret var secret *api.Secret
var code int var code int

View file

@ -249,6 +249,7 @@ func TestKV_Patch_RootToken(t *testing.T) {
data := map[string]interface{}{ data := map[string]interface{}{
"data": map[string]interface{}{ "data": map[string]interface{}{
"bar": "baz", "bar": "baz",
"foo": "qux",
}, },
} }
@ -263,6 +264,7 @@ func TestKV_Patch_RootToken(t *testing.T) {
data := map[string]interface{}{ data := map[string]interface{}{
"data": map[string]interface{}{ "data": map[string]interface{}{
"bar": "quux", "bar": "quux",
"foo": nil,
}, },
} }
return client.Logical().JSONMergePatch(context.Background(), "kv/data/foo", data) return client.Logical().JSONMergePatch(context.Background(), "kv/data/foo", data)
@ -288,4 +290,8 @@ func TestKV_Patch_RootToken(t *testing.T) {
if bar != "quux" { if bar != "quux" {
t.Fatalf("expected bar to be quux but it was %q", bar) t.Fatalf("expected bar to be quux but it was %q", bar)
} }
if _, ok := secret.Data["data"].(map[string]interface{})["foo"]; ok {
t.Fatalf("expected data not to include foo")
}
} }