265 lines
7 KiB
Go
265 lines
7 KiB
Go
package testhelpers
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/helper/consts"
|
|
"github.com/hashicorp/vault/helper/xor"
|
|
"github.com/hashicorp/vault/vault"
|
|
"github.com/mitchellh/go-testing-interface"
|
|
)
|
|
|
|
// Generates a root token on the target cluster.
|
|
func GenerateRoot(t testing.T, cluster *vault.TestCluster, drToken bool) string {
|
|
token, err := GenerateRootWithError(t, cluster, drToken)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return token
|
|
}
|
|
|
|
func GenerateRootWithError(t testing.T, cluster *vault.TestCluster, drToken bool) (string, error) {
|
|
// If recovery keys supported, use those to perform root token generation instead
|
|
var keys [][]byte
|
|
if cluster.Cores[0].SealAccess().RecoveryKeySupported() {
|
|
keys = cluster.RecoveryKeys
|
|
} else {
|
|
keys = cluster.BarrierKeys
|
|
}
|
|
|
|
client := cluster.Cores[0].Client
|
|
f := client.Sys().GenerateRootInit
|
|
if drToken {
|
|
f = client.Sys().GenerateDROperationTokenInit
|
|
}
|
|
status, err := f("", "")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if status.Required > len(keys) {
|
|
return "", fmt.Errorf("need more keys than have, need %d have %d", status.Required, len(keys))
|
|
}
|
|
|
|
otp := status.OTP
|
|
|
|
for i, key := range keys {
|
|
if i >= status.Required {
|
|
break
|
|
}
|
|
f := client.Sys().GenerateRootUpdate
|
|
if drToken {
|
|
f = client.Sys().GenerateDROperationTokenUpdate
|
|
}
|
|
status, err = f(base64.StdEncoding.EncodeToString(key), status.Nonce)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
if !status.Complete {
|
|
return "", errors.New("generate root operation did not end successfully")
|
|
}
|
|
|
|
tokenBytes, err := base64.RawStdEncoding.DecodeString(status.EncodedToken)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
tokenBytes, err = xor.XORBytes(tokenBytes, []byte(otp))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(tokenBytes), nil
|
|
}
|
|
|
|
// RandomWithPrefix is used to generate a unique name with a prefix, for
|
|
// randomizing names in acceptance tests
|
|
func RandomWithPrefix(name string) string {
|
|
return fmt.Sprintf("%s-%d", name, rand.New(rand.NewSource(time.Now().UnixNano())).Int())
|
|
}
|
|
|
|
func EnsureCoresUnsealed(t testing.T, c *vault.TestCluster) {
|
|
t.Helper()
|
|
for _, core := range c.Cores {
|
|
if !core.Sealed() {
|
|
continue
|
|
}
|
|
|
|
client := core.Client
|
|
client.Sys().ResetUnsealProcess()
|
|
for j := 0; j < len(c.BarrierKeys); j++ {
|
|
statusResp, err := client.Sys().Unseal(base64.StdEncoding.EncodeToString(c.BarrierKeys[j]))
|
|
if err != nil {
|
|
// Sometimes when we get here it's already unsealed on its own
|
|
// and then this fails for DR secondaries so check again
|
|
if core.Sealed() {
|
|
t.Fatal(err)
|
|
}
|
|
break
|
|
}
|
|
if statusResp == nil {
|
|
t.Fatal("nil status response during unseal")
|
|
}
|
|
if !statusResp.Sealed {
|
|
break
|
|
}
|
|
}
|
|
if core.Sealed() {
|
|
t.Fatal("core is still sealed")
|
|
}
|
|
}
|
|
}
|
|
|
|
func WaitForReplicationState(t testing.T, c *vault.Core, state consts.ReplicationState) {
|
|
timeout := time.Now().Add(10 * time.Second)
|
|
for {
|
|
if time.Now().After(timeout) {
|
|
t.Fatalf("timeout waiting for core to have state %d", uint32(state))
|
|
}
|
|
state := c.ReplicationState()
|
|
if state.HasState(state) {
|
|
break
|
|
}
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
}
|
|
|
|
func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDRSecondary, perfSecondaryDRSecondary *vault.TestCluster) {
|
|
// Enable dr primary
|
|
_, err := perfPrimary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/enable", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
WaitForReplicationState(t, perfPrimary.Cores[0].Core, consts.ReplicationDRPrimary)
|
|
|
|
// Enable performance primary
|
|
_, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/enable", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
WaitForReplicationState(t, perfPrimary.Cores[0].Core, consts.ReplicationPerformancePrimary)
|
|
|
|
// get dr token
|
|
secret, err := perfPrimary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/secondary-token", map[string]interface{}{
|
|
"id": "1",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
token := secret.WrapInfo.Token
|
|
|
|
// enable dr secondary
|
|
secret, err = perfDRSecondary.Cores[0].Client.Logical().Write("sys/replication/dr/secondary/enable", map[string]interface{}{
|
|
"token": token,
|
|
"ca_file": perfPrimary.CACertPEMFile,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
WaitForReplicationState(t, perfDRSecondary.Cores[0].Core, consts.ReplicationDRSecondary)
|
|
perfDRSecondary.BarrierKeys = perfPrimary.BarrierKeys
|
|
EnsureCoresUnsealed(t, perfDRSecondary)
|
|
|
|
// get performance token
|
|
secret, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/secondary-token", map[string]interface{}{
|
|
"id": "1",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
token = secret.WrapInfo.Token
|
|
|
|
// enable performace secondary
|
|
secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/secondary/enable", map[string]interface{}{
|
|
"token": token,
|
|
"ca_file": perfPrimary.CACertPEMFile,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
WaitForReplicationState(t, perfSecondary.Cores[0].Core, consts.ReplicationPerformanceSecondary)
|
|
time.Sleep(time.Second * 3)
|
|
perfSecondary.BarrierKeys = perfPrimary.BarrierKeys
|
|
|
|
EnsureCoresUnsealed(t, perfSecondary)
|
|
rootToken := GenerateRoot(t, perfSecondary, false)
|
|
perfSecondary.Cores[0].Client.SetToken(rootToken)
|
|
|
|
// Enable dr primary on perf secondary
|
|
_, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/enable", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
WaitForReplicationState(t, perfSecondary.Cores[0].Core, consts.ReplicationDRPrimary)
|
|
|
|
// get dr token from perf secondary
|
|
secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/secondary-token", map[string]interface{}{
|
|
"id": "1",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
token = secret.WrapInfo.Token
|
|
|
|
// enable dr secondary
|
|
secret, err = perfSecondaryDRSecondary.Cores[0].Client.Logical().Write("sys/replication/dr/secondary/enable", map[string]interface{}{
|
|
"token": token,
|
|
"ca_file": perfSecondary.CACertPEMFile,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
WaitForReplicationState(t, perfSecondaryDRSecondary.Cores[0].Core, consts.ReplicationDRSecondary)
|
|
perfSecondaryDRSecondary.BarrierKeys = perfPrimary.BarrierKeys
|
|
EnsureCoresUnsealed(t, perfSecondaryDRSecondary)
|
|
|
|
perfDRSecondary.Cores[0].Client.SetToken(perfPrimary.Cores[0].Client.Token())
|
|
perfSecondaryDRSecondary.Cores[0].Client.SetToken(rootToken)
|
|
}
|
|
|
|
func DeriveActiveCore(t testing.T, cluster *vault.TestCluster) *vault.TestClusterCore {
|
|
for i := 0; i < 10; i++ {
|
|
for _, core := range cluster.Cores {
|
|
leaderResp, err := core.Client.Sys().Leader()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if leaderResp.IsSelf {
|
|
return core
|
|
}
|
|
}
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
t.Fatal("could not derive the active core")
|
|
return nil
|
|
}
|
|
|
|
func WaitForNCoresSealed(t testing.T, cluster *vault.TestCluster, n int) {
|
|
for i := 0; i < 10; i++ {
|
|
sealed := 0
|
|
for _, core := range cluster.Cores {
|
|
if core.Core.Sealed() {
|
|
sealed++
|
|
}
|
|
}
|
|
|
|
if sealed >= n {
|
|
return
|
|
}
|
|
time.Sleep(time.Second)
|
|
}
|
|
|
|
t.Fatalf("%d cores were not sealed", n)
|
|
}
|