mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 02:02:43 +00:00
* Remove CE-only warning from shared tests * Add tests for all warnings emitted during raft config parsing * Unmark warnings as CE only that are universal
431 lines
12 KiB
Go
431 lines
12 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package raft
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/go-hclog"
|
|
"github.com/hashicorp/vault/helper/constants"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func ceOnlyWarnings(warns ...string) []string {
|
|
if !constants.IsEnterprise {
|
|
return warns
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func TestRaft_ParseConfig(t *testing.T) {
|
|
// Note some of these can be parallel tests but since we need to setEnv in
|
|
// some we can't make them all parallel so it's don inside the loop. We assume
|
|
// if a case doesn't set anything on the Env it's safe to run in parallel.
|
|
tcs := []struct {
|
|
name string
|
|
conf map[string]string
|
|
env map[string]string
|
|
wantMutation func(cfg *RaftBackendConfig)
|
|
wantErr string
|
|
wantWarns []string
|
|
}{
|
|
// RAFT WAL --------------------------------------------------------------
|
|
{
|
|
name: "WAL backend junk",
|
|
conf: map[string]string{
|
|
"raft_wal": "notabooleanlol",
|
|
},
|
|
wantErr: "does not parse as a boolean",
|
|
},
|
|
{
|
|
name: "WAL verifier junk",
|
|
conf: map[string]string{
|
|
"raft_wal": "true",
|
|
"raft_log_verifier_enabled": "notabooleanlol",
|
|
},
|
|
wantErr: "does not parse as a boolean",
|
|
},
|
|
{
|
|
name: "WAL verifier interval, zero",
|
|
conf: map[string]string{
|
|
"raft_log_verifier_enabled": "true",
|
|
"raft_log_verification_interval": "0s",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RaftLogVerifierEnabled = true
|
|
cfg.RaftLogVerificationInterval = defaultRaftLogVerificationInterval
|
|
},
|
|
wantWarns: []string{"raft_log_verification_interval is less than the minimum allowed"},
|
|
},
|
|
{
|
|
name: "WAL verifier interval, one",
|
|
conf: map[string]string{
|
|
"raft_log_verifier_enabled": "true",
|
|
"raft_log_verification_interval": "0s",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RaftLogVerifierEnabled = true
|
|
|
|
// Below min so should get default
|
|
cfg.RaftLogVerificationInterval = defaultRaftLogVerificationInterval
|
|
},
|
|
wantWarns: []string{"raft_log_verification_interval is less than the minimum allowed"},
|
|
},
|
|
{
|
|
name: "WAL verifier interval, nothing",
|
|
conf: map[string]string{
|
|
"raft_log_verifier_enabled": "true",
|
|
"raft_log_verification_interval": "",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RaftLogVerifierEnabled = true
|
|
cfg.RaftLogVerificationInterval = defaultRaftLogVerificationInterval
|
|
},
|
|
wantWarns: []string{"raft_log_verification_interval is less than the minimum allowed"},
|
|
},
|
|
{
|
|
name: "WAL verifier interval, valid",
|
|
conf: map[string]string{
|
|
"raft_log_verifier_enabled": "true",
|
|
"raft_log_verification_interval": "75s",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RaftLogVerifierEnabled = true
|
|
cfg.RaftLogVerificationInterval = 75 * time.Second
|
|
},
|
|
},
|
|
{
|
|
name: "WAL verifier interval, junk",
|
|
conf: map[string]string{
|
|
"raft_log_verifier_enabled": "true",
|
|
"raft_log_verification_interval": "notaduration",
|
|
},
|
|
wantErr: "does not parse as a duration",
|
|
},
|
|
|
|
// AUTOPILOT Upgrades ----------------------------------------------------
|
|
{
|
|
name: "Autopilot upgrade version, junk",
|
|
conf: map[string]string{
|
|
"autopilot_upgrade_version": "hahano",
|
|
},
|
|
wantErr: "does not parse",
|
|
},
|
|
|
|
// AUTOPILOT Redundancy Zone ---------------------------------------------
|
|
{
|
|
name: "Autopilot redundancy zone, ok",
|
|
conf: map[string]string{
|
|
"autopilot_redundancy_zone": "us-east-1a",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.AutopilotRedundancyZone = "us-east-1a"
|
|
},
|
|
wantWarns: ceOnlyWarnings("configuration for a Vault Enterprise feature has been ignored: field=autopilot_redundancy_zone"),
|
|
},
|
|
|
|
// Non-voter config ------------------------------------------------------
|
|
{
|
|
name: "non-voter, no retry-join, valid false",
|
|
conf: map[string]string{
|
|
raftNonVoterConfigKey: "false",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
// Should be default
|
|
},
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, valid false",
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
raftNonVoterConfigKey: "false",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RetryJoin = "not-empty"
|
|
},
|
|
},
|
|
{
|
|
name: "non-voter, no retry-join, valid true",
|
|
conf: map[string]string{
|
|
raftNonVoterConfigKey: "true",
|
|
},
|
|
wantErr: "only valid if at least one retry_join stanza is specified",
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, valid true",
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
raftNonVoterConfigKey: "true",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RetryJoin = "not-empty"
|
|
cfg.RaftNonVoter = true
|
|
},
|
|
wantWarns: ceOnlyWarnings("configuration for a Vault Enterprise feature has been ignored: field=retry_join_as_non_voter"),
|
|
},
|
|
{
|
|
name: "non-voter, no retry-join, invalid empty",
|
|
conf: map[string]string{
|
|
raftNonVoterConfigKey: "",
|
|
},
|
|
wantErr: "failed to parse retry_join_as_non_voter",
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, invalid empty",
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
raftNonVoterConfigKey: "",
|
|
},
|
|
wantErr: "failed to parse retry_join_as_non_voter",
|
|
},
|
|
{
|
|
name: "non-voter, no retry-join, invalid truthy",
|
|
conf: map[string]string{
|
|
raftNonVoterConfigKey: "no",
|
|
},
|
|
wantErr: "failed to parse retry_join_as_non_voter",
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, invalid truthy",
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
raftNonVoterConfigKey: "no",
|
|
},
|
|
wantErr: "failed to parse retry_join_as_non_voter",
|
|
},
|
|
{
|
|
name: "non-voter, no retry-join, invalid",
|
|
conf: map[string]string{
|
|
raftNonVoterConfigKey: "totallywrong",
|
|
},
|
|
wantErr: "failed to parse retry_join_as_non_voter",
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, invalid",
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
raftNonVoterConfigKey: "totallywrong",
|
|
},
|
|
wantErr: "failed to parse retry_join_as_non_voter",
|
|
},
|
|
{
|
|
// Note for historical reasons we treat any non-empty value as true in ENV
|
|
// vars.
|
|
name: "non-voter, no retry-join, valid env false",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "false",
|
|
},
|
|
wantErr: "only valid if at least one retry_join stanza is specified",
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, valid env false",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "false",
|
|
},
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RetryJoin = "not-empty"
|
|
cfg.RaftNonVoter = true // Any non-empty value is true
|
|
},
|
|
wantWarns: ceOnlyWarnings("configuration for a Vault Enterprise feature has been ignored: field=retry_join_as_non_voter"),
|
|
},
|
|
{
|
|
name: "non-voter, no retry-join, valid env true",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "true",
|
|
},
|
|
wantErr: "only valid if at least one retry_join stanza is specified",
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, valid env true",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "true",
|
|
},
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RetryJoin = "not-empty"
|
|
cfg.RaftNonVoter = true
|
|
},
|
|
wantWarns: ceOnlyWarnings("configuration for a Vault Enterprise feature has been ignored: field=retry_join_as_non_voter"),
|
|
},
|
|
{
|
|
name: "non-voter, no retry-join, valid env not-boolean",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "anything",
|
|
},
|
|
wantErr: "only valid if at least one retry_join stanza is specified",
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, valid env not-boolean",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "anything",
|
|
},
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RetryJoin = "not-empty"
|
|
cfg.RaftNonVoter = true
|
|
},
|
|
wantWarns: ceOnlyWarnings("configuration for a Vault Enterprise feature has been ignored: field=retry_join_as_non_voter"),
|
|
},
|
|
{
|
|
name: "non-voter, no retry-join, valid env empty",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
// Default
|
|
},
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, valid env empty",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "",
|
|
},
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RetryJoin = "not-empty"
|
|
},
|
|
},
|
|
{
|
|
name: "non-voter, no retry-join, both set env preferred",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "true",
|
|
},
|
|
conf: map[string]string{
|
|
raftNonVoterConfigKey: "false",
|
|
},
|
|
wantErr: "only valid if at least one retry_join stanza is specified",
|
|
},
|
|
{
|
|
name: "non-voter, retry-join, both set env preferred",
|
|
env: map[string]string{
|
|
EnvVaultRaftNonVoter: "true",
|
|
},
|
|
conf: map[string]string{
|
|
"retry_join": "not-empty",
|
|
raftNonVoterConfigKey: "false",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.RetryJoin = "not-empty"
|
|
cfg.RaftNonVoter = true // Env should win
|
|
},
|
|
wantWarns: ceOnlyWarnings("configuration for a Vault Enterprise feature has been ignored: field=retry_join_as_non_voter"),
|
|
},
|
|
|
|
// Entry Size Limits -----------------------------------------------------
|
|
{
|
|
name: "entry size, happy path",
|
|
conf: map[string]string{
|
|
"max_entry_size": "123456",
|
|
"max_mount_and_namespace_table_entry_size": "654321",
|
|
},
|
|
wantMutation: func(cfg *RaftBackendConfig) {
|
|
cfg.MaxEntrySize = 123456
|
|
cfg.MaxMountAndNamespaceTableEntrySize = 654321
|
|
},
|
|
wantWarns: ceOnlyWarnings("configuration for a Vault Enterprise feature has been ignored: field=max_mount_and_namespace_table_entry_size"),
|
|
},
|
|
{
|
|
name: "entry size, junk entry size",
|
|
conf: map[string]string{
|
|
"max_entry_size": "sadfsaf",
|
|
"max_mount_and_namespace_table_entry_size": "654321",
|
|
},
|
|
wantErr: "failed to parse 'max_entry_size'",
|
|
},
|
|
{
|
|
name: "entry size, junk mount entry size",
|
|
conf: map[string]string{
|
|
"max_entry_size": "123456",
|
|
"max_mount_and_namespace_table_entry_size": "1MiB",
|
|
},
|
|
wantErr: "failed to parse 'max_mount_and_namespace_table_entry_size'",
|
|
},
|
|
{
|
|
name: "entry size, way too small mount entry size",
|
|
conf: map[string]string{
|
|
"max_mount_and_namespace_table_entry_size": "1",
|
|
},
|
|
wantErr: "'max_mount_and_namespace_table_entry_size' must be at least 1024 bytes",
|
|
},
|
|
{
|
|
name: "entry size, way too big mount entry size",
|
|
conf: map[string]string{
|
|
"max_mount_and_namespace_table_entry_size": "20000000",
|
|
},
|
|
wantErr: "'max_mount_and_namespace_table_entry_size' must be at most 10,485,760 bytes (10MiB)",
|
|
},
|
|
}
|
|
|
|
// Set a nodeid and path to remove noise from all the test cases.
|
|
baseConf := map[string]string{
|
|
"node_id": "abc123",
|
|
"path": "/dummy/path",
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
tc := tc
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if len(tc.env) == 0 {
|
|
// Only run in parallel if there are no env vars to set.
|
|
t.Parallel()
|
|
}
|
|
|
|
var logs bytes.Buffer
|
|
logger := hclog.New(&hclog.LoggerOptions{
|
|
Level: hclog.Warn,
|
|
Output: &logs,
|
|
})
|
|
|
|
if tc.conf == nil {
|
|
tc.conf = make(map[string]string)
|
|
}
|
|
|
|
for k, v := range baseConf {
|
|
if _, ok := tc.conf[k]; !ok {
|
|
tc.conf[k] = v
|
|
}
|
|
}
|
|
|
|
// Make a default-valued config to compare against later. Note we do this
|
|
// before setting ENV as that would could change behavior!
|
|
wantCfg, err := parseRaftBackendConfig(baseConf, hclog.NewNullLogger())
|
|
require.NoError(t, err)
|
|
|
|
for k, v := range tc.env {
|
|
t.Setenv(k, v)
|
|
}
|
|
|
|
cfg, err := parseRaftBackendConfig(tc.conf, logger)
|
|
|
|
if tc.wantErr != "" {
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), tc.wantErr)
|
|
return
|
|
}
|
|
|
|
tc.wantMutation(wantCfg)
|
|
|
|
require.Equal(t, wantCfg, cfg)
|
|
allLogs := logs.String()
|
|
for _, warn := range tc.wantWarns {
|
|
require.Contains(t, allLogs, warn)
|
|
}
|
|
if len(tc.wantWarns) == 0 {
|
|
require.NotContains(t, allLogs, "[WARN]", "no warnings expected")
|
|
}
|
|
})
|
|
}
|
|
}
|