add retry logic to TestKV_Patch_RootToken (#13586)

This commit is contained in:
Chris Capurso 2022-01-07 14:46:26 -05:00 committed by GitHub
parent e3426c238f
commit c925324057
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 223 additions and 197 deletions

View File

@ -13,14 +13,9 @@ import (
"reflect"
"strings"
"testing"
"time"
"github.com/go-test/deep"
"github.com/hashicorp/go-cleanhttp"
kv "github.com/hashicorp/vault-plugin-secrets-kv"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/audit"
auditFile "github.com/hashicorp/vault/builtin/audit/file"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/logical"
@ -863,192 +858,4 @@ func TestHandler_Parse_Form(t *testing.T) {
if diff := deep.Equal(expected, apiResp.Data); diff != nil {
t.Fatal(diff)
}
}
func TestHandler_Patch_BadContentTypeHeader(t *testing.T) {
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"kv": kv.VersionedKVFactory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: Handler,
})
cluster.Start()
defer cluster.Cleanup()
cores := cluster.Cores
core := cores[0].Core
c := cluster.Cores[0].Client
vault.TestWaitActive(t, core)
// Mount a KVv2 backend
err := c.Sys().Mount("kv", &api.MountInput{
Type: "kv-v2",
})
if err != nil {
t.Fatal(err)
}
kvData := map[string]interface{}{
"data": map[string]interface{}{
"bar": "a",
},
}
resp, err := c.Logical().Write("kv/data/foo", kvData)
if err != nil {
t.Fatalf("write failed - err :%#v, resp: %#v\n", err, resp)
}
resp, err = c.Logical().Read("kv/data/foo")
if err != nil {
t.Fatalf("read failed - err :%#v, resp: %#v\n", err, resp)
}
req := c.NewRequest("PATCH", "/v1/kv/data/foo")
req.Headers = http.Header{
"Content-Type": []string{"application/json"},
}
if err := req.SetJSONBody(kvData); err != nil {
t.Fatal(err)
}
apiResp, err := c.RawRequestWithContext(context.Background(), req)
if err == nil || apiResp.StatusCode != http.StatusUnsupportedMediaType {
t.Fatalf("expected PATCH request to fail with %d status code - err :%#v, resp: %#v\n", http.StatusUnsupportedMediaType, err, apiResp)
}
}
func kvRequestWithRetry(t *testing.T, req func() (*api.Secret, error)) (*api.Secret, error) {
t.Helper()
var err error
var resp *api.Secret
// Loop until return message does not indicate upgrade, or timeout.
timeout := time.After(20 * time.Second)
ticker := time.Tick(time.Second)
for {
select {
case <-timeout:
t.Error("timeout expired waiting for upgrade")
case <-ticker:
resp, err = req()
if err == nil {
return resp, nil
}
responseError := err.(*api.ResponseError)
if !strings.Contains(responseError.Error(), "Upgrading from non-versioned to versioned data") {
return resp, err
}
}
}
}
func TestHandler_Patch_Audit(t *testing.T) {
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"kv": kv.VersionedKVFactory,
},
AuditBackends: map[string]audit.Factory{
"file": auditFile.Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: Handler,
})
cluster.Start()
defer cluster.Cleanup()
cores := cluster.Cores
core := cores[0].Core
c := cluster.Cores[0].Client
vault.TestWaitActive(t, core)
if err := c.Sys().Mount("kv/", &api.MountInput{
Type: "kv-v2",
}); err != nil {
t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err)
}
auditLogFile, err := ioutil.TempFile("", "httppatch")
if err != nil {
t.Fatal(err)
}
err = c.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{
Type: "file",
Options: map[string]string{
"file_path": auditLogFile.Name(),
},
})
if err != nil {
t.Fatal(err)
}
writeData := map[string]interface{}{
"data": map[string]interface{}{
"bar": "a",
},
}
resp, err := kvRequestWithRetry(t, func() (*api.Secret, error) {
return c.Logical().Write("kv/data/foo", writeData)
})
if err != nil {
t.Fatalf("write request failed, err: %#v, resp: %#v\n", err, resp)
}
patchData := map[string]interface{}{
"data": map[string]interface{}{
"baz": "b",
},
}
resp, err = kvRequestWithRetry(t, func() (*api.Secret, error) {
return c.Logical().JSONMergePatch(context.Background(), "kv/data/foo", patchData)
})
if err != nil {
t.Fatalf("patch request failed, err: %#v, resp: %#v\n", err, resp)
}
patchRequestLogCount := 0
patchResponseLogCount := 0
decoder := json.NewDecoder(auditLogFile)
var auditRecord map[string]interface{}
for decoder.Decode(&auditRecord) == nil {
auditRequest := map[string]interface{}{}
if req, ok := auditRecord["request"]; ok {
auditRequest = req.(map[string]interface{})
}
if auditRequest["operation"] == "patch" && auditRecord["type"] == "request" {
patchRequestLogCount += 1
} else if auditRequest["operation"] == "patch" && auditRecord["type"] == "response" {
patchResponseLogCount += 1
}
}
if patchRequestLogCount != 1 {
t.Fatalf("expected 1 patch request audit log record, saw %d\n", patchRequestLogCount)
}
if patchResponseLogCount != 1 {
t.Fatalf("expected 1 patch response audit log record, saw %d\n", patchResponseLogCount)
}
}
}

View File

