From 68734517f8a0d028e80ebe95cdbf8b6a66e35e2b Mon Sep 17 00:00:00 2001 From: John Ewart Date: Thu, 14 May 2015 16:17:18 -0700 Subject: [PATCH 1/4] Initial SNS support --- builtin/providers/aws/config.go | 5 + builtin/providers/aws/provider.go | 2 + .../providers/aws/resource_aws_sns_topic.go | 146 +++++++++++++++ .../resource_aws_sns_topic_subscription.go | 174 ++++++++++++++++++ ...esource_aws_sns_topic_subscription_test.go | 98 ++++++++++ .../aws/resource_aws_sns_topic_test.go | 88 +++++++++ .../providers/aws/r/sns_topic.html.markdown | 36 ++++ .../r/sns_topic_subscription.html.markdown | 91 +++++++++ website/source/layouts/aws.erb | 8 + 9 files changed, 648 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_sns_topic.go create mode 100644 builtin/providers/aws/resource_aws_sns_topic_subscription.go create mode 100644 builtin/providers/aws/resource_aws_sns_topic_subscription_test.go create mode 100644 builtin/providers/aws/resource_aws_sns_topic_test.go create mode 100644 website/source/docs/providers/aws/r/sns_topic.html.markdown create mode 100644 website/source/docs/providers/aws/r/sns_topic_subscription.html.markdown diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index ff742436b1e6..8f299ed5ae95 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -18,6 +18,7 @@ import ( "github.com/awslabs/aws-sdk-go/service/route53" "github.com/awslabs/aws-sdk-go/service/s3" "github.com/awslabs/aws-sdk-go/service/sqs" + "github.com/awslabs/aws-sdk-go/service/sns" ) type Config struct { @@ -37,6 +38,7 @@ type AWSClient struct { autoscalingconn *autoscaling.AutoScaling s3conn *s3.S3 sqsconn *sqs.SQS + snsconn *sns.SNS r53conn *route53.Route53 region string rdsconn *rds.RDS @@ -89,6 +91,9 @@ func (c *Config) Client() (interface{}, error) { log.Println("[INFO] Initializing SQS connection") client.sqsconn = sqs.New(awsConfig) + log.Println("[INFO] Initializing SNS connection") + client.snsconn = sns.New(awsConfig) + log.Println("[INFO] Initializing RDS Connection") client.rdsconn = rds.New(awsConfig) diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index db90549d2f0d..605097935379 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -123,6 +123,8 @@ func Provider() terraform.ResourceProvider { "aws_security_group": resourceAwsSecurityGroup(), "aws_security_group_rule": resourceAwsSecurityGroupRule(), "aws_sqs_queue": resourceAwsSqsQueue(), + "aws_sns_topic": resourceAwsSnsTopic(), + "aws_sns_topic_subscription": resourceAwsSnsTopicSubscription(), "aws_subnet": resourceAwsSubnet(), "aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(), "aws_vpc_dhcp_options": resourceAwsVpcDhcpOptions(), diff --git a/builtin/providers/aws/resource_aws_sns_topic.go b/builtin/providers/aws/resource_aws_sns_topic.go new file mode 100644 index 000000000000..f327927a4024 --- /dev/null +++ b/builtin/providers/aws/resource_aws_sns_topic.go @@ -0,0 +1,146 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/sns" +) + +// Mutable attributes +var SNSAttributeMap = map[string]string{ + "display_name" : "DisplayName", + "policy" : "Policy", + "delivery_policy": "DeliveryPolicy", +} + + +func resourceAwsSnsTopic() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSnsTopicCreate, + Read: resourceAwsSnsTopicRead, + Update: resourceAwsSnsTopicUpdate, + Delete: resourceAwsSnsTopicDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "display_name": &schema.Schema{ + Type: schema.TypeString, + Required: false, + ForceNew: false, + }, + "policy": &schema.Schema{ + Type: schema.TypeString, + Required: false, + ForceNew: false, + Computed: true, + }, + "delivery_policy": &schema.Schema{ + Type: schema.TypeString, + Required: false, + ForceNew: false, + }, + }, + } +} + +func resourceAwsSnsTopicCreate(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + name := d.Get("name").(string) + + log.Printf("[DEBUG] SNS create topic: %s", name) + + req := &sns.CreateTopicInput{ + Name: aws.String(name), + } + + output, err := snsconn.CreateTopic(req) + if err != nil { + return fmt.Errorf("Error creating SNS topic: %s", err) + } + + d.SetId(*output.TopicARN) + + return resourceAwsSnsTopicUpdate(d, meta) +} + +func resourceAwsSnsTopicUpdate(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + resource := *resourceAwsSnsTopic() + + for k, _ := range resource.Schema { + if attrKey, ok := SNSAttributeMap[k]; ok { + if d.HasChange(k) { + log.Printf("[DEBUG] Updating %s", attrKey) + _, n := d.GetChange(k) + // Ignore an empty policy + if !(k == "policy" && n == "") { + // Make API call to update attributes + req := &sns.SetTopicAttributesInput{ + TopicARN: aws.String(d.Id()), + AttributeName: aws.String(attrKey), + AttributeValue: aws.String(n.(string)), + } + snsconn.SetTopicAttributes(req) + } + } + } + } + + return resourceAwsSnsTopicRead(d, meta) +} + +func resourceAwsSnsTopicRead(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + attributeOutput, err := snsconn.GetTopicAttributes(&sns.GetTopicAttributesInput{ + TopicARN: aws.String(d.Id()), + }) + if err != nil { + return err + } + + if attributeOutput.Attributes != nil && len(*attributeOutput.Attributes) > 0 { + attrmap := *attributeOutput.Attributes + resource := *resourceAwsSnsTopic() + // iKey = internal struct key, oKey = AWS Attribute Map key + for iKey, oKey := range SNSAttributeMap { + log.Printf("[DEBUG] Updating %s => %s", iKey, oKey) + + if attrmap[oKey] != nil { + // Some of the fetched attributes are stateful properties such as + // the number of subscriptions, the owner, etc. skip those + if resource.Schema[iKey] != nil { + value := *attrmap[oKey] + log.Printf("[DEBUG] Updating %s => %s -> %s", iKey, oKey, value) + d.Set(iKey, *attrmap[oKey]) + + } + } + } + } + + return nil +} + +func resourceAwsSnsTopicDelete(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + log.Printf("[DEBUG] SNS Delete Topic: %s", d.Id()) + _, err := snsconn.DeleteTopic(&sns.DeleteTopicInput{ + TopicARN: aws.String(d.Id()), + }) + if err != nil { + return err + } + return nil +} \ No newline at end of file diff --git a/builtin/providers/aws/resource_aws_sns_topic_subscription.go b/builtin/providers/aws/resource_aws_sns_topic_subscription.go new file mode 100644 index 000000000000..542de0021b95 --- /dev/null +++ b/builtin/providers/aws/resource_aws_sns_topic_subscription.go @@ -0,0 +1,174 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/sns" +) + + +func resourceAwsSnsTopicSubscription() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSnsTopicSubscriptionCreate, + Read: resourceAwsSnsTopicSubscriptionRead, + Update: resourceAwsSnsTopicSubscriptionUpdate, + Delete: resourceAwsSnsTopicSubscriptionDelete, + + Schema: map[string]*schema.Schema{ + "protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "endpoint": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "topic_arn": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "delivery_policy": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "raw_message_delivery": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: false, + Default: false, + }, + }, + } +} + +func resourceAwsSnsTopicSubscriptionCreate(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + if(d.Get("protocol") == "email") { + return fmt.Errorf("Email endpoints are not supported!") + } + + output, err := subscribeToSNSTopic(d, snsconn) + + if err != nil { + return err + } + + log.Printf("New subscription ARN: %s", *output.SubscriptionARN) + d.SetId(*output.SubscriptionARN) + + return resourceAwsSnsTopicSubscriptionUpdate(d, meta) +} + +func resourceAwsSnsTopicSubscriptionUpdate(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + // If any changes happened, un-subscribe and re-subscribe + if d.HasChange("protocol") || d.HasChange("endpoint") || d.HasChange("topic_arn") { + log.Printf("[DEBUG] Updating subscription %s", d.Id()) + // Unsubscribe + _, err := snsconn.Unsubscribe(&sns.UnsubscribeInput{ + SubscriptionARN: aws.String(d.Id()), + }) + + if err != nil { + return fmt.Errorf("Error unsubscribing from SNS topic: %s", err) + } + + // Re-subscribe and set id + output, err := subscribeToSNSTopic(d, snsconn) + d.SetId(*output.SubscriptionARN) + + } + + if d.HasChange("raw_message_delivery") { + _, n := d.GetChange("raw_message_delivery") + + attrValue := "false" + + if n.(bool) { + attrValue = "true" + } + + req := &sns.SetSubscriptionAttributesInput{ + SubscriptionARN: aws.String(d.Id()), + AttributeName: aws.String("RawMessageDelivery"), + AttributeValue: aws.String(attrValue), + } + _, err := snsconn.SetSubscriptionAttributes(req) + + if err != nil { + return fmt.Errorf("Unable to set raw message delivery attribute on subscription") + } + } + + return resourceAwsSnsTopicSubscriptionRead(d, meta) +} + +func resourceAwsSnsTopicSubscriptionRead(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + log.Printf("[DEBUG] Loading subscription %s", d.Id()) + + attributeOutput, err := snsconn.GetSubscriptionAttributes(&sns.GetSubscriptionAttributesInput{ + SubscriptionARN: aws.String(d.Id()), + }) + if err != nil { + return err + } + + if attributeOutput.Attributes != nil && len(*attributeOutput.Attributes) > 0 { + attrHash := *attributeOutput.Attributes + log.Printf("[DEBUG] raw message delivery: %s", *attrHash["RawMessageDelivery"]) + if *attrHash["RawMessageDelivery"] == "true" { + d.Set("raw_message_delivery", true) + } else { + d.Set("raw_message_delivery", false) + } + } + + return nil +} + +func resourceAwsSnsTopicSubscriptionDelete(d *schema.ResourceData, meta interface{}) error { + snsconn := meta.(*AWSClient).snsconn + + log.Printf("[DEBUG] SNS delete topic subscription: %s", d.Id()) + _, err := snsconn.Unsubscribe(&sns.UnsubscribeInput{ + SubscriptionARN: aws.String(d.Id()), + }) + if err != nil { + return err + } + return nil +} + +func subscribeToSNSTopic(d *schema.ResourceData, snsconn *sns.SNS) (output *sns.SubscribeOutput, err error) { + protocol := d.Get("protocol").(string) + endpoint := d.Get("endpoint").(string) + topic_arn := d.Get("topic_arn").(string) + + log.Printf("[DEBUG] SNS create topic subscription: %s (%s) @ '%s'", endpoint, protocol, topic_arn) + + req := &sns.SubscribeInput{ + Protocol: aws.String(protocol), + Endpoint: aws.String(endpoint), + TopicARN: aws.String(topic_arn), + } + + output, err = snsconn.Subscribe(req) + if err != nil { + return nil, fmt.Errorf("Error creating SNS topic: %s", err) + } + + log.Printf("[DEBUG] Created new subscription!") + return output, nil +} \ No newline at end of file diff --git a/builtin/providers/aws/resource_aws_sns_topic_subscription_test.go b/builtin/providers/aws/resource_aws_sns_topic_subscription_test.go new file mode 100644 index 000000000000..6c630a4f98fd --- /dev/null +++ b/builtin/providers/aws/resource_aws_sns_topic_subscription_test.go @@ -0,0 +1,98 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/sns" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSSNSTopicSubscription(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSNSTopicSubscriptionDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSSNSTopicSubscriptionConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSNSTopicExists("aws_sns_topic.test_topic"), + testAccCheckAWSSNSTopicSubscriptionExists("aws_sns_topic_subscription.test_subscription"), + ), + }, + }, + }) +} + + +func testAccCheckAWSSNSTopicSubscriptionDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).snsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_sns_topic" { + continue + } + + // Try to find key pair + req := &sns.GetSubscriptionAttributesInput{ + SubscriptionARN: aws.String(rs.Primary.ID), + } + + + _, err := conn.GetSubscriptionAttributes(req) + + if err == nil { + return fmt.Errorf("Subscription still exists, can't continue.") + } + + // Verify the error is an API error, not something else + _, ok := err.(aws.APIError) + if !ok { + return err + } + } + + return nil +} + + +func testAccCheckAWSSNSTopicSubscriptionExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No SNS subscription with that ARN exists") + } + + conn := testAccProvider.Meta().(*AWSClient).snsconn + + params := &sns.GetSubscriptionAttributesInput{ + SubscriptionARN: aws.String(rs.Primary.ID), + } + _, err := conn.GetSubscriptionAttributes(params) + + if err != nil { + return err + } + + return nil + } +} + +const testAccAWSSNSTopicSubscriptionConfig = ` +resource "aws_sns_topic" "test_topic" { + name = "terraform-test-topic" +} + +resource "aws_sns_topic_subscription" "test_subscription" { + topic_arn = "${aws_sns_topic.test_topic.id}" + protocol = "sqs" + endpoint = "arn:aws:sqs:us-west-2:432981146916:terraform-queue-too" +} +` \ No newline at end of file diff --git a/builtin/providers/aws/resource_aws_sns_topic_test.go b/builtin/providers/aws/resource_aws_sns_topic_test.go new file mode 100644 index 000000000000..ab52bd1524c1 --- /dev/null +++ b/builtin/providers/aws/resource_aws_sns_topic_test.go @@ -0,0 +1,88 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/sns" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSSNSTopic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSNSTopicDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSSNSTopicConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSNSTopicExists("aws_sns_topic.test_topic"), + ), + }, + }, + }) +} + + +func testAccCheckAWSSNSTopicDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).snsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_sns_topic" { + continue + } + + // Check if the topic exists by fetching its attributes + params := &sns.GetTopicAttributesInput{ + TopicARN: aws.String(rs.Primary.ID), + } + _, err := conn.GetTopicAttributes(params) + if err == nil { + return fmt.Errorf("Topic exists when it should be destroyed!") + } + + // Verify the error is an API error, not something else + _, ok := err.(aws.APIError) + if !ok { + return err + } + } + + return nil +} + + +func testAccCheckAWSSNSTopicExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No SNS topic with that ARN exists") + } + + conn := testAccProvider.Meta().(*AWSClient).snsconn + + params := &sns.GetTopicAttributesInput{ + TopicARN: aws.String(rs.Primary.ID), + } + _, err := conn.GetTopicAttributes(params) + + if err != nil { + return err + } + + return nil + } +} + +const testAccAWSSNSTopicConfig = ` +resource "aws_sns_topic" "test_topic" { + name = "terraform-test-topic" +} +` \ No newline at end of file diff --git a/website/source/docs/providers/aws/r/sns_topic.html.markdown b/website/source/docs/providers/aws/r/sns_topic.html.markdown new file mode 100644 index 000000000000..1306d780d71d --- /dev/null +++ b/website/source/docs/providers/aws/r/sns_topic.html.markdown @@ -0,0 +1,36 @@ +--- +layout: "aws" +page_title: "AWS: sns_topic" +sidebar_current: "docs-aws-resource-sns-topic" +description: |- + Provides an SNS topic resource. +--- + +# aws\_sns\_topic + +Provides an SNS topic resource + +## Example Usage + +``` +resource "aws_sns_topic" "user_updates" { + name = "user-updates-topic" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The friendly name for the SNS topic +* `policy` - (Optional) The fully-formed AWS policy as JSON +* `delivery_policy` - (Optional) The SNS delivery policy + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ARN of the SNS topic +* `policy` - The fully-formed AWS policy as JSON +* `delivery_policy` - The SNS delivery policy + diff --git a/website/source/docs/providers/aws/r/sns_topic_subscription.html.markdown b/website/source/docs/providers/aws/r/sns_topic_subscription.html.markdown new file mode 100644 index 000000000000..027b6cee85f6 --- /dev/null +++ b/website/source/docs/providers/aws/r/sns_topic_subscription.html.markdown @@ -0,0 +1,91 @@ +--- +layout: "aws" +page_title: "AWS: sns_topic_subscription" +sidebar_current: "docs-aws-resource-sns-topic-subscription" +description: |- + Provides a resource for subscribing to SNS topics. +--- + +# aws\_sns\_topic\_subscription + + Provides a resource for subscribing to SNS topics. Requires that an SNS topic exist for the subscription to attach to. +This resource allows you to automatically place messages sent to SNS topics in SQS queues, send them as HTTP(S) POST requests +to a given endpoint, send SMS messages, or notify devices / applications. The most likely use case for Terraform users will +probably be SQS queues. + +## Example Usage + +You can directly supply a topic ARN by hand in the `topic_arn` property: + +``` +resource "aws_sns_topic_subscription" "user_updates_sqs_target" { + topic_arn = "arn:aws:sns:us-west-2:432981146916:user-updates-topic" + topic_arn = "${aws_sns_topic.user_updates.id}" + protocol = "sqs" + endpoint = "arn:aws:sqs:us-west-2:432981146916:terraform-queue-too" +} +``` + +Alternatively you can use the identifier of a previously created SNS topic: + +``` +resource "aws_sns_topic" "user_updates" { + name = "user-updates-topic" +} + +resource "aws_sns_topic_subscription" "user_updates_sqs_target" { + topic_arn = "${aws_sns_topic.user_updates.id}" + protocol = "sqs" + endpoint = "arn:aws:sqs:us-west-2:432981146916:terraform-queue-too" +} +``` + + +Currently there is no SQS support, so you need to know the queue ARN ahead of time, however it would make sense to be +able to populate the endpoint from an SQS resource in your JSON file. + +## Argument Reference + +The following arguments are supported: + +* `topic_arn` - (Required) The ARN of the SNS topic to subscribe to +* `protocol` - (Required) The protocol to use. The possible values for this are: `sqs`, `http`, `https`, `sms`, or `application`. (`email` is an option but unsupported, see below) +* `endpoint` - (Required) The endpoint to send data to, the contents will vary with the protocol. (see below for more information) + +### Protocols supported + +Supported SNS protocols include: + +* `http` -- delivery of JSON-encoded message via HTTP POST +* `https` -- delivery of JSON-encoded message via HTTPS POST +* `sms` -- delivery of message via SMS +* `sqs` -- delivery of JSON-encoded message to an Amazon SQS queue +* `application` -- delivery of JSON-encoded message to an EndpointArn for a mobile app and device + +Unsupported protocols include the following: + +* `email` -- delivery of message via SMTP +* `email-json` -- delivery of JSON-encoded message via SMTP + +These are unsupported because the email address needs to be authorized and does not generate an ARN until the target email address has been validated. This breaks +the Terraform model and as a result are not currently supported. + +### Specifying endpoints + +Endpoints have different format requirements according to the protocol that is chosen. + +* HTTP/HTTPS endpoints will require a URL to POST data to +* SMS endpoints are mobile numbers that are capable of receiving an SMS +* SQS endpoints come in the form of the SQS queue's ARN (not the URL of the queue) e.g: `arn:aws:sqs:us-west-2:432981146916:terraform-queue-too` +* Application endpoints are also the endpoint ARN for the mobile app and device. + + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ARN of the subscription +* `topic_arn` - The ARN of the topic the subscription belongs to +* `protocol` - The protocol being used +* `endpoint` - The full endpoint to send data to (SQS ARN, HTTP(S) URL, Application ARN, SMS number, etc.) + diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index 22358e010208..b7a9e23e14c1 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -152,6 +152,14 @@ aws_s3_bucket + > + aws_sns_topic + + + > + aws_sns_topic_subscription + + > aws_security_group From 0196a0c2aefea6b85f495b0bbe32a855021f0a24 Mon Sep 17 00:00:00 2001 From: John Ewart Date: Fri, 15 May 2015 13:34:23 -0700 Subject: [PATCH 2/4] Changed Required: false to Optional: true in the SNS topic schema --- builtin/providers/aws/resource_aws_sns_topic.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_sns_topic.go b/builtin/providers/aws/resource_aws_sns_topic.go index f327927a4024..81eb34376d17 100644 --- a/builtin/providers/aws/resource_aws_sns_topic.go +++ b/builtin/providers/aws/resource_aws_sns_topic.go @@ -33,18 +33,18 @@ func resourceAwsSnsTopic() *schema.Resource { }, "display_name": &schema.Schema{ Type: schema.TypeString, - Required: false, + Optional: true, ForceNew: false, }, "policy": &schema.Schema{ Type: schema.TypeString, - Required: false, + Optional: true, ForceNew: false, Computed: true, }, "delivery_policy": &schema.Schema{ Type: schema.TypeString, - Required: false, + Optional: true, ForceNew: false, }, }, From 1dd95df5ab78aa746a8f55c9997d18c5a2606118 Mon Sep 17 00:00:00 2001 From: John Ewart Date: Fri, 22 May 2015 21:12:25 -0700 Subject: [PATCH 3/4] Export ARN in SQS queue and SNS topic / subscription; updated tests for new AWS SDK errors; updated documentation. --- .../providers/aws/resource_aws_sns_topic.go | 9 ++++++++- .../resource_aws_sns_topic_subscription.go | 7 +++++++ ...esource_aws_sns_topic_subscription_test.go | 11 +++++++--- .../aws/resource_aws_sns_topic_test.go | 3 ++- .../providers/aws/resource_aws_sqs_queue.go | 6 ++++++ .../providers/aws/r/sns_topic.html.markdown | 3 +-- .../r/sns_topic_subscription.html.markdown | 20 ++++++++++--------- .../providers/aws/r/sqs_queue.html.markdown | 5 +++-- 8 files changed, 46 insertions(+), 18 deletions(-) diff --git a/builtin/providers/aws/resource_aws_sns_topic.go b/builtin/providers/aws/resource_aws_sns_topic.go index 81eb34376d17..780ff2f38f25 100644 --- a/builtin/providers/aws/resource_aws_sns_topic.go +++ b/builtin/providers/aws/resource_aws_sns_topic.go @@ -47,6 +47,10 @@ func resourceAwsSnsTopic() *schema.Resource { Optional: true, ForceNew: false, }, + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -69,6 +73,9 @@ func resourceAwsSnsTopicCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(*output.TopicARN) + // Write the ARN to the 'arn' field for export + d.Set("arn", *output.TopicARN) + return resourceAwsSnsTopicUpdate(d, meta) } @@ -105,6 +112,7 @@ func resourceAwsSnsTopicRead(d *schema.ResourceData, meta interface{}) error { attributeOutput, err := snsconn.GetTopicAttributes(&sns.GetTopicAttributesInput{ TopicARN: aws.String(d.Id()), }) + if err != nil { return err } @@ -123,7 +131,6 @@ func resourceAwsSnsTopicRead(d *schema.ResourceData, meta interface{}) error { value := *attrmap[oKey] log.Printf("[DEBUG] Updating %s => %s -> %s", iKey, oKey, value) d.Set(iKey, *attrmap[oKey]) - } } } diff --git a/builtin/providers/aws/resource_aws_sns_topic_subscription.go b/builtin/providers/aws/resource_aws_sns_topic_subscription.go index 542de0021b95..fd8c676046cd 100644 --- a/builtin/providers/aws/resource_aws_sns_topic_subscription.go +++ b/builtin/providers/aws/resource_aws_sns_topic_subscription.go @@ -45,6 +45,10 @@ func resourceAwsSnsTopicSubscription() *schema.Resource { ForceNew: false, Default: false, }, + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -65,6 +69,9 @@ func resourceAwsSnsTopicSubscriptionCreate(d *schema.ResourceData, meta interfac log.Printf("New subscription ARN: %s", *output.SubscriptionARN) d.SetId(*output.SubscriptionARN) + // Write the ARN to the 'arn' field for export + d.Set("arn", *output.SubscriptionARN) + return resourceAwsSnsTopicSubscriptionUpdate(d, meta) } diff --git a/builtin/providers/aws/resource_aws_sns_topic_subscription_test.go b/builtin/providers/aws/resource_aws_sns_topic_subscription_test.go index 6c630a4f98fd..ada177ccd15a 100644 --- a/builtin/providers/aws/resource_aws_sns_topic_subscription_test.go +++ b/builtin/providers/aws/resource_aws_sns_topic_subscription_test.go @@ -8,6 +8,7 @@ import ( "github.com/awslabs/aws-sdk-go/service/sns" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/awslabs/aws-sdk-go/aws/awserr" ) func TestAccAWSSNSTopicSubscription(t *testing.T) { @@ -49,7 +50,7 @@ func testAccCheckAWSSNSTopicSubscriptionDestroy(s *terraform.State) error { } // Verify the error is an API error, not something else - _, ok := err.(aws.APIError) + _, ok := err.(awserr.Error) if !ok { return err } @@ -90,9 +91,13 @@ resource "aws_sns_topic" "test_topic" { name = "terraform-test-topic" } +resource "aws_sqs_queue" "test_queue" { + name = "terraform-subscription-test-queue" +} + resource "aws_sns_topic_subscription" "test_subscription" { - topic_arn = "${aws_sns_topic.test_topic.id}" + topic_arn = "${aws_sns_topic.test_topic.arn}" protocol = "sqs" - endpoint = "arn:aws:sqs:us-west-2:432981146916:terraform-queue-too" + endpoint = "${aws_sqs_queue.test_queue.arn}" } ` \ No newline at end of file diff --git a/builtin/providers/aws/resource_aws_sns_topic_test.go b/builtin/providers/aws/resource_aws_sns_topic_test.go index ab52bd1524c1..f13c0b677bab 100644 --- a/builtin/providers/aws/resource_aws_sns_topic_test.go +++ b/builtin/providers/aws/resource_aws_sns_topic_test.go @@ -8,6 +8,7 @@ import ( "github.com/awslabs/aws-sdk-go/service/sns" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/awslabs/aws-sdk-go/aws/awserr" ) func TestAccAWSSNSTopic(t *testing.T) { @@ -45,7 +46,7 @@ func testAccCheckAWSSNSTopicDestroy(s *terraform.State) error { } // Verify the error is an API error, not something else - _, ok := err.(aws.APIError) + _, ok := err.(awserr.Error) if !ok { return err } diff --git a/builtin/providers/aws/resource_aws_sqs_queue.go b/builtin/providers/aws/resource_aws_sqs_queue.go index b664bf2eb201..7c7473f04987 100644 --- a/builtin/providers/aws/resource_aws_sqs_queue.go +++ b/builtin/providers/aws/resource_aws_sqs_queue.go @@ -19,6 +19,7 @@ var AttributeMap = map[string]string{ "visibility_timeout_seconds": "VisibilityTimeout", "policy": "Policy", "redrive_policy": "RedrivePolicy", + "arn": "QueueArn", } // A number of these are marked as computed because if you don't @@ -70,6 +71,10 @@ func resourceAwsSqsQueue() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -154,6 +159,7 @@ func resourceAwsSqsQueueRead(d *schema.ResourceData, meta interface{}) error { QueueURL: aws.String(d.Id()), AttributeNames: []*string{aws.String("All")}, }) + if err != nil { return err } diff --git a/website/source/docs/providers/aws/r/sns_topic.html.markdown b/website/source/docs/providers/aws/r/sns_topic.html.markdown index 1306d780d71d..62a3c23f7487 100644 --- a/website/source/docs/providers/aws/r/sns_topic.html.markdown +++ b/website/source/docs/providers/aws/r/sns_topic.html.markdown @@ -31,6 +31,5 @@ The following arguments are supported: The following attributes are exported: * `id` - The ARN of the SNS topic -* `policy` - The fully-formed AWS policy as JSON -* `delivery_policy` - The SNS delivery policy +* `arn` - The ARN of the SNS topic, as a more obvious property (clone of id) diff --git a/website/source/docs/providers/aws/r/sns_topic_subscription.html.markdown b/website/source/docs/providers/aws/r/sns_topic_subscription.html.markdown index 027b6cee85f6..f27644350c08 100644 --- a/website/source/docs/providers/aws/r/sns_topic_subscription.html.markdown +++ b/website/source/docs/providers/aws/r/sns_topic_subscription.html.markdown @@ -15,35 +15,35 @@ probably be SQS queues. ## Example Usage -You can directly supply a topic ARN by hand in the `topic_arn` property: +You can directly supply a topic and ARN by hand in the `topic_arn` property along with the queue ARN: ``` resource "aws_sns_topic_subscription" "user_updates_sqs_target" { topic_arn = "arn:aws:sns:us-west-2:432981146916:user-updates-topic" - topic_arn = "${aws_sns_topic.user_updates.id}" protocol = "sqs" endpoint = "arn:aws:sqs:us-west-2:432981146916:terraform-queue-too" } ``` -Alternatively you can use the identifier of a previously created SNS topic: +Alternatively you can use the ARN properties of a managed SNS topic and SQS queue: ``` resource "aws_sns_topic" "user_updates" { name = "user-updates-topic" } +resource "aws_sqs_queue" "user_updates_queue" { + name = "user-updates-queue" +} + resource "aws_sns_topic_subscription" "user_updates_sqs_target" { - topic_arn = "${aws_sns_topic.user_updates.id}" - protocol = "sqs" - endpoint = "arn:aws:sqs:us-west-2:432981146916:terraform-queue-too" + topic_arn = "${aws_sns_topic.user_updates.arn}" + protocol = "sqs" + endpoint = "${aws_sqs_queue.user_updates_queue.arn}" } ``` -Currently there is no SQS support, so you need to know the queue ARN ahead of time, however it would make sense to be -able to populate the endpoint from an SQS resource in your JSON file. - ## Argument Reference The following arguments are supported: @@ -51,6 +51,7 @@ The following arguments are supported: * `topic_arn` - (Required) The ARN of the SNS topic to subscribe to * `protocol` - (Required) The protocol to use. The possible values for this are: `sqs`, `http`, `https`, `sms`, or `application`. (`email` is an option but unsupported, see below) * `endpoint` - (Required) The endpoint to send data to, the contents will vary with the protocol. (see below for more information) +* `raw_message_delivery` - (Optional) Boolean indicating whether or not to enable raw message delivery (the original message is directly passed, not wrapped in JSON with the original message in the message property). ### Protocols supported @@ -88,4 +89,5 @@ The following attributes are exported: * `topic_arn` - The ARN of the topic the subscription belongs to * `protocol` - The protocol being used * `endpoint` - The full endpoint to send data to (SQS ARN, HTTP(S) URL, Application ARN, SMS number, etc.) +* `arn` - The ARN of the subscription stored as a more user-friendly property diff --git a/website/source/docs/providers/aws/r/sqs_queue.html.markdown b/website/source/docs/providers/aws/r/sqs_queue.html.markdown index e03c4a2f55dd..47cae1f7a3c1 100644 --- a/website/source/docs/providers/aws/r/sqs_queue.html.markdown +++ b/website/source/docs/providers/aws/r/sqs_queue.html.markdown @@ -11,7 +11,7 @@ description: |- ## Example Usage ``` -resource "aws_sqs_queue" "terrform_queue" { +resource "aws_sqs_queue" "terraform_queue" { name = "terraform-example-queue" delay_seconds = 90 max_message_size = 2048 @@ -35,4 +35,5 @@ The following arguments are supported: The following attributes are exported: -* `id` - The URL for the created Amazon SQS queue. \ No newline at end of file +* `id` - The URL for the created Amazon SQS queue. +* `arn` - The ARN of the SQS queue \ No newline at end of file From 17d9d314d0d73ee9504a5ad5027586401dccb929 Mon Sep 17 00:00:00 2001 From: John Ewart Date: Fri, 22 May 2015 21:19:43 -0700 Subject: [PATCH 4/4] Indentation issue --- builtin/providers/aws/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index 8f299ed5ae95..92a69d6bbb81 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -38,7 +38,7 @@ type AWSClient struct { autoscalingconn *autoscaling.AutoScaling s3conn *s3.S3 sqsconn *sqs.SQS - snsconn *sns.SNS + snsconn *sns.SNS r53conn *route53.Route53 region string rdsconn *rds.RDS