Skip to content

Commit

Permalink
resource/aws_glacier_vault: Prevent GetVaultAccessPolicy crash, add p…
Browse files Browse the repository at this point in the history
…lan-time validations, general refactoring (#12645)

Output from acceptance testing:

```
--- PASS: TestAccAWSGlacierVault_disappears (12.48s)
--- PASS: TestAccAWSGlacierVault_basic (17.66s)
--- PASS: TestAccAWSGlacierVault_policy (41.22s)
--- PASS: TestAccAWSGlacierVault_tags (43.39s)
--- PASS: TestAccAWSGlacierVault_notification (46.12s)
```
  • Loading branch information
DrFaust92 committed Jan 29, 2021
1 parent 80871e5 commit c5375a2
Show file tree
Hide file tree
Showing 2 changed files with 322 additions and 126 deletions.
181 changes: 96 additions & 85 deletions aws/resource_aws_glacier_vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"regexp"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/glacier"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
Expand All @@ -31,18 +30,11 @@ func resourceAwsGlacierVault() *schema.Resource {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[.0-9A-Za-z-_]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only alphanumeric characters, hyphens, underscores, and periods are allowed in %q", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 255 characters", k))
}
return
},
ValidateFunc: validation.All(
validation.StringLenBetween(1, 255),
validation.StringMatch(regexp.MustCompile(`^[.0-9A-Za-z-_]+$`),
"only alphanumeric characters, hyphens, underscores, and periods are allowed"),
),
},

"location": {
Expand All @@ -56,29 +48,34 @@ func resourceAwsGlacierVault() *schema.Resource {
},

"access_policy": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringIsJSON,
StateFunc: func(v interface{}) string {
json, _ := structure.NormalizeJsonString(v)
return json
},
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringIsJSON,
DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
},

"notification": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"events": {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{
"ArchiveRetrievalCompleted",
"InventoryRetrievalCompleted",
}, false),
},
Set: schema.HashString,
},
"sns_topic": {
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Required: true,
ValidateFunc: validateArn,
},
},
},
Expand All @@ -90,59 +87,81 @@ func resourceAwsGlacierVault() *schema.Resource {
}

func resourceAwsGlacierVaultCreate(d *schema.ResourceData, meta interface{}) error {
glacierconn := meta.(*AWSClient).glacierconn
conn := meta.(*AWSClient).glacierconn

input := &glacier.CreateVaultInput{
VaultName: aws.String(d.Get("name").(string)),
}

out, err := glacierconn.CreateVault(input)
_, err := conn.CreateVault(input)
if err != nil {
return fmt.Errorf("Error creating Glacier Vault: %s", err)
return fmt.Errorf("Error creating Glacier Vault: %w", err)
}

d.SetId(d.Get("name").(string))
d.Set("location", out.Location)

return resourceAwsGlacierVaultUpdate(d, meta)
if v, ok := d.GetOk("tags"); ok {
if err := keyvaluetags.GlacierUpdateTags(conn, d.Id(), nil, v.(map[string]interface{})); err != nil {
return fmt.Errorf("error updating Glacier Vault (%s) tags: %w", d.Id(), err)
}
}

if _, ok := d.GetOk("access_policy"); ok {
if err := resourceAwsGlacierVaultPolicyUpdate(conn, d); err != nil {
return fmt.Errorf("error updating Glacier Vault (%s) access policy: %w", d.Id(), err)
}
}

if _, ok := d.GetOk("notification"); ok {
if err := resourceAwsGlacierVaultNotificationUpdate(conn, d); err != nil {
return fmt.Errorf("error updating Glacier Vault (%s) notification: %w", d.Id(), err)
}
}

return resourceAwsGlacierVaultRead(d, meta)
}

