From 5266db31e26712f29d950abe46e22a9a925934d6 Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 3 Sep 2015 22:57:56 +0100 Subject: [PATCH 1/4] Adding the ability to manage a glacier vault --- builtin/providers/aws/config.go | 5 + builtin/providers/aws/provider.go | 1 + .../aws/resource_aws_glacier_vault.go | 380 ++++++++++++++++++ 3 files changed, 386 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_glacier_vault.go diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index 5eac34e8aaac..f8f443b73ddd 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -21,6 +21,7 @@ import ( "github.com/aws/aws-sdk-go/service/elasticache" elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice" "github.com/aws/aws-sdk-go/service/elb" + "github.com/aws/aws-sdk-go/service/glacier" "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/kinesis" "github.com/aws/aws-sdk-go/service/lambda" @@ -67,6 +68,7 @@ type AWSClient struct { elasticacheconn *elasticache.ElastiCache lambdaconn *lambda.Lambda opsworksconn *opsworks.OpsWorks + glacierconn *glacier.Glacier } // Client configures and returns a fully initialized AWSClient @@ -184,6 +186,9 @@ func (c *Config) Client() (interface{}, error) { log.Println("[INFO] Initializing Directory Service connection") client.dsconn = directoryservice.New(awsConfig) + + log.Println("[INFO] Initializing Glacier connection") + client.glacierconn = glacier.New(awsConfig) } if len(errs) > 0 { diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index c740e4bc8c5b..f73580d0f742 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -187,6 +187,7 @@ func Provider() terraform.ResourceProvider { "aws_elasticsearch_domain": resourceAwsElasticSearchDomain(), "aws_elb": resourceAwsElb(), "aws_flow_log": resourceAwsFlowLog(), + "aws_glacier_vault": resourceAwsGlacierVault(), "aws_iam_access_key": resourceAwsIamAccessKey(), "aws_iam_group_policy": resourceAwsIamGroupPolicy(), "aws_iam_group": resourceAwsIamGroup(), diff --git a/builtin/providers/aws/resource_aws_glacier_vault.go b/builtin/providers/aws/resource_aws_glacier_vault.go new file mode 100644 index 000000000000..b077a35cd93a --- /dev/null +++ b/builtin/providers/aws/resource_aws_glacier_vault.go @@ -0,0 +1,380 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/glacier" +) + +func resourceAwsGlacierVault() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsGlacierVaultCreate, + Read: resourceAwsGlacierVaultRead, + Update: resourceAwsGlacierVaultUpdate, + Delete: resourceAwsGlacierVaultDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + 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 allowed in %q", k)) + } + if len(value) > 255 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 255 characters", k)) + } + return + }, + }, + + "location": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "access_policy": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + StateFunc: normalizeJson, + }, + + "notification": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "events": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "sns_topic": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceAwsGlacierVaultCreate(d *schema.ResourceData, meta interface{}) error { + glacierconn := meta.(*AWSClient).glacierconn + + input := &glacier.CreateVaultInput{ + VaultName: aws.String(d.Get("name").(string)), + } + + out, err := glacierconn.CreateVault(input) + if err != nil { + return fmt.Errorf("Error creating Glacier Vault: %s", err) + } + + d.SetId(d.Get("name").(string)) + d.Set("location", *out.Location) + + return resourceAwsGlacierVaultUpdate(d, meta) +} + +func resourceAwsGlacierVaultUpdate(d *schema.ResourceData, meta interface{}) error { + glacierconn := meta.(*AWSClient).glacierconn + + if err := setGlacierVaultTags(glacierconn, d); err != nil { + return err + } + + if d.HasChange("access_policy") { + if err := resourceAwsGlacierVaultPolicyUpdate(glacierconn, d); err != nil { + return err + } + } + + if d.HasChange("notification") { + if err := resourceAwsGlacierVaultNotificationUpdate(glacierconn, d); err != nil { + return err + } + } + + return resourceAwsGlacierVaultRead(d, meta) +} + +func resourceAwsGlacierVaultRead(d *schema.ResourceData, meta interface{}) error { + glacierconn := meta.(*AWSClient).glacierconn + + input := &glacier.DescribeVaultInput{ + VaultName: aws.String(d.Id()), + } + + out, err := glacierconn.DescribeVault(input) + if err != nil { + return fmt.Errorf("Error reading Glacier Vault: %s", err.Error()) + } + + d.Set("arn", *out.VaultARN) + + tags, err := getGlacierVaultTags(glacierconn, d.Id()) + if err != nil { + return err + } + d.Set("tags", tags) + + log.Printf("[DEBUG] Getting the access_policy for Vault %s", d.Id()) + pol, err := glacierconn.GetVaultAccessPolicy(&glacier.GetVaultAccessPolicyInput{ + VaultName: aws.String(d.Id()), + }) + + if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "ResourceNotFoundException" { + d.Set("access_policy", "") + } else if pol != nil { + d.Set("access_policy", normalizeJson(*pol.Policy.Policy)) + } else { + return err + } + + notifications, err := getGlacierVaultNotification(glacierconn, d.Id()) + if err != nil { + return err + } + d.Set("notification", notifications) + + return nil +} + +func resourceAwsGlacierVaultDelete(d *schema.ResourceData, meta interface{}) error { + glacierconn := meta.(*AWSClient).glacierconn + + log.Printf("[DEBUG] Glacier Delete Vault: %s", d.Id()) + _, err := glacierconn.DeleteVault(&glacier.DeleteVaultInput{ + VaultName: aws.String(d.Id()), + }) + if err != nil { + return fmt.Errorf("Error deleting Glacier Vault: %s", err.Error()) + } + return nil +} + +func resourceAwsGlacierVaultNotificationUpdate(glacierconn *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 setup 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() { + event := id.(string) + if event != "ArchiveRetrievalCompleted" && event != "InventoryRetrievalCompleted" { + return fmt.Errorf("Glacier Vault Notification Events can only be 'ArchiveRetrievalCompleted' or 'InventoryRetrievalCompleted'") + } else { + events = append(events, aws.String(event)) + } + } + + _, err := glacierconn.SetVaultNotifications(&glacier.SetVaultNotificationsInput{ + VaultName: aws.String(d.Id()), + VaultNotificationConfig: &glacier.VaultNotificationConfig{ + SNSTopic: aws.String(s["sns_topic"].(string)), + Events: events, + }, + }) + + if err != nil { + return fmt.Errorf("Error Updating Glacier Vault Notifications: %s", err.Error()) + } + } + } + + return nil +} + +func resourceAwsGlacierVaultPolicyUpdate(glacierconn *glacier.Glacier, d *schema.ResourceData) error { + vaultName := d.Id() + policyContents := d.Get("access_policy").(string) + + policy := &glacier.VaultAccessPolicy{ + Policy: aws.String(policyContents), + } + + if policyContents != "" { + log.Printf("[DEBUG] Glacier Vault: %s, put policy", vaultName) + + _, err := glacierconn.SetVaultAccessPolicy(&glacier.SetVaultAccessPolicyInput{ + VaultName: aws.String(d.Id()), + Policy: policy, + }) + + if err != nil { + return fmt.Errorf("Error putting Glacier Vault policy: %s", err.Error()) + } + } else { + log.Printf("[DEBUG] Glacier Vault: %s, delete policy: %s", vaultName, policy) + _, err := glacierconn.DeleteVaultAccessPolicy(&glacier.DeleteVaultAccessPolicyInput{ + VaultName: aws.String(d.Id()), + }) + + if err != nil { + return fmt.Errorf("Error deleting Glacier Vault policy: %s", err.Error()) + } + } + + return nil +} + +func setGlacierVaultTags(conn *glacier.Glacier, d *schema.ResourceData) error { + if d.HasChange("tags") { + oraw, nraw := d.GetChange("tags") + o := oraw.(map[string]interface{}) + n := nraw.(map[string]interface{}) + create, remove := diffGlacierVaultTags(mapGlacierVaultTags(o), mapGlacierVaultTags(n)) + + // Set tags + if len(remove) > 0 { + tagsToRemove := &glacier.RemoveTagsFromVaultInput{ + VaultName: aws.String(d.Id()), + TagKeys: glacierStringsToPointyString(remove), + } + + log.Printf("[DEBUG] Removing tags: from %s", d.Id()) + _, err := conn.RemoveTagsFromVault(tagsToRemove) + if err != nil { + return err + } + } + if len(create) > 0 { + tagsToAdd := &glacier.AddTagsToVaultInput{ + VaultName: aws.String(d.Id()), + Tags: glacierVaultTagsFromMap(create), + } + + log.Printf("[DEBUG] Creating tags: for %s", d.Id()) + _, err := conn.AddTagsToVault(tagsToAdd) + if err != nil { + return err + } + } + } + + return nil +} + +func mapGlacierVaultTags(m map[string]interface{}) map[string]string { + results := make(map[string]string) + for k, v := range m { + results[k] = v.(string) + } + + return results +} + +func diffGlacierVaultTags(oldTags, newTags map[string]string) (map[string]string, []string) { + + create := make(map[string]string) + for k, v := range newTags { + create[k] = v + } + + // Build the list of what to remove + var remove []string + for k, v := range oldTags { + old, ok := create[k] + if !ok || old != v { + // Delete it! + remove = append(remove, k) + } + } + + return create, remove +} + +func getGlacierVaultTags(glacierconn *glacier.Glacier, vaultName string) (map[string]string, error) { + request := &glacier.ListTagsForVaultInput{ + VaultName: aws.String(vaultName), + } + + log.Printf("[DEBUG] Getting the tags: for %s", vaultName) + response, err := glacierconn.ListTagsForVault(request) + if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "NoSuchTagSet" { + return map[string]string{}, nil + } else if err != nil { + return nil, err + } + + return glacierVaultTagsToMap(response.Tags), nil +} + +func glacierVaultTagsToMap(responseTags map[string]*string) map[string]string { + results := make(map[string]string, len(responseTags)) + for k, v := range responseTags { + results[k] = *v + } + + return results +} + +func glacierVaultTagsFromMap(responseTags map[string]string) map[string]*string { + results := make(map[string]*string, len(responseTags)) + for k, v := range responseTags { + results[k] = aws.String(v) + } + + return results +} + +func glacierStringsToPointyString(s []string) []*string { + results := make([]*string, len(s)) + for i, x := range s { + results[i] = aws.String(x) + } + + return results +} + +func glacierPointersToStringList(pointers []*string) []interface{} { + list := make([]interface{}, len(pointers)) + for i, v := range pointers { + list[i] = *v + } + return list +} + +func getGlacierVaultNotification(glacierconn *glacier.Glacier, vaultName string) ([]map[string]interface{}, error) { + request := &glacier.GetVaultNotificationsInput{ + VaultName: aws.String(vaultName), + } + + response, err := glacierconn.GetVaultNotifications(request) + if err != nil { + return nil, fmt.Errorf("Error reading Glacier Vault Notifications: %s", err.Error()) + } + + notifications := make(map[string]interface{}, 0) + + log.Print("[DEBUG] Flattening Glacier Vault Notifications") + + notifications["events"] = schema.NewSet(schema.HashString, glacierPointersToStringList(response.VaultNotificationConfig.Events)) + notifications["sns_topic"] = *response.VaultNotificationConfig.SNSTopic + + return []map[string]interface{}{notifications}, nil +} From 95d35ad77f5609a54f59b454b4860f3aa7dba33c Mon Sep 17 00:00:00 2001 From: stack72 Date: Tue, 15 Sep 2015 23:32:54 +0100 Subject: [PATCH 2/4] Adding the the docs for the Glacier Vault resource Updating the glacier docs to include a link to the AWS developer guide --- .../aws/r/glacier_vault.html.markdown | 68 +++++++++++++++++++ website/source/layouts/aws.erb | 9 +++ 2 files changed, 77 insertions(+) create mode 100644 website/source/docs/providers/aws/r/glacier_vault.html.markdown diff --git a/website/source/docs/providers/aws/r/glacier_vault.html.markdown b/website/source/docs/providers/aws/r/glacier_vault.html.markdown new file mode 100644 index 000000000000..ad7e2a6d1457 --- /dev/null +++ b/website/source/docs/providers/aws/r/glacier_vault.html.markdown @@ -0,0 +1,68 @@ +--- +layout: "aws" +page_title: "AWS: aws_glacier_vault" +sidebar_current: "docs-aws-resource-glacier-vault" +description: |- + Provides a Glacier Vault. +--- + +# aws\_glacier\_vault + +Provides a Glacier Vault Resource. You can refer to the [Glacier Developer Guide](http://docs.aws.amazon.com/amazonglacier/latest/dev/working-with-vaults.html) for a full explanation of the Glacier Vault functionality + +## Example Usage + +``` +resource "aws_glacier_vault" "my_archive" { + name = "MyArchive" + + notification { + sns_topic = "arn:aws:sns:us-west-2:432981146916:MyArchiveTopic" + events = ["ArchiveRetrievalCompleted","InventoryRetrievalCompleted"] + } + + access_policy = < + > + Glacier Resources + + + > IAM Resources From 2a7b8be9f3aae116c5168f10aa19ea6c7273e643 Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 17 Sep 2015 01:46:10 +0100 Subject: [PATCH 3/4] Gofmt of the aws glacier vault resource --- .../aws/resource_aws_glacier_vault.go | 29 +-- .../aws/resource_aws_glacier_vault_test.go | 175 ++++++++++++++++++ .../aws/r/glacier_vault.html.markdown | 19 +- 3 files changed, 206 insertions(+), 17 deletions(-) create mode 100644 builtin/providers/aws/resource_aws_glacier_vault_test.go diff --git a/builtin/providers/aws/resource_aws_glacier_vault.go b/builtin/providers/aws/resource_aws_glacier_vault.go index b077a35cd93a..21ac4d7cc1d3 100644 --- a/builtin/providers/aws/resource_aws_glacier_vault.go +++ b/builtin/providers/aws/resource_aws_glacier_vault.go @@ -143,7 +143,7 @@ func resourceAwsGlacierVaultRead(d *schema.ResourceData, meta interface{}) error VaultName: aws.String(d.Id()), }) - if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "ResourceNotFoundException" { + if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" { d.Set("access_policy", "") } else if pol != nil { d.Set("access_policy", normalizeJson(*pol.Policy.Policy)) @@ -152,10 +152,13 @@ func resourceAwsGlacierVaultRead(d *schema.ResourceData, meta interface{}) error } notifications, err := getGlacierVaultNotification(glacierconn, d.Id()) - if err != nil { + if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" { + d.Set("notification", "") + } else if pol != nil { + d.Set("notification", notifications) + } else { return err } - d.Set("notification", notifications) return nil } @@ -179,17 +182,12 @@ func resourceAwsGlacierVaultNotificationUpdate(glacierconn *glacier.Glacier, d * settings := v.([]interface{}) if len(settings) > 1 { - return fmt.Errorf("Only a single Notification setup is allowed for Glacier Vault") + 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() { - event := id.(string) - if event != "ArchiveRetrievalCompleted" && event != "InventoryRetrievalCompleted" { - return fmt.Errorf("Glacier Vault Notification Events can only be 'ArchiveRetrievalCompleted' or 'InventoryRetrievalCompleted'") - } else { - events = append(events, aws.String(event)) - } + events = append(events, aws.String(id.(string))) } _, err := glacierconn.SetVaultNotifications(&glacier.SetVaultNotificationsInput{ @@ -204,6 +202,15 @@ func resourceAwsGlacierVaultNotificationUpdate(glacierconn *glacier.Glacier, d * return fmt.Errorf("Error Updating Glacier Vault Notifications: %s", err.Error()) } } + } else { + _, err := glacierconn.DeleteVaultNotifications(&glacier.DeleteVaultNotificationsInput{ + VaultName: aws.String(d.Id()), + }) + + if err != nil { + return fmt.Errorf("Error Removing Glacier Vault Notifications: %s", err.Error()) + } + } return nil @@ -315,7 +322,7 @@ func getGlacierVaultTags(glacierconn *glacier.Glacier, vaultName string) (map[st log.Printf("[DEBUG] Getting the tags: for %s", vaultName) response, err := glacierconn.ListTagsForVault(request) - if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "NoSuchTagSet" { + if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "NoSuchTagSet" { return map[string]string{}, nil } else if err != nil { return nil, err diff --git a/builtin/providers/aws/resource_aws_glacier_vault_test.go b/builtin/providers/aws/resource_aws_glacier_vault_test.go new file mode 100644 index 000000000000..fc5e150d949c --- /dev/null +++ b/builtin/providers/aws/resource_aws_glacier_vault_test.go @@ -0,0 +1,175 @@ +package aws + +import ( + "fmt" + "testing" + + "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/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSGlacierVault_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGlacierVaultDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccGlacierVault_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckGlacierVaultExists("aws_glacier_vault.test"), + ), + }, + }, + }) +} + +func TestAccAWSGlacierVault_full(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGlacierVaultDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccGlacierVault_full, + Check: resource.ComposeTestCheckFunc( + testAccCheckGlacierVaultExists("aws_glacier_vault.full"), + ), + }, + }, + }) +} + +func TestAccAWSGlacierVault_RemoveNotifications(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGlacierVaultDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccGlacierVault_full, + Check: resource.ComposeTestCheckFunc( + testAccCheckGlacierVaultExists("aws_glacier_vault.full"), + ), + }, + resource.TestStep{ + Config: testAccGlacierVault_withoutNotification, + Check: resource.ComposeTestCheckFunc( + testAccCheckGlacierVaultExists("aws_glacier_vault.full"), + testAccCheckVaultNotificationsMissing("aws_glacier_vault.full"), + ), + }, + }, + }) +} + +func testAccCheckGlacierVaultExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + glacierconn := testAccProvider.Meta().(*AWSClient).glacierconn + out, err := glacierconn.DescribeVault(&glacier.DescribeVaultInput{ + VaultName: aws.String(rs.Primary.ID), + }) + + if err != nil { + return err + } + + if out.VaultARN == nil { + return fmt.Errorf("No Glacier Vault Found") + } + + if *out.VaultName != rs.Primary.ID { + return fmt.Errorf("Glacier Vault Mismatch - existing: %q, state: %q", + *out.VaultName, rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckVaultNotificationsMissing(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + glacierconn := testAccProvider.Meta().(*AWSClient).glacierconn + out, err := glacierconn.GetVaultNotifications(&glacier.GetVaultNotificationsInput{ + VaultName: aws.String(rs.Primary.ID), + }) + + if awserr, ok := err.(awserr.Error); ok && awserr.Code() != "ResourceNotFoundException" { + return fmt.Errorf("Expected ResourceNotFoundException for Vault %s Notification Block but got %s", rs.Primary.ID, awserr.Code()) + } + + if out.VaultNotificationConfig != nil { + return fmt.Errorf("Vault Notification Block has been found for %s", rs.Primary.ID) + } + + return nil + } + +} + +func testAccCheckGlacierVaultDestroy(s *terraform.State) error { + if len(s.RootModule().Resources) > 0 { + return fmt.Errorf("Expected all resources to be gone, but found: %#v", + s.RootModule().Resources) + } + + return nil +} + +const testAccGlacierVault_basic = ` +resource "aws_glacier_vault" "test" { + name = "my_test_vault" +} +` + +const testAccGlacierVault_full = ` +resource "aws_sns_topic" "aws_sns_topic" { + name = "glacier-sns-topic" +} + +resource "aws_glacier_vault" "full" { + name = "my_test_vault" + notification { + sns_topic = "${aws_sns_topic.aws_sns_topic.arn}" + events = ["ArchiveRetrievalCompleted","InventoryRetrievalCompleted"] + } + tags { + Test="Test1" + } +} +` + +const testAccGlacierVault_withoutNotification = ` +resource "aws_sns_topic" "aws_sns_topic" { + name = "glacier-sns-topic" +} + +resource "aws_glacier_vault" "full" { + name = "my_test_vault" + tags { + Test="Test1" + } +} +` diff --git a/website/source/docs/providers/aws/r/glacier_vault.html.markdown b/website/source/docs/providers/aws/r/glacier_vault.html.markdown index ad7e2a6d1457..920bee4f54b3 100644 --- a/website/source/docs/providers/aws/r/glacier_vault.html.markdown +++ b/website/source/docs/providers/aws/r/glacier_vault.html.markdown @@ -10,14 +10,21 @@ description: |- Provides a Glacier Vault Resource. You can refer to the [Glacier Developer Guide](http://docs.aws.amazon.com/amazonglacier/latest/dev/working-with-vaults.html) for a full explanation of the Glacier Vault functionality +~> **NOTE:** When trying to remove a Glacier Vault, the Vault must be empty. + ## Example Usage ``` + +resource "aws_sns_topic" "aws_sns_topic" { + name = "glacier-sns-topic" +} + resource "aws_glacier_vault" "my_archive" { name = "MyArchive" notification { - sns_topic = "arn:aws:sns:us-west-2:432981146916:MyArchiveTopic" + sns_topic = "${aws_sns_topic.aws_sns_topic.arn}" events = ["ArchiveRetrievalCompleted","InventoryRetrievalCompleted"] } @@ -51,15 +58,15 @@ EOF The following arguments are supported: -* `name` - (Required) The name of the Vault. Names can be between 1 and 255 characters long and the valid characters are a-z, A-Z, 0-9, '_' (underscore), '-' (hyphen), and '.' (period). -* `access_policy` - (Required) The policy document. This is a JSON formatted string. - The heredoc syntax or `file` function is helpful here. -* `notification` - (Required) The notifications for the Vault. Fields documented below. +* `name` - (Required) The name of the Vault. Names can be between 1 and 255 characters long and the valid characters are a-z, A-Z, 0-9, '\_' (underscore), '-' (hyphen), and '.' (period). +* `access_policy` - (Optional) The policy document. This is a JSON formatted string. + The heredoc syntax or `file` function is helpful here. Use the [Glacier Developer Guide](https://docs.aws.amazon.com/amazonglacier/latest/dev/vault-access-policy.html) for more information on Glacier Vault Policy +* `notification` - (Optional) The notifications for the Vault. Fields documented below. * `tags` - (Optional) A mapping of tags to assign to the resource. **notification** supports the following: -* `events` - (Required) You can configure a vault to public a notification for `ArchiveRetrievalCompleted` and `InventoryRetrievalCompleted` events. +* `events` - (Required) You can configure a vault to publish a notification for `ArchiveRetrievalCompleted` and `InventoryRetrievalCompleted` events. * `sns_topic` - (Required) The SNS Topic ARN. The following attributes are exported: From 9f01efae6f027ce6cd646ebb7c018f95a410104d Mon Sep 17 00:00:00 2001 From: stack72 Date: Mon, 5 Oct 2015 11:24:09 +0100 Subject: [PATCH 4/4] Adding a test to make sure that the diffGlacierVaultTags func works as expected --- .../aws/resource_aws_glacier_vault_test.go | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/builtin/providers/aws/resource_aws_glacier_vault_test.go b/builtin/providers/aws/resource_aws_glacier_vault_test.go index fc5e150d949c..4f5c26bf280f 100644 --- a/builtin/providers/aws/resource_aws_glacier_vault_test.go +++ b/builtin/providers/aws/resource_aws_glacier_vault_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "reflect" "testing" "github.com/aws/aws-sdk-go/aws" @@ -67,6 +68,57 @@ func TestAccAWSGlacierVault_RemoveNotifications(t *testing.T) { }) } +func TestDiffGlacierVaultTags(t *testing.T) { + cases := []struct { + Old, New map[string]interface{} + Create map[string]string + Remove []string + }{ + // Basic add/remove + { + Old: map[string]interface{}{ + "foo": "bar", + }, + New: map[string]interface{}{ + "bar": "baz", + }, + Create: map[string]string{ + "bar": "baz", + }, + Remove: []string{ + "foo", + }, + }, + + // Modify + { + Old: map[string]interface{}{ + "foo": "bar", + }, + New: map[string]interface{}{ + "foo": "baz", + }, + Create: map[string]string{ + "foo": "baz", + }, + Remove: []string{ + "foo", + }, + }, + } + + for i, tc := range cases { + c, r := diffGlacierVaultTags(mapGlacierVaultTags(tc.Old), mapGlacierVaultTags(tc.New)) + + if !reflect.DeepEqual(c, tc.Create) { + t.Fatalf("%d: bad create: %#v", i, c) + } + if !reflect.DeepEqual(r, tc.Remove) { + t.Fatalf("%d: bad remove: %#v", i, r) + } + } +} + func testAccCheckGlacierVaultExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name]