@ -2,15 +2,210 @@ package kv
import (
"context"
"encoding/json"
"io/ioutil"
"net/http"
"strings"
"testing"
"time"
logicalKv "github.com/hashicorp/vault-plugin-secrets-kv"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/audit"
auditFile "github.com/hashicorp/vault/builtin/audit/file"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault"
)
func TestKV_Patch_BadContentTypeHeader(t *testing.T) {
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"kv": logicalKv.VersionedKVFactory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
cores := cluster.Cores
core := cores[0].Core
c := cluster.Cores[0].Client
vault.TestWaitActive(t, core)
// Mount a KVv2 backend
err := c.Sys().Mount("kv", &api.MountInput{
Type: "kv-v2",
})
if err != nil {
t.Fatal(err)
}
kvData := map[string]interface{}{
"data": map[string]interface{}{
"bar": "a",
},
}
resp, err := c.Logical().Write("kv/data/foo", kvData)
if err != nil {
t.Fatalf("write failed - err :%#v, resp: %#v\n", err, resp)
}
resp, err = c.Logical().Read("kv/data/foo")
if err != nil {
t.Fatalf("read failed - err :%#v, resp: %#v\n", err, resp)
}
req := c.NewRequest("PATCH", "/v1/kv/data/foo")
req.Headers = http.Header{
"Content-Type": []string{"application/json"},
}
if err := req.SetJSONBody(kvData); err != nil {
t.Fatal(err)
}
apiResp, err := c.RawRequestWithContext(context.Background(), req)
if err == nil || apiResp.StatusCode != http.StatusUnsupportedMediaType {
t.Fatalf("expected PATCH request to fail with %d status code - err :%#v, resp: %#v\n", http.StatusUnsupportedMediaType, err, apiResp)
}
}
func kvRequestWithRetry(t *testing.T, req func() (*api.Secret, error)) (*api.Secret, error) {
t.Helper()
var err error
var resp *api.Secret
// Loop until return message does not indicate upgrade, or timeout.
timeout := time.After(20 * time.Second)
ticker := time.Tick(time.Second)
for {
select {
case <-timeout:
t.Error("timeout expired waiting for upgrade")
case <-ticker:
resp, err = req()
if err == nil {
return resp, nil
}
responseError := err.(*api.ResponseError)
if !strings.Contains(responseError.Error(), "Upgrading from non-versioned to versioned data") {
return resp, err
}
}
}
}
func TestKV_Patch_Audit(t *testing.T) {
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"kv": logicalKv.VersionedKVFactory,
},
AuditBackends: map[string]audit.Factory{
"file": auditFile.Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
cores := cluster.Cores
core := cores[0].Core
c := cluster.Cores[0].Client
vault.TestWaitActive(t, core)
if err := c.Sys().Mount("kv/", &api.MountInput{
Type: "kv-v2",
}); err != nil {
t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err)
}
auditLogFile, err := ioutil.TempFile("", "httppatch")
if err != nil {
t.Fatal(err)
}
err = c.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{
Type: "file",
Options: map[string]string{
"file_path": auditLogFile.Name(),
},
})
if err != nil {
t.Fatal(err)
}
writeData := map[string]interface{}{
"data": map[string]interface{}{
"bar": "a",
},
}
resp, err := kvRequestWithRetry(t, func() (*api.Secret, error) {
return c.Logical().Write("kv/data/foo", writeData)
})
if err != nil {
t.Fatalf("write request failed, err: %#v, resp: %#v\n", err, resp)
}
patchData := map[string]interface{}{
"data": map[string]interface{}{
"baz": "b",
},
}
resp, err = kvRequestWithRetry(t, func() (*api.Secret, error) {
return c.Logical().JSONMergePatch(context.Background(), "kv/data/foo", patchData)
})
if err != nil {
t.Fatalf("patch request failed, err: %#v, resp: %#v\n", err, resp)
}
patchRequestLogCount := 0
patchResponseLogCount := 0
decoder := json.NewDecoder(auditLogFile)
var auditRecord map[string]interface{}
for decoder.Decode(&auditRecord) == nil {
auditRequest := map[string]interface{}{}
if req, ok := auditRecord["request"]; ok {
auditRequest = req.(map[string]interface{})
}
if auditRequest["operation"] == "patch" && auditRecord["type"] == "request" {
patchRequestLogCount += 1
} else if auditRequest["operation"] == "patch" && auditRecord["type"] == "response" {
patchResponseLogCount += 1
}
}
if patchRequestLogCount != 1 {
t.Fatalf("expected 1 patch request audit log record, saw %d\n", patchRequestLogCount)
}
if patchResponseLogCount != 1 {
t.Fatalf("expected 1 patch response audit log record, saw %d\n", patchResponseLogCount)
}
}
// Verifies that patching works by default with the root token
func TestKV_Patch_RootToken(t *testing.T) {
coreConfig := &vault.CoreConfig{
@ -39,17 +234,41 @@ func TestKV_Patch_RootToken(t *testing.T) {
}
// Write a kv value and patch it
_, err = client.Logical().Write("kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}})
_, err = kvRequestWithRetry(t, func() (*api.Secret, error) {
data := map[string]interface{}{
"data": map[string]interface{}{
"bar": "baz",
},
}
return client.Logical().Write("kv/data/foo", data)
})
if err != nil {
t.Fatal(err)
}
_, err = client.Logical().JSONMergePatch(context.Background(), "kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "quux"}})
_, err = kvRequestWithRetry(t, func() (*api.Secret, error) {
data := map[string]interface{}{
"data": map[string]interface{}{
"bar": "quux",
},
}
return client.Logical().JSONMergePatch(context.Background(), "kv/data/foo", data)
})
if err != nil {
t.Fatal(err)
}
secret, err := kvRequestWithRetry(t, func() (*api.Secret, error) {
return client.Logical().Read("kv/data/foo")
})
if err != nil {
t.Fatal(err)
}
secret, err := client.Logical().Read("kv/data/foo")
bar := secret.Data["data"].(map[string]interface{})["bar"]
if bar != "quux" {
t.Fatalf("expected bar to be quux but it was %q", bar)