Skip to content

Commit

Permalink
Use semantic version checking for Consul secrets backend logic (hashi…
Browse files Browse the repository at this point in the history
  • Loading branch information
robmonte authored and marcboudreau committed Nov 6, 2022
1 parent 1c93988 commit 605a102
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 171 deletions.
17 changes: 12 additions & 5 deletions testutil/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -682,16 +682,23 @@ func CheckJSONData(resourceName, attr, expected string) resource.TestCheckFunc {
}
}

// GetImportTestStep for resource name. Optionally include field names that should be ignored during the import
// verification, typically ignore fields should only be provided for values that are not returned from the
// provisioning API.
func GetImportTestStep(resourceName string, skipVerify bool, ignoreFields ...string) resource.TestStep {
return resource.TestStep{
// GetImportTestStep for resource name. If a custom ImportStateCheck function is not desired, pass
// a nil value. Optionally include field names that should be ignored during the import
// verification, typically ignore fields should only be provided for values that are not returned
// from the provisioning API.
func GetImportTestStep(resourceName string, skipVerify bool, check resource.ImportStateCheckFunc, ignoreFields ...string) resource.TestStep {
ts := resource.TestStep{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: !skipVerify,
ImportStateVerifyIgnore: ignoreFields,
}

if check != nil {
ts.ImportStateCheck = check
}

return ts
}

// GetNamespaceImportStateCheck checks that the namespace was properly imported into the state.
Expand Down
2 changes: 1 addition & 1 deletion vault/resource_ad_secret_backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestADSecretBackend(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "userdn", "CN=Users,DC=corp,DC=example,DC=net"),
),
},
testutil.GetImportTestStep(resourceName, false, "bindpass", "description"),
testutil.GetImportTestStep(resourceName, false, nil, "bindpass", "description"),
// TODO: on vault-1.11+ length should conflict with password_policy
// We should re-enable this check when we have the adaptive version support.
//{
Expand Down
77 changes: 36 additions & 41 deletions vault/resource_consul_secret_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package vault
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-provider-vault/internal/consts"
"github.com/hashicorp/terraform-provider-vault/internal/semver"
"log"
"strings"

