-
Notifications
You must be signed in to change notification settings - Fork 9.6k
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
Initial SNS topic / subscription support #1974
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
|
||
"github.com/awslabs/aws-sdk-go/aws" | ||
"github.com/awslabs/aws-sdk-go/service/sns" | ||
) | ||
|
||
// Mutable attributes | ||
var SNSAttributeMap = map[string]string{ | ||
"display_name" : "DisplayName", | ||
"policy" : "Policy", | ||
"delivery_policy": "DeliveryPolicy", | ||
} | ||
|
||
|
||
func resourceAwsSnsTopic() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsSnsTopicCreate, | ||
Read: resourceAwsSnsTopicRead, | ||
Update: resourceAwsSnsTopicUpdate, | ||
Delete: resourceAwsSnsTopicDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"display_name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: false, | ||
}, | ||
"policy": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: false, | ||
Computed: true, | ||
}, | ||
"delivery_policy": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: false, | ||
}, | ||
"arn": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsSnsTopicCreate(d *schema.ResourceData, meta interface{}) error { | ||
snsconn := meta.(*AWSClient).snsconn | ||
|
||
name := d.Get("name").(string) | ||
|
||
log.Printf("[DEBUG] SNS create topic: %s", name) | ||
|
||
req := &sns.CreateTopicInput{ | ||
Name: aws.String(name), | ||
} | ||
|
||
output, err := snsconn.CreateTopic(req) | ||
if err != nil { | ||
return fmt.Errorf("Error creating SNS topic: %s", err) | ||
} | ||
|
||
d.SetId(*output.TopicARN) | ||
|
||
// Write the ARN to the 'arn' field for export | ||
d.Set("arn", *output.TopicARN) | ||
|
||
return resourceAwsSnsTopicUpdate(d, meta) | ||
} | ||
|
||
func resourceAwsSnsTopicUpdate(d *schema.ResourceData, meta interface{}) error { | ||
snsconn := meta.(*AWSClient).snsconn | ||
|
||
resource := *resourceAwsSnsTopic() | ||
|
||
for k, _ := range resource.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{ | ||
TopicARN: aws.String(d.Id()), | ||
AttributeName: aws.String(attrKey), | ||
AttributeValue: aws.String(n.(string)), | ||
} | ||
snsconn.SetTopicAttributes(req) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return resourceAwsSnsTopicRead(d, meta) | ||
} | ||
|
||
func resourceAwsSnsTopicRead(d *schema.ResourceData, meta interface{}) error { | ||
snsconn := meta.(*AWSClient).snsconn | ||
|
||
attributeOutput, err := snsconn.GetTopicAttributes(&sns.GetTopicAttributesInput{ | ||
TopicARN: aws.String(d.Id()), | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
if attributeOutput.Attributes != nil && len(*attributeOutput.Attributes) > 0 { | ||
attrmap := *attributeOutput.Attributes | ||
resource := *resourceAwsSnsTopic() | ||
// iKey = internal struct key, oKey = AWS Attribute Map key | ||
for iKey, oKey := range SNSAttributeMap { | ||
log.Printf("[DEBUG] Updating %s => %s", iKey, oKey) | ||
|
||
if attrmap[oKey] != nil { | ||
// Some of the fetched attributes are stateful properties such as | ||
// the number of subscriptions, the owner, etc. skip those | ||
if resource.Schema[iKey] != nil { | ||
value := *attrmap[oKey] | ||
log.Printf("[DEBUG] Updating %s => %s -> %s", iKey, oKey, value) | ||
d.Set(iKey, *attrmap[oKey]) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsSnsTopicDelete(d *schema.ResourceData, meta interface{}) error { | ||
snsconn := meta.(*AWSClient).snsconn | ||
|
||
log.Printf("[DEBUG] SNS Delete Topic: %s", d.Id()) | ||
_, err := snsconn.DeleteTopic(&sns.DeleteTopicInput{ | ||
TopicARN: aws.String(d.Id()), | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
|
||
"github.com/awslabs/aws-sdk-go/aws" | ||
"github.com/awslabs/aws-sdk-go/service/sns" | ||
) | ||
|
||
|
||
func resourceAwsSnsTopicSubscription() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsSnsTopicSubscriptionCreate, | ||
Read: resourceAwsSnsTopicSubscriptionRead, | ||
Update: resourceAwsSnsTopicSubscriptionUpdate, | ||
Delete: resourceAwsSnsTopicSubscriptionDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"protocol": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: false, | ||
}, | ||
"endpoint": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: false, | ||
}, | ||
"topic_arn": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: false, | ||
}, | ||
"delivery_policy": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: false, | ||
}, | ||
"raw_message_delivery": &schema.Schema{ | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
ForceNew: false, | ||
Default: false, | ||
}, | ||
"arn": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsSnsTopicSubscriptionCreate(d *schema.ResourceData, meta interface{}) error { | ||
snsconn := meta.(*AWSClient).snsconn | ||
|
||
if(d.Get("protocol") == "email") { | ||
return fmt.Errorf("Email endpoints are not supported!") | ||
} | ||
|
||
output, err := subscribeToSNSTopic(d, snsconn) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Printf("New subscription ARN: %s", *output.SubscriptionARN) | ||
d.SetId(*output.SubscriptionARN) | ||
|
||
// Write the ARN to the 'arn' field for export | ||
d.Set("arn", *output.SubscriptionARN) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question as above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ^^ - if so, it's being exported to make recipes more readable (i.e There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I see your point and I agree, that I was just thinking that we're allowing user to do it two ways (both I'm leaving this up to @catsby to decide. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now, I'm fine with the I agree the mis-match of |
||
|
||
return resourceAwsSnsTopicSubscriptionUpdate(d, meta) | ||
} | ||
|
||
func resourceAwsSnsTopicSubscriptionUpdate(d *schema.ResourceData, meta interface{}) error { | ||
snsconn := meta.(*AWSClient).snsconn | ||
|
||
// If any changes happened, un-subscribe and re-subscribe | ||
if d.HasChange("protocol") || d.HasChange("endpoint") || d.HasChange("topic_arn") { | ||
log.Printf("[DEBUG] Updating subscription %s", d.Id()) | ||
// Unsubscribe | ||
_, err := snsconn.Unsubscribe(&sns.UnsubscribeInput{ | ||
SubscriptionARN: aws.String(d.Id()), | ||
}) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Error unsubscribing from SNS topic: %s", err) | ||
} | ||
|
||
// Re-subscribe and set id | ||
output, err := subscribeToSNSTopic(d, snsconn) | ||
d.SetId(*output.SubscriptionARN) | ||
|
||
} | ||
|
||
if d.HasChange("raw_message_delivery") { | ||
_, n := d.GetChange("raw_message_delivery") | ||
|
||
attrValue := "false" | ||
|
||
if n.(bool) { | ||
attrValue = "true" | ||
} | ||
|
||
req := &sns.SetSubscriptionAttributesInput{ | ||
SubscriptionARN: aws.String(d.Id()), | ||
AttributeName: aws.String("RawMessageDelivery"), | ||
AttributeValue: aws.String(attrValue), | ||
} | ||
_, err := snsconn.SetSubscriptionAttributes(req) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Unable to set raw message delivery attribute on subscription") | ||
} | ||
} | ||
|
||
return resourceAwsSnsTopicSubscriptionRead(d, meta) | ||
} | ||
|
||
func resourceAwsSnsTopicSubscriptionRead(d *schema.ResourceData, meta interface{}) error { | ||
snsconn := meta.(*AWSClient).snsconn | ||
|
||
log.Printf("[DEBUG] Loading subscription %s", d.Id()) | ||
|
||
attributeOutput, err := snsconn.GetSubscriptionAttributes(&sns.GetSubscriptionAttributesInput{ | ||
SubscriptionARN: aws.String(d.Id()), | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if attributeOutput.Attributes != nil && len(*attributeOutput.Attributes) > 0 { | ||
attrHash := *attributeOutput.Attributes | ||
log.Printf("[DEBUG] raw message delivery: %s", *attrHash["RawMessageDelivery"]) | ||
if *attrHash["RawMessageDelivery"] == "true" { | ||
d.Set("raw_message_delivery", true) | ||
} else { | ||
d.Set("raw_message_delivery", false) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsSnsTopicSubscriptionDelete(d *schema.ResourceData, meta interface{}) error { | ||
snsconn := meta.(*AWSClient).snsconn | ||
|
||
log.Printf("[DEBUG] SNS delete topic subscription: %s", d.Id()) | ||
_, err := snsconn.Unsubscribe(&sns.UnsubscribeInput{ | ||
SubscriptionARN: aws.String(d.Id()), | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func subscribeToSNSTopic(d *schema.ResourceData, snsconn *sns.SNS) (output *sns.SubscribeOutput, err error) { | ||
protocol := d.Get("protocol").(string) | ||
endpoint := d.Get("endpoint").(string) | ||
topic_arn := d.Get("topic_arn").(string) | ||
|
||
log.Printf("[DEBUG] SNS create topic subscription: %s (%s) @ '%s'", endpoint, protocol, topic_arn) | ||
|
||
req := &sns.SubscribeInput{ | ||
Protocol: aws.String(protocol), | ||
Endpoint: aws.String(endpoint), | ||
TopicARN: aws.String(topic_arn), | ||
} | ||
|
||
output, err = snsconn.Subscribe(req) | ||
if err != nil { | ||
return nil, fmt.Errorf("Error creating SNS topic: %s", err) | ||
} | ||
|
||
log.Printf("[DEBUG] Created new subscription!") | ||
return output, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK
id
is being exported by default, therefore this seems to be a bit superfluous, or am I wrong?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which part? Exporting ARN?