Skip to content

Commit

Permalink
Implement aws_codestarnotification_rule resource
Browse files Browse the repository at this point in the history
  • Loading branch information
spirius committed Feb 2, 2020
1 parent dcf7e42 commit 65f85e4
Show file tree
Hide file tree
Showing 10 changed files with 822 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .hashibot.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ behavior "regexp_issue_labeler_v2" "service_labels" {
"service/codestar" = [
"aws_codestar_",
],
"service/codestarnotifications" = [
"aws_codestarnotifications_",
],
"service/cognito" = [
"aws_cognito_",
],
Expand Down Expand Up @@ -695,6 +698,10 @@ behavior "pull_request_path_labeler" "service_labels" {
"**/*_codestar_*",
"**/codestar_*"
]
"service/codestarnotifications" = [
"**/*_codestarnotifications_*",
"**/codestarnotifications_*"
]
"service/cognito" = [
"**/*_cognito_*",
"**/cognito_*"
Expand Down
3 changes: 3 additions & 0 deletions aws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/aws/aws-sdk-go/service/codecommit"
"github.com/aws/aws-sdk-go/service/codedeploy"
"github.com/aws/aws-sdk-go/service/codepipeline"
"github.com/aws/aws-sdk-go/service/codestarnotifications"
"github.com/aws/aws-sdk-go/service/cognitoidentity"
"github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
"github.com/aws/aws-sdk-go/service/configservice"
Expand Down Expand Up @@ -212,6 +213,7 @@ type AWSClient struct {
codecommitconn *codecommit.CodeCommit
codedeployconn *codedeploy.CodeDeploy
codepipelineconn *codepipeline.CodePipeline
codestarnotificationsconn *codestarnotifications.CodeStarNotifications
cognitoconn *cognitoidentity.CognitoIdentity
cognitoidpconn *cognitoidentityprovider.CognitoIdentityProvider
configconn *configservice.ConfigService
Expand Down Expand Up @@ -415,6 +417,7 @@ func (c *Config) Client() (interface{}, error) {
codecommitconn: codecommit.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["codecommit"])})),
codedeployconn: codedeploy.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["codedeploy"])})),
codepipelineconn: codepipeline.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["codepipeline"])})),
codestarnotificationsconn: codestarnotifications.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["codestarnotifications"])})),
cognitoconn: cognitoidentity.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["cognitoidentity"])})),
cognitoidpconn: cognitoidentityprovider.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["cognitoidp"])})),
configconn: configservice.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["configservice"])})),
Expand Down
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ func Provider() terraform.ResourceProvider {
"aws_codebuild_webhook": resourceAwsCodeBuildWebhook(),
"aws_codepipeline": resourceAwsCodePipeline(),
"aws_codepipeline_webhook": resourceAwsCodePipelineWebhook(),
"aws_codestarnotifications_notification_rule": resourceAwsCodeStarNotificationsNotificationRule(),
"aws_cur_report_definition": resourceAwsCurReportDefinition(),
"aws_customer_gateway": resourceAwsCustomerGateway(),
"aws_datapipeline_pipeline": resourceAwsDataPipelinePipeline(),
Expand Down
291 changes: 291 additions & 0 deletions aws/resource_aws_codestarnotifications_notification_rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
package aws

import (
"fmt"
"log"
"regexp"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/codestarnotifications"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func resourceAwsCodeStarNotificationsNotificationRule() *schema.Resource {
return &schema.Resource{
Create: resourceAwsCodeStarNotificationsNotificationRuleCreate,
Read: resourceAwsCodeStarNotificationsNotificationRuleRead,
Update: resourceAwsCodeStarNotificationsNotificationRuleUpdate,
Delete: resourceAwsCodeStarNotificationsNotificationRuleDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},

"detail_type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
codestarnotifications.DetailTypeBasic,
codestarnotifications.DetailTypeFull,
}, false),
},

"event_type_ids": {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
MinItems: 1,
MaxItems: 200,
},

"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 64),
validation.StringMatch(regexp.MustCompile(`^[A-Za-z0-9\-_ ]+$`), "must be one or more alphanumeric, hyphen, underscore or space characters"),
),
},

"resource": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateArn,
},

"status": {
Type: schema.TypeString,
Optional: true,
Default: codestarnotifications.NotificationRuleStatusEnabled,
ValidateFunc: validation.StringInSlice([]string{
codestarnotifications.NotificationRuleStatusEnabled,
codestarnotifications.NotificationRuleStatusDisabled,
}, false),
},

"tags": tagsSchema(),

"target": {
Type: schema.TypeSet,
Optional: true,
MaxItems: 10,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"address": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateArn,
},
"type": {
Type: schema.TypeString,
Default: "SNS",
Optional: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
}
}

func expandCodeStarNotificationsNotificationRuleTargets(targetsData []interface{}) []*codestarnotifications.Target {
targets := make([]*codestarnotifications.Target, 0, len(targetsData))
for _, t := range targetsData {
target := t.(map[string]interface{})
targets = append(targets, &codestarnotifications.Target{
TargetAddress: aws.String(target["address"].(string)),
TargetType: aws.String(target["type"].(string)),
})
}
return targets
}

func resourceAwsCodeStarNotificationsNotificationRuleCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).codestarnotificationsconn

params := &codestarnotifications.CreateNotificationRuleInput{
DetailType: aws.String(d.Get("detail_type").(string)),
EventTypeIds: expandStringList(d.Get("event_type_ids").(*schema.Set).List()),
Name: aws.String(d.Get("name").(string)),
Resource: aws.String(d.Get("resource").(string)),
Status: aws.String(d.Get("status").(string)),
Targets: expandCodeStarNotificationsNotificationRuleTargets(d.Get("target").(*schema.Set).List()),
}

