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