mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-09 23:54:17 -06:00
6c27f175b5
This commit adds support for new helper function which is used to normalise and validate JSON string. This commit also removes unnecessary code from the StateFunc function, and reduces it so that it only uses the normalizeJsonString helper. Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>
222 lines
6.0 KiB
Go
222 lines
6.0 KiB
Go
package aws
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
"github.com/aws/aws-sdk-go/service/sns"
|
|
"github.com/hashicorp/errwrap"
|
|
"github.com/hashicorp/terraform/helper/resource"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
)
|
|
|
|
// Mutable attributes
|
|
var SNSAttributeMap = map[string]string{
|
|
"arn": "TopicArn",
|
|
"display_name": "DisplayName",
|
|
"policy": "Policy",
|
|
"delivery_policy": "DeliveryPolicy",
|
|
}
|
|
|
|
func resourceAwsSnsTopic() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceAwsSnsTopicCreate,
|
|
Read: resourceAwsSnsTopicRead,
|
|
Update: resourceAwsSnsTopicUpdate,
|
|
Delete: resourceAwsSnsTopicDelete,
|
|
Importer: &schema.ResourceImporter{
|
|
State: schema.ImportStatePassthrough,
|
|
},
|
|
|
|
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,
|
|
Computed: true,
|
|
ValidateFunc: validateJsonString,
|
|
DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
|
|
StateFunc: func(v interface{}) string {
|
|
json, _ := normalizeJsonString(v)
|
|
return json
|
|
},
|
|
},
|
|
"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 {
|
|
r := *resourceAwsSnsTopic()
|
|
|
|
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{
|
|
TopicArn: aws.String(d.Id()),
|
|
AttributeName: aws.String(attrKey),
|
|
AttributeValue: aws.String(n.(string)),
|
|
}
|
|
|
|
// 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: []string{"success"},
|
|
Refresh: resourceAwsSNSUpdateRefreshFunc(meta, req),
|
|
Timeout: 1 * time.Minute,
|
|
MinTimeout: 3 * time.Second,
|
|
}
|
|
_, err := stateConf.WaitForState()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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(¶ms); 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
|
|
|
|
attributeOutput, err := snsconn.GetTopicAttributes(&sns.GetTopicAttributesInput{
|
|
TopicArn: aws.String(d.Id()),
|
|
})
|
|
if err != nil {
|
|
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFound" {
|
|
log.Printf("[WARN] SNS Topic (%s) not found, error code (404)", d.Id())
|
|
d.SetId("")
|
|
return 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] Reading %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 {
|
|
var value string
|
|
if iKey == "policy" {
|
|
value, err = normalizeJsonString(*attrmap[oKey])
|
|
if err != nil {
|
|
return errwrap.Wrapf("policy contains an invalid JSON: {{err}}", err)
|
|
}
|
|
} else {
|
|
value = *attrmap[oKey]
|
|
}
|
|
log.Printf("[DEBUG] Reading %s => %s -> %s", iKey, oKey, value)
|
|
d.Set(iKey, value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we have no name set (import) then determine it from the ARN.
|
|
// This is a bit of a heuristic for now since AWS provides no other
|
|
// way to get it.
|
|
if _, ok := d.GetOk("name"); !ok {
|
|
arn := d.Get("arn").(string)
|
|
idx := strings.LastIndex(arn, ":")
|
|
if idx > -1 {
|
|
d.Set("name", arn[idx+1:])
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|