Expand All @@ -14,14 +17,13 @@ import (

func consulSecretBackendResource() *schema.Resource {
return &schema.Resource{
Create: consulSecretBackendCreate,
Read: ReadWrapper(consulSecretBackendRead),
Update: consulSecretBackendUpdate,
Delete: consulSecretBackendDelete,
Exists: consulSecretBackendExists,
CreateContext: consulSecretBackendCreate,
ReadContext: ReadContextWrapper(consulSecretBackendRead),
UpdateContext: consulSecretBackendUpdate,
DeleteContext: consulSecretBackendDelete,
CustomizeDiff: consulSecretsBackendCustomizeDiff,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -110,10 +112,10 @@ func consulSecretBackendResource() *schema.Resource {
}
}

func consulSecretBackendCreate(d *schema.ResourceData, meta interface{}) error {
func consulSecretBackendCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, e := provider.GetClient(d, meta)
if e != nil {
return e
return diag.FromErr(e)
}

path := d.Get("path").(string)
Expand All @@ -140,8 +142,19 @@ func consulSecretBackendCreate(d *schema.ResourceData, meta interface{}) error {
d.Partial(true)
log.Printf("[DEBUG] Mounting Consul backend at %q", path)

// If a token isn't provided and the Vault version is less than 1.11, fail before
// mounting the path in Vault.
useAPIVer1, _, err := semver.GreaterThanOrEqual(ctx, client, consts.VaultVersion11)
if err != nil {
return diag.Errorf("failed to read Vault client version: %s", err)
}
if token == "" && !useAPIVer1 {
return diag.Errorf(`error writing Consul configuration: no token provided and the
Vault client version does not meet the minimum requirement for this feature (Vault 1.11+)`)
}

if err := client.Sys().Mount(path, info); err != nil {
return fmt.Errorf("Error mounting to %q: %s", path, err)
return diag.Errorf("error mounting to %q: %s", path, err)
}

log.Printf("[DEBUG] Mounted Consul backend at %q", path)
Expand All @@ -160,18 +173,18 @@ func consulSecretBackendCreate(d *schema.ResourceData, meta interface{}) error {
}

if _, err := client.Logical().Write(configPath, data); err != nil {
return fmt.Errorf("Error writing Consul configuration for %q: %s", path, err)
return diag.Errorf("error writing Consul configuration for %q: %s", path, err)
}
log.Printf("[DEBUG] Wrote Consul configuration to %q", configPath)
d.Partial(false)

return consulSecretBackendRead(d, meta)
return consulSecretBackendRead(ctx, d, meta)
}

func consulSecretBackendRead(d *schema.ResourceData, meta interface{}) error {
func consulSecretBackendRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, e := provider.GetClient(d, meta)
if e != nil {
return e
return diag.FromErr(e)
}

path := d.Id()
Expand All @@ -181,7 +194,7 @@ func consulSecretBackendRead(d *schema.ResourceData, meta interface{}) error {

mounts, err := client.Sys().ListMounts()
if err != nil {
return fmt.Errorf("Error reading mount %q: %s", path, err)
return diag.Errorf("error reading mount %q: %s", path, err)
}

// path can have a trailing slash, but doesn't need to have one
Expand All @@ -203,7 +216,7 @@ func consulSecretBackendRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Reading %s from Vault", configPath)
secret, err := client.Logical().Read(configPath)
if err != nil {
return fmt.Errorf("error reading from Vault: %s", err)
return diag.Errorf("error reading from Vault: %s", err)
}

// token, sadly, we can't read out
Expand All @@ -215,10 +228,10 @@ func consulSecretBackendRead(d *schema.ResourceData, meta interface{}) error {
return nil
}

func consulSecretBackendUpdate(d *schema.ResourceData, meta interface{}) error {
func consulSecretBackendUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, e := provider.GetClient(d, meta)
if e != nil {
return e
return diag.FromErr(e)
}

path := d.Id()
Expand All @@ -234,7 +247,7 @@ func consulSecretBackendUpdate(d *schema.ResourceData, meta interface{}) error {

log.Printf("[DEBUG] Updating lease TTLs for %q", path)
if err := client.Sys().TuneMount(path, config); err != nil {
return fmt.Errorf("Error updating mount TTLs for %q: %s", path, err)
return diag.Errorf("error updating mount TTLs for %q: %s", path, err)
}

}
Expand All @@ -250,49 +263,31 @@ func consulSecretBackendUpdate(d *schema.ResourceData, meta interface{}) error {
"client_key": d.Get("client_key").(string),
}
if _, err := client.Logical().Write(configPath, data); err != nil {
return fmt.Errorf("Error configuring Consul configuration for %q: %s", path, err)
return diag.Errorf("error configuring Consul configuration for %q: %s", path, err)
}
log.Printf("[DEBUG] Updated Consul configuration at %q", configPath)
}
d.Partial(false)
return consulSecretBackendRead(d, meta)
return consulSecretBackendRead(ctx, d, meta)
}

func consulSecretBackendDelete(d *schema.ResourceData, meta interface{}) error {
func consulSecretBackendDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, e := provider.GetClient(d, meta)
if e != nil {
return e
return diag.FromErr(e)
}

path := d.Id()

log.Printf("[DEBUG] Unmounting Consul backend %q", path)
err := client.Sys().Unmount(path)
if err != nil {
return fmt.Errorf("Error unmounting Consul backend from %q: %s", path, err)
return diag.Errorf("error unmounting Consul backend from %q: %s", path, err)
}
log.Printf("[DEBUG] Unmounted Consul backend %q", path)
return nil
}

func consulSecretBackendExists(d *schema.ResourceData, meta interface{}) (bool, error) {
client, e := provider.GetClient(d, meta)
if e != nil {
return false, e
}

path := d.Id()

log.Printf("[DEBUG] Checking if Consul backend exists at %q", path)
mounts, err := client.Sys().ListMounts()
if err != nil {
return true, fmt.Errorf("Error retrieving list of mounts: %s", err)
}
log.Printf("[DEBUG] Checked if Consul backend exists at %q", path)
_, ok := mounts[strings.Trim(path, "/")+"/"]
return ok, nil
}

func consulSecretBackendConfigPath(backend string) string {
return strings.Trim(backend, "/") + "/config/access"
}
Expand Down
Loading

0 comments on commit 605a102

Please sign in to comment.