628 lines
19 KiB
Go
628 lines
19 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package api
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
logicalKv "github.com/hashicorp/vault-plugin-secrets-kv"
|
|
"github.com/hashicorp/vault/api"
|
|
vaulthttp "github.com/hashicorp/vault/http"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
"github.com/hashicorp/vault/vault"
|
|
)
|
|
|
|
const (
|
|
v1MountPath = "secret"
|
|
v2MountPath = "secret-v2"
|
|
secretPath = "my-secret"
|
|
)
|
|
|
|
var (
|
|
client *api.Client
|
|
secretData = map[string]interface{}{
|
|
"foo": "bar",
|
|
}
|
|
)
|
|
|
|
// setupKVv2Test creates the secret that will be used in each KV v2 subtest. It
|
|
// returns a function (that should be deferred whenever setupKVv2Test is called)
|
|
// which will perform the cleanup of all existing versions of the secret, as
|
|
// well as the secret that was written for comparison.
|
|
func setupKVv2Test(t *testing.T) (func(t *testing.T), *api.KVSecret) {
|
|
writtenSecret, err := client.KVv2(v2MountPath).Put(context.Background(), secretPath, secretData)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if writtenSecret == nil || writtenSecret.VersionMetadata == nil {
|
|
t.Fatal("secret created during kv v2 subtest setup did not have expected contents")
|
|
}
|
|
|
|
return func(t *testing.T) {
|
|
err := client.KVv2(v2MountPath).DeleteMetadata(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}, writtenSecret
|
|
}
|
|
|
|
func TestKVHelpers(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// initialize test cluster
|
|
coreConfig := &vault.CoreConfig{
|
|
LogicalBackends: map[string]logical.Factory{
|
|
"kv": logicalKv.Factory,
|
|
"kv-v2": logicalKv.VersionedKVFactory,
|
|
},
|
|
}
|
|
|
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
|
HandlerFunc: vaulthttp.Handler,
|
|
})
|
|
|
|
cluster.Start()
|
|
defer cluster.Cleanup()
|
|
|
|
cores := cluster.Cores
|
|
core := cores[0].Core
|
|
client = cluster.Cores[0].Client
|
|
vault.TestWaitActive(t, core)
|
|
|
|
// mount the KVv2 backend
|
|
// (the test cluster has already mounted the KVv1 backend at "secret")
|
|
err := client.Sys().MountWithContext(context.Background(), "secret-v2", &api.MountInput{
|
|
Type: "kv-v2",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
//// v1 ////
|
|
t.Run("kv v1: put, get, and delete data", func(t *testing.T) {
|
|
if err := client.KVv1(v1MountPath).Put(context.Background(), secretPath, secretData); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
secret, err := client.KVv1(v1MountPath).Get(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if secret.Data["foo"] != "bar" {
|
|
t.Fatalf("kv v1 secret did not contain expected value")
|
|
}
|
|
|
|
if err := client.KVv1(v1MountPath).Delete(context.Background(), secretPath); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = client.KVv1(v1MountPath).Get(context.Background(), secretPath)
|
|
if !errors.Is(err, api.ErrSecretNotFound) {
|
|
t.Fatalf("KVv1.Get is expected to return an api.ErrSecretNotFound wrapped error after secret had been deleted; got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("kv v1: get secret that does not exist", func(t *testing.T) {
|
|
_, err = client.KVv1(v1MountPath).Get(context.Background(), "does/not/exist")
|
|
if err == nil {
|
|
t.Fatalf("KVv1.Get is expected to return an error for a missing secret")
|
|
}
|
|
if !errors.Is(err, api.ErrSecretNotFound) {
|
|
t.Fatalf("KVv1.Get is expected to return an api.ErrSecretNotFound wrapped error for a missing secret; got %v", err)
|
|
}
|
|
})
|
|
|
|
//// v2 ////
|
|
t.Run("kv v2: get data and full metadata", func(t *testing.T) {
|
|
teardownTest, originalSecret := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
secret, err := client.KVv2(v2MountPath).Get(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Data["foo"] != "bar" {
|
|
t.Fatal("kv v2 secret did not contain expected value")
|
|
}
|
|
if secret.VersionMetadata.CreatedTime != originalSecret.VersionMetadata.CreatedTime {
|
|
t.Fatal("the created_time on the secret did not match the response from when it was created")
|
|
}
|
|
|
|
// get its full metadata
|
|
fullMetadata, err := client.KVv2(v2MountPath).GetMetadata(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(secret.CustomMetadata, fullMetadata.CustomMetadata) {
|
|
t.Fatalf("custom metadata on the secret does not match the custom metadata in the full metadata")
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: get secret that does not exist", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
_, err = client.KVv2(v1MountPath).Get(context.Background(), "does/not/exist")
|
|
if !errors.Is(err, api.ErrSecretNotFound) {
|
|
t.Fatalf("KVv2.Get is expected to return an api.ErrSecretNotFound wrapped error for a missing secret; got %v", err)
|
|
}
|
|
|
|
_, err = client.KVv2(v1MountPath).GetMetadata(context.Background(), "does/not/exist")
|
|
if !errors.Is(err, api.ErrSecretNotFound) {
|
|
t.Fatalf("KVv2.GetMetadata is expected to return an api.ErrSecretNotFound wrapped error for a missing secret; got %v", err)
|
|
}
|
|
|
|
_, err = client.KVv2(v1MountPath).GetVersion(context.Background(), secretPath, 99)
|
|
if !errors.Is(err, api.ErrSecretNotFound) {
|
|
t.Fatalf("KVv2.GetVersion is expected to return an api.ErrSecretNotFound wrapped error for a missing secret version; got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: multiple versions", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
// create a second version
|
|
_, err = client.KVv2(v2MountPath).Put(context.Background(), secretPath, map[string]interface{}{
|
|
"foo": "baz",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s2, err := client.KVv2(v2MountPath).Get(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if s2.Data["foo"] != "baz" {
|
|
t.Fatalf("second version of secret did not have expected contents")
|
|
}
|
|
if s2.VersionMetadata.Version != 2 {
|
|
t.Fatalf("wrong version of kv v2 secret was read, expected 2 but got %d", s2.VersionMetadata.Version)
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: delete and undelete", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
// create a second version
|
|
_, err = client.KVv2(v2MountPath).Put(context.Background(), secretPath, map[string]interface{}{
|
|
"foo": "baz",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// get a specific past version
|
|
s1, err := client.KVv2(v2MountPath).GetVersion(context.Background(), secretPath, 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if s1.VersionMetadata.Version != 1 {
|
|
t.Fatalf("wrong version of kv v2 secret was read, expected 1 but got %d", s1.VersionMetadata.Version)
|
|
}
|
|
|
|
// delete that version
|
|
if err = client.KVv2(v2MountPath).DeleteVersions(context.Background(), secretPath, []int{1}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s1AfterDelete, err := client.KVv2(v2MountPath).GetVersion(context.Background(), secretPath, 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if s1AfterDelete.VersionMetadata.DeletionTime.IsZero() {
|
|
t.Fatalf("the deletion_time in the first version of the secret was not updated")
|
|
}
|
|
|
|
if s1AfterDelete.Data != nil {
|
|
t.Fatalf("data still exists on the first version of the secret despite this version being deleted")
|
|
}
|
|
|
|
// undelete it
|
|
err = client.KVv2(v2MountPath).Undelete(context.Background(), secretPath, []int{1})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s1AfterUndelete, err := client.KVv2(v2MountPath).GetVersion(context.Background(), secretPath, 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if s1AfterUndelete.Data == nil {
|
|
t.Fatalf("data is empty for the first version of the secret despite this version being undeleted")
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: destroy", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
err = client.KVv2(v2MountPath).Destroy(context.Background(), secretPath, []int{1})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
destroyedSecret, err := client.KVv2(v2MountPath).Get(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !destroyedSecret.VersionMetadata.Destroyed {
|
|
t.Fatalf("expected secret to be destroyed but it wasn't")
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: use named functional options and generic WithOption", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
// check that KVOption works
|
|
// WithCheckAndSet
|
|
_, err = client.KVv2(v2MountPath).Put(context.Background(), secretPath, map[string]interface{}{
|
|
"meow": "woof",
|
|
}, api.WithCheckAndSet(99))
|
|
// should fail
|
|
if err == nil {
|
|
t.Fatalf("expected error from trying to update different version from check-and-set value using WithCheckAndSet")
|
|
}
|
|
|
|
// WithOption (generic)
|
|
_, err = client.KVv2(v2MountPath).Put(context.Background(), secretPath, map[string]interface{}{
|
|
"bow": "wow",
|
|
}, api.WithOption("cas", 99))
|
|
// should fail
|
|
if err == nil {
|
|
t.Fatalf("expected error from trying to update different version from check-and-set value using generic WithOption")
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: patch", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
// WithMergeMethod Patch (implicit)
|
|
patch, err := client.KVv2(v2MountPath).Patch(context.Background(), secretPath, map[string]interface{}{
|
|
"dog": "cat",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if patch.VersionMetadata.Version != 2 {
|
|
t.Fatalf("incorrect version %d, expected 2", patch.VersionMetadata.Version)
|
|
}
|
|
|
|
// WithMergeMethod Patch (explicit)
|
|
patchExp, err := client.KVv2(v2MountPath).Patch(context.Background(), secretPath, map[string]interface{}{
|
|
"rat": "mouse",
|
|
}, api.WithMergeMethod(api.KVMergeMethodPatch))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if patchExp.VersionMetadata.Version != 3 {
|
|
t.Fatalf("incorrect version %d, expected 3", patchExp.VersionMetadata.Version)
|
|
}
|
|
|
|
// WithMergeMethod RW
|
|
patchRW, err := client.KVv2(v2MountPath).Patch(context.Background(), secretPath, map[string]interface{}{
|
|
"bird": "tweet",
|
|
}, api.WithMergeMethod(api.KVMergeMethodReadWrite))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if patchRW.VersionMetadata.Version != 4 {
|
|
t.Fatalf("incorrect version %d, expected 4", patchRW.VersionMetadata.Version)
|
|
}
|
|
|
|
secretAfterPatches, err := client.KVv2(v2MountPath).Get(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, ok := secretAfterPatches.Data["dog"]
|
|
if !ok {
|
|
t.Fatalf("secret did not contain data patched with implicit Patch method")
|
|
}
|
|
_, ok = secretAfterPatches.Data["rat"]
|
|
if !ok {
|
|
t.Fatalf("secret did not contain data patched with explicit Patch method")
|
|
}
|
|
_, ok = secretAfterPatches.Data["bird"]
|
|
if !ok {
|
|
t.Fatalf("secret did not contain data patched with RW method")
|
|
}
|
|
value, ok := secretAfterPatches.Data["foo"]
|
|
if !ok || value != "bar" {
|
|
t.Fatalf("secret did not keep original data after patch")
|
|
}
|
|
|
|
// patch an existing field
|
|
_, err = client.KVv2(v2MountPath).Patch(context.Background(), secretPath, map[string]interface{}{
|
|
"dog": "pug",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
patchedFieldKV, err := client.KVv2(v2MountPath).Get(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v, ok := patchedFieldKV.Data["dog"]
|
|
if !ok || v != "pug" {
|
|
t.Fatalf("secret's data was not replaced by patch")
|
|
}
|
|
|
|
// delete a key in a secret via patch
|
|
_, err = client.KVv2(v2MountPath).Patch(context.Background(), secretPath, map[string]interface{}{
|
|
"dog": nil,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
deletedFieldKV, err := client.KVv2(v2MountPath).Get(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, ok = deletedFieldKV.Data["dog"]
|
|
if ok {
|
|
t.Fatalf("secret key \"dog\" should have been removed by nil patch")
|
|
}
|
|
|
|
// set a key to an empty string via patch
|
|
_, err = client.KVv2(v2MountPath).Patch(context.Background(), secretPath, map[string]interface{}{
|
|
"dog": "",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
emptyValueKV, err := client.KVv2(v2MountPath).Get(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v, ok = emptyValueKV.Data["dog"]
|
|
if !ok || v != "" {
|
|
t.Fatalf("secret key \"dog\" should have an empty string value")
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: patch a secret that does not exist", func(t *testing.T) {
|
|
for _, method := range [][]api.KVOption{
|
|
{},
|
|
{api.WithMergeMethod(api.KVMergeMethodPatch)},
|
|
{api.WithMergeMethod(api.KVMergeMethodReadWrite)},
|
|
} {
|
|
_, err = client.KVv2(v2MountPath).Patch(
|
|
context.Background(),
|
|
"does/not/exist",
|
|
map[string]interface{}{"no": "nope"},
|
|
method...,
|
|
)
|
|
if !errors.Is(err, api.ErrSecretNotFound) {
|
|
t.Fatalf("expected an api.ErrSecretNotFound wrapped error from trying to patch something that doesn't exist for %v method; got: %v", method, err)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: roll back to an old version", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
// create a second version
|
|
_, err = client.KVv2(v2MountPath).Put(context.Background(), secretPath, map[string]interface{}{
|
|
"color": "yellow",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// get versions as list
|
|
versions, err := client.KVv2(v2MountPath).GetVersionsAsList(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedLength := 2
|
|
if len(versions) != expectedLength {
|
|
t.Fatalf("expected there to be %d versions of the secret but got %d", expectedLength, len(versions))
|
|
}
|
|
|
|
if versions[0].Version != 1 || versions[len(versions)-1].Version != expectedLength {
|
|
t.Fatalf("versions list is not ordered as expected")
|
|
}
|
|
|
|
// roll back to version 1
|
|
rb, err := client.KVv2(v2MountPath).Rollback(context.Background(), secretPath, 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if rb.VersionMetadata.Version != 3 {
|
|
t.Fatalf("expected returned secret's version %d to be the latest version, which should be 3", rb.VersionMetadata.Version)
|
|
}
|
|
|
|
// destroy version 1
|
|
err = client.KVv2(v2MountPath).Destroy(context.Background(), secretPath, []int{1})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// roll back but fail
|
|
_, err = client.KVv2(v2MountPath).Rollback(context.Background(), secretPath, 1)
|
|
if err == nil {
|
|
t.Fatalf("expected error from trying to rollback to destroyed version")
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: delete all versions of a secret", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
// create a second version
|
|
_, err = client.KVv2(v2MountPath).Put(context.Background(), secretPath, map[string]interface{}{
|
|
"color": "yellow",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// delete it all
|
|
err = client.KVv2(v2MountPath).DeleteMetadata(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
versions, err := client.KVv2(v2MountPath).GetVersionsAsList(context.Background(), secretPath)
|
|
if err == nil {
|
|
t.Fatalf("expected to be unable to get list of versions since all metadata was destroyed")
|
|
}
|
|
if len(versions) > 0 {
|
|
t.Fatalf("expected no versions of secret after deleting all metadata")
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: create a secret with metadata but no data", func(t *testing.T) {
|
|
// put and patch metadata
|
|
////
|
|
noDataSecretPath := "empty"
|
|
|
|
// create a secret with metadata but no data
|
|
err = client.KVv2(v2MountPath).PutMetadata(context.Background(), noDataSecretPath, api.KVMetadataPutInput{
|
|
DeleteVersionAfter: 5 * time.Hour,
|
|
MaxVersions: 5,
|
|
CustomMetadata: map[string]interface{}{"ape": "gorilla"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// get its metadata to make sure it was created successfully
|
|
md, err := client.KVv2(v2MountPath).GetMetadata(context.Background(), noDataSecretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if md.CreatedTime.IsZero() {
|
|
t.Fatalf("secret metadata was not populated as expected: %v", err)
|
|
}
|
|
if len(md.Versions) > 0 {
|
|
t.Fatalf("no secret versions should have been created since only metadata was populated")
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: put and patch metadata", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
md, err := client.KVv2(v2MountPath).GetMetadata(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// replace all modifiable metadata fields
|
|
err = client.KVv2(v2MountPath).PutMetadata(context.Background(), secretPath, api.KVMetadataPutInput{
|
|
CASRequired: true,
|
|
DeleteVersionAfter: 6 * time.Hour,
|
|
MaxVersions: 6,
|
|
CustomMetadata: map[string]interface{}{"foo": "fwah", "cat": "tabby"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// check that metadata was replaced
|
|
md2, err := client.KVv2(v2MountPath).GetMetadata(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if md.CASRequired == md2.CASRequired || md.MaxVersions == md2.MaxVersions || md.DeleteVersionAfter == md2.DeleteVersionAfter || reflect.DeepEqual(md.CustomMetadata, md2.CustomMetadata) {
|
|
t.Fatalf("metadata fields should have been updated by PutMetadata")
|
|
}
|
|
|
|
// now let's try a patch
|
|
maxVersions := 7
|
|
err = client.KVv2(v2MountPath).PatchMetadata(context.Background(), secretPath, api.KVMetadataPatchInput{
|
|
MaxVersions: &maxVersions,
|
|
CustomMetadata: map[string]interface{}{"foo": nil, "rat": "brown"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// check that the metadata was only partially replaced
|
|
md3, err := client.KVv2(v2MountPath).GetMetadata(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if md2.CASRequired != md3.CASRequired || md2.DeleteVersionAfter != md3.DeleteVersionAfter {
|
|
t.Fatalf("expected fields to remain unchanged but they were updated")
|
|
}
|
|
if md3.MaxVersions == 0 {
|
|
t.Fatalf("field was reset to its zero value when it should not have been")
|
|
}
|
|
if md2.MaxVersions == md3.MaxVersions {
|
|
t.Fatalf("expected field to be updated but it remained unchanged")
|
|
}
|
|
|
|
// let's check the custom metadata was updated correctly
|
|
if r, ok := md3.CustomMetadata["rat"]; ok {
|
|
if r != "brown" {
|
|
t.Fatalf("expected value to be \"brown\"")
|
|
}
|
|
} else {
|
|
t.Fatalf("expected there to be a new \"rat\" key")
|
|
}
|
|
|
|
if _, ok := md3.CustomMetadata["foo"]; ok {
|
|
t.Fatalf("expected \"foo\" key to be removed")
|
|
}
|
|
|
|
if _, ok := md3.CustomMetadata["cat"]; !ok {
|
|
t.Fatalf("did not expect \"cat\" key to be removed")
|
|
}
|
|
})
|
|
|
|
t.Run("kv v2: patch with explicit zero values", func(t *testing.T) {
|
|
teardownTest, _ := setupKVv2Test(t)
|
|
defer teardownTest(t)
|
|
|
|
// now let's do another patch to test the "explicit zero value" use case
|
|
var (
|
|
explicitFalse bool
|
|
explicitZero int
|
|
explicitTimeZero time.Duration
|
|
)
|
|
err = client.KVv2(v2MountPath).PatchMetadata(context.Background(), secretPath, api.KVMetadataPatchInput{
|
|
CASRequired: &explicitFalse,
|
|
MaxVersions: &explicitZero,
|
|
DeleteVersionAfter: &explicitTimeZero,
|
|
CustomMetadata: map[string]interface{}{},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// check that those fields were reset to their zero value
|
|
md4, err := client.KVv2(v2MountPath).GetMetadata(context.Background(), secretPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(md4.CustomMetadata) > 0 {
|
|
t.Fatalf("expected empty map to cause deletion of all custom metadata")
|
|
}
|
|
|
|
if md4.MaxVersions != 0 || md4.CASRequired != false {
|
|
t.Fatalf("expected fields to be reset to their zero values but they were %d and %t instead", md4.MaxVersions, md4.CASRequired)
|
|
}
|
|
|
|
if md4.DeleteVersionAfter.String() != "0s" {
|
|
t.Fatalf("expected delete-version-after to be reset to its zero value but instead it was %s", md4.DeleteVersionAfter.String())
|
|
}
|
|
})
|
|
}
|