func resourceAwsGlacierVaultUpdate(d *schema.ResourceData, meta interface{}) error {
glacierconn := meta.(*AWSClient).glacierconn
conn := meta.(*AWSClient).glacierconn

if d.HasChange("tags") {
o, n := d.GetChange("tags")
if err := keyvaluetags.GlacierUpdateTags(glacierconn, d.Id(), o, n); err != nil {
if err := keyvaluetags.GlacierUpdateTags(conn, d.Id(), o, n); err != nil {
return fmt.Errorf("error updating Glacier Vault (%s) tags: %s", d.Id(), err)
}
}

if d.HasChange("access_policy") {
if err := resourceAwsGlacierVaultPolicyUpdate(glacierconn, d); err != nil {
return err
if err := resourceAwsGlacierVaultPolicyUpdate(conn, d); err != nil {
return fmt.Errorf("error updating Glacier Vault (%s) access policy: %w", d.Id(), err)
}
}

if d.HasChange("notification") {
if err := resourceAwsGlacierVaultNotificationUpdate(glacierconn, d); err != nil {
return err
if err := resourceAwsGlacierVaultNotificationUpdate(conn, d); err != nil {
return fmt.Errorf("error updating Glacier Vault (%s) notification: %w", d.Id(), err)
}
}

return resourceAwsGlacierVaultRead(d, meta)
}

func resourceAwsGlacierVaultRead(d *schema.ResourceData, meta interface{}) error {
glacierconn := meta.(*AWSClient).glacierconn
conn := meta.(*AWSClient).glacierconn
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

input := &glacier.DescribeVaultInput{
VaultName: aws.String(d.Id()),
}

out, err := glacierconn.DescribeVault(input)
out, err := conn.DescribeVault(input)
if isAWSErr(err, glacier.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] Glaier Vault (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
if err != nil {
return fmt.Errorf("Error reading Glacier Vault: %s", err.Error())
return fmt.Errorf("Error reading Glacier Vault: %w", err)
}

awsClient := meta.(*AWSClient)
Expand All @@ -155,99 +174,91 @@ func resourceAwsGlacierVaultRead(d *schema.ResourceData, meta interface{}) error
}
d.Set("location", location)

tags, err := keyvaluetags.GlacierListTags(glacierconn, d.Id())
tags, err := keyvaluetags.GlacierListTags(conn, d.Id())

if err != nil {
return fmt.Errorf("error listing tags for Glacier Vault (%s): %s", d.Id(), err)
return fmt.Errorf("error listing tags for Glacier Vault (%s): %w", d.Id(), err)
}

if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %s", err)
return fmt.Errorf("error setting tags: %w", err)
}

log.Printf("[DEBUG] Getting the access_policy for Vault %s", d.Id())
pol, err := glacierconn.GetVaultAccessPolicy(&glacier.GetVaultAccessPolicyInput{
pol, err := conn.GetVaultAccessPolicy(&glacier.GetVaultAccessPolicyInput{
VaultName: aws.String(d.Id()),
})

if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" {
if isAWSErr(err, glacier.ErrCodeResourceNotFoundException, "") {
d.Set("access_policy", "")
} else if pol != nil {
policy, err := structure.NormalizeJsonString(*pol.Policy.Policy)
} else if err != nil {
return fmt.Errorf("error getting access policy for Glacier Vault (%s): %w", d.Id(), err)
} else if pol != nil && pol.Policy != nil {
policy, err := structure.NormalizeJsonString(aws.StringValue(pol.Policy.Policy))
if err != nil {
return fmt.Errorf("access policy contains an invalid JSON: %s", err)
return fmt.Errorf("access policy contains an invalid JSON: %w", err)
}
d.Set("access_policy", policy)
} else {
return err
}

notifications, err := getGlacierVaultNotification(glacierconn, d.Id())
if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" {
d.Set("notification", "")
notifications, err := getGlacierVaultNotification(conn, d.Id())
if isAWSErr(err, glacier.ErrCodeResourceNotFoundException, "") {
d.Set("notification", []map[string]interface{}{})
} else if pol != nil {
d.Set("notification", notifications)
} else {
return err
return fmt.Errorf("error setting notification: %w", err)
}

return nil
}

func resourceAwsGlacierVaultDelete(d *schema.ResourceData, meta interface{}) error {
glacierconn := meta.(*AWSClient).glacierconn
conn := meta.(*AWSClient).glacierconn

log.Printf("[DEBUG] Glacier Delete Vault: %s", d.Id())
_, err := glacierconn.DeleteVault(&glacier.DeleteVaultInput{
_, err := conn.DeleteVault(&glacier.DeleteVaultInput{
VaultName: aws.String(d.Id()),
})
if err != nil {
return fmt.Errorf("Error deleting Glacier Vault: %s", err.Error())
return fmt.Errorf("Error deleting Glacier Vault: %w", err)
}
return nil
}

func resourceAwsGlacierVaultNotificationUpdate(glacierconn *glacier.Glacier, d *schema.ResourceData) error {
func resourceAwsGlacierVaultNotificationUpdate(conn *glacier.Glacier, d *schema.ResourceData) error {

if v, ok := d.GetOk("notification"); ok {
settings := v.([]interface{})

if len(settings) > 1 {
return fmt.Errorf("Only a single Notification Block is allowed for Glacier Vault")
} else if len(settings) == 1 {
s := settings[0].(map[string]interface{})
var events []*string
for _, id := range s["events"].(*schema.Set).List() {
events = append(events, aws.String(id.(string)))
}

_, err := glacierconn.SetVaultNotifications(&glacier.SetVaultNotificationsInput{
VaultName: aws.String(d.Id()),
VaultNotificationConfig: &glacier.VaultNotificationConfig{
SNSTopic: aws.String(s["sns_topic"].(string)),
Events: events,
},
})
s := settings[0].(map[string]interface{})

_, err := conn.SetVaultNotifications(&glacier.SetVaultNotificationsInput{
VaultName: aws.String(d.Id()),
VaultNotificationConfig: &glacier.VaultNotificationConfig{
SNSTopic: aws.String(s["sns_topic"].(string)),
Events: expandStringSet(s["events"].(*schema.Set)),
},
})

if err != nil {
return fmt.Errorf("Error Updating Glacier Vault Notifications: %s", err.Error())
}
if err != nil {
return fmt.Errorf("Error Updating Glacier Vault Notifications: %w", err)
}
} else {
_, err := glacierconn.DeleteVaultNotifications(&glacier.DeleteVaultNotificationsInput{
_, err := conn.DeleteVaultNotifications(&glacier.DeleteVaultNotificationsInput{
VaultName: aws.String(d.Id()),
})

if err != nil {
return fmt.Errorf("Error Removing Glacier Vault Notifications: %s", err.Error())
return fmt.Errorf("Error Removing Glacier Vault Notifications: %w", err)
}

}

return nil
}

func resourceAwsGlacierVaultPolicyUpdate(glacierconn *glacier.Glacier, d *schema.ResourceData) error {
func resourceAwsGlacierVaultPolicyUpdate(conn *glacier.Glacier, d *schema.ResourceData) error {
vaultName := d.Id()
policyContents := d.Get("access_policy").(string)

Expand All @@ -258,22 +269,22 @@ func resourceAwsGlacierVaultPolicyUpdate(glacierconn *glacier.Glacier, d *schema
if policyContents != "" {
log.Printf("[DEBUG] Glacier Vault: %s, put policy", vaultName)

_, err := glacierconn.SetVaultAccessPolicy(&glacier.SetVaultAccessPolicyInput{
_, err := conn.SetVaultAccessPolicy(&glacier.SetVaultAccessPolicyInput{
VaultName: aws.String(d.Id()),
Policy: policy,
})

if err != nil {
return fmt.Errorf("Error putting Glacier Vault policy: %s", err.Error())
return fmt.Errorf("Error putting Glacier Vault policy: %w", err)
}
} else {
log.Printf("[DEBUG] Glacier Vault: %s, delete policy: %s", vaultName, policy)
_, err := glacierconn.DeleteVaultAccessPolicy(&glacier.DeleteVaultAccessPolicyInput{
_, err := conn.DeleteVaultAccessPolicy(&glacier.DeleteVaultAccessPolicyInput{
VaultName: aws.String(d.Id()),
})

if err != nil {
return fmt.Errorf("Error deleting Glacier Vault policy: %s", err.Error())
return fmt.Errorf("Error deleting Glacier Vault policy: %w", err)
}
}

Expand All @@ -287,14 +298,14 @@ func buildGlacierVaultLocation(accountId, vaultName string) (string, error) {
return fmt.Sprintf("/" + accountId + "/vaults/" + vaultName), nil
}

func getGlacierVaultNotification(glacierconn *glacier.Glacier, vaultName string) ([]map[string]interface{}, error) {
func getGlacierVaultNotification(conn *glacier.Glacier, vaultName string) ([]map[string]interface{}, error) {
request := &glacier.GetVaultNotificationsInput{
VaultName: aws.String(vaultName),
}

response, err := glacierconn.GetVaultNotifications(request)
response, err := conn.GetVaultNotifications(request)
if err != nil {
return nil, fmt.Errorf("Error reading Glacier Vault Notifications: %s", err.Error())
return nil, fmt.Errorf("Error reading Glacier Vault Notifications: %w", err)
}

notifications := make(map[string]interface{})
Expand Down
Loading

0 comments on commit c5375a2

Please sign in to comment.