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

@ -24,6 +24,7 @@ type KVMetadataPatchCommand struct {
flagCASRequired BoolPtr
flagDeleteVersionAfter time.Duration
flagCustomMetadata map[string]string
flagRemoveCustomMetadata []string
flagMount string
testStdin io.Reader // for tests
}
@ -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
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.
` + c.Flags().Help()
@ -111,6 +116,13 @@ func (c *KVMetadataPatchCommand) Flags() *FlagSets {
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{
Name: "mount",
Target: &c.flagMount,
@ -198,7 +210,7 @@ func (c *KVMetadataPatchCommand) Run(args []string) int {
fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata")
data := map[string]interface{}{}
data := make(map[string]interface{}, 0)
if c.flagMaxVersions >= 0 {
data["max_versions"] = c.flagMaxVersions
@ -212,10 +224,19 @@ func (c *KVMetadataPatchCommand) Run(args []string) int {
data["delete_version_after"] = c.flagDeleteVersionAfter.String()
}
if len(c.flagCustomMetadata) > 0 {
data["custom_metadata"] = c.flagCustomMetadata
customMetadata := make(map[string]interface{})
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)
if err != nil {
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",
[]string{"-delete-version-after=5s"},

View File

@ -25,6 +25,7 @@ type KVPatchCommand struct {
flagMethod string
flagMount string
testStdin io.Reader // for tests
flagRemoveData []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
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.
` + c.Flags().Help()
@ -117,6 +122,13 @@ func (c *KVPatchCommand) Flags() *FlagSets {
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
}
@ -147,7 +159,7 @@ func (c *KVPatchCommand) Run(args []string) int {
case len(args) < 1:
c.UI.Error(fmt.Sprintf("Not enough arguments (expected >1, got %d)", len(args)))
return 1
case len(args) == 1:
case len(c.flagRemoveData) == 0 && len(args) == 1:
c.UI.Error("Must supply data")
return 1
}
@ -211,6 +223,16 @@ func (c *KVPatchCommand) Run(args []string) int {
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
var secret *api.Secret
var code int

View File

@ -249,6 +249,7 @@ func TestKV_Patch_RootToken(t *testing.T) {
data := map[string]interface{}{
"data": map[string]interface{}{
"bar": "baz",
"foo": "qux",
},
}
@ -263,6 +264,7 @@ func TestKV_Patch_RootToken(t *testing.T) {
data := map[string]interface{}{
"data": map[string]interface{}{
"bar": "quux",
"foo": nil,
},
}
return client.Logical().JSONMergePatch(context.Background(), "kv/data/foo", data)
@ -288,4 +290,8 @@ func TestKV_Patch_RootToken(t *testing.T) {
if bar != "quux" {
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")
}
}