Skip to content

Commit

Permalink
Add enterprise check for new Raft Autopilot parameter (#1721)
Browse files Browse the repository at this point in the history
  • Loading branch information
vinay-gopalan authored Jan 18, 2023
1 parent 00e8d4a commit b5f2b80
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 26 deletions.
29 changes: 28 additions & 1 deletion internal/provider/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import (
"github.com/hashicorp/terraform-provider-vault/internal/consts"
)

const DefaultMaxHTTPRetries = 2
const (
DefaultMaxHTTPRetries = 2
enterpriseMetadata = "ent"
)

var (
MaxHTTPRetriesCCC int
Expand Down Expand Up @@ -101,6 +104,15 @@ func (p *ProviderMeta) IsAPISupported(minVersion *version.Version) bool {
return ver.GreaterThanOrEqual(minVersion)
}

// IsEnterpriseSupported returns a boolean
// describing whether the ProviderMeta
// vaultVersion supports enterprise
// features.
func (p *ProviderMeta) IsEnterpriseSupported() bool {
ver := p.GetVaultVersion()
return strings.Contains(ver.Metadata(), enterpriseMetadata)
}

// GetVaultVersion returns the providerMeta
// vaultVersion attribute.
func (p *ProviderMeta) GetVaultVersion() *version.Version {
Expand Down Expand Up @@ -337,6 +349,21 @@ func IsAPISupported(meta interface{}, minVersion *version.Version) bool {
return p.IsAPISupported(minVersion)
}

// IsEnterpriseSupported confirms that
// the providerMeta API supports enterprise
// features.
func IsEnterpriseSupported(meta interface{}) bool {
var p *ProviderMeta
switch v := meta.(type) {
case *ProviderMeta:
p = v
default:
panic(fmt.Sprintf("meta argument must be a %T, not %T", p, meta))
}

return p.IsEnterpriseSupported()
}

func getVaultVersion(client *api.Client) (*version.Version, error) {
resp, err := client.Sys().SealStatus()
if err != nil {
Expand Down
77 changes: 77 additions & 0 deletions internal/provider/meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,80 @@ func TestIsAPISupported(t *testing.T) {
})
}
}

func TestIsEnterpriseSupported(t *testing.T) {
rootClient, err := api.NewClient(api.DefaultConfig())
if err != nil {
t.Fatalf("error initializing root client, err=%s", err)
}

VaultVersion10, err := version.NewVersion("1.10.0")
if err != nil {
t.Fatal(err)
}

VaultVersion11HSM, err := version.NewVersion("1.11.0+ent.hsm")
if err != nil {
t.Fatal(err)
}

VaultVersion12, err := version.NewVersion("1.12.0+ent")
if err != nil {
t.Fatal(err)
}

testCases := []struct {
name string
expected bool
meta interface{}
}{
{
name: "not-enterprise",
expected: false,
meta: &ProviderMeta{
client: rootClient,
vaultVersion: VaultVersion10,
},
},
{
name: "enterprise-hsm",
expected: true,
meta: &ProviderMeta{
client: rootClient,
vaultVersion: VaultVersion11HSM,
},
},
{
name: "enterprise",
expected: true,
meta: &ProviderMeta{
client: rootClient,
vaultVersion: VaultVersion12,
},
},
}

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
if tt.meta != nil {
m := tt.meta.(*ProviderMeta)
m.resourceData = schema.TestResourceDataRaw(t,
map[string]*schema.Schema{
consts.FieldNamespace: {
Type: schema.TypeString,
Required: true,
},
},
map[string]interface{}{},
)
tt.meta = m
}

isEnterprise := tt.meta.(*ProviderMeta).IsEnterpriseSupported()

if isEnterprise != tt.expected {
t.Errorf("IsEnterpriseSupported() got = %v, want %v", isEnterprise, tt.expected)
}
})
}
}
40 changes: 30 additions & 10 deletions vault/resource_raft_autopilot.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ var (
"max_trailing_logs": 1000,
"min_quorum": 3,
"server_stabilization_time": "10s",
"disable_upgrade_migration": false,
}
)

Expand Down Expand Up @@ -63,7 +62,6 @@ func raftAutopilotConfigResource() *schema.Resource {
"disable_upgrade_migration": {
Type: schema.TypeBool,
Description: "Disables automatically upgrading Vault using autopilot. (Enterprise-only)",
Default: autopilotDefaults["disable_upgrade_migration"],
Optional: true,
},
}
Expand All @@ -73,6 +71,9 @@ func raftAutopilotConfigResource() *schema.Resource {
Read: ReadWrapper(readAutopilotConfigResource),
Delete: deleteAutopilotConfigResource,
Schema: fields,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
}
}

Expand All @@ -82,14 +83,33 @@ func createOrUpdateAutopilotConfigResource(d *schema.ResourceData, meta interfac
return e
}

