From c90f1cb3f3ee7f65b8400c9026bfbefee5091be3 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 6 Nov 2020 10:17:03 -0500 Subject: [PATCH] resource/aws_cloudwatch_event_target: Include state upgrade for new default event bus name in Read path and only include EventBusName when non-empty in API requests Reference: https://github.com/hashicorp/terraform-provider-aws/issues/16069 Until we sort out the details of setting up the acceptance testing framework to handle state upgrades (likely via Terraform CLI 0.13+ and TestCase.ExternalProviders), manually verified with the following: ```terraform terraform { required_providers { aws = "3.13.0" } required_version = "0.12.29" } provider "aws" { region = "us-east-2" } resource "aws_cloudwatch_event_rule" "test" { name = "16069-test" schedule_expression = "rate(1 hour)" } resource "aws_cloudwatch_event_target" "test" { rule = aws_cloudwatch_event_rule.test.name target_id = "16069-test" arn = aws_sns_topic.test.arn } resource "aws_sns_topic" "test" { name = "16069-test" } ``` ```console $ terraform init $ terraform apply # update version to 3.14.0 $ terraform init $ terraform apply aws_cloudwatch_event_rule.test: Refreshing state... [id=16069-test] aws_sns_topic.test: Refreshing state... [id=arn:aws:sns:us-east-2:--OMITTED--:16069-test] aws_cloudwatch_event_target.test: Refreshing state... [id=16069-test-16069-test] Error: InvalidParameter: 1 validation error(s) found. - minimum field size of 1, ListTargetsByRuleInput.EventBusName. ``` After updating local plugin: ```console $ terraform apply ... Apply complete! Resources: 0 added, 0 changed, 0 destroyed. ``` Output from acceptance testing in AWS Commercial: ``` --- PASS: TestAccAWSCloudWatchEventTarget_basic (41.56s) --- PASS: TestAccAWSCloudWatchEventTarget_batch (146.89s) --- PASS: TestAccAWSCloudWatchEventTarget_disappears (15.51s) --- PASS: TestAccAWSCloudWatchEventTarget_ecs (34.45s) --- PASS: TestAccAWSCloudWatchEventTarget_ecsWithBlankTaskCount (34.90s) --- PASS: TestAccAWSCloudWatchEventTarget_EventBusName (36.74s) --- PASS: TestAccAWSCloudWatchEventTarget_full (62.30s) --- PASS: TestAccAWSCloudWatchEventTarget_GeneratedTargetId (17.97s) --- PASS: TestAccAWSCloudWatchEventTarget_input_transformer (43.71s) --- PASS: TestAccAWSCloudWatchEventTarget_inputTransformerJsonString (48.88s) --- PASS: TestAccAWSCloudWatchEventTarget_kinesis (62.16s) --- PASS: TestAccAWSCloudWatchEventTarget_sqs (18.22s) --- PASS: TestAccAWSCloudWatchEventTarget_ssmDocument (21.40s) ``` Output from acceptance testing in AWS GovCloud (US): ``` --- PASS: TestAccAWSCloudWatchEventTarget_basic (51.37s) --- PASS: TestAccAWSCloudWatchEventTarget_batch (153.85s) --- PASS: TestAccAWSCloudWatchEventTarget_disappears (19.90s) --- PASS: TestAccAWSCloudWatchEventTarget_ecs (40.23s) --- PASS: TestAccAWSCloudWatchEventTarget_ecsWithBlankTaskCount (40.10s) --- PASS: TestAccAWSCloudWatchEventTarget_EventBusName (43.57s) --- PASS: TestAccAWSCloudWatchEventTarget_full (67.95s) --- PASS: TestAccAWSCloudWatchEventTarget_GeneratedTargetId (22.57s) --- PASS: TestAccAWSCloudWatchEventTarget_input_transformer (49.06s) --- PASS: TestAccAWSCloudWatchEventTarget_inputTransformerJsonString (39.35s) --- PASS: TestAccAWSCloudWatchEventTarget_kinesis (66.38s) --- PASS: TestAccAWSCloudWatchEventTarget_sqs (22.96s) --- PASS: TestAccAWSCloudWatchEventTarget_ssmDocument (25.39s) ``` --- .../service/cloudwatchevents/lister/list.go | 10 +- aws/resource_aws_cloudwatch_event_target.go | 19 +- ...rce_aws_cloudwatch_event_target_migrate.go | 177 ++++++++++++++++++ ...ws_cloudwatch_event_target_migrate_test.go | 37 ++++ 4 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 aws/resource_aws_cloudwatch_event_target_migrate.go create mode 100644 aws/resource_aws_cloudwatch_event_target_migrate_test.go diff --git a/aws/internal/service/cloudwatchevents/lister/list.go b/aws/internal/service/cloudwatchevents/lister/list.go index b1d3b4ecd4a..713766d9c99 100644 --- a/aws/internal/service/cloudwatchevents/lister/list.go +++ b/aws/internal/service/cloudwatchevents/lister/list.go @@ -9,9 +9,13 @@ import ( func ListAllTargetsForRulePages(conn *events.CloudWatchEvents, busName, ruleName string, fn func(*events.ListTargetsByRuleOutput, bool) bool) error { input := &events.ListTargetsByRuleInput{ - Rule: aws.String(ruleName), - EventBusName: aws.String(busName), - Limit: aws.Int64(100), // Set limit to allowed maximum to prevent API throttling + Rule: aws.String(ruleName), + Limit: aws.Int64(100), // Set limit to allowed maximum to prevent API throttling } + + if busName != "" { + input.EventBusName = aws.String(busName) + } + return ListTargetsByRulePages(conn, input, fn) } diff --git a/aws/resource_aws_cloudwatch_event_target.go b/aws/resource_aws_cloudwatch_event_target.go index bc893558bdb..28e6ff5199c 100644 --- a/aws/resource_aws_cloudwatch_event_target.go +++ b/aws/resource_aws_cloudwatch_event_target.go @@ -27,6 +27,15 @@ func resourceAwsCloudWatchEventTarget() *schema.Resource { State: resourceAwsCloudWatchEventTargetImport, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: resourceAwsCloudWatchEventTargetV0().CoreConfigSchema().ImpliedType(), + Upgrade: resourceAwsCloudWatchEventTargetStateUpgradeV0, + Version: 0, + }, + }, + Schema: map[string]*schema.Schema{ "event_bus_name": { Type: schema.TypeString, @@ -361,11 +370,13 @@ func resourceAwsCloudWatchEventTargetUpdate(d *schema.ResourceData, meta interfa func resourceAwsCloudWatchEventTargetDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatcheventsconn - busName := d.Get("event_bus_name").(string) input := &events.RemoveTargetsInput{ - Ids: []*string{aws.String(d.Get("target_id").(string))}, - Rule: aws.String(d.Get("rule").(string)), - EventBusName: aws.String(busName), + Ids: []*string{aws.String(d.Get("target_id").(string))}, + Rule: aws.String(d.Get("rule").(string)), + } + + if v, ok := d.GetOk("event_bus_name"); ok { + input.EventBusName = aws.String(v.(string)) } output, err := conn.RemoveTargets(input) diff --git a/aws/resource_aws_cloudwatch_event_target_migrate.go b/aws/resource_aws_cloudwatch_event_target_migrate.go new file mode 100644 index 00000000000..185f76a1dab --- /dev/null +++ b/aws/resource_aws_cloudwatch_event_target_migrate.go @@ -0,0 +1,177 @@ +package aws + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + tfevents "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents" +) + +func resourceAwsCloudWatchEventTargetV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Required: true, + }, + "batch_target": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "array_size": { + Type: schema.TypeInt, + Optional: true, + }, + "job_attempts": { + Type: schema.TypeInt, + Optional: true, + }, + "job_definition": { + Type: schema.TypeString, + Required: true, + }, + "job_name": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "ecs_target": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group": { + Type: schema.TypeString, + Optional: true, + }, + "launch_type": { + Type: schema.TypeString, + Optional: true, + }, + "network_configuration": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "assign_public_ip": { + Type: schema.TypeBool, + Optional: true, + }, + "security_groups": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "subnets": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "platform_version": { + Type: schema.TypeString, + Optional: true, + }, + "task_count": { + Type: schema.TypeInt, + Optional: true, + }, + "task_definition_arn": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "input": { + Type: schema.TypeString, + Optional: true, + }, + "input_path": { + Type: schema.TypeString, + Optional: true, + }, + "input_transformer": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "input_paths": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "input_template": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "kinesis_target": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "partition_key_path": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "role_arn": { + Type: schema.TypeString, + Optional: true, + }, + "rule": { + Type: schema.TypeString, + Required: true, + }, + "run_command_targets": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + }, + "values": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "sqs_target": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "message_group_id": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "target_id": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func resourceAwsCloudWatchEventTargetStateUpgradeV0(_ context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + rawState["event_bus_name"] = tfevents.DefaultEventBusName + + return rawState, nil +} diff --git a/aws/resource_aws_cloudwatch_event_target_migrate_test.go b/aws/resource_aws_cloudwatch_event_target_migrate_test.go new file mode 100644 index 00000000000..4dbed0b2911 --- /dev/null +++ b/aws/resource_aws_cloudwatch_event_target_migrate_test.go @@ -0,0 +1,37 @@ +package aws + +import ( + "context" + "reflect" + "testing" +) + +func testResourceAwsCloudWatchEventTargetStateDataV0() map[string]interface{} { + return map[string]interface{}{ + "arn": "arn:aws:test:us-east-1:123456789012:test", //lintignore:AWSAT003,AWSAT005 + "rule": "testrule", + "target_id": "testtargetid", + } +} + +func testResourceAwsCloudWatchEventTargetStateDataV1() map[string]interface{} { + v0 := testResourceAwsCloudWatchEventTargetStateDataV0() + return map[string]interface{}{ + "arn": v0["arn"], + "event_bus_name": "default", + "rule": v0["rule"], + "target_id": v0["target_id"], + } +} + +func TestResourceAwsCloudWatchEventTargetStateUpgradeV0(t *testing.T) { + expected := testResourceAwsCloudWatchEventTargetStateDataV1() + actual, err := resourceAwsCloudWatchEventTargetStateUpgradeV0(context.Background(), testResourceAwsCloudWatchEventTargetStateDataV0(), nil) + if err != nil { + t.Fatalf("error migrating state: %s", err) + } + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("\n\nexpected:\n\n%#v\n\ngot:\n\n%#v\n\n", expected, actual) + } +}