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(&params); 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  }