diff --git a/.changelog/24909.txt b/.changelog/24909.txt new file mode 100644 index 000000000000..73dd24788bde --- /dev/null +++ b/.changelog/24909.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_redshift_event_subscription: Add plan time validations for `event_categories`, `source_type`, and `severity`. +``` \ No newline at end of file diff --git a/internal/service/redshift/event_subscription.go b/internal/service/redshift/event_subscription.go index 02e8ba830c02..eae1f8eb908a 100644 --- a/internal/service/redshift/event_subscription.go +++ b/internal/service/redshift/event_subscription.go @@ -9,8 +9,8 @@ import ( "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/redshift" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -24,60 +24,81 @@ func ResourceEventSubscription() *schema.Resource { Read: resourceEventSubscriptionRead, Update: resourceEventSubscriptionUpdate, Delete: resourceEventSubscriptionDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, + Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(40 * time.Minute), Delete: schema.DefaultTimeout(40 * time.Minute), Update: schema.DefaultTimeout(40 * time.Minute), }, + Schema: map[string]*schema.Schema{ "arn": { Type: schema.TypeString, Computed: true, }, - + "customer_aws_id": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "event_categories": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "configuration", + "management", + "monitoring", + "security", + "pending", + }, false), + }, + }, "name": { Type: schema.TypeString, Required: true, ForceNew: true, }, + "severity": { + Type: schema.TypeString, + Optional: true, + Default: "INFO", + ValidateFunc: validation.StringInSlice([]string{ + "ERROR", + "INFO", + }, false), + }, "sns_topic_arn": { Type: schema.TypeString, Required: true, ValidateFunc: verify.ValidARN, }, - "status": { - Type: schema.TypeString, - Computed: true, - }, - "event_categories": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, "source_ids": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, "source_type": { Type: schema.TypeString, Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "cluster", + "cluster-parameter-group", + "cluster-security-group", + "cluster-snapshot", + "scheduled-action", + }, false), }, - "enabled": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - "severity": { - Type: schema.TypeString, - Optional: true, - }, - "customer_aws_id": { + "status": { Type: schema.TypeString, Computed: true, }, @@ -98,13 +119,25 @@ func resourceEventSubscriptionCreate(d *schema.ResourceData, meta interface{}) e SubscriptionName: aws.String(d.Get("name").(string)), SnsTopicArn: aws.String(d.Get("sns_topic_arn").(string)), Enabled: aws.Bool(d.Get("enabled").(bool)), - SourceIds: flex.ExpandStringSet(d.Get("source_ids").(*schema.Set)), - SourceType: aws.String(d.Get("source_type").(string)), - Severity: aws.String(d.Get("severity").(string)), - EventCategories: flex.ExpandStringSet(d.Get("event_categories").(*schema.Set)), Tags: Tags(tags.IgnoreAWS()), } + if v, ok := d.GetOk("event_categories"); ok && v.(*schema.Set).Len() > 0 { + request.EventCategories = flex.ExpandStringSet(v.(*schema.Set)) + } + + if v, ok := d.GetOk("source_ids"); ok && v.(*schema.Set).Len() > 0 { + request.SourceIds = flex.ExpandStringSet(v.(*schema.Set)) + } + + if v, ok := d.GetOk("severity"); ok { + request.Severity = aws.String(v.(string)) + } + + if v, ok := d.GetOk("source_type"); ok { + request.SourceType = aws.String(v.(string)) + } + log.Println("[DEBUG] Create Redshift Event Subscription:", request) output, err := conn.CreateEventSubscription(request) @@ -122,17 +155,8 @@ func resourceEventSubscriptionRead(d *schema.ResourceData, meta interface{}) err defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - arn := arn.ARN{ - Partition: meta.(*conns.AWSClient).Partition, - Service: "redshift", - Region: meta.(*conns.AWSClient).Region, - AccountID: meta.(*conns.AWSClient).AccountID, - Resource: fmt.Sprintf("eventsubscription:%s", d.Id()), - }.String() - - d.Set("arn", arn) + sub, err := FindEventSubscriptionByName(conn, d.Id()) - sub, err := findEventSubscription(conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Redshift Event Subscription (%s) not found, removing from state", d.Id()) d.SetId("") @@ -142,33 +166,28 @@ func resourceEventSubscriptionRead(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("error retrieving Redshift Event Subscription %s: %w", d.Id(), err) } - if err := d.Set("name", sub.CustSubscriptionId); err != nil { - return err - } - if err := d.Set("sns_topic_arn", sub.SnsTopicArn); err != nil { - return err - } - if err := d.Set("status", sub.Status); err != nil { - return err - } - if err := d.Set("source_type", sub.SourceType); err != nil { - return err - } - if err := d.Set("severity", sub.Severity); err != nil { - return err - } - if err := d.Set("enabled", sub.Enabled); err != nil { - return err - } - if err := d.Set("source_ids", flex.FlattenStringList(sub.SourceIdsList)); err != nil { - return err - } - if err := d.Set("event_categories", flex.FlattenStringList(sub.EventCategoriesList)); err != nil { - return err - } - if err := d.Set("customer_aws_id", sub.CustomerAwsId); err != nil { - return err + if err != nil { + return fmt.Errorf("error reading Redshift Event Subscription (%s): %w", d.Id(), err) } + + arn := arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Service: "redshift", + Region: meta.(*conns.AWSClient).Region, + AccountID: meta.(*conns.AWSClient).AccountID, + Resource: fmt.Sprintf("eventsubscription:%s", d.Id()), + }.String() + d.Set("arn", arn) + d.Set("customer_aws_id", sub.CustomerAwsId) + d.Set("enabled", sub.Enabled) + d.Set("event_categories", aws.StringValueSlice(sub.EventCategoriesList)) + d.Set("name", sub.CustSubscriptionId) + d.Set("severity", sub.Severity) + d.Set("sns_topic_arn", sub.SnsTopicArn) + d.Set("source_ids", aws.StringValueSlice(sub.SourceIdsList)) + d.Set("source_type", sub.SourceType) + d.Set("status", sub.Status) + tags := KeyValueTags(sub.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 @@ -183,48 +202,25 @@ func resourceEventSubscriptionRead(d *schema.ResourceData, meta interface{}) err return nil } -func findEventSubscription(conn *redshift.Redshift, name string) (*redshift.EventSubscription, error) { - input := &redshift.DescribeEventSubscriptionsInput{ - SubscriptionName: aws.String(name), - } - out, err := conn.DescribeEventSubscriptions(input) - if tfawserr.ErrCodeEquals(err, redshift.ErrCodeSubscriptionNotFoundFault) { - return nil, &resource.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - if err != nil { - return nil, err - } - - switch count := len(out.EventSubscriptionsList); count { - case 0: - return nil, tfresource.NewEmptyResultError(input) - case 1: - return out.EventSubscriptionsList[0], nil - default: - return nil, tfresource.NewTooManyResultsError(count, input) - } -} - func resourceEventSubscriptionUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).RedshiftConn - req := &redshift.ModifyEventSubscriptionInput{ - SubscriptionName: aws.String(d.Id()), - SnsTopicArn: aws.String(d.Get("sns_topic_arn").(string)), - Enabled: aws.Bool(d.Get("enabled").(bool)), - SourceIds: flex.ExpandStringSet(d.Get("source_ids").(*schema.Set)), - SourceType: aws.String(d.Get("source_type").(string)), - Severity: aws.String(d.Get("severity").(string)), - EventCategories: flex.ExpandStringSet(d.Get("event_categories").(*schema.Set)), - } + if d.HasChangesExcept("tags", "tags_all") { + req := &redshift.ModifyEventSubscriptionInput{ + SubscriptionName: aws.String(d.Id()), + SnsTopicArn: aws.String(d.Get("sns_topic_arn").(string)), + Enabled: aws.Bool(d.Get("enabled").(bool)), + SourceIds: flex.ExpandStringSet(d.Get("source_ids").(*schema.Set)), + SourceType: aws.String(d.Get("source_type").(string)), + Severity: aws.String(d.Get("severity").(string)), + EventCategories: flex.ExpandStringSet(d.Get("event_categories").(*schema.Set)), + } - log.Printf("[DEBUG] Redshift Event Subscription modification request: %#v", req) - _, err := conn.ModifyEventSubscription(req) - if err != nil { - return fmt.Errorf("Modifying Redshift Event Subscription %s failed: %s", d.Id(), err) + log.Printf("[DEBUG] Redshift Event Subscription modification request: %#v", req) + _, err := conn.ModifyEventSubscription(req) + if err != nil { + return fmt.Errorf("Modifying Redshift Event Subscription %s failed: %s", d.Id(), err) + } } if d.HasChange("tags_all") { diff --git a/internal/service/redshift/event_subscription_test.go b/internal/service/redshift/event_subscription_test.go index 8a2c2551b9ca..e4106b78315d 100644 --- a/internal/service/redshift/event_subscription_test.go +++ b/internal/service/redshift/event_subscription_test.go @@ -2,22 +2,23 @@ package redshift_test import ( "fmt" + "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/redshift" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfredshift "github.com/hashicorp/terraform-provider-aws/internal/service/redshift" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func TestAccRedshiftEventSubscription_basicUpdate(t *testing.T) { +func TestAccRedshiftEventSubscription_basic(t *testing.T) { var v redshift.EventSubscription - rInt := sdkacctest.RandInt() - rName := fmt.Sprintf("tf-acc-test-redshift-event-subs-%d", rInt) + resourceName := "aws_redshift_event_subscription.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -26,73 +27,47 @@ func TestAccRedshiftEventSubscription_basicUpdate(t *testing.T) { CheckDestroy: testAccCheckEventSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: testAccEventSubscriptionConfig(rInt), + Config: testAccEventSubscriptionConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEventSubscriptionExists("aws_redshift_event_subscription.bar", &v), - resource.TestCheckResourceAttr("aws_redshift_event_subscription.bar", "enabled", "true"), - resource.TestCheckResourceAttr("aws_redshift_event_subscription.bar", "source_type", "cluster"), - resource.TestCheckResourceAttr("aws_redshift_event_subscription.bar", "name", rName), - resource.TestCheckResourceAttr("aws_redshift_event_subscription.bar", "tags.%", "1"), - resource.TestCheckResourceAttr("aws_redshift_event_subscription.bar", "tags.Name", "name"), - ), - }, - { - Config: testAccEventSubscriptionUpdateConfig(rInt), - Check: resource.ComposeTestCheckFunc( - testAccCheckEventSubscriptionExists("aws_redshift_event_subscription.bar", &v), - resource.TestCheckResourceAttr("aws_redshift_event_subscription.bar", "enabled", "false"), - resource.TestCheckResourceAttr("aws_redshift_event_subscription.bar", "source_type", "cluster-snapshot"), - resource.TestCheckResourceAttr("aws_redshift_event_subscription.bar", "tags.%", "1"), - resource.TestCheckResourceAttr("aws_redshift_event_subscription.bar", "tags.Name", "new-name"), + testAccCheckEventSubscriptionExists(resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "redshift", regexp.MustCompile(`eventsubscription:.+`)), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "severity", "INFO"), + resource.TestCheckResourceAttr(resourceName, "status", "active"), + acctest.CheckResourceAttrAccountID(resourceName, "customer_aws_id"), + resource.TestCheckResourceAttr(resourceName, "event_categories.#", "0"), + resource.TestCheckResourceAttr(resourceName, "source_ids.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "sns_topic_arn", "aws_sns_topic.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { - ResourceName: "aws_redshift_event_subscription.bar", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, - }, - }) -} - -func TestAccRedshiftEventSubscription_withPrefix(t *testing.T) { - var v redshift.EventSubscription - rInt := sdkacctest.RandInt() - rName := fmt.Sprintf("tf-acc-test-redshift-event-subs-%d", rInt) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, redshift.EndpointsID), - ProviderFactories: acctest.ProviderFactories, - CheckDestroy: testAccCheckEventSubscriptionDestroy, - Steps: []resource.TestStep{ { - Config: testAccEventSubscriptionConfig(rInt), + Config: testAccEventSubscriptionUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEventSubscriptionExists("aws_redshift_event_subscription.bar", &v), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "enabled", "true"), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "source_type", "cluster"), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "name", rName), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "tags.Name", "name"), + testAccCheckEventSubscriptionExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "severity", "INFO"), + resource.TestCheckResourceAttr(resourceName, "source_type", "cluster-snapshot"), + resource.TestCheckTypeSetElemAttr(resourceName, "event_categories.*", "monitoring"), + resource.TestCheckResourceAttrPair(resourceName, "sns_topic_arn", "aws_sns_topic.test", "arn"), + acctest.CheckResourceAttrAccountID(resourceName, "customer_aws_id"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, - { - ResourceName: "aws_redshift_event_subscription.bar", - ImportState: true, - ImportStateVerify: true, - }, }, }) } func TestAccRedshiftEventSubscription_withSourceIDs(t *testing.T) { var v redshift.EventSubscription - rInt := sdkacctest.RandInt() - rName := fmt.Sprintf("tf-acc-test-redshift-event-subs-with-ids-%d", rInt) + resourceName := "aws_redshift_event_subscription.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -101,35 +76,27 @@ func TestAccRedshiftEventSubscription_withSourceIDs(t *testing.T) { CheckDestroy: testAccCheckEventSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: testAccEventSubscriptionWithSourceIDsConfig(rInt), + Config: testAccEventSubscriptionWithSourceIDsConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEventSubscriptionExists("aws_redshift_event_subscription.bar", &v), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "enabled", "true"), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "source_type", "cluster-parameter-group"), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "name", rName), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "source_ids.#", "1"), + testAccCheckEventSubscriptionExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "source_type", "cluster-parameter-group"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "source_ids.#", "1"), ), }, { - Config: testAccEventSubscriptionUpdateSourceIDsConfig(rInt), + Config: testAccEventSubscriptionUpdateSourceIDsConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEventSubscriptionExists("aws_redshift_event_subscription.bar", &v), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "enabled", "true"), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "source_type", "cluster-parameter-group"), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "name", rName), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "source_ids.#", "2"), + testAccCheckEventSubscriptionExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "source_type", "cluster-parameter-group"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "source_ids.#", "2"), ), }, { - ResourceName: "aws_redshift_event_subscription.bar", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -139,8 +106,8 @@ func TestAccRedshiftEventSubscription_withSourceIDs(t *testing.T) { func TestAccRedshiftEventSubscription_categoryUpdate(t *testing.T) { var v redshift.EventSubscription - rInt := sdkacctest.RandInt() - rName := fmt.Sprintf("tf-acc-test-redshift-event-subs-%d", rInt) + resourceName := "aws_redshift_event_subscription.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -149,29 +116,23 @@ func TestAccRedshiftEventSubscription_categoryUpdate(t *testing.T) { CheckDestroy: testAccCheckEventSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: testAccEventSubscriptionConfig(rInt), + Config: testAccEventSubscriptionConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEventSubscriptionExists("aws_redshift_event_subscription.bar", &v), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "enabled", "true"), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "source_type", "cluster"), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "name", rName), + testAccCheckEventSubscriptionExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "name", rName), ), }, { - Config: testAccEventSubscriptionUpdateCategoriesConfig(rInt), + Config: testAccEventSubscriptionUpdateCategoriesConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckEventSubscriptionExists("aws_redshift_event_subscription.bar", &v), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "enabled", "true"), - resource.TestCheckResourceAttr( - "aws_redshift_event_subscription.bar", "source_type", "cluster"), + testAccCheckEventSubscriptionExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "source_type", "cluster"), ), }, { - ResourceName: "aws_redshift_event_subscription.bar", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -179,10 +140,10 @@ func TestAccRedshiftEventSubscription_categoryUpdate(t *testing.T) { }) } -func TestAccRedshiftEventSubscription_tagsUpdate(t *testing.T) { +func TestAccRedshiftEventSubscription_tags(t *testing.T) { var v redshift.EventSubscription - rInt := sdkacctest.RandInt() - resourceName := "aws_redshift_event_subscription.bar" + resourceName := "aws_redshift_event_subscription.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -191,38 +152,57 @@ func TestAccRedshiftEventSubscription_tagsUpdate(t *testing.T) { CheckDestroy: testAccCheckEventSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: testAccEventSubscriptionConfig(rInt), + Config: testAccEventSubscriptionConfigTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( testAccCheckEventSubscriptionExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.Name", "name"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { - Config: testAccEventSubscriptionUpdateTagsConfig(rInt, "aaaaa"), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccEventSubscriptionConfigTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckEventSubscriptionExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.Name", "name"), - resource.TestCheckResourceAttr(resourceName, "tags.Test", "aaaaa"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, { - Config: testAccEventSubscriptionUpdateTagsConfig(rInt, "bbbbb"), + Config: testAccEventSubscriptionConfigTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckEventSubscriptionExists(resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.Name", "name"), - resource.TestCheckResourceAttr(resourceName, "tags.Test", "bbbbb"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, + }, + }) +} + +func TestAccRedshiftEventSubscription_disappears(t *testing.T) { + var v redshift.EventSubscription + resourceName := "aws_redshift_event_subscription.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, redshift.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckEventSubscriptionDestroy, + Steps: []resource.TestStep{ { - Config: testAccEventSubscriptionConfig(rInt), + Config: testAccEventSubscriptionConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckEventSubscriptionExists(resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.Name", "name"), + acctest.CheckResourceDisappears(acctest.Provider, tfredshift.ResourceEventSubscription(), resourceName), ), + ExpectNonEmptyPlan: true, }, }, }) @@ -241,22 +221,14 @@ func testAccCheckEventSubscriptionExists(n string, v *redshift.EventSubscription conn := acctest.Provider.Meta().(*conns.AWSClient).RedshiftConn - opts := redshift.DescribeEventSubscriptionsInput{ - SubscriptionName: aws.String(rs.Primary.ID), - } - - resp, err := conn.DescribeEventSubscriptions(&opts) + out, err := tfredshift.FindEventSubscriptionByName(conn, rs.Primary.ID) if err != nil { return err } - if len(resp.EventSubscriptionsList) != 1 || - *resp.EventSubscriptionsList[0].CustSubscriptionId != rs.Primary.ID { - return fmt.Errorf("Redshift Event Subscription not found") - } + *v = *out - *v = *resp.EventSubscriptionsList[0] return nil } } @@ -269,13 +241,9 @@ func testAccCheckEventSubscriptionDestroy(s *terraform.State) error { continue } - var err error - resp, err := conn.DescribeEventSubscriptions( - &redshift.DescribeEventSubscriptionsInput{ - SubscriptionName: aws.String(rs.Primary.ID), - }) + _, err := tfredshift.FindEventSubscriptionByName(conn, rs.Primary.ID) - if tfawserr.ErrCodeEquals(err, redshift.ErrCodeSubscriptionNotFoundFault) { + if tfresource.NotFound(err) { continue } @@ -283,50 +251,34 @@ func testAccCheckEventSubscriptionDestroy(s *terraform.State) error { return err } - if len(resp.EventSubscriptionsList) != 0 && - *resp.EventSubscriptionsList[0].CustSubscriptionId == rs.Primary.ID { - return fmt.Errorf("Event Subscription still exists") - } + return fmt.Errorf("Redshift Event Subscription %s still exists", rs.Primary.ID) } return nil } -func testAccEventSubscriptionConfig(rInt int) string { +func testAccEventSubscriptionConfig(rName string) string { return fmt.Sprintf(` -resource "aws_sns_topic" "aws_sns_topic" { - name = "tf-acc-test-redshift-event-subs-sns-topic-%d" +resource "aws_sns_topic" "test" { + name = %[1]q } -resource "aws_redshift_event_subscription" "bar" { - name = "tf-acc-test-redshift-event-subs-%d" - sns_topic_arn = aws_sns_topic.aws_sns_topic.arn - source_type = "cluster" - severity = "INFO" - - event_categories = [ - "configuration", - "management", - "monitoring", - "security", - ] - - tags = { - Name = "name" - } +resource "aws_redshift_event_subscription" "test" { + name = %[1]q + sns_topic_arn = aws_sns_topic.test.arn } -`, rInt, rInt) +`, rName) } -func testAccEventSubscriptionUpdateConfig(rInt int) string { +func testAccEventSubscriptionUpdateConfig(rName string) string { return fmt.Sprintf(` -resource "aws_sns_topic" "aws_sns_topic" { - name = "tf-acc-test-redshift-event-subs-sns-topic-%d" +resource "aws_sns_topic" "test" { + name = %[1]q } -resource "aws_redshift_event_subscription" "bar" { - name = "tf-acc-test-redshift-event-subs-%d" - sns_topic_arn = aws_sns_topic.aws_sns_topic.arn +resource "aws_redshift_event_subscription" "test" { + name = %[1]q + sns_topic_arn = aws_sns_topic.test.arn enabled = false source_type = "cluster-snapshot" severity = "INFO" @@ -334,112 +286,122 @@ resource "aws_redshift_event_subscription" "bar" { event_categories = [ "monitoring", ] - - tags = { - Name = "new-name" - } } -`, rInt, rInt) +`, rName) } -func testAccEventSubscriptionWithSourceIDsConfig(rInt int) string { +func testAccEventSubscriptionWithSourceIDsConfig(rName string) string { return fmt.Sprintf(` -resource "aws_sns_topic" "aws_sns_topic" { - name = "tf-acc-test-redshift-event-subs-sns-topic-%d" +resource "aws_sns_topic" "test" { + name = %[1]q } -resource "aws_redshift_parameter_group" "bar" { - name = "redshift-parameter-group-event-%d" +resource "aws_redshift_parameter_group" "test" { + name = %[1]q family = "redshift-1.0" description = "Test parameter group for terraform" } -resource "aws_redshift_event_subscription" "bar" { - name = "tf-acc-test-redshift-event-subs-with-ids-%d" - sns_topic_arn = aws_sns_topic.aws_sns_topic.arn +resource "aws_redshift_event_subscription" "test" { + name = %[1]q + sns_topic_arn = aws_sns_topic.test.arn source_type = "cluster-parameter-group" severity = "INFO" - source_ids = [aws_redshift_parameter_group.bar.id] + source_ids = [aws_redshift_parameter_group.test.id] event_categories = [ "configuration", ] - - tags = { - Name = "name" - } } -`, rInt, rInt, rInt) +`, rName) } -func testAccEventSubscriptionUpdateSourceIDsConfig(rInt int) string { +func testAccEventSubscriptionUpdateSourceIDsConfig(rName string) string { return fmt.Sprintf(` -resource "aws_sns_topic" "aws_sns_topic" { - name = "tf-acc-test-redshift-event-subs-sns-topic-%d" +resource "aws_sns_topic" "test" { + name = %[1]q } -resource "aws_redshift_parameter_group" "bar" { - name = "tf-acc-redshift-parameter-group-event-%d" +resource "aws_redshift_parameter_group" "test" { + name = %[1]q family = "redshift-1.0" description = "Test parameter group for terraform" } resource "aws_redshift_parameter_group" "foo" { - name = "tf-acc-redshift-parameter-group-event-2-%d" + name = "%[1]s-2" family = "redshift-1.0" description = "Test parameter group for terraform" } -resource "aws_redshift_event_subscription" "bar" { - name = "tf-acc-test-redshift-event-subs-with-ids-%d" - sns_topic_arn = aws_sns_topic.aws_sns_topic.arn +resource "aws_redshift_event_subscription" "test" { + name = %[1]q + sns_topic_arn = aws_sns_topic.test.arn source_type = "cluster-parameter-group" severity = "INFO" - source_ids = [aws_redshift_parameter_group.bar.id, aws_redshift_parameter_group.foo.id] + source_ids = [aws_redshift_parameter_group.test.id, aws_redshift_parameter_group.foo.id] event_categories = [ "configuration", ] +} +`, rName) +} - tags = { - Name = "name" - } +func testAccEventSubscriptionUpdateCategoriesConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_sns_topic" "test" { + name = %[1]q +} + +resource "aws_redshift_event_subscription" "test" { + name = %[1]q + sns_topic_arn = aws_sns_topic.test.arn + source_type = "cluster" + severity = "INFO" + + event_categories = [ + "monitoring", + ] } -`, rInt, rInt, rInt, rInt) +`, rName) } -func testAccEventSubscriptionUpdateCategoriesConfig(rInt int) string { +func testAccEventSubscriptionConfigTags1(rName, tagKey1, tagValue1 string) string { return fmt.Sprintf(` -resource "aws_sns_topic" "aws_sns_topic" { - name = "tf-acc-test-redshift-event-subs-sns-topic-%d" +resource "aws_sns_topic" "test" { + name = %[1]q } -resource "aws_redshift_event_subscription" "bar" { - name = "tf-acc-test-redshift-event-subs-%d" - sns_topic_arn = aws_sns_topic.aws_sns_topic.arn +resource "aws_redshift_event_subscription" "test" { + name = %[1]q + sns_topic_arn = aws_sns_topic.test.arn source_type = "cluster" severity = "INFO" event_categories = [ + "configuration", + "management", "monitoring", + "security", ] tags = { - Name = "name" + %[2]q = %[3]q } } -`, rInt, rInt) +`, rName, tagKey1, tagValue1) } -func testAccEventSubscriptionUpdateTagsConfig(rInt int, rString string) string { +func testAccEventSubscriptionConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { return fmt.Sprintf(` -resource "aws_sns_topic" "aws_sns_topic" { - name = "tf-acc-test-redshift-event-subs-sns-topic-%d" +resource "aws_sns_topic" "test" { + name = %[1]q } -resource "aws_redshift_event_subscription" "bar" { - name = "tf-acc-test-redshift-event-subs-%d" - sns_topic_arn = aws_sns_topic.aws_sns_topic.arn +resource "aws_redshift_event_subscription" "test" { + name = %[1]q + sns_topic_arn = aws_sns_topic.test.arn source_type = "cluster" severity = "INFO" @@ -451,9 +413,9 @@ resource "aws_redshift_event_subscription" "bar" { ] tags = { - Name = "name" - Test = "%s" + %[2]q = %[3]q + %[4]q = %[5]q } } -`, rInt, rInt, rString) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) } diff --git a/internal/service/redshift/find.go b/internal/service/redshift/find.go index c4b88cba5f10..6956e8e2794b 100644 --- a/internal/service/redshift/find.go +++ b/internal/service/redshift/find.go @@ -200,3 +200,32 @@ func FindAuthenticationProfileByID(conn *redshift.Redshift, id string) (*redshif return out.AuthenticationProfiles[0], nil } + +func FindEventSubscriptionByName(conn *redshift.Redshift, name string) (*redshift.EventSubscription, error) { + input := &redshift.DescribeEventSubscriptionsInput{ + SubscriptionName: aws.String(name), + } + + output, err := conn.DescribeEventSubscriptions(input) + + if tfawserr.ErrCodeEquals(err, redshift.ErrCodeSubscriptionNotFoundFault) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.EventSubscriptionsList) == 0 || output.EventSubscriptionsList[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output.EventSubscriptionsList); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output.EventSubscriptionsList[0], nil +} diff --git a/website/docs/r/redshift_event_subscription.html.markdown b/website/docs/r/redshift_event_subscription.html.markdown index 366d68bd11c1..f79c9de0cc9b 100644 --- a/website/docs/r/redshift_event_subscription.html.markdown +++ b/website/docs/r/redshift_event_subscription.html.markdown @@ -52,11 +52,11 @@ The following arguments are supported: * `name` - (Required) The name of the Redshift event subscription. * `sns_topic_arn` - (Required) The ARN of the SNS topic to send events to. -* `source_ids` - (Optional) A list of identifiers of the event sources for which events will be returned. If not specified, then all sources are included in the response. If specified, a source_type must also be specified. -* `source_type` - (Optional) The type of source that will be generating the events. Valid options are `cluster`, `cluster-parameter-group`, `cluster-security-group`, or `cluster-snapshot`. If not set, all sources will be subscribed to. -* `severity` - (Optional) The event severity to be published by the notification subscription. Valid options are `INFO` or `ERROR`. +* `source_ids` - (Optional) A list of identifiers of the event sources for which events will be returned. If not specified, then all sources are included in the response. If specified, a `source_type` must also be specified. +* `source_type` - (Optional) The type of source that will be generating the events. Valid options are `cluster`, `cluster-parameter-group`, `cluster-security-group`, `cluster-snapshot`, or `scheduled-action`. If not set, all sources will be subscribed to. +* `severity` - (Optional) The event severity to be published by the notification subscription. Valid options are `INFO` or `ERROR`. Default value of `INFO`. * `event_categories` - (Optional) A list of event categories for a SourceType that you want to subscribe to. See https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-event-notifications.html or run `aws redshift describe-event-categories`. -* `enabled` - (Optional) A boolean flag to enable/disable the subscription. Defaults to true. +* `enabled` - (Optional) A boolean flag to enable/disable the subscription. Defaults to `true`. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ## Attributes Reference