diff --git a/.changelog/17704.txt b/.changelog/17704.txt new file mode 100644 index 000000000000..e5ccf784f629 --- /dev/null +++ b/.changelog/17704.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_synthetics_canary: Fix Canary Update when in running state +``` diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index ee730dabcd92..4da90e5ed94a 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -341,61 +341,52 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).syntheticsconn - input := &synthetics.UpdateCanaryInput{ - Name: aws.String(d.Id()), - } - - updateFlag := false + if d.HasChangesExcept("tags", "start_canary") { + input := &synthetics.UpdateCanaryInput{ + Name: aws.String(d.Id()), + } - if d.HasChange("vpc_config") { - input.VpcConfig = expandAwsSyntheticsCanaryVpcConfig(d.Get("vpc_config").([]interface{})) - updateFlag = true - } + if d.HasChange("vpc_config") { + input.VpcConfig = expandAwsSyntheticsCanaryVpcConfig(d.Get("vpc_config").([]interface{})) + } - if d.HasChange("runtime_version") { - input.RuntimeVersion = aws.String(d.Get("runtime_version").(string)) - updateFlag = true - } + if d.HasChange("runtime_version") { + input.RuntimeVersion = aws.String(d.Get("runtime_version").(string)) + } - if d.HasChanges("handler", "zip_file", "s3_bucket", "s3_key", "s3_version") { - code, err := expandAwsSyntheticsCanaryCode(d) - if err != nil { - return err + if d.HasChanges("handler", "zip_file", "s3_bucket", "s3_key", "s3_version") { + code, err := expandAwsSyntheticsCanaryCode(d) + if err != nil { + return err + } + input.Code = code } - input.Code = code - updateFlag = true - } - if d.HasChange("run_config") { - input.RunConfig = expandAwsSyntheticsCanaryRunConfig(d.Get("run_config").([]interface{})) - updateFlag = true - } + if d.HasChange("run_config") { + input.RunConfig = expandAwsSyntheticsCanaryRunConfig(d.Get("run_config").([]interface{})) + } - if d.HasChange("schedule") { - input.Schedule = expandAwsSyntheticsCanarySchedule(d.Get("schedule").([]interface{})) - updateFlag = true - } + if d.HasChange("schedule") { + input.Schedule = expandAwsSyntheticsCanarySchedule(d.Get("schedule").([]interface{})) + } - if d.HasChange("success_retention_period") { - _, n := d.GetChange("success_retention_period") - input.SuccessRetentionPeriodInDays = aws.Int64(int64(n.(int))) - updateFlag = true - } + if d.HasChange("success_retention_period") { + _, n := d.GetChange("success_retention_period") + input.SuccessRetentionPeriodInDays = aws.Int64(int64(n.(int))) + } - if d.HasChange("failure_retention_period") { - _, n := d.GetChange("failure_retention_period") - input.FailureRetentionPeriodInDays = aws.Int64(int64(n.(int))) - updateFlag = true - } + if d.HasChange("failure_retention_period") { + _, n := d.GetChange("failure_retention_period") + input.FailureRetentionPeriodInDays = aws.Int64(int64(n.(int))) + } - if d.HasChange("execution_role_arn") { - _, n := d.GetChange("execution_role_arn") - input.ExecutionRoleArn = aws.String(n.(string)) - updateFlag = true - } + if d.HasChange("execution_role_arn") { + _, n := d.GetChange("execution_role_arn") + input.ExecutionRoleArn = aws.String(n.(string)) + } - if updateFlag { - if status := d.Get("status"); status.(string) == synthetics.CanaryStateRunning { + status := d.Get("status").(string) + if status == synthetics.CanaryStateRunning { if err := syntheticsStopCanary(d.Id(), conn); err != nil { return err } @@ -406,22 +397,36 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error updating Synthetics Canary: %w", err) } - if _, err := waiter.CanaryReady(conn, d.Id()); err != nil { - return fmt.Errorf("error waiting for Synthetics Canary (%s) updating: %w", d.Id(), err) + if status != synthetics.CanaryStateReady { + if _, err := waiter.CanaryStopped(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Synthetics Canary (%s) to be stopped: %w", d.Id(), err) + } + } else { + if _, err := waiter.CanaryReady(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Synthetics Canary (%s) updating: %w", d.Id(), err) + } } - } - status := d.Get("status") - if v := d.Get("start_canary"); v.(bool) { - if status.(string) != synthetics.CanaryStateRunning { + if v := d.Get("start_canary"); v.(bool) { if err := syntheticsStartCanary(d.Id(), conn); err != nil { return err } } - } else { - if status.(string) == synthetics.CanaryStateRunning { - if err := syntheticsStopCanary(d.Id(), conn); err != nil { - return err + } + + if d.HasChange("start_canary") { + status := d.Get("status").(string) + if v := d.Get("start_canary"); v.(bool) { + if status != synthetics.CanaryStateRunning { + if err := syntheticsStartCanary(d.Id(), conn); err != nil { + return err + } + } + } else { + if status == synthetics.CanaryStateRunning { + if err := syntheticsStopCanary(d.Id(), conn); err != nil { + return err + } } } } diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 5202928fe732..95f6ab11bb54 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -108,6 +108,7 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "artifact_s3_location", fmt.Sprintf("%s/", rName)), resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), resource.TestCheckResourceAttrSet(resourceName, "timeline.0.created"), + resource.TestCheckResourceAttr(resourceName, "status", "READY"), ), }, { @@ -139,6 +140,7 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), resource.TestCheckResourceAttrSet(resourceName, "timeline.0.created"), resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_modified"), + resource.TestCheckResourceAttr(resourceName, "status", "READY"), testAccCheckAwsSyntheticsCanaryIsUpdated(&conf1, &conf2), ), }, @@ -221,6 +223,7 @@ func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf3), resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_started"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_stopped"), testAccCheckAwsSyntheticsCanaryIsStartedAfter(&conf2, &conf3), ), }, @@ -228,6 +231,48 @@ func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { }) } +func TestAccAWSSyntheticsCanary_startCanary_codeChanges(t *testing.T) { + var conf1, conf2 synthetics.Canary + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) + resourceName := "aws_synthetics_canary.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSyntheticsCanaryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSyntheticsCanaryStartCanaryConfig(rName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf1), + resource.TestCheckResourceAttr(resourceName, "status", "RUNNING"), + resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_started"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, + }, + { + Config: testAccAWSSyntheticsCanaryStartCanaryZipUpdatedConfig(rName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf2), + resource.TestCheckResourceAttr(resourceName, "status", "RUNNING"), + resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_started"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_stopped"), + testAccCheckAwsSyntheticsCanaryIsStartedAfter(&conf1, &conf2), + ), + }, + }, + }) +} + func TestAccAWSSyntheticsCanary_s3(t *testing.T) { var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) @@ -831,6 +876,24 @@ resource "aws_synthetics_canary" "test" { `, rName, state)) } +func testAccAWSSyntheticsCanaryStartCanaryZipUpdatedConfig(rName string, state bool) string { + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` +resource "aws_synthetics_canary" "test" { + name = %[1]q + artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" + execution_role_arn = aws_iam_role.test.arn + handler = "exports.handler" + zip_file = "test-fixtures/lambdatest_modified.zip" + start_canary = %[2]t + runtime_version = "syn-1.0" + + schedule { + expression = "rate(0 minute)" + } +} +`, rName, state)) +} + func testAccAWSSyntheticsCanaryBasicS3CodeConfig(rName string) string { return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" {