Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provider/aws: Refresh SNS Topic updates in event of IAM role failure #3700

Merged
merged 1 commit into from
Oct 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 53 additions & 6 deletions builtin/providers/aws/resource_aws_sns_topic.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package aws

import (
"bytes"
"encoding/json"
"fmt"
"log"
"strings"
"time"

"github.com/hashicorp/terraform/helper/resource"
"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/sns"
)

Expand Down Expand Up @@ -40,6 +46,15 @@ func resourceAwsSnsTopic() *schema.Resource {
Optional: true,
ForceNew: false,
Computed: true,
StateFunc: func(v interface{}) string {
jsonb := []byte(v.(string))
buffer := new(bytes.Buffer)
if err := json.Compact(buffer, jsonb); err != nil {
log.Printf("[WARN] Error compacting JSON for Policy in SNS Topic")
return ""
}
return buffer.String()
},
},
"delivery_policy": &schema.Schema{
Type: schema.TypeString,
Expand Down Expand Up @@ -79,24 +94,37 @@ func resourceAwsSnsTopicCreate(d *schema.ResourceData, meta interface{}) error {
}

func resourceAwsSnsTopicUpdate(d *schema.ResourceData, meta interface{}) error {
snsconn := meta.(*AWSClient).snsconn

resource := *resourceAwsSnsTopic()
r := *resourceAwsSnsTopic()

for k, _ := range resource.Schema {
for k, _ := range r.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{
req := sns.SetTopicAttributesInput{
TopicArn: aws.String(d.Id()),
AttributeName: aws.String(attrKey),
AttributeValue: aws.String(n.(string)),
}
snsconn.SetTopicAttributes(req)

// Retry the update in the event of an eventually consistent style of
// error, where say an IAM resource is successfully created but not
// actually available. See https://github.com/hashicorp/terraform/issues/3660
log.Printf("[DEBUG] Updating SNS Topic (%s) attributes request: %s", d.Id(), req)
stateConf := &resource.StateChangeConf{
Pending: []string{"retrying"},
Target: "success",
Refresh: resourceAwsSNSUpdateRefreshFunc(meta, req),
Timeout: 1 * time.Minute,
MinTimeout: 3 * time.Second,
}
_, err := stateConf.WaitForState()
if err != nil {
return err
}
}
}
}
Expand All @@ -105,6 +133,25 @@ func resourceAwsSnsTopicUpdate(d *schema.ResourceData, meta interface{}) error {
return resourceAwsSnsTopicRead(d, meta)
}

func resourceAwsSNSUpdateRefreshFunc(
meta interface{}, params sns.SetTopicAttributesInput) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
snsconn := meta.(*AWSClient).snsconn
if _, err := snsconn.SetTopicAttributes(&params); err != nil {
log.Printf("[WARN] Erroring updating topic attributes: %s", err)
if awsErr, ok := err.(awserr.Error); ok {
// if the error contains the PrincipalNotFound message, we can retry
if strings.Contains(awsErr.Message(), "PrincipalNotFound") {
log.Printf("[DEBUG] Retrying AWS SNS Topic Update: %s", params)
return nil, "retrying", nil
}
}
return nil, "failed", err
}
return 42, "success", nil
}
}

func resourceAwsSnsTopicRead(d *schema.ResourceData, meta interface{}) error {
snsconn := meta.(*AWSClient).snsconn

Expand Down
60 changes: 60 additions & 0 deletions builtin/providers/aws/resource_aws_sns_topic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@ func TestAccAWSSNSTopic_basic(t *testing.T) {
})
}

func TestAccAWSSNSTopic_withIAMRole(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSNSTopicDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSSNSTopicConfig_withIAMRole,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSNSTopicExists("aws_sns_topic.test_topic"),
),
},
},
})
}

func testAccCheckAWSSNSTopicDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).snsconn

Expand Down Expand Up @@ -85,3 +101,47 @@ resource "aws_sns_topic" "test_topic" {
name = "terraform-test-topic"
}
`

// Test for https://github.com/hashicorp/terraform/issues/3660
const testAccAWSSNSTopicConfig_withIAMRole = `
resource "aws_iam_role" "example" {
name = "terraform_bug"
path = "/test/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_sns_topic" "test_topic" {
name = "example"
policy = <<EOF
{
"Version": "2008-10-17",
"Id": "Policy1445931846145",
"Statement": [
{
"Sid": "Stmt1445931846145",
"Effect": "Allow",
"Principal": {
"AWS": "${aws_iam_role.example.arn}"
},
"Action": "sns:Publish",
"Resource": "arn:aws:sns:us-west-2::example"
}
]
}
EOF
}
`