github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/aws/resource_aws_sns_topic.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "log" 8 "strings" 9 "time" 10 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 14 "github.com/aws/aws-sdk-go/aws" 15 "github.com/aws/aws-sdk-go/aws/awserr" 16 "github.com/aws/aws-sdk-go/service/sns" 17 ) 18 19 // Mutable attributes 20 var SNSAttributeMap = map[string]string{ 21 "display_name": "DisplayName", 22 "policy": "Policy", 23 "delivery_policy": "DeliveryPolicy", 24 } 25 26 func resourceAwsSnsTopic() *schema.Resource { 27 return &schema.Resource{ 28 Create: resourceAwsSnsTopicCreate, 29 Read: resourceAwsSnsTopicRead, 30 Update: resourceAwsSnsTopicUpdate, 31 Delete: resourceAwsSnsTopicDelete, 32 33 Schema: map[string]*schema.Schema{ 34 "name": &schema.Schema{ 35 Type: schema.TypeString, 36 Required: true, 37 ForceNew: true, 38 }, 39 "display_name": &schema.Schema{ 40 Type: schema.TypeString, 41 Optional: true, 42 ForceNew: false, 43 }, 44 "policy": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 Computed: true, 48 StateFunc: func(v interface{}) string { 49 s, ok := v.(string) 50 if !ok || s == "" { 51 return "" 52 } 53 jsonb := []byte(s) 54 buffer := new(bytes.Buffer) 55 if err := json.Compact(buffer, jsonb); err != nil { 56 log.Printf("[WARN] Error compacting JSON for Policy in SNS Topic") 57 return "" 58 } 59 return buffer.String() 60 }, 61 }, 62 "delivery_policy": &schema.Schema{ 63 Type: schema.TypeString, 64 Optional: true, 65 ForceNew: false, 66 }, 67 "arn": &schema.Schema{ 68 Type: schema.TypeString, 69 Computed: true, 70 }, 71 }, 72 } 73 } 74 75 func resourceAwsSnsTopicCreate(d *schema.ResourceData, meta interface{}) error { 76 snsconn := meta.(*AWSClient).snsconn 77 78 name := d.Get("name").(string) 79 80 log.Printf("[DEBUG] SNS create topic: %s", name) 81 82 req := &sns.CreateTopicInput{ 83 Name: aws.String(name), 84 } 85 86 output, err := snsconn.CreateTopic(req) 87 if err != nil { 88 return fmt.Errorf("Error creating SNS topic: %s", err) 89 } 90 91 d.SetId(*output.TopicArn) 92 93 // Write the ARN to the 'arn' field for export 94 d.Set("arn", *output.TopicArn) 95 96 return resourceAwsSnsTopicUpdate(d, meta) 97 } 98 99 func resourceAwsSnsTopicUpdate(d *schema.ResourceData, meta interface{}) error { 100 r := *resourceAwsSnsTopic() 101 102 for k, _ := range r.Schema { 103 if attrKey, ok := SNSAttributeMap[k]; ok { 104 if d.HasChange(k) { 105 log.Printf("[DEBUG] Updating %s", attrKey) 106 _, n := d.GetChange(k) 107 // Ignore an empty policy 108 if !(k == "policy" && n == "") { 109 // Make API call to update attributes 110 req := sns.SetTopicAttributesInput{ 111 TopicArn: aws.String(d.Id()), 112 AttributeName: aws.String(attrKey), 113 AttributeValue: aws.String(n.(string)), 114 } 115 116 // Retry the update in the event of an eventually consistent style of 117 // error, where say an IAM resource is successfully created but not 118 // actually available. See https://github.com/hashicorp/terraform/issues/3660 119 log.Printf("[DEBUG] Updating SNS Topic (%s) attributes request: %s", d.Id(), req) 120 stateConf := &resource.StateChangeConf{ 121 Pending: []string{"retrying"}, 122 Target: "success", 123 Refresh: resourceAwsSNSUpdateRefreshFunc(meta, req), 124 Timeout: 1 * time.Minute, 125 MinTimeout: 3 * time.Second, 126 } 127 _, err := stateConf.WaitForState() 128 if err != nil { 129 return err 130 } 131 } 132 } 133 } 134 } 135 136 return resourceAwsSnsTopicRead(d, meta) 137 } 138 139 func resourceAwsSNSUpdateRefreshFunc( 140 meta interface{}, params sns.SetTopicAttributesInput) resource.StateRefreshFunc { 141 return func() (interface{}, string, error) { 142 snsconn := meta.(*AWSClient).snsconn 143 if _, err := snsconn.SetTopicAttributes(¶ms); err != nil { 144 log.Printf("[WARN] Erroring updating topic attributes: %s", err) 145 if awsErr, ok := err.(awserr.Error); ok { 146 // if the error contains the PrincipalNotFound message, we can retry 147 if strings.Contains(awsErr.Message(), "PrincipalNotFound") { 148 log.Printf("[DEBUG] Retrying AWS SNS Topic Update: %s", params) 149 return nil, "retrying", nil 150 } 151 } 152 return nil, "failed", err 153 } 154 return 42, "success", nil 155 } 156 } 157 158 func resourceAwsSnsTopicRead(d *schema.ResourceData, meta interface{}) error { 159 snsconn := meta.(*AWSClient).snsconn 160 161 attributeOutput, err := snsconn.GetTopicAttributes(&sns.GetTopicAttributesInput{ 162 TopicArn: aws.String(d.Id()), 163 }) 164 165 if err != nil { 166 return err 167 } 168 169 if attributeOutput.Attributes != nil && len(attributeOutput.Attributes) > 0 { 170 attrmap := attributeOutput.Attributes 171 resource := *resourceAwsSnsTopic() 172 // iKey = internal struct key, oKey = AWS Attribute Map key 173 for iKey, oKey := range SNSAttributeMap { 174 log.Printf("[DEBUG] Updating %s => %s", iKey, oKey) 175 176 if attrmap[oKey] != nil { 177 // Some of the fetched attributes are stateful properties such as 178 // the number of subscriptions, the owner, etc. skip those 179 if resource.Schema[iKey] != nil { 180 value := *attrmap[oKey] 181 log.Printf("[DEBUG] Updating %s => %s -> %s", iKey, oKey, value) 182 d.Set(iKey, *attrmap[oKey]) 183 } 184 } 185 } 186 } 187 188 return nil 189 } 190 191 func resourceAwsSnsTopicDelete(d *schema.ResourceData, meta interface{}) error { 192 snsconn := meta.(*AWSClient).snsconn 193 194 log.Printf("[DEBUG] SNS Delete Topic: %s", d.Id()) 195 _, err := snsconn.DeleteTopic(&sns.DeleteTopicInput{ 196 TopicArn: aws.String(d.Id()), 197 }) 198 if err != nil { 199 return err 200 } 201 return nil 202 }