cli: Expand kv version attribution map (#9105) (#9261)

Co-authored-by: Mike Palmiotto <mike.palmiotto@hashicorp.com>
This commit is contained in:
Vault Automation 2025-11-20 09:09:28 -05:00 committed by GitHub
parent 51c6f056bb
commit 3d8588d506
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 38 additions and 4 deletions

5
changelog/_9105.txt Normal file
View file

@ -0,0 +1,5 @@
```release-note:feature
**KV v2 Version Attribution**: Vault now includes attribution metadata for
versioned KV secrets. This allows lookup of attribution information for each
version of KV v2 secrets from CLI and API.
```

View file

@ -4,6 +4,7 @@
package command
import (
"encoding/json"
"fmt"
"path"
"sort"
@ -186,12 +187,38 @@ func (c *KVMetadataGetCommand) Run(args []string) int {
versionKeys = append(versionKeys, i)
}
// Sort the versions by key and display them in order.
sort.Ints(versionKeys)
for _, v := range versionKeys {
c.UI.Info("\n" + getHeaderForMap(fmt.Sprintf("Version %d", v), versions[strconv.Itoa(v)].(map[string]interface{})))
OutputData(c.UI, versions[strconv.Itoa(v)])
version, err := expandVersionAttribution(versions[strconv.Itoa(v)])
if err != nil {
c.UI.Error(fmt.Sprintf("Error parsing version %d: %s", v, err.Error()))
return 2
}
c.UI.Info("\n" + getHeaderForMap(fmt.Sprintf("Version %d", v), version))
OutputData(c.UI, version)
}
return 0
}
func expandVersionAttribution(versionData interface{}) (map[string]interface{}, error) {
version, ok := versionData.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("version is not a map")
}
attributionKeys := []string{"created_by", "deleted_by"}
for _, key := range attributionKeys {
if version[key] == nil {
continue
}
attr, err := json.Marshal(version[key])
if err != nil {
return nil, fmt.Errorf("failed to parse attribution data for %q", key)
}
version[key] = string(attr)
}
return version, nil
}

View file

@ -88,7 +88,7 @@ func kvPatchWithRetry(t *testing.T, client *api.Client, args []string, stdin *io
func TestKVPutCommand(t *testing.T) {
t.Parallel()
v2ExpectedFields := []string{"created_time", "custom_metadata", "deletion_time", "deletion_time", "version"}
v2ExpectedFields := []string{"created_time", "custom_metadata", "deletion_time", "version"}
cases := []struct {
name string
@ -744,7 +744,9 @@ func TestKVMetadataGetCommand(t *testing.T) {
}
expectedVersionFields := []string{
"created_by",
"created_time", // field is redundant
"deleted_by",
"deletion_time",
"destroyed",
}