if v, ok := d.GetOk("tags"); ok {
params.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().CodestarnotificationsTags()
}

res, err := conn.CreateNotificationRule(params)
if err != nil {
return fmt.Errorf("error creating codestar notification rule: %s", err)
}

d.SetId(aws.StringValue(res.Arn))

return resourceAwsCodeStarNotificationsNotificationRuleRead(d, meta)
}

func resourceAwsCodeStarNotificationsNotificationRuleRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).codestarnotificationsconn

rule, err := conn.DescribeNotificationRule(&codestarnotifications.DescribeNotificationRuleInput{
Arn: aws.String(d.Id()),
})

if err != nil {
if isAWSErr(err, codestarnotifications.ErrCodeResourceNotFoundException, "") {
log.Printf("[WARN] codestar notification rule (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("error reading codestar notification rule: %s", err)
}

d.Set("arn", rule.Arn)
d.Set("detail_type", rule.DetailType)
eventTypeIds := make([]string, 0, len(rule.EventTypes))
for _, et := range rule.EventTypes {
eventTypeIds = append(eventTypeIds, aws.StringValue(et.EventTypeId))
}
if err := d.Set("event_type_ids", eventTypeIds); err != nil {
return fmt.Errorf("error setting event_type_ids: %s", err)
}
d.Set("name", rule.Name)
d.Set("status", rule.Status)
d.Set("resource", rule.Resource)
d.Set("tags", keyvaluetags.New(rule.Tags).IgnoreAws().Map())

targets := make([]map[string]interface{}, 0, len(rule.Targets))
for _, t := range rule.Targets {
targets = append(targets, map[string]interface{}{
"address": aws.StringValue(t.TargetAddress),
"type": aws.StringValue(t.TargetType),
"status": aws.StringValue(t.TargetStatus),
})
}
if err = d.Set("target", targets); err != nil {
return fmt.Errorf("error setting codestar notification target: %s", err)
}

return nil
}

const awsCodeStartNotificationsNotificationRuleErrorSubscribed = "The target cannot be deleted because it is subscribed to one or more notification rules."

// cleanupCodeStarNotificationsNotificationRuleTargets tries to remove unused notification targets. AWS API does not
// provide expicit way for creating targets, they are created on first subscription. Here we are trying to remove all
// unused targets which were unsubscribed from this notification rule.
func cleanupCodeStarNotificationsNotificationRuleTargets(conn *codestarnotifications.CodeStarNotifications, oldVal *schema.Set, newVal *schema.Set) error {
removedTargets := oldVal
if newVal != nil {
removedTargets = oldVal.Difference(newVal)
}

targets := removedTargets.List()

err := resource.Retry(time.Duration(5)*time.Second, func() *resource.RetryError {
var (
reterr error
i int
)

for _, t := range targets {
target := t.(map[string]interface{})
_, err := conn.DeleteTarget(&codestarnotifications.DeleteTargetInput{
ForceUnsubscribeAll: aws.Bool(false),
TargetAddress: aws.String(target["address"].(string)),
})
if isAWSErr(err, codestarnotifications.ErrCodeValidationException, awsCodeStartNotificationsNotificationRuleErrorSubscribed) {
reterr = err
targets[i] = target
i++
} else if err != nil {
return resource.NonRetryableError(err)
}
}
targets = targets[:i]

return resource.RetryableError(reterr)
})

if isAWSErr(err, codestarnotifications.ErrCodeValidationException, awsCodeStartNotificationsNotificationRuleErrorSubscribed) {
err = nil
}

return err
}

func resourceAwsCodeStarNotificationsNotificationRuleUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).codestarnotificationsconn

d.Partial(true)

params := &codestarnotifications.UpdateNotificationRuleInput{
Arn: aws.String(d.Id()),
DetailType: aws.String(d.Get("detail_type").(string)),
EventTypeIds: expandStringList(d.Get("event_type_ids").(*schema.Set).List()),
Name: aws.String(d.Get("name").(string)),
Status: aws.String(d.Get("status").(string)),
Targets: expandCodeStarNotificationsNotificationRuleTargets(d.Get("target").(*schema.Set).List()),
}

if _, err := conn.UpdateNotificationRule(params); err != nil {
return fmt.Errorf("error updating codestar notification rule: %s", err)
}

d.SetPartial("detail_type")
d.SetPartial("event_type_ids")
d.SetPartial("name")
d.SetPartial("target")

if d.HasChange("tags") {
o, n := d.GetChange("tags")
if err := keyvaluetags.CodestarnotificationsUpdateTags(conn, d.Id(), o, n); err != nil {
return fmt.Errorf("error updating codestar notification rule tags: %s", err)
}
}

if d.HasChange("target") {
o, n := d.GetChange("target")
if err := cleanupCodeStarNotificationsNotificationRuleTargets(conn, o.(*schema.Set), n.(*schema.Set)); err != nil {
return err
}
}

d.Partial(false)

return resourceAwsCodeStarNotificationsNotificationRuleRead(d, meta)
}

func resourceAwsCodeStarNotificationsNotificationRuleDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).codestarnotificationsconn

_, err := conn.DeleteNotificationRule(&codestarnotifications.DeleteNotificationRuleInput{
Arn: aws.String(d.Id()),
})

if err != nil {
return fmt.Errorf("error deleting codestar notification rule: %s", err)
}

if err = cleanupCodeStarNotificationsNotificationRuleTargets(conn, d.Get("target").(*schema.Set), nil); err != nil {
return fmt.Errorf("error deleting codestar notification targets: %s", err)
}

return nil
}
Loading

0 comments on commit 65f85e4

Please sign in to comment.