c := map[string]interface{}{
"cleanup_dead_servers": d.Get("cleanup_dead_servers").(bool),
"last_contact_threshold": d.Get("last_contact_threshold").(string),
"dead_server_last_contact_threshold": d.Get("dead_server_last_contact_threshold").(string),
"max_trailing_logs": d.Get("max_trailing_logs").(int),
"min_quorum": d.Get("min_quorum").(int),
"server_stabilization_time": d.Get("server_stabilization_time").(string),
"disable_upgrade_migration": d.Get("disable_upgrade_migration").(bool),
fields := []string{
"cleanup_dead_servers",
"last_contact_threshold",
"dead_server_last_contact_threshold",
"max_trailing_logs",
"min_quorum",
"server_stabilization_time",
}

c := map[string]interface{}{}

for _, k := range fields {
if v, ok := d.GetOk(k); ok {
c[k] = v
}
}

isEnterprise := provider.IsEnterpriseSupported(meta)
isAPISupported := provider.IsAPISupported(meta, provider.VaultVersion111)
enterpriseAPIField := "disable_upgrade_migration"
val, ok := d.GetOk(enterpriseAPIField)

if (!isAPISupported || !isEnterprise) && ok {
return fmt.Errorf("%s is not supported by "+
"this version of vault", enterpriseAPIField)
} else if (isAPISupported && isEnterprise) && ok {
c[enterpriseAPIField] = val
}

log.Print("[DEBUG] Configuring autopilot")
Expand Down
33 changes: 18 additions & 15 deletions vault/resource_raft_autopilot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,41 @@ import (
)

func TestAccRaftAutopilotConfig_basic(t *testing.T) {
resourceName := "vault_raft_autopilot.test"

resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() {
testutil.TestAccPreCheck(t)
testutil.TestEntPreCheck(t)
testutil.SkipTestEnvSet(t, "SKIP_RAFT_TESTS")
},
CheckDestroy: testAccRaftAutopilotConfigCheckDestroy,
Steps: []resource.TestStep{
{
Config: testAccRaftAutopilotConfig_basic(true, "12h0m0s", 3),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "cleanup_dead_servers", "true"),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "dead_server_last_contact_threshold", "12h0m0s"),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "last_contact_threshold", autopilotDefaults["last_contact_threshold"].(string)),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "max_trailing_logs", strconv.Itoa(autopilotDefaults["max_trailing_logs"].(int))),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "min_quorum", strconv.Itoa(autopilotDefaults["min_quorum"].(int))),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "server_stabilization_time", autopilotDefaults["server_stabilization_time"].(string)),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "disable_upgrade_migration", "false"),
resource.TestCheckResourceAttr(resourceName, "cleanup_dead_servers", "true"),
resource.TestCheckResourceAttr(resourceName, "dead_server_last_contact_threshold", "12h0m0s"),
resource.TestCheckResourceAttr(resourceName, "last_contact_threshold", autopilotDefaults["last_contact_threshold"].(string)),
resource.TestCheckResourceAttr(resourceName, "max_trailing_logs", strconv.Itoa(autopilotDefaults["max_trailing_logs"].(int))),
resource.TestCheckResourceAttr(resourceName, "min_quorum", strconv.Itoa(autopilotDefaults["min_quorum"].(int))),
resource.TestCheckResourceAttr(resourceName, "server_stabilization_time", autopilotDefaults["server_stabilization_time"].(string)),
resource.TestCheckResourceAttr(resourceName, "disable_upgrade_migration", "false"),
),
},
{
Config: testAccRaftAutopilotConfig_updated(true, true, "30s", "20s", "50s", 100, 5),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "cleanup_dead_servers", "true"),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "dead_server_last_contact_threshold", "30s"),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "last_contact_threshold", "20s"),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "max_trailing_logs", "100"),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "min_quorum", "5"),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "server_stabilization_time", "50s"),
resource.TestCheckResourceAttr("vault_raft_autopilot.test", "disable_upgrade_migration", "true"),
resource.TestCheckResourceAttr(resourceName, "cleanup_dead_servers", "true"),
resource.TestCheckResourceAttr(resourceName, "dead_server_last_contact_threshold", "30s"),
resource.TestCheckResourceAttr(resourceName, "last_contact_threshold", "20s"),
resource.TestCheckResourceAttr(resourceName, "max_trailing_logs", "100"),
resource.TestCheckResourceAttr(resourceName, "min_quorum", "5"),
resource.TestCheckResourceAttr(resourceName, "server_stabilization_time", "50s"),
resource.TestCheckResourceAttr(resourceName, "disable_upgrade_migration", "true"),
),
},
testutil.GetImportTestStep(resourceName, false, nil),
},
})
}
Expand Down
8 changes: 8 additions & 0 deletions website/docs/r/raft_autopilot.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,11 @@ stable in the 'healthy' state before being added to the cluster.
## Attributes Reference

No additional attributes are exported by this resource.

## Import

Raft Autopilot config can be imported using the ID, e.g.

```
$ terraform import vault_raft_autopilot.autopilot sys/storage/raft/autopilot/configuration
```

0 comments on commit b5f2b80

Please sign in to comment.