From ed8644b01c4a934bb8b30504d559894a57ddb495 Mon Sep 17 00:00:00 2001 From: Alexander Kalach Date: Mon, 13 Apr 2020 21:18:41 +0200 Subject: [PATCH 01/12] Add event_bus_name attribute to aws_cloudwatch_event_rule --- aws/resource_aws_cloudwatch_event_rule.go | 84 +++++++++++++++++-- ...resource_aws_cloudwatch_event_rule_test.go | 2 + .../r/cloudwatch_event_rule.html.markdown | 3 +- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_rule.go b/aws/resource_aws_cloudwatch_event_rule.go index 62a58bfc86d1..bb3e7db61d7e 100644 --- a/aws/resource_aws_cloudwatch_event_rule.go +++ b/aws/resource_aws_cloudwatch_event_rule.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "strings" "time" "github.com/aws/aws-sdk-go/aws" @@ -26,7 +27,7 @@ func resourceAwsCloudWatchEventRule() *schema.Resource { Update: resourceAwsCloudWatchEventRuleUpdate, Delete: resourceAwsCloudWatchEventRuleDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: resourceAwsCloudWatchEventRuleImport, }, Schema: map[string]*schema.Schema{ @@ -49,6 +50,20 @@ func resourceAwsCloudWatchEventRule() *schema.Resource { Optional: true, ValidateFunc: validation.StringLenBetween(0, 256), }, + "event_bus_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 256), + StateFunc: func(v interface{}) string { + if v.(string) == "default" { + // "default" event bus name is not stored in the state to support the case when event_bus_name is omitted + return "" + } + + return v.(string) + }, + }, "event_pattern": { Type: schema.TypeString, Optional: true, @@ -123,7 +138,12 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface } d.Set("arn", out.RuleArn) - d.SetId(*input.Name) + id := aws.StringValue(input.Name) + eventBusName := aws.StringValue(input.EventBusName) + if eventBusName != "" && eventBusName != "default" { + id = eventBusName + "/" + id + } + d.SetId(id) log.Printf("[INFO] CloudWatch Event Rule %q created", *out.RuleArn) @@ -132,9 +152,13 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatcheventsconn - + busName, ruleName, err := determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(d.Id()) + if err != nil { + return err + } input := events.DescribeRuleInput{ - Name: aws.String(d.Id()), + Name: aws.String(ruleName), + EventBusName: aws.String(busName), } log.Printf("[DEBUG] Reading CloudWatch Event Rule: %s", input) out, err := conn.DescribeRule(&input) @@ -163,6 +187,9 @@ func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{} d.Set("name", out.Name) d.Set("role_arn", out.RoleArn) d.Set("schedule_expression", out.ScheduleExpression) + if aws.StringValue(out.EventBusName) != "default" { + d.Set("event_bus_name", out.EventBusName) + } boolState, err := getBooleanStateFromString(*out.State) if err != nil { @@ -186,8 +213,11 @@ func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{} func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatcheventsconn - - input, err := buildPutRuleInputStruct(d, d.Id()) + _, ruleName, err := determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(d.Id()) + if err != nil { + return err + } + input, err := buildPutRuleInputStruct(d, ruleName) if err != nil { return fmt.Errorf("Updating CloudWatch Event Rule failed: %s", err) } @@ -228,12 +258,16 @@ func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface func resourceAwsCloudWatchEventRuleDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatcheventsconn - + busName, ruleName, err := determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(d.Id()) + if err != nil { + return err + } input := &events.DeleteRuleInput{ - Name: aws.String(d.Id()), + Name: aws.String(ruleName), + EventBusName: aws.String(busName), } - err := resource.Retry(cloudWatchEventRuleDeleteRetryTimeout, func() *resource.RetryError { + err = resource.Retry(cloudWatchEventRuleDeleteRetryTimeout, func() *resource.RetryError { _, err := conn.DeleteRule(input) if isAWSErr(err, "ValidationException", "Rule can't be deleted since it has targets") { @@ -265,6 +299,9 @@ func buildPutRuleInputStruct(d *schema.ResourceData, name string) (*events.PutRu if v, ok := d.GetOk("description"); ok { input.Description = aws.String(v.(string)) } + if v, ok := d.GetOk("event_bus_name"); ok { + input.EventBusName = aws.String(v.(string)) + } if v, ok := d.GetOk("event_pattern"); ok { pattern, err := structure.NormalizeJsonString(v) if err != nil { @@ -328,3 +365,32 @@ func validateEventPatternValue() schema.SchemaValidateFunc { return } } + +func determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(id string) (string, string, error) { + splitId := strings.Split(id, "/") + busName := "default" + var ruleName string + if len(splitId) > 2 { + return busName, ruleName, fmt.Errorf("wrong format of resource: %s. Please follow / or ", id) + } else if len(splitId) == 2 { + busName = splitId[0] + ruleName = splitId[1] + } else { + ruleName = splitId[0] + } + + return busName, ruleName, nil +} + +func resourceAwsCloudWatchEventRuleImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + busName, ruleName, err := determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(d.Id()) + if err != nil { + return []*schema.ResourceData{}, err + } + if busName != "default" { + d.Set("event_bus_name", busName) + } + d.Set("name", ruleName) + + return []*schema.ResourceData{d}, nil +} diff --git a/aws/resource_aws_cloudwatch_event_rule_test.go b/aws/resource_aws_cloudwatch_event_rule_test.go index aab1f3533fcd..006566636d28 100644 --- a/aws/resource_aws_cloudwatch_event_rule_test.go +++ b/aws/resource_aws_cloudwatch_event_rule_test.go @@ -86,6 +86,7 @@ func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { testAccCheckCloudWatchEventRuleExists(resourceName, &rule), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "events", regexp.MustCompile(`rule/.+`)), resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckNoResourceAttr(resourceName, "event_bus_name"), resource.TestCheckResourceAttr(resourceName, "schedule_expression", "rate(1 hour)"), resource.TestCheckResourceAttr(resourceName, "role_arn", ""), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), @@ -398,6 +399,7 @@ func testAccAWSCloudWatchEventRuleConfig(name string) string { resource "aws_cloudwatch_event_rule" "test" { name = "%s" schedule_expression = "rate(1 hour)" + event_bus_name = "default" } `, name) } diff --git a/website/docs/r/cloudwatch_event_rule.html.markdown b/website/docs/r/cloudwatch_event_rule.html.markdown index ae6131be17d1..4f2704543a0e 100644 --- a/website/docs/r/cloudwatch_event_rule.html.markdown +++ b/website/docs/r/cloudwatch_event_rule.html.markdown @@ -64,6 +64,7 @@ The following arguments are supported: * `name_prefix` - (Optional) The rule's name. Conflicts with `name`. * `schedule_expression` - (Required, if `event_pattern` isn't specified) The scheduling expression. For example, `cron(0 20 * * ? *)` or `rate(5 minutes)`. +* `event_bus_name` - (Optional) The event bus to associate with this rule. If you omit this, the `default` event bus is used. * `event_pattern` - (Required, if `schedule_expression` isn't specified) Event pattern described a JSON object. See full documentation of [CloudWatch Events and Event Patterns](http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CloudWatchEventsandEventPatterns.html) for details. @@ -82,7 +83,7 @@ In addition to all arguments above, the following attributes are exported: ## Import -Cloudwatch Event Rules can be imported using the `name`, e.g. +Cloudwatch Event Rules can be imported using the `event_bus_name/rule_name` (if you omit `event_bus_name`, the `default` event bus will be used), e.g. ``` $ terraform import aws_cloudwatch_event_rule.console capture-console-sign-in From e6bb2d3674199fd18b9ef38972feba2aa3e7bc78 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 16 Oct 2020 15:49:51 -0700 Subject: [PATCH 02/12] Clean up tests --- ...resource_aws_cloudwatch_event_rule_test.go | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_rule_test.go b/aws/resource_aws_cloudwatch_event_rule_test.go index 271fe95ba0f1..36bdbd273a3a 100644 --- a/aws/resource_aws_cloudwatch_event_rule_test.go +++ b/aws/resource_aws_cloudwatch_event_rule_test.go @@ -70,7 +70,7 @@ func testSweepCloudWatchEventRules(region string) error { } func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { - var rule events.DescribeRuleOutput + var v1, v2 events.DescribeRuleOutput rName := acctest.RandomWithPrefix("tf-acc-test") rName2 := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cloudwatch_event_rule.test" @@ -83,11 +83,13 @@ func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), - testAccMatchResourceAttrRegionalARN(resourceName, "arn", "events", regexp.MustCompile(`rule/.+`)), + testAccCheckCloudWatchEventRuleExists(resourceName, &v1), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "events", regexp.MustCompile(fmt.Sprintf(`rule/%s$`, rName))), resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckNoResourceAttr(resourceName, "event_bus_name"), resource.TestCheckResourceAttr(resourceName, "schedule_expression", "rate(1 hour)"), + resource.TestCheckNoResourceAttr(resourceName, "event_bus_name"), + resource.TestCheckNoResourceAttr(resourceName, "event_pattern"), + resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttr(resourceName, "role_arn", ""), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"), @@ -95,15 +97,15 @@ func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"is_enabled"}, //this has a default value + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, { Config: testAccAWSCloudWatchEventRuleConfig(rName2), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v2), + testAccCheckCloudWatchEventRuleRecreated(&v1, &v2), resource.TestCheckResourceAttr(resourceName, "name", rName2), resource.TestCheckResourceAttr(resourceName, "schedule_expression", "rate(1 hour)"), resource.TestCheckResourceAttr(resourceName, "role_arn", ""), @@ -117,9 +119,11 @@ func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { } func TestAccAWSCloudWatchEventRule_role(t *testing.T) { - var rule events.DescribeRuleOutput + var v events.DescribeRuleOutput rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cloudwatch_event_rule.test" + iamRoleResourceName := "aws_iam_role.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -129,9 +133,9 @@ func TestAccAWSCloudWatchEventRule_role(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigRole(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "name", rName), - testAccMatchResourceAttrGlobalARN(resourceName, "role_arn", "iam", regexp.MustCompile(`role/.+`)), + resource.TestCheckResourceAttrPair(resourceName, "role_arn", iamRoleResourceName, "arn"), ), }, { @@ -144,7 +148,7 @@ func TestAccAWSCloudWatchEventRule_role(t *testing.T) { } func TestAccAWSCloudWatchEventRule_description(t *testing.T) { - var rule events.DescribeRuleOutput + var v1, v2 events.DescribeRuleOutput rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cloudwatch_event_rule.test" @@ -156,7 +160,7 @@ func TestAccAWSCloudWatchEventRule_description(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigDescription(rName, "description1"), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v1), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "description", "description1"), ), @@ -169,7 +173,7 @@ func TestAccAWSCloudWatchEventRule_description(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigDescription(rName, "description2"), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "description", "description2"), ), @@ -179,7 +183,7 @@ func TestAccAWSCloudWatchEventRule_description(t *testing.T) { } func TestAccAWSCloudWatchEventRule_pattern(t *testing.T) { - var rule events.DescribeRuleOutput + var v1, v2 events.DescribeRuleOutput rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cloudwatch_event_rule.test" @@ -191,7 +195,7 @@ func TestAccAWSCloudWatchEventRule_pattern(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigPattern(rName, "{\"source\":[\"aws.ec2\"]}"), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v1), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "event_pattern", "{\"source\":[\"aws.ec2\"]}"), ), @@ -204,7 +208,7 @@ func TestAccAWSCloudWatchEventRule_pattern(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigPattern(rName, "{\"source\":[\"aws.lambda\"]}"), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "event_pattern", "{\"source\":[\"aws.lambda\"]}"), ), @@ -214,7 +218,7 @@ func TestAccAWSCloudWatchEventRule_pattern(t *testing.T) { } func TestAccAWSCloudWatchEventRule_prefix(t *testing.T) { - var rule events.DescribeRuleOutput + var v events.DescribeRuleOutput rName := acctest.RandomWithPrefix("tf-acc-test") startsWithPrefix := regexp.MustCompile(rName) resourceName := "aws_cloudwatch_event_rule.test" @@ -227,7 +231,7 @@ func TestAccAWSCloudWatchEventRule_prefix(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigPrefix(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v), resource.TestMatchResourceAttr(resourceName, "name", startsWithPrefix), ), }, @@ -236,7 +240,7 @@ func TestAccAWSCloudWatchEventRule_prefix(t *testing.T) { } func TestAccAWSCloudWatchEventRule_tags(t *testing.T) { - var rule events.DescribeRuleOutput + var v1, v2, v3 events.DescribeRuleOutput rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cloudwatch_event_rule.test" @@ -248,7 +252,7 @@ func TestAccAWSCloudWatchEventRule_tags(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v1), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), @@ -261,7 +265,7 @@ func TestAccAWSCloudWatchEventRule_tags(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -270,7 +274,7 @@ func TestAccAWSCloudWatchEventRule_tags(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v3), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), @@ -280,7 +284,7 @@ func TestAccAWSCloudWatchEventRule_tags(t *testing.T) { } func TestAccAWSCloudWatchEventRule_IsEnabled(t *testing.T) { - var rule events.DescribeRuleOutput + var v1, v2, v3 events.DescribeRuleOutput rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cloudwatch_event_rule.test" @@ -292,7 +296,7 @@ func TestAccAWSCloudWatchEventRule_IsEnabled(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigIsEnabled(rName, false), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v1), resource.TestCheckResourceAttr(resourceName, "is_enabled", "false"), testAccCheckCloudWatchEventRuleEnabled(resourceName, "DISABLED"), ), @@ -305,7 +309,7 @@ func TestAccAWSCloudWatchEventRule_IsEnabled(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigIsEnabled(rName, true), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"), testAccCheckCloudWatchEventRuleEnabled(resourceName, "ENABLED"), ), @@ -313,7 +317,7 @@ func TestAccAWSCloudWatchEventRule_IsEnabled(t *testing.T) { { Config: testAccAWSCloudWatchEventRuleConfigIsEnabled(rName, false), Check: resource.ComposeTestCheckFunc( - testAccCheckCloudWatchEventRuleExists(resourceName, &rule), + testAccCheckCloudWatchEventRuleExists(resourceName, &v3), resource.TestCheckResourceAttr(resourceName, "is_enabled", "false"), testAccCheckCloudWatchEventRuleEnabled(resourceName, "DISABLED"), ), @@ -394,6 +398,15 @@ func testAccCheckAWSCloudWatchEventRuleDestroy(s *terraform.State) error { return nil } +func testAccCheckCloudWatchEventRuleRecreated(i, j *events.DescribeRuleOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aws.StringValue(i.Arn) == aws.StringValue(j.Arn) { + return fmt.Errorf("CloudWatch Events rule not recreated") + } + return nil + } +} + func testAccAWSCloudWatchEventRuleConfig(name string) string { return fmt.Sprintf(` resource "aws_cloudwatch_event_rule" "test" { From ea702fd7391fd6d8a4491f214763d2f3c1f5028d Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 16 Oct 2020 17:40:25 -0700 Subject: [PATCH 03/12] Properly handle `name` and `name_prefix` --- aws/resource_aws_cloudwatch_event_rule.go | 20 +++----- ...resource_aws_cloudwatch_event_rule_test.go | 51 ++++++++++++++++--- docs/contributing/contribution-checklists.md | 9 +++- .../r/cloudwatch_event_rule.html.markdown | 4 +- 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_rule.go b/aws/resource_aws_cloudwatch_event_rule.go index e3f0a3785848..393923617772 100644 --- a/aws/resource_aws_cloudwatch_event_rule.go +++ b/aws/resource_aws_cloudwatch_event_rule.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" ) const ( @@ -40,10 +41,11 @@ func resourceAwsCloudWatchEventRule() *schema.Resource { ValidateFunc: validateCloudWatchEventRuleName, }, "name_prefix": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validateCloudWatchEventRuleName, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"name"}, + ValidateFunc: validateCloudWatchEventRuleName, }, "schedule_expression": { Type: schema.TypeString, @@ -100,14 +102,7 @@ func resourceAwsCloudWatchEventRule() *schema.Resource { func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatcheventsconn - var name string - if v, ok := d.GetOk("name"); ok { - name = v.(string) - } else if v, ok := d.GetOk("name_prefix"); ok { - name = resource.PrefixedUniqueId(v.(string)) - } else { - name = resource.UniqueId() - } + name := naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string)) input, err := buildPutRuleInputStruct(d, name) if err != nil { @@ -188,6 +183,7 @@ func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{} d.Set("event_pattern", pattern) } d.Set("name", out.Name) + d.Set("name_prefix", aws.StringValue(naming.NamePrefixFromName(aws.StringValue(out.Name)))) d.Set("role_arn", out.RoleArn) d.Set("schedule_expression", out.ScheduleExpression) if aws.StringValue(out.EventBusName) != "default" { diff --git a/aws/resource_aws_cloudwatch_event_rule_test.go b/aws/resource_aws_cloudwatch_event_rule_test.go index 36bdbd273a3a..37da0cbc16ae 100644 --- a/aws/resource_aws_cloudwatch_event_rule_test.go +++ b/aws/resource_aws_cloudwatch_event_rule_test.go @@ -11,6 +11,7 @@ import ( "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/terraform-providers/terraform-provider-aws/aws/internal/naming" ) func init() { @@ -86,6 +87,7 @@ func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { testAccCheckCloudWatchEventRuleExists(resourceName, &v1), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "events", regexp.MustCompile(fmt.Sprintf(`rule/%s$`, rName))), resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "name_prefix", ""), resource.TestCheckResourceAttr(resourceName, "schedule_expression", "rate(1 hour)"), resource.TestCheckNoResourceAttr(resourceName, "event_bus_name"), resource.TestCheckNoResourceAttr(resourceName, "event_pattern"), @@ -217,10 +219,35 @@ func TestAccAWSCloudWatchEventRule_pattern(t *testing.T) { }) } -func TestAccAWSCloudWatchEventRule_prefix(t *testing.T) { +func TestAccAWSCloudWatchEventRule_NamePrefix(t *testing.T) { + var v events.DescribeRuleOutput + rName := "tf-acc-test-prefix-" + resourceName := "aws_cloudwatch_event_rule.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudWatchEventRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudWatchEventRuleConfigNamePrefix(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchEventRuleExists(resourceName, &v), + naming.TestCheckResourceAttrNameFromPrefix(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "name_prefix", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSCloudWatchEventRule_Name_Generated(t *testing.T) { var v events.DescribeRuleOutput - rName := acctest.RandomWithPrefix("tf-acc-test") - startsWithPrefix := regexp.MustCompile(rName) resourceName := "aws_cloudwatch_event_rule.test" resource.ParallelTest(t, resource.TestCase{ @@ -229,12 +256,18 @@ func TestAccAWSCloudWatchEventRule_prefix(t *testing.T) { CheckDestroy: testAccCheckAWSCloudWatchEventRuleDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCloudWatchEventRuleConfigPrefix(rName), + Config: testAccAWSCloudWatchEventRuleConfigNameGenerated, Check: resource.ComposeTestCheckFunc( testAccCheckCloudWatchEventRuleExists(resourceName, &v), - resource.TestMatchResourceAttr(resourceName, "name", startsWithPrefix), + naming.TestCheckResourceAttrNameGenerated(resourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", ""), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -449,7 +482,7 @@ resource "aws_cloudwatch_event_rule" "test" { `, name, enabled) } -func testAccAWSCloudWatchEventRuleConfigPrefix(name string) string { +func testAccAWSCloudWatchEventRuleConfigNamePrefix(name string) string { return fmt.Sprintf(` resource "aws_cloudwatch_event_rule" "test" { name_prefix = "%s" @@ -458,6 +491,12 @@ resource "aws_cloudwatch_event_rule" "test" { `, name) } +const testAccAWSCloudWatchEventRuleConfigNameGenerated = ` +resource "aws_cloudwatch_event_rule" "test" { + schedule_expression = "rate(5 minutes)" +} +` + func testAccAWSCloudWatchEventRuleConfigTags1(name, tagKey1, tagValue1 string) string { return fmt.Sprintf(` resource "aws_cloudwatch_event_rule" "test" { diff --git a/docs/contributing/contribution-checklists.md b/docs/contributing/contribution-checklists.md index c7c9c8dc6454..d356f99c04e5 100644 --- a/docs/contributing/contribution-checklists.md +++ b/docs/contributing/contribution-checklists.md @@ -116,6 +116,13 @@ name := naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string)) // ... in AWS Go SDK Input types, etc. use aws.String(name) ``` +- If the resource supports import, in the resource `Read` function add a call to `d.Set("name_prefix", ...)`, e.g. + +```go +d.Set("name", resp.Name) +d.Set("name_prefix", aws.StringValue(naming.NamePrefixFromName(aws.StringValue(resp.Name)))) +``` + ### Resource Name Generation Testing Implementation - In the resource testing (e.g. `aws/resource_aws_service_thing_test.go`), add the following Go import: `"github.com/terraform-providers/terraform-provider-aws/aws/internal/naming"` @@ -193,7 +200,7 @@ resource "aws_service_thing" "test" { } ``` -### Resource Code Generation Documentation Implementation +### Resource Name Generation Documentation Implementation - In the resource documentation (e.g. `website/docs/r/service_thing.html.markdown`), add the following to the arguments reference: diff --git a/website/docs/r/cloudwatch_event_rule.html.markdown b/website/docs/r/cloudwatch_event_rule.html.markdown index 761bcb705d87..a51c55bf3cdc 100644 --- a/website/docs/r/cloudwatch_event_rule.html.markdown +++ b/website/docs/r/cloudwatch_event_rule.html.markdown @@ -60,8 +60,8 @@ data "aws_iam_policy_document" "sns_topic_policy" { The following arguments are supported: -* `name` - (Optional) The rule's name. By default generated by Terraform. -* `name_prefix` - (Optional) The rule's name. Conflicts with `name`. +* `name` - (Optional) The name of the rule. If omitted, Terraform will assign a random, unique name. Conflicts with `name_prefix`. +* `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `schedule_expression` - (Required, if `event_pattern` isn't specified) The scheduling expression. For example, `cron(0 20 * * ? *)` or `rate(5 minutes)`. * `event_bus_name` - (Optional) The event bus to associate with this rule. If you omit this, the `default` event bus is used. From 85fed3b7f2db34650b03840798708bd00da977cc Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 19 Oct 2020 11:42:50 -0700 Subject: [PATCH 04/12] Adds `AtLeastOneOf` validation to `schedule_expression` and `event_pattern` --- aws/resource_aws_cloudwatch_event_rule.go | 3 +- ...resource_aws_cloudwatch_event_rule_test.go | 44 ++++++++++++++++++- .../r/cloudwatch_event_rule.html.markdown | 7 ++- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_rule.go b/aws/resource_aws_cloudwatch_event_rule.go index 393923617772..3f889e71cb04 100644 --- a/aws/resource_aws_cloudwatch_event_rule.go +++ b/aws/resource_aws_cloudwatch_event_rule.go @@ -51,6 +51,7 @@ func resourceAwsCloudWatchEventRule() *schema.Resource { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringLenBetween(0, 256), + AtLeastOneOf: []string{"schedule_expression", "event_pattern"}, }, "event_bus_name": { Type: schema.TypeString, @@ -62,7 +63,6 @@ func resourceAwsCloudWatchEventRule() *schema.Resource { // "default" event bus name is not stored in the state to support the case when event_bus_name is omitted return "" } - return v.(string) }, }, @@ -70,6 +70,7 @@ func resourceAwsCloudWatchEventRule() *schema.Resource { Type: schema.TypeString, Optional: true, ValidateFunc: validateEventPatternValue(), + AtLeastOneOf: []string{"schedule_expression", "event_pattern"}, StateFunc: func(v interface{}) string { json, _ := structure.NormalizeJsonString(v.(string)) return json diff --git a/aws/resource_aws_cloudwatch_event_rule_test.go b/aws/resource_aws_cloudwatch_event_rule_test.go index 37da0cbc16ae..e571308d691b 100644 --- a/aws/resource_aws_cloudwatch_event_rule_test.go +++ b/aws/resource_aws_cloudwatch_event_rule_test.go @@ -199,7 +199,8 @@ func TestAccAWSCloudWatchEventRule_pattern(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckCloudWatchEventRuleExists(resourceName, &v1), resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "event_pattern", "{\"source\":[\"aws.ec2\"]}"), + resource.TestCheckResourceAttr(resourceName, "schedule_expression", ""), + testAccCheckResourceAttrEquivalentJSON(resourceName, "event_pattern", "{\"source\":[\"aws.ec2\"]}"), ), }, { @@ -212,13 +213,41 @@ func TestAccAWSCloudWatchEventRule_pattern(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckCloudWatchEventRuleExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "event_pattern", "{\"source\":[\"aws.lambda\"]}"), + testAccCheckResourceAttrEquivalentJSON(resourceName, "event_pattern", "{\"source\":[\"aws.lambda\"]}"), ), }, }, }) } +func TestAccAWSCloudWatchEventRule_ScheduleAndPattern(t *testing.T) { + var v events.DescribeRuleOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cloudwatch_event_rule.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudWatchEventRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudWatchEventRuleConfigScheduleAndPattern(rName, "{\"source\":[\"aws.ec2\"]}"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchEventRuleExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "schedule_expression", "rate(1 hour)"), + testAccCheckResourceAttrEquivalentJSON(resourceName, "event_pattern", "{\"source\":[\"aws.ec2\"]}"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSCloudWatchEventRule_NamePrefix(t *testing.T) { var v events.DescribeRuleOutput rName := "tf-acc-test-prefix-" @@ -452,6 +481,17 @@ resource "aws_cloudwatch_event_rule" "test" { func testAccAWSCloudWatchEventRuleConfigPattern(name, pattern string) string { return fmt.Sprintf(` +resource "aws_cloudwatch_event_rule" "test" { + name = "%s" + event_pattern = < Date: Mon, 19 Oct 2020 12:15:05 -0700 Subject: [PATCH 05/12] Cleanup --- aws/resource_aws_cloudwatch_event_rule.go | 41 +++++++++---------- ...resource_aws_cloudwatch_event_rule_test.go | 24 +++++++---- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_rule.go b/aws/resource_aws_cloudwatch_event_rule.go index 3f889e71cb04..c8ee85055b9f 100644 --- a/aws/resource_aws_cloudwatch_event_rule.go +++ b/aws/resource_aws_cloudwatch_event_rule.go @@ -107,9 +107,9 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface input, err := buildPutRuleInputStruct(d, name) if err != nil { - return fmt.Errorf("Creating CloudWatch Event Rule failed: %s", err) + return fmt.Errorf("Creating CloudWatch Events Rule failed: %w", err) } - log.Printf("[DEBUG] Creating CloudWatch Event Rule: %s", input) + log.Printf("[DEBUG] Creating CloudWatch Events Rule: %s", input) // IAM Roles take some time to propagate var out *events.PutRuleOutput @@ -117,7 +117,7 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface out, err = conn.PutRule(input) if isAWSErr(err, "ValidationException", "cannot be assumed by principal") { - log.Printf("[DEBUG] Retrying update of CloudWatch Event Rule %q", *input.Name) + log.Printf("[DEBUG] Retrying update of CloudWatch Events Rule %q", aws.StringValue(input.Name)) return resource.RetryableError(err) } if err != nil { @@ -130,7 +130,7 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface } if err != nil { - return fmt.Errorf("Updating CloudWatch Event Rule failed: %s", err) + return fmt.Errorf("Updating CloudWatch Events Rule failed: %w", err) } d.Set("arn", out.RuleArn) @@ -141,7 +141,7 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface } d.SetId(id) - log.Printf("[INFO] CloudWatch Event Rule %q created", *out.RuleArn) + log.Printf("[INFO] CloudWatch Events Rule (%s) created", aws.StringValue(out.RuleArn)) return resourceAwsCloudWatchEventRuleRead(d, meta) } @@ -159,11 +159,11 @@ func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{} Name: aws.String(ruleName), EventBusName: aws.String(busName), } - log.Printf("[DEBUG] Reading CloudWatch Event Rule: %s", input) + log.Printf("[DEBUG] Reading CloudWatch Events Rule: %s", input) out, err := conn.DescribeRule(&input) if awsErr, ok := err.(awserr.Error); ok { if awsErr.Code() == events.ErrCodeResourceNotFoundException { - log.Printf("[WARN] Removing CloudWatch Event Rule %q because it's gone.", d.Id()) + log.Printf("[WARN] Removing CloudWatch Events Rule (%s) because it's gone.", d.Id()) d.SetId("") return nil } @@ -179,7 +179,7 @@ func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{} if out.EventPattern != nil { pattern, err := structure.NormalizeJsonString(*out.EventPattern) if err != nil { - return fmt.Errorf("event pattern contains an invalid JSON: %s", err) + return fmt.Errorf("event pattern contains an invalid JSON: %w", err) } d.Set("event_pattern", pattern) } @@ -201,11 +201,11 @@ func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{} tags, err := keyvaluetags.CloudwatcheventsListTags(conn, arn) if err != nil { - return fmt.Errorf("error listing tags for CloudWatch Event Rule (%s): %s", arn, err) + return fmt.Errorf("error listing tags for CloudWatch Events Rule (%s): %w", arn, err) } if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + return fmt.Errorf("error setting tags: %w", err) } return nil @@ -219,16 +219,16 @@ func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface } input, err := buildPutRuleInputStruct(d, ruleName) if err != nil { - return fmt.Errorf("Updating CloudWatch Event Rule failed: %s", err) + return fmt.Errorf("Updating CloudWatch Events Rule (%s) failed: %w", ruleName, err) } - log.Printf("[DEBUG] Updating CloudWatch Event Rule: %s", input) + log.Printf("[DEBUG] Updating CloudWatch Events Rule: %s", input) // IAM Roles take some time to propagate err = resource.Retry(30*time.Second, func() *resource.RetryError { _, err := conn.PutRule(input) if isAWSErr(err, "ValidationException", "cannot be assumed by principal") { - log.Printf("[DEBUG] Retrying update of CloudWatch Event Rule %q", *input.Name) + log.Printf("[DEBUG] Retrying update of CloudWatch Events Rule %q", aws.StringValue(input.Name)) return resource.RetryableError(err) } if err != nil { @@ -241,7 +241,7 @@ func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface } if err != nil { - return fmt.Errorf("Updating CloudWatch Event Rule failed: %s", err) + return fmt.Errorf("Updating CloudWatch Events Rule (%s) failed: %w", ruleName, err) } arn := d.Get("arn").(string) @@ -249,7 +249,7 @@ func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface o, n := d.GetChange("tags") if err := keyvaluetags.CloudwatcheventsUpdateTags(conn, arn, o, n); err != nil { - return fmt.Errorf("error updating CloudwWatch Event Rule (%s) tags: %s", arn, err) + return fmt.Errorf("error updating CloudwWatch Event Rule (%s) tags: %w", arn, err) } } @@ -286,7 +286,7 @@ func resourceAwsCloudWatchEventRuleDelete(d *schema.ResourceData, meta interface } if err != nil { - return fmt.Errorf("error deleting CloudWatch Event Rule (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting CloudWatch Events Rule (%s): %w", d.Id(), err) } return nil @@ -305,7 +305,7 @@ func buildPutRuleInputStruct(d *schema.ResourceData, name string) (*events.PutRu if v, ok := d.GetOk("event_pattern"); ok { pattern, err := structure.NormalizeJsonString(v) if err != nil { - return nil, fmt.Errorf("event pattern contains an invalid JSON: %s", err) + return nil, fmt.Errorf("event pattern contains an invalid JSON: %w", err) } input.EventPattern = aws.String(pattern) } @@ -349,7 +349,7 @@ func validateEventPatternValue() schema.SchemaValidateFunc { return func(v interface{}, k string) (ws []string, errors []error) { json, err := structure.NormalizeJsonString(v) if err != nil { - errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) + errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %w", k, err)) // Invalid JSON? Return immediately, // there is no need to collect other @@ -359,8 +359,7 @@ func validateEventPatternValue() schema.SchemaValidateFunc { // Check whether the normalized JSON is within the given length. if len(json) > 2048 { - errors = append(errors, fmt.Errorf( - "%q cannot be longer than %d characters: %q", k, 2048, json)) + errors = append(errors, fmt.Errorf("%q cannot be longer than %d characters: %q", k, 2048, json)) } return } @@ -371,7 +370,7 @@ func determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(id string) (strin busName := "default" var ruleName string if len(splitId) > 2 { - return busName, ruleName, fmt.Errorf("wrong format of resource: %s. Please follow / or ", id) + return busName, ruleName, fmt.Errorf("wrong format of resource: %q. Please follow / or ", id) } else if len(splitId) == 2 { busName = splitId[0] ruleName = splitId[1] diff --git a/aws/resource_aws_cloudwatch_event_rule_test.go b/aws/resource_aws_cloudwatch_event_rule_test.go index e571308d691b..81114819cb86 100644 --- a/aws/resource_aws_cloudwatch_event_rule_test.go +++ b/aws/resource_aws_cloudwatch_event_rule_test.go @@ -27,7 +27,7 @@ func init() { func testSweepCloudWatchEventRules(region string) error { client, err := sharedClientForRegion(region) if err != nil { - return fmt.Errorf("Error getting client: %s", err) + return fmt.Errorf("Error getting client: %w", err) } conn := client.(*AWSClient).cloudwatcheventsconn @@ -37,27 +37,27 @@ func testSweepCloudWatchEventRules(region string) error { output, err := conn.ListRules(input) if err != nil { if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping CloudWatch Event Rule sweep for %s: %s", region, err) + log.Printf("[WARN] Skipping CloudWatch Events Rule sweep for %s: %s", region, err) return nil } - return fmt.Errorf("Error retrieving CloudWatch Event Rules: %s", err) + return fmt.Errorf("Error retrieving CloudWatch Events Rules: %w", err) } if len(output.Rules) == 0 { - log.Print("[DEBUG] No CloudWatch Event Rules to sweep") + log.Print("[DEBUG] No CloudWatch Events Rules to sweep") return nil } for _, rule := range output.Rules { name := aws.StringValue(rule.Name) - log.Printf("[INFO] Deleting CloudWatch Event Rule %s", name) + log.Printf("[INFO] Deleting CloudWatch Events Rule (%s)", name) _, err := conn.DeleteRule(&events.DeleteRuleInput{ Name: aws.String(name), Force: aws.Bool(true), }) if err != nil { - return fmt.Errorf("Error deleting CloudWatch Event Rule %s: %s", name, err) + return fmt.Errorf("Error deleting CloudWatch Events Rule (%s): %w", name, err) } } @@ -452,8 +452,7 @@ func testAccCheckAWSCloudWatchEventRuleDestroy(s *terraform.State) error { resp, err := conn.DescribeRule(¶ms) if err == nil { - return fmt.Errorf("CloudWatch Event Rule %q still exists: %s", - rs.Primary.ID, resp) + return fmt.Errorf("CloudWatch Events Rule (%s) still exists: %s", rs.Primary.ID, resp) } } @@ -564,6 +563,15 @@ resource "aws_cloudwatch_event_rule" "test" { `, name, tagKey1, tagValue1, tagKey2, tagValue2) } +func testAccAWSCloudWatchEventRuleConfigTags0(name string) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_event_rule" "test" { + name = %[1]q + schedule_expression = "rate(1 hour)" +} +`, name) +} + func testAccAWSCloudWatchEventRuleConfigRole(name string) string { return fmt.Sprintf(` resource "aws_iam_role" "test" { From 155045eea1f00248dc99b59a65b6bca3eb67f6b6 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 19 Oct 2020 12:15:28 -0700 Subject: [PATCH 06/12] Add test for removing tags --- aws/resource_aws_cloudwatch_event_rule_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/aws/resource_aws_cloudwatch_event_rule_test.go b/aws/resource_aws_cloudwatch_event_rule_test.go index 81114819cb86..9a6b633f42c4 100644 --- a/aws/resource_aws_cloudwatch_event_rule_test.go +++ b/aws/resource_aws_cloudwatch_event_rule_test.go @@ -341,6 +341,13 @@ func TestAccAWSCloudWatchEventRule_tags(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, + { + Config: testAccAWSCloudWatchEventRuleConfigTags0(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchEventRuleExists(resourceName, &v3), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, }, }) } From 87fe45ecc758beee68f5d05b18be80bf59faee35 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 19 Oct 2020 22:34:51 -0700 Subject: [PATCH 07/12] Handles custom event buses --- .../service/cloudwatchevents/finder/finder.go | 28 ++++ aws/internal/service/cloudwatchevents/id.go | 29 ++++ aws/resource_aws_cloudwatch_event_rule.go | 92 ++++-------- ...resource_aws_cloudwatch_event_rule_test.go | 132 +++++++++++++++--- aws/validators.go | 5 + .../r/cloudwatch_event_rule.html.markdown | 2 +- 6 files changed, 206 insertions(+), 82 deletions(-) create mode 100644 aws/internal/service/cloudwatchevents/finder/finder.go create mode 100644 aws/internal/service/cloudwatchevents/id.go diff --git a/aws/internal/service/cloudwatchevents/finder/finder.go b/aws/internal/service/cloudwatchevents/finder/finder.go new file mode 100644 index 000000000000..76ee55a06d95 --- /dev/null +++ b/aws/internal/service/cloudwatchevents/finder/finder.go @@ -0,0 +1,28 @@ +package finder + +import ( + "github.com/aws/aws-sdk-go/aws" + events "github.com/aws/aws-sdk-go/service/cloudwatchevents" + tfevents "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents" +) + +func Rule(conn *events.CloudWatchEvents, eventBusName, ruleName string) (*events.DescribeRuleOutput, error) { + input := events.DescribeRuleInput{ + Name: aws.String(ruleName), + } + if eventBusName != "" { + input.EventBusName = aws.String(eventBusName) + } + + return conn.DescribeRule(&input) + +} + +func RuleByID(conn *events.CloudWatchEvents, ruleID string) (*events.DescribeRuleOutput, error) { + busName, ruleName, err := tfevents.RuleParseID(ruleID) + if err != nil { + return nil, err + } + + return Rule(conn, busName, ruleName) +} diff --git a/aws/internal/service/cloudwatchevents/id.go b/aws/internal/service/cloudwatchevents/id.go new file mode 100644 index 000000000000..80d947370ff8 --- /dev/null +++ b/aws/internal/service/cloudwatchevents/id.go @@ -0,0 +1,29 @@ +package cloudwatchevents + +import ( + "fmt" + "strings" +) + +const DefaultEventBusName = "default" + +const ruleIDSeparator = "/" + +func RuleCreateID(eventBusName, ruleName string) string { + if eventBusName == "" || eventBusName == DefaultEventBusName { + return ruleName + } + return eventBusName + ruleIDSeparator + ruleName +} + +func RuleParseID(id string) (string, string, error) { + parts := strings.Split(id, ruleIDSeparator) + if len(parts) == 1 && parts[0] != "" { + return DefaultEventBusName, parts[0], nil + } + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%q), expected "+ruleIDSeparator+" or ", id) +} diff --git a/aws/resource_aws_cloudwatch_event_rule.go b/aws/resource_aws_cloudwatch_event_rule.go index c8ee85055b9f..4115fc92a26f 100644 --- a/aws/resource_aws_cloudwatch_event_rule.go +++ b/aws/resource_aws_cloudwatch_event_rule.go @@ -1,20 +1,22 @@ package aws import ( + "errors" "fmt" "log" - "strings" "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" events "github.com/aws/aws-sdk-go/service/cloudwatchevents" + "github.com/hashicorp/aws-sdk-go-base/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/structure" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" + tfevents "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents/finder" ) const ( @@ -57,14 +59,8 @@ func resourceAwsCloudWatchEventRule() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validation.StringLenBetween(1, 256), - StateFunc: func(v interface{}) string { - if v.(string) == "default" { - // "default" event bus name is not stored in the state to support the case when event_bus_name is omitted - return "" - } - return v.(string) - }, + ValidateFunc: validateCloudWatchEventBusName, + Default: tfevents.DefaultEventBusName, }, "event_pattern": { Type: schema.TypeString, @@ -130,15 +126,12 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface } if err != nil { - return fmt.Errorf("Updating CloudWatch Events Rule failed: %w", err) + return fmt.Errorf("Creating CloudWatch Events Rule failed: %w", err) } d.Set("arn", out.RuleArn) - id := aws.StringValue(input.Name) - eventBusName := aws.StringValue(input.EventBusName) - if eventBusName != "" && eventBusName != "default" { - id = eventBusName + "/" + id - } + + id := tfevents.RuleCreateID(aws.StringValue(input.EventBusName), aws.StringValue(input.Name)) d.SetId(id) log.Printf("[INFO] CloudWatch Events Rule (%s) created", aws.StringValue(out.RuleArn)) @@ -150,34 +143,22 @@ func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{} conn := meta.(*AWSClient).cloudwatcheventsconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - busName, ruleName, err := determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(d.Id()) - if err != nil { - return err - } - - input := events.DescribeRuleInput{ - Name: aws.String(ruleName), - EventBusName: aws.String(busName), - } - log.Printf("[DEBUG] Reading CloudWatch Events Rule: %s", input) - out, err := conn.DescribeRule(&input) - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == events.ErrCodeResourceNotFoundException { - log.Printf("[WARN] Removing CloudWatch Events Rule (%s) because it's gone.", d.Id()) - d.SetId("") - return nil - } + out, err := finder.RuleByID(conn, d.Id()) + if tfawserr.ErrCodeEquals(err, events.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Removing CloudWatch Events Rule (%s) because it's gone.", d.Id()) + d.SetId("") + return nil } if err != nil { - return err + return fmt.Errorf("error reading CloudWatch Events Rule (%s): %w", d.Id(), err) } log.Printf("[DEBUG] Found Event Rule: %s", out) - arn := *out.Arn + arn := aws.StringValue(out.Arn) d.Set("arn", arn) d.Set("description", out.Description) if out.EventPattern != nil { - pattern, err := structure.NormalizeJsonString(*out.EventPattern) + pattern, err := structure.NormalizeJsonString(aws.StringValue(out.EventPattern)) if err != nil { return fmt.Errorf("event pattern contains an invalid JSON: %w", err) } @@ -187,9 +168,7 @@ func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{} d.Set("name_prefix", aws.StringValue(naming.NamePrefixFromName(aws.StringValue(out.Name)))) d.Set("role_arn", out.RoleArn) d.Set("schedule_expression", out.ScheduleExpression) - if aws.StringValue(out.EventBusName) != "default" { - d.Set("event_bus_name", out.EventBusName) - } + d.Set("event_bus_name", out.EventBusName) boolState, err := getBooleanStateFromString(*out.State) if err != nil { @@ -213,7 +192,7 @@ func resourceAwsCloudWatchEventRuleRead(d *schema.ResourceData, meta interface{} func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatcheventsconn - _, ruleName, err := determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(d.Id()) + _, ruleName, err := tfevents.RuleParseID(d.Id()) if err != nil { return err } @@ -258,7 +237,7 @@ func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface func resourceAwsCloudWatchEventRuleDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatcheventsconn - busName, ruleName, err := determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(d.Id()) + busName, ruleName, err := tfevents.RuleParseID(d.Id()) if err != nil { return err } @@ -296,11 +275,13 @@ func buildPutRuleInputStruct(d *schema.ResourceData, name string) (*events.PutRu input := events.PutRuleInput{ Name: aws.String(name), } + var eventBusName string if v, ok := d.GetOk("description"); ok { input.Description = aws.String(v.(string)) } if v, ok := d.GetOk("event_bus_name"); ok { - input.EventBusName = aws.String(v.(string)) + eventBusName = v.(string) + input.EventBusName = aws.String(eventBusName) } if v, ok := d.GetOk("event_pattern"); ok { pattern, err := structure.NormalizeJsonString(v) @@ -313,7 +294,12 @@ func buildPutRuleInputStruct(d *schema.ResourceData, name string) (*events.PutRu input.RoleArn = aws.String(v.(string)) } if v, ok := d.GetOk("schedule_expression"); ok { - input.ScheduleExpression = aws.String(v.(string)) + scheduleExpression := v.(string) + if eventBusName == tfevents.DefaultEventBusName { + input.ScheduleExpression = aws.String(scheduleExpression) + } else { + return nil, errors.New("schedule_expression can only be set on the default event bus") + } } if v, ok := d.GetOk("tags"); ok { @@ -365,28 +351,12 @@ func validateEventPatternValue() schema.SchemaValidateFunc { } } -func determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(id string) (string, string, error) { - splitId := strings.Split(id, "/") - busName := "default" - var ruleName string - if len(splitId) > 2 { - return busName, ruleName, fmt.Errorf("wrong format of resource: %q. Please follow / or ", id) - } else if len(splitId) == 2 { - busName = splitId[0] - ruleName = splitId[1] - } else { - ruleName = splitId[0] - } - - return busName, ruleName, nil -} - func resourceAwsCloudWatchEventRuleImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - busName, ruleName, err := determineCloudwatchEventBusNameAndEventRuleNameFromRuleId(d.Id()) + busName, ruleName, err := tfevents.RuleParseID(d.Id()) if err != nil { return []*schema.ResourceData{}, err } - if busName != "default" { + if busName != tfevents.DefaultEventBusName { d.Set("event_bus_name", busName) } d.Set("name", ruleName) diff --git a/aws/resource_aws_cloudwatch_event_rule_test.go b/aws/resource_aws_cloudwatch_event_rule_test.go index 9a6b633f42c4..4c90214135d0 100644 --- a/aws/resource_aws_cloudwatch_event_rule_test.go +++ b/aws/resource_aws_cloudwatch_event_rule_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents/finder" ) func init() { @@ -71,7 +72,7 @@ func testSweepCloudWatchEventRules(region string) error { } func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { - var v1, v2 events.DescribeRuleOutput + var v1, v2, v3 events.DescribeRuleOutput rName := acctest.RandomWithPrefix("tf-acc-test") rName2 := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cloudwatch_event_rule.test" @@ -89,7 +90,7 @@ func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "name_prefix", ""), resource.TestCheckResourceAttr(resourceName, "schedule_expression", "rate(1 hour)"), - resource.TestCheckNoResourceAttr(resourceName, "event_bus_name"), + resource.TestCheckResourceAttr(resourceName, "event_bus_name", "default"), resource.TestCheckNoResourceAttr(resourceName, "event_pattern"), resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttr(resourceName, "role_arn", ""), @@ -108,7 +109,9 @@ func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckCloudWatchEventRuleExists(resourceName, &v2), testAccCheckCloudWatchEventRuleRecreated(&v1, &v2), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "events", regexp.MustCompile(fmt.Sprintf(`rule/%s$`, rName2))), resource.TestCheckResourceAttr(resourceName, "name", rName2), + resource.TestCheckResourceAttr(resourceName, "event_bus_name", "default"), resource.TestCheckResourceAttr(resourceName, "schedule_expression", "rate(1 hour)"), resource.TestCheckResourceAttr(resourceName, "role_arn", ""), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), @@ -116,6 +119,66 @@ func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { testAccCheckCloudWatchEventRuleEnabled(resourceName, "ENABLED"), ), }, + { + Config: testAccAWSCloudWatchEventRuleConfigDefaultEventBusName(rName2), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchEventRuleExists(resourceName, &v3), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "events", regexp.MustCompile(fmt.Sprintf(`rule/%s$`, rName2))), + testAccCheckCloudWatchEventRuleNotRecreated(&v2, &v3), + resource.TestCheckResourceAttr(resourceName, "name", rName2), + resource.TestCheckResourceAttr(resourceName, "event_bus_name", "default"), + ), + }, + }, + }) +} + +func TestAccAWSCloudWatchEventRule_EventBusName(t *testing.T) { + var v1, v2, v3 events.DescribeRuleOutput + rName := acctest.RandomWithPrefix("tf-acc-test-rule") + rName2 := acctest.RandomWithPrefix("tf-acc-test-rule") + busName := acctest.RandomWithPrefix("tf-acc-test-bus") + busName2 := acctest.RandomWithPrefix("tf-acc-test-bus") + resourceName := "aws_cloudwatch_event_rule.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudWatchEventRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudWatchEventRuleConfigEventBusName(rName, busName, "description 1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchEventRuleExists(resourceName, &v1), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "event_bus_name", busName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "events", regexp.MustCompile(fmt.Sprintf(`rule/%s/%s$`, busName, rName))), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSCloudWatchEventRuleConfigEventBusName(rName, busName, "description 2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchEventRuleExists(resourceName, &v2), + testAccCheckCloudWatchEventRuleNotRecreated(&v1, &v2), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "event_bus_name", busName), + ), + }, + { + Config: testAccAWSCloudWatchEventRuleConfigEventBusName(rName2, busName2, "description 2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchEventRuleExists(resourceName, &v3), + testAccCheckCloudWatchEventRuleRecreated(&v2, &v3), + resource.TestCheckResourceAttr(resourceName, "name", rName2), + resource.TestCheckResourceAttr(resourceName, "event_bus_name", busName2), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "events", regexp.MustCompile(fmt.Sprintf(`rule/%s/%s$`, busName2, rName2))), + ), + }, }, }) } @@ -403,15 +466,13 @@ func testAccCheckCloudWatchEventRuleExists(n string, rule *events.DescribeRuleOu } conn := testAccProvider.Meta().(*AWSClient).cloudwatcheventsconn - params := events.DescribeRuleInput{ - Name: aws.String(rs.Primary.ID), - } - resp, err := conn.DescribeRule(¶ms) + + resp, err := finder.RuleByID(conn, rs.Primary.ID) if err != nil { return err } if resp == nil { - return fmt.Errorf("Rule not found") + return fmt.Errorf("CloudWatch Events rule (%s) not found", rs.Primary.ID) } *rule = *resp @@ -428,16 +489,13 @@ func testAccCheckCloudWatchEventRuleEnabled(n string, desired string) resource.T } conn := testAccProvider.Meta().(*AWSClient).cloudwatcheventsconn - params := events.DescribeRuleInput{ - Name: aws.String(rs.Primary.ID), - } - resp, err := conn.DescribeRule(¶ms) + resp, err := finder.RuleByID(conn, rs.Primary.ID) if err != nil { return err } - if *resp.State != desired { - return fmt.Errorf("Expected state %q, given %q", desired, *resp.State) + if aws.StringValue(resp.State) != desired { + return fmt.Errorf("Expected state %q, given %q", desired, aws.StringValue(resp.State)) } return nil @@ -452,12 +510,7 @@ func testAccCheckAWSCloudWatchEventRuleDestroy(s *terraform.State) error { continue } - params := events.DescribeRuleInput{ - Name: aws.String(rs.Primary.ID), - } - - resp, err := conn.DescribeRule(¶ms) - + resp, err := finder.RuleByID(conn, rs.Primary.ID) if err == nil { return fmt.Errorf("CloudWatch Events Rule (%s) still exists: %s", rs.Primary.ID, resp) } @@ -469,7 +522,16 @@ func testAccCheckAWSCloudWatchEventRuleDestroy(s *terraform.State) error { func testAccCheckCloudWatchEventRuleRecreated(i, j *events.DescribeRuleOutput) resource.TestCheckFunc { return func(s *terraform.State) error { if aws.StringValue(i.Arn) == aws.StringValue(j.Arn) { - return fmt.Errorf("CloudWatch Events rule not recreated") + return fmt.Errorf("CloudWatch Events rule not recreated, but expected it to be") + } + return nil + } +} + +func testAccCheckCloudWatchEventRuleNotRecreated(i, j *events.DescribeRuleOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aws.StringValue(i.Arn) != aws.StringValue(j.Arn) { + return fmt.Errorf("CloudWatch Events rule recreated, but expected it to not be") } return nil } @@ -480,11 +542,41 @@ func testAccAWSCloudWatchEventRuleConfig(name string) string { resource "aws_cloudwatch_event_rule" "test" { name = "%s" schedule_expression = "rate(1 hour)" +} +`, name) +} + +func testAccAWSCloudWatchEventRuleConfigDefaultEventBusName(name string) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_event_rule" "test" { + name = %[1]q + schedule_expression = "rate(1 hour)" event_bus_name = "default" } `, name) } +func testAccAWSCloudWatchEventRuleConfigEventBusName(name, eventBusName, description string) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_event_rule" "test" { + name = %[1]q + event_bus_name = aws_cloudwatch_event_bus.test.name + description = %[2]q + event_pattern = < Date: Mon, 19 Oct 2020 22:42:02 -0700 Subject: [PATCH 08/12] Uses IAM propagation timeout --- aws/resource_aws_cloudwatch_event_rule.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_rule.go b/aws/resource_aws_cloudwatch_event_rule.go index 4115fc92a26f..35bc7a03606b 100644 --- a/aws/resource_aws_cloudwatch_event_rule.go +++ b/aws/resource_aws_cloudwatch_event_rule.go @@ -17,6 +17,7 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" tfevents "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents/finder" + iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" ) const ( @@ -109,7 +110,7 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface // IAM Roles take some time to propagate var out *events.PutRuleOutput - err = resource.Retry(30*time.Second, func() *resource.RetryError { + err = resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { out, err = conn.PutRule(input) if isAWSErr(err, "ValidationException", "cannot be assumed by principal") { @@ -203,7 +204,7 @@ func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface log.Printf("[DEBUG] Updating CloudWatch Events Rule: %s", input) // IAM Roles take some time to propagate - err = resource.Retry(30*time.Second, func() *resource.RetryError { + err = resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { _, err := conn.PutRule(input) if isAWSErr(err, "ValidationException", "cannot be assumed by principal") { From 32b2eea9c8e02ba4a04c9ce19b947f2d8b57794f Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 19 Oct 2020 22:54:17 -0700 Subject: [PATCH 09/12] Tagging on PUT only works when creating a rule --- aws/resource_aws_cloudwatch_event_rule.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_rule.go b/aws/resource_aws_cloudwatch_event_rule.go index 35bc7a03606b..a1bfb27313ab 100644 --- a/aws/resource_aws_cloudwatch_event_rule.go +++ b/aws/resource_aws_cloudwatch_event_rule.go @@ -106,6 +106,11 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface if err != nil { return fmt.Errorf("Creating CloudWatch Events Rule failed: %w", err) } + + if v, ok := d.GetOk("tags"); ok { + input.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().CloudwatcheventsTags() + } + log.Printf("[DEBUG] Creating CloudWatch Events Rule: %s", input) // IAM Roles take some time to propagate @@ -303,10 +308,6 @@ func buildPutRuleInputStruct(d *schema.ResourceData, name string) (*events.PutRu } } - if v, ok := d.GetOk("tags"); ok { - input.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().CloudwatcheventsTags() - } - input.State = aws.String(getStringStateFromBoolean(d.Get("is_enabled").(bool))) return &input, nil From 4331fb1fb7662fa284a27215341ce0d99215bfa7 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 21 Oct 2020 16:34:21 -0700 Subject: [PATCH 10/12] Adds test for import without BusName --- aws/resource_aws_cloudwatch_event_rule.go | 15 +-------------- aws/resource_aws_cloudwatch_event_rule_test.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_rule.go b/aws/resource_aws_cloudwatch_event_rule.go index a1bfb27313ab..eb8b03248270 100644 --- a/aws/resource_aws_cloudwatch_event_rule.go +++ b/aws/resource_aws_cloudwatch_event_rule.go @@ -31,7 +31,7 @@ func resourceAwsCloudWatchEventRule() *schema.Resource { Update: resourceAwsCloudWatchEventRuleUpdate, Delete: resourceAwsCloudWatchEventRuleDelete, Importer: &schema.ResourceImporter{ - State: resourceAwsCloudWatchEventRuleImport, + State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ @@ -352,16 +352,3 @@ func validateEventPatternValue() schema.SchemaValidateFunc { return } } - -func resourceAwsCloudWatchEventRuleImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - busName, ruleName, err := tfevents.RuleParseID(d.Id()) - if err != nil { - return []*schema.ResourceData{}, err - } - if busName != tfevents.DefaultEventBusName { - d.Set("event_bus_name", busName) - } - d.Set("name", ruleName) - - return []*schema.ResourceData{d}, nil -} diff --git a/aws/resource_aws_cloudwatch_event_rule_test.go b/aws/resource_aws_cloudwatch_event_rule_test.go index 3f6711808884..d03e4734c95d 100644 --- a/aws/resource_aws_cloudwatch_event_rule_test.go +++ b/aws/resource_aws_cloudwatch_event_rule_test.go @@ -109,6 +109,12 @@ func TestAccAWSCloudWatchEventRule_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccAWSCloudWatchEventRuleNoBusNameImportStateIdFunc(resourceName), + ImportStateVerify: true, + }, { Config: testAccAWSCloudWatchEventRuleConfig(rName2), Check: resource.ComposeTestCheckFunc( @@ -542,6 +548,17 @@ func testAccCheckCloudWatchEventRuleNotRecreated(i, j *events.DescribeRuleOutput } } +func testAccAWSCloudWatchEventRuleNoBusNameImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not found: %s", resourceName) + } + + return rs.Primary.Attributes["name"], nil + } +} + func testAccAWSCloudWatchEventRuleConfig(name string) string { return fmt.Sprintf(` resource "aws_cloudwatch_event_rule" "test" { From 5a389ea3d407470aa15641f8326b4769520907f5 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 27 Oct 2020 11:21:02 -0700 Subject: [PATCH 11/12] Removes check for schedule_expression in non-default bus --- aws/resource_aws_cloudwatch_event_rule.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/aws/resource_aws_cloudwatch_event_rule.go b/aws/resource_aws_cloudwatch_event_rule.go index eb8b03248270..50ee65e2f591 100644 --- a/aws/resource_aws_cloudwatch_event_rule.go +++ b/aws/resource_aws_cloudwatch_event_rule.go @@ -1,7 +1,6 @@ package aws import ( - "errors" "fmt" "log" "time" @@ -300,12 +299,7 @@ func buildPutRuleInputStruct(d *schema.ResourceData, name string) (*events.PutRu input.RoleArn = aws.String(v.(string)) } if v, ok := d.GetOk("schedule_expression"); ok { - scheduleExpression := v.(string) - if eventBusName == tfevents.DefaultEventBusName { - input.ScheduleExpression = aws.String(scheduleExpression) - } else { - return nil, errors.New("schedule_expression can only be set on the default event bus") - } + input.ScheduleExpression = aws.String(v.(string)) } input.State = aws.String(getStringStateFromBoolean(d.Get("is_enabled").(bool))) From 38e5f778c63f32bcbad4cce325b338b45e1b1e74 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 27 Oct 2020 12:32:59 -0700 Subject: [PATCH 12/12] Fixes terraform formatting --- aws/resource_aws_cloudwatch_event_rule_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_cloudwatch_event_rule_test.go b/aws/resource_aws_cloudwatch_event_rule_test.go index d03e4734c95d..c5bb9c565c49 100644 --- a/aws/resource_aws_cloudwatch_event_rule_test.go +++ b/aws/resource_aws_cloudwatch_event_rule_test.go @@ -595,7 +595,7 @@ PATTERN resource "aws_cloudwatch_event_bus" "test" { name = %[3]q -} +} `, name, description, eventBusName) }