diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index 552a0f446..bd03236ad 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -17,6 +17,7 @@ import ( "github.com/awslabs/aws-sdk-go/service/rds" "github.com/awslabs/aws-sdk-go/service/route53" "github.com/awslabs/aws-sdk-go/service/s3" + "github.com/awslabs/aws-sdk-go/service/sqs" ) type Config struct { @@ -35,6 +36,7 @@ type AWSClient struct { elbconn *elb.ELB autoscalingconn *autoscaling.AutoScaling s3conn *s3.S3 + sqsconn *sqs.SQS r53conn *route53.Route53 region string rdsconn *rds.RDS @@ -84,6 +86,9 @@ func (c *Config) Client() (interface{}, error) { log.Println("[INFO] Initializing S3 connection") client.s3conn = s3.New(awsConfig) + log.Println("[INFO] Initializing SQS connection") + client.sqsconn = sqs.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 66446a902..db90549d2 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -122,6 +122,7 @@ func Provider() terraform.ResourceProvider { "aws_s3_bucket": resourceAwsS3Bucket(), "aws_security_group": resourceAwsSecurityGroup(), "aws_security_group_rule": resourceAwsSecurityGroupRule(), + "aws_sqs_queue": resourceAwsSqsQueue(), "aws_subnet": resourceAwsSubnet(), "aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(), "aws_vpc_dhcp_options": resourceAwsVpcDhcpOptions(), diff --git a/builtin/providers/aws/resource_aws_sqs_queue.go b/builtin/providers/aws/resource_aws_sqs_queue.go new file mode 100644 index 000000000..6aa56a553 --- /dev/null +++ b/builtin/providers/aws/resource_aws_sqs_queue.go @@ -0,0 +1,197 @@ +package aws + +import ( + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/sqs" +) + +var AttributeMap = map[string]string{ + "delay_seconds" : "DelaySeconds", + "max_message_size" : "MaximumMessageSize", + "message_retention_seconds" : "MessageRetentionPeriod", + "receive_wait_time_seconds" : "ReceiveMessageWaitTimeSeconds", + "visibility_timeout_seconds" : "VisibilityTimeout", + "policy" : "Policy", + "redrive_policy": "RedrivePolicy", +} + + +// A number of these are marked as computed because if you don't +// provide a value, SQS will provide you with defaults (which are the +// default values specified below) +func resourceAwsSqsQueue() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSqsQueueCreate, + Read: resourceAwsSqsQueueRead, + Update: resourceAwsSqsQueueUpdate, + Delete: resourceAwsSqsQueueDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "delay_seconds": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "max_message_size": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "message_retention_seconds": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "receive_wait_time_seconds": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "visibility_timeout_seconds": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "policy": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "redrive_policy": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func resourceAwsSqsQueueCreate(d *schema.ResourceData, meta interface{}) error { + sqsconn := meta.(*AWSClient).sqsconn + + name := d.Get("name").(string) + + log.Printf("[DEBUG] SQS queue create: %s", name) + + req := &sqs.CreateQueueInput{ + QueueName: aws.String(name), + } + + attributes := make(map[string]*string) + + resource := *resourceAwsSqsQueue() + + for k, s := range resource.Schema { + if attrKey, ok := AttributeMap[k]; ok { + if value, ok := d.GetOk(k); ok { + if s.Type == schema.TypeInt { + attributes[attrKey] = aws.String(strconv.Itoa(value.(int))) + } else { + attributes[attrKey] = aws.String(value.(string)) + } + } + + } + } + + if len(attributes) > 0 { + req.Attributes = &attributes + } + + output, err := sqsconn.CreateQueue(req) + if err != nil { + return fmt.Errorf("Error creating SQS queue: %s", err) + } + + d.SetId(*output.QueueURL) + + return resourceAwsSqsQueueUpdate(d, meta) +} + +func resourceAwsSqsQueueUpdate(d *schema.ResourceData, meta interface{}) error { + sqsconn := meta.(*AWSClient).sqsconn + attributes := make(map[string]*string) + + resource := *resourceAwsSqsQueue() + + for k, s := range resource.Schema { + if attrKey, ok := AttributeMap[k]; ok { + if d.HasChange(k) { + log.Printf("[DEBUG] Updating %s", attrKey) + _, n := d.GetChange(k) + if s.Type == schema.TypeInt { + attributes[attrKey] = aws.String(strconv.Itoa(n.(int))) + } else { + attributes[attrKey] = aws.String(n.(string)) + } + } + } + } + + if len(attributes) > 0 { + req := &sqs.SetQueueAttributesInput{ + QueueURL: aws.String(d.Id()), + Attributes: &attributes, + } + sqsconn.SetQueueAttributes(req) + } + + return resourceAwsSqsQueueRead(d, meta) +} + +func resourceAwsSqsQueueRead(d *schema.ResourceData, meta interface{}) error { + sqsconn := meta.(*AWSClient).sqsconn + + attributeOutput, err := sqsconn.GetQueueAttributes(&sqs.GetQueueAttributesInput{ + QueueURL: aws.String(d.Id()), + AttributeNames: []*string{aws.String("All")}, + }) + if err != nil { + return err + } + + if attributeOutput.Attributes != nil && len(*attributeOutput.Attributes) > 0 { + attrmap := *attributeOutput.Attributes + resource := *resourceAwsSqsQueue() + // iKey = internal struct key, oKey = AWS Attribute Map key + for iKey, oKey := range AttributeMap { + if attrmap[oKey] != nil { + if resource.Schema[iKey].Type == schema.TypeInt { + value, err := strconv.Atoi(*attrmap[oKey]) + if err != nil { + return err + } + d.Set(iKey, value) + } else { + d.Set(iKey, *attrmap[oKey]) + } + } + } + } + + return nil +} + +func resourceAwsSqsQueueDelete(d *schema.ResourceData, meta interface{}) error { + sqsconn := meta.(*AWSClient).sqsconn + + log.Printf("[DEBUG] SQS Delete Queue: %s", d.Id()) + _, err := sqsconn.DeleteQueue(&sqs.DeleteQueueInput{ + QueueURL: aws.String(d.Id()), + }) + if err != nil { + return err + } + return nil +} + + diff --git a/builtin/providers/aws/resource_aws_sqs_queue_test.go b/builtin/providers/aws/resource_aws_sqs_queue_test.go new file mode 100644 index 000000000..5f99b6615 --- /dev/null +++ b/builtin/providers/aws/resource_aws_sqs_queue_test.go @@ -0,0 +1,179 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/sqs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSSQSQueue(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSQSQueueDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSSQSConfigWithDefaults, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSQSExistsWithDefaults("aws_sqs_queue.queue-with-defaults"), + ), + }, + resource.TestStep{ + Config: testAccAWSSQSConfigWithOverrides, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSQSExistsWithOverrides("aws_sqs_queue.queue-with-overrides"), + ), + }, + }, + }) +} + + +func testAccCheckAWSSQSQueueDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).sqsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_sqs_queue" { + continue + } + + // Check if queue exists by checking for its attributes + params := &sqs.GetQueueAttributesInput{ + QueueURL: aws.String(rs.Primary.ID), + } + _, err := conn.GetQueueAttributes(params) + if err == nil { + return fmt.Errorf("Queue %s still exists. Failing!", rs.Primary.ID) + } + + // Verify the error is what we want + _, ok := err.(aws.APIError) + if !ok { + return err + } + } + + return nil +} + + +func testAccCheckAWSSQSExistsWithDefaults(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 Queue URL specified!") + } + + conn := testAccProvider.Meta().(*AWSClient).sqsconn + + params := &sqs.GetQueueAttributesInput{ + QueueURL: aws.String(rs.Primary.ID), + AttributeNames: []*string{aws.String("All")}, + } + resp, err := conn.GetQueueAttributes(params) + + if err != nil { + return err + } + + // checking if attributes are defaults + for k, v := range *resp.Attributes { + if k == "VisibilityTimeout" && *v != "30" { + return fmt.Errorf("VisibilityTimeout (%s) was not set to 30", *v) + } + + if k == "MessageRetentionPeriod" && *v != "345600" { + return fmt.Errorf("MessageRetentionPeriod (%s) was not set to 345600", *v) + } + + if k == "MaximumMessageSize" && *v != "262144" { + return fmt.Errorf("MaximumMessageSize (%s) was not set to 262144", *v) + } + + if k == "DelaySeconds" && *v != "0" { + return fmt.Errorf("DelaySeconds (%s) was not set to 0", *v) + } + + if k == "ReceiveMessageWaitTimeSeconds" && *v != "0" { + return fmt.Errorf("ReceiveMessageWaitTimeSeconds (%s) was not set to 0", *v) + } + } + + return nil + } +} + +func testAccCheckAWSSQSExistsWithOverrides(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 Queue URL specified!") + } + + conn := testAccProvider.Meta().(*AWSClient).sqsconn + + params := &sqs.GetQueueAttributesInput{ + QueueURL: aws.String(rs.Primary.ID), + AttributeNames: []*string{aws.String("All")}, + } + resp, err := conn.GetQueueAttributes(params) + + if err != nil { + return err + } + + // checking if attributes match our overrides + for k, v := range *resp.Attributes { + if k == "VisibilityTimeout" && *v != "60" { + return fmt.Errorf("VisibilityTimeout (%s) was not set to 60", *v) + } + + if k == "MessageRetentionPeriod" && *v != "86400" { + return fmt.Errorf("MessageRetentionPeriod (%s) was not set to 86400", *v) + } + + if k == "MaximumMessageSize" && *v != "2048" { + return fmt.Errorf("MaximumMessageSize (%s) was not set to 2048", *v) + } + + if k == "DelaySeconds" && *v != "90" { + return fmt.Errorf("DelaySeconds (%s) was not set to 90", *v) + } + + if k == "ReceiveMessageWaitTimeSeconds" && *v != "10" { + return fmt.Errorf("ReceiveMessageWaitTimeSeconds (%s) was not set to 10", *v) + } + } + + return nil + } +} + +const testAccAWSSQSConfigWithDefaults = ` +resource "aws_sqs_queue" "queue-with-defaults" { + name = "test-sqs-queue-with-defaults" +} +` + +const testAccAWSSQSConfigWithOverrides = ` +resource "aws_sqs_queue" "queue-with-overrides" { + name = "test-sqs-queue-with-overrides" + delay_seconds = 90 + max_message_size = 2048 + message_retention_seconds = 86400 + receive_wait_time_seconds = 10 + visibility_timeout_seconds = 60 +} +` \ No newline at end of file diff --git a/website/source/docs/providers/aws/r/sqs_queue.html.markdown b/website/source/docs/providers/aws/r/sqs_queue.html.markdown new file mode 100644 index 000000000..e03c4a2f5 --- /dev/null +++ b/website/source/docs/providers/aws/r/sqs_queue.html.markdown @@ -0,0 +1,38 @@ +--- +layout: "aws" +page_title: "AWS: aws_sqs_queue" +sidebar_current: "docs-aws-resource-sqs" +description: |- + Provides a SQS resource. +--- + +# aws\_sqs\_queue + +## Example Usage + +``` +resource "aws_sqs_queue" "terrform_queue" { + name = "terraform-example-queue" + delay_seconds = 90 + max_message_size = 2048 + message_retention_seconds = 86400 + receive_wait_time_seconds = 10 +} +``` + +## Argument Reference + +The following arguments are supported: +* `name` - (Required) This is the human-readable name of the queue +* `visibility_timeout_seconds` - (Optional) The time in seconds that the delivery of all messages in the queue will be delayed. An integer from 0 to 900 (15 minutes). The default for this attribute is 30 seconds +* `message_retention_seconds` - (Optional) The number of seconds Amazon SQS retains a message. Integer representing seconds, from 60 (1 minute) to 1209600 (14 days). The default for this attribute is 345600 (4 days). +* `max_message_size` - (Optional) The limit of how many bytes a message can contain before Amazon SQS rejects it. An integer from 1024 bytes (1 KiB) up to 262144 bytes (256 KiB). The default for this attribute is 262144 (256 KiB). +* `delay_seconds` - (Optional) The visibility timeout for the queue. An integer from 0 to 43200 (12 hours). The default for this attribute is 30. For more information about visibility timeout. +* `receive_wait_time_seconds` - (Optional) The time for which a ReceiveMessage call will wait for a message to arrive (long polling) before returning. An integer from 0 to 20 (seconds). The default for this attribute is 0, meaning that the call will return immediately. +* `policy` - (Optional) The JSON policy for the SQS queue + +## Attributes Reference + +The following attributes are exported: + +* `id` - The URL for the created Amazon SQS queue. \ No newline at end of file diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index 473152864..22358e010 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -156,9 +156,13 @@ aws_security_group - > - aws_security_group_rule - + > + aws_security_group_rule + + + > + aws_sqs_queue + > aws_subnet