From 2981497bf7f92a8ae95ec50334af798ba761c14a Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sun, 3 May 2020 23:09:34 +0300 Subject: [PATCH 01/67] add canary --- aws/resource_aws_synthetics_canary.go | 400 ++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 aws/resource_aws_synthetics_canary.go diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go new file mode 100644 index 000000000000..8d2e2c78e51c --- /dev/null +++ b/aws/resource_aws_synthetics_canary.go @@ -0,0 +1,400 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/aws/arn" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/synthetics" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func resourceAwsSyntheticsCanary() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSyntheticsCanaryCreate, + Read: resourceAwsSyntheticsCanaryRead, + Update: resourceAwsSyntheticsCanaryUpdate, + Delete: resourceAwsSyntheticsCanaryDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "artifact_s3_location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "code": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "handler": { + Type: schema.TypeString, + Required: true, + }, + "s3_bucket": { + Type: schema.TypeString, + Optional: true, + }, + "s3_key": { + Type: schema.TypeString, + Optional: true, + }, + "s3_version": { + Type: schema.TypeString, + Optional: true, + }, + "zip_file": { + Type: schema.TypeString, + Optional: true, + }, + "source_location_arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "execution_role_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + "failure_retention_period": { + Type: schema.TypeInt, + Optional: true, + Default: 31, + ValidateFunc: validation.IntBetween(1, 455), + }, + "success_retention_period": { + Type: schema.TypeInt, + Optional: true, + Default: 31, + ValidateFunc: validation.IntBetween(1, 455), + }, + "run_config": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "duration_in_seconds": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "schedule": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "expression": { + Type: schema.TypeString, + Required: true, + }, + "duration_in_seconds": { + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + }, + "vpc_config": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "security_group_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "subnet_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "vpc_id": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "engine_arn": { + Type: schema.TypeString, + Computed: true, + }, + "runtime_version": { + Type: schema.TypeString, + Computed: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tagsSchema(), + }, + } +} + +func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).syntheticsconn + + input := &synthetics.CreateCanaryInput{ + Name: aws.String(d.Get("name").(string)), + ArtifactS3Location: aws.String(d.Get("artifact_s3_location").(string)), + ExecutionRoleArn: aws.String(d.Get("execution_role_arn").(string)), + RuntimeVersion: aws.String("syn-1.0"), + Code: expandAwsSyntheticsCanaryCode(d.Get("code").([]interface{})), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().SyntheticsTags(), + } + + if v, ok := d.GetOk("run_config"); ok { + input.RunConfig = expandAwsSyntheticsCanaryRunConfig(v.([]interface{})) + } + + if v, ok := d.GetOk("schedule"); ok { + input.Schedule = expandAwsSyntheticsCanarySchedule(v.([]interface{})) + } + + if v, ok := d.GetOk("vpc_config"); ok { + input.VpcConfig = expandAwsSyntheticsCanaryVpcConfig(v.([]interface{})) + } + + if v, ok := d.GetOk("failure_retention_period"); ok { + input.FailureRetentionPeriodInDays = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("success_retention_period"); ok { + input.SuccessRetentionPeriodInDays = aws.Int64(int64(v.(int))) + } + + resp, err := conn.CreateCanary(input) + if err != nil { + return err + } + + d.SetId(aws.StringValue(resp.Canary.Name)) + + return resourceAwsSyntheticsCanaryRead(d, meta) +} + +func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).syntheticsconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + input := &synthetics.GetCanaryInput{ + Name: aws.String(d.Id()), + } + + resp, err := conn.GetCanary(input) + if err != nil { + return fmt.Errorf("error reading Synthetics Canary: %s", err) + } + + canary := resp.Canary + d.Set("name", canary.Name) + d.Set("engine_arn", canary.EngineArn) + d.Set("execution_role_arn", canary.ExecutionRoleArn) + d.Set("runtime_version", canary.RuntimeVersion) + d.Set("artifact_s3_location", canary.ArtifactS3Location) + d.Set("failure_retention_period", canary.FailureRetentionPeriodInDays) + d.Set("success_retention_period", canary.SuccessRetentionPeriodInDays) + + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "synthetics", + Region: meta.(*AWSClient).region, + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("canary/%s", aws.StringValue(canary.Name)), + }.String() + + d.Set("arn", arn) + + if err := d.Set("code", flattenAwsSyntheticsCanaryCode(canary.Code)); err != nil { + return fmt.Errorf("error setting code: %s", err) + } + + if err := d.Set("vpc_config", flattenAwsSyntheticsCanaryVpcConfig(canary.VpcConfig)); err != nil { + return fmt.Errorf("error setting vpc config: %s", err) + } + + if err := d.Set("run_config", flattenAwsSyntheticsCanaryRunConfig(canary.RunConfig)); err != nil { + return fmt.Errorf("error setting run config: %s", err) + } + + if err := d.Set("schedule", flattenAwsSyntheticsCanarySchedule(canary.Schedule)); err != nil { + return fmt.Errorf("error setting schedule: %s", err) + } + + if err := d.Set("tags", keyvaluetags.SyntheticsKeyValueTags(canary.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + return nil +} + +func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).swfconn + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + if err := keyvaluetags.SyntheticsUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating Synthetics Canary (%s) tags: %s", d.Id(), err) + } + } + + return resourceAwsSyntheticsCanaryRead(d, meta) +} + +func resourceAwsSyntheticsCanaryDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).syntheticsconn + + input := &synthetics.DeleteCanaryInput{ + Name: aws.String(d.Id()), + } + + _, err := conn.DeleteCanary(input) + if err != nil { + if isAWSErr(err, synthetics.ErrCodeResourceNotFoundException, "") { + return nil + } + return fmt.Errorf("error deleting Synthetics Canary: %s", err) + } + + return nil +} + +func expandAwsSyntheticsCanaryCode(l []interface{}) *synthetics.CanaryCodeInput { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + codeConfig := &synthetics.CanaryCodeInput{ + Handler: aws.String(m["handler"].(string)), + } + + return codeConfig +} + +func flattenAwsSyntheticsCanaryCode(canaryCodeOut *synthetics.CanaryCodeOutput) []interface{} { + if canaryCodeOut == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "handler": aws.StringValue(canaryCodeOut.Handler), + "source_location_arn": aws.StringValue(canaryCodeOut.SourceLocationArn), + } + + return []interface{}{m} +} + +func expandAwsSyntheticsCanarySchedule(l []interface{}) *synthetics.CanaryScheduleInput { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + codeConfig := &synthetics.CanaryScheduleInput{ + Expression: aws.String(m["expression"].(string)), + } + + if v, ok := m["duration_in_seconds"]; ok { + codeConfig.DurationInSeconds = aws.Int64(int64(v.(int))) + } + + return codeConfig +} + +func flattenAwsSyntheticsCanarySchedule(canarySchedule *synthetics.CanaryScheduleOutput) []interface{} { + if canarySchedule == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "expression": aws.StringValue(canarySchedule.Expression), + "duration_in_seconds": aws.Int64Value(canarySchedule.DurationInSeconds), + } + + return []interface{}{m} +} + +func expandAwsSyntheticsCanaryRunConfig(l []interface{}) *synthetics.CanaryRunConfigInput { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + codeConfig := &synthetics.CanaryRunConfigInput{ + TimeoutInSeconds: aws.Int64(int64(m["timeout_in_seconds"].(int))), + } + + return codeConfig +} + +func flattenAwsSyntheticsCanaryRunConfig(canaryCodeOut *synthetics.CanaryRunConfigOutput) []interface{} { + if canaryCodeOut == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "timeout_in_seconds": aws.Int64Value(canaryCodeOut.TimeoutInSeconds), + } + + return []interface{}{m} +} + +func flattenAwsSyntheticsCanaryVpcConfig(canaryVpcOutput *synthetics.VpcConfigOutput) []interface{} { + if canaryVpcOutput == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "subnet_ids": flattenStringSet(canaryVpcOutput.SubnetIds), + "security_group_ids": flattenStringSet(canaryVpcOutput.SecurityGroupIds), + "vpc_id": aws.StringValue(canaryVpcOutput.VpcId), + } + + return []interface{}{m} +} + +func expandAwsSyntheticsCanaryVpcConfig(l []interface{}) *synthetics.VpcConfigInput { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + codeConfig := &synthetics.VpcConfigInput{ + SubnetIds: expandStringSet(m["subnet_ids"].(*schema.Set)), + SecurityGroupIds: expandStringSet(m["security_group_ids"].(*schema.Set)), + } + + return codeConfig +} From 155fef5d3b1d8d91945ae1acfdb23e9c166edf06 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sun, 3 May 2020 23:37:57 +0300 Subject: [PATCH 02/67] add canary to provider.go --- aws/provider.go | 1 + aws/resource_aws_synthetics_canary.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/aws/provider.go b/aws/provider.go index b99c34da705f..d07e1c7deca5 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -994,6 +994,7 @@ func Provider() *schema.Provider { "aws_default_subnet": resourceAwsDefaultSubnet(), "aws_subnet": resourceAwsSubnet(), "aws_swf_domain": resourceAwsSwfDomain(), + "aws_synthetics_canary": resourceAwsSyntheticsCanary(), "aws_transfer_server": resourceAwsTransferServer(), "aws_transfer_ssh_key": resourceAwsTransferSshKey(), "aws_transfer_user": resourceAwsTransferUser(), diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 8d2e2c78e51c..8de1eec435ce 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -2,9 +2,9 @@ package aws import ( "fmt" - "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/synthetics" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" From 7dce1f64c66dde39878e1cdcefd362f0c1b72177 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sun, 3 May 2020 23:39:23 +0300 Subject: [PATCH 03/67] fix conn for update --- aws/resource_aws_synthetics_canary.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 8de1eec435ce..37337bcb5709 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -255,7 +255,7 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e } func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).swfconn + conn := meta.(*AWSClient).syntheticsconn if d.HasChange("tags") { o, n := d.GetChange("tags") From c8d4c1d1378881aaed41bb7ec0c658898a6760bf Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 00:00:33 +0300 Subject: [PATCH 04/67] add update logic --- aws/resource_aws_synthetics_canary.go | 52 ++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 37337bcb5709..30a772022604 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -69,7 +69,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { "execution_role_arn": { Type: schema.TypeString, Required: true, - ForceNew: true, ValidateFunc: validateArn, }, "failure_retention_period": { @@ -88,7 +87,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Type: schema.TypeList, MaxItems: 1, Optional: true, - ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "duration_in_seconds": { @@ -102,7 +100,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Type: schema.TypeList, MaxItems: 1, Optional: true, - ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "expression": { @@ -120,7 +117,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Type: schema.TypeList, MaxItems: 1, Optional: true, - ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "security_group_ids": { @@ -191,7 +187,7 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) resp, err := conn.CreateCanary(input) if err != nil { - return err + return fmt.Errorf("error creating Synthetics Canary: %s", err) } d.SetId(aws.StringValue(resp.Canary.Name)) @@ -257,6 +253,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.HasChange("vpc_config") { + input.VpcConfig = expandAwsSyntheticsCanaryVpcConfig(d.Get("vpc_config").([]interface{})) + updateFlag = true + } + + if d.HasChange("run_config") { + input.RunConfig = expandAwsSyntheticsCanaryRunConfig(d.Get("run_config").([]interface{})) + updateFlag = true + } + + if d.HasChange("schedule") { + input.Schedule = expandAwsSyntheticsCanarySchedule(d.Get("schedule").([]interface{})) + updateFlag = true + } + + if d.HasChange("success_retention_period") { + _, n := d.GetChange("success_retention_period") + input.SuccessRetentionPeriodInDays = 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))) + updateFlag = true + } + + if d.HasChange("execution_role_arn") { + _, n := d.GetChange("execution_role_arn") + input.ExecutionRoleArn = aws.String(n.(string)) + updateFlag = true + } + + if updateFlag { + _, err := conn.UpdateCanary(input) + if err != nil { + return fmt.Errorf("error updating Synthetics Canary: %s", err) + } + } + if d.HasChange("tags") { o, n := d.GetChange("tags") From 6a820b82d18e214dc26ab760de10fe8cf07b56a5 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 00:59:50 +0300 Subject: [PATCH 05/67] add support for zip file + mutex validate name len schedule is required tags cant be empty --- aws/resource_aws_synthetics_canary.go | 41 +++- aws/resource_aws_synthetics_canary_test.go | 260 +++++++++++++++++++++ 2 files changed, 292 insertions(+), 9 deletions(-) create mode 100644 aws/resource_aws_synthetics_canary_test.go diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 30a772022604..b48efc58f42d 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -11,6 +11,8 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) +const awsMutexCanary = `aws_synthetics_canary` + func resourceAwsSyntheticsCanary() *schema.Resource { return &schema.Resource{ Create: resourceAwsSyntheticsCanaryCreate, @@ -23,9 +25,10 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 21), }, "artifact_s3_location": { Type: schema.TypeString, @@ -99,7 +102,7 @@ func resourceAwsSyntheticsCanary() *schema.Resource { "schedule": { Type: schema.TypeList, MaxItems: 1, - Optional: true, + Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "expression": { @@ -161,10 +164,19 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) ArtifactS3Location: aws.String(d.Get("artifact_s3_location").(string)), ExecutionRoleArn: aws.String(d.Get("execution_role_arn").(string)), RuntimeVersion: aws.String("syn-1.0"), - Code: expandAwsSyntheticsCanaryCode(d.Get("code").([]interface{})), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().SyntheticsTags(), } + if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { + input.Tags = keyvaluetags.New(v).IgnoreAws().SyntheticsTags() + } + + code, err := expandAwsSyntheticsCanaryCode(d.Get("code").([]interface{})) + if err != nil { + return err + } + + input.Code = code + if v, ok := d.GetOk("run_config"); ok { input.RunConfig = expandAwsSyntheticsCanaryRunConfig(v.([]interface{})) } @@ -328,9 +340,9 @@ func resourceAwsSyntheticsCanaryDelete(d *schema.ResourceData, meta interface{}) return nil } -func expandAwsSyntheticsCanaryCode(l []interface{}) *synthetics.CanaryCodeInput { +func expandAwsSyntheticsCanaryCode(l []interface{}) (*synthetics.CanaryCodeInput, error) { if len(l) == 0 || l[0] == nil { - return nil + return nil, nil } m := l[0].(map[string]interface{}) @@ -339,7 +351,18 @@ func expandAwsSyntheticsCanaryCode(l []interface{}) *synthetics.CanaryCodeInput Handler: aws.String(m["handler"].(string)), } - return codeConfig + if v, ok := m["zip_file"]; ok { + awsMutexKV.Lock(awsMutexCanary) + defer awsMutexKV.Unlock(awsMutexCanary) + file, err := loadFileContent(v.(string)) + if err != nil { + return nil, fmt.Errorf("Unable to load %q: %s", v.(string), err) + } + + codeConfig.ZipFile = file + } + + return codeConfig, nil } func flattenAwsSyntheticsCanaryCode(canaryCodeOut *synthetics.CanaryCodeOutput) []interface{} { diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go new file mode 100644 index 000000000000..d8ab69719e4a --- /dev/null +++ b/aws/resource_aws_synthetics_canary_test.go @@ -0,0 +1,260 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/service/synthetics" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccAWSSyntheticsCanary_basic(t *testing.T) { + 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: testAccAWSSyntheticsCanaryBasicConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary/.+`)), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"code"}, + }, + }, + }) +} + +func TestAccAWSSyntheticsCanary_tags(t *testing.T) { + 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: testAccAWSSyntheticsCanaryConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSyntheticsCanaryConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSSyntheticsCanaryConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAwsSyntheticsCanaryDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).syntheticsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_synthetics_canary" { + continue + } + + name := rs.Primary.ID + input := &synthetics.GetCanaryInput{ + Name: aws.String(name), + } + + _, err := conn.GetCanary(input) + if err != nil { + return err + } + } + + return nil +} + +func testAccCheckAwsSyntheticsCanaryExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("synthetics Canary not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("synthetics Canary name not set") + } + + name := rs.Primary.ID + conn := testAccProvider.Meta().(*AWSClient).syntheticsconn + + input := &synthetics.GetCanaryInput{ + Name: aws.String(name), + } + + _, err := conn.GetCanary(input) + if err != nil { + return fmt.Errorf("syntherics Canary %s not found in AWS", name) + } + return nil + } +} + +func testAccAWSSyntheticsCanaryConfigBase(rName string) string { + return fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = < Date: Mon, 4 May 2020 01:06:10 +0300 Subject: [PATCH 06/67] add some initial tests --- aws/resource_aws_synthetics_canary_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index d8ab69719e4a..5eef6378adf3 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -223,7 +223,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn code { - handler = "exports.example" + handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" } From bb65dff2fb49ad5fb5a8f7f7bddea8f91c77a8a2 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 11:12:40 +0300 Subject: [PATCH 07/67] suppress diff for `artifact_s3_location` fix `timeout_in_seconds` --- aws/resource_aws_synthetics_canary.go | 6 +++++- aws/resource_aws_synthetics_canary_test.go | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index b48efc58f42d..cb65853d254f 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" @@ -34,6 +35,9 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return strings.TrimPrefix(old, "s3://") == new + }, }, "code": { Type: schema.TypeList, @@ -92,7 +96,7 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "duration_in_seconds": { + "timeout_in_seconds": { Type: schema.TypeInt, Required: true, }, diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 5eef6378adf3..49c0b52883c5 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -219,7 +219,7 @@ func testAccAWSSyntheticsCanaryBasicConfig(rName string) string { return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q - artifact_s3_location = aws_s3_bucket.test.arn + artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" execution_role_arn = aws_iam_role.test.arn code { From 7d735375498163d759d9f612ee2601245a508a5c Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 11:48:09 +0300 Subject: [PATCH 08/67] fix suppress diff + conflict --- aws/resource_aws_synthetics_canary.go | 41 +++++++++++++--------- aws/resource_aws_synthetics_canary_test.go | 3 ++ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index cb65853d254f..cd61a0cb433c 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -36,7 +36,7 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Required: true, ForceNew: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return strings.TrimPrefix(old, "s3://") == new + return strings.TrimPrefix(new, "s3://") == old }, }, "code": { @@ -51,24 +51,24 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Required: true, }, "s3_bucket": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"code.0.zip_file"}, }, "s3_key": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"code.0.zip_file"}, }, "s3_version": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"code.0.zip_file"}, }, "zip_file": { - Type: schema.TypeString, - Optional: true, - }, - "source_location_arn": { - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"code.0.s3_bucket", "code.0.s3_key", "code.0.s3_version"}, }, }, }, @@ -98,7 +98,8 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Schema: map[string]*schema.Schema{ "timeout_in_seconds": { Type: schema.TypeInt, - Required: true, + Optional: true, + Default: 840, }, }, }, @@ -280,6 +281,15 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) updateFlag = true } + if d.HasChange("code") { + code, err := expandAwsSyntheticsCanaryCode(d.Get("code").([]interface{})) + if err != nil { + return err + } + input.Code = code + updateFlag = true + } + if d.HasChange("run_config") { input.RunConfig = expandAwsSyntheticsCanaryRunConfig(d.Get("run_config").([]interface{})) updateFlag = true @@ -375,8 +385,7 @@ func flattenAwsSyntheticsCanaryCode(canaryCodeOut *synthetics.CanaryCodeOutput) } m := map[string]interface{}{ - "handler": aws.StringValue(canaryCodeOut.Handler), - "source_location_arn": aws.StringValue(canaryCodeOut.SourceLocationArn), + "handler": aws.StringValue(canaryCodeOut.Handler), } return []interface{}{m} diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 49c0b52883c5..89b211266d77 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -102,6 +102,9 @@ func testAccCheckAwsSyntheticsCanaryDestroy(s *terraform.State) error { _, err := conn.GetCanary(input) if err != nil { + if isAWSErr(err, synthetics.ErrCodeResourceNotFoundException, "") { + return nil + } return err } } From f6feeafc3f4bca0eb350338e1c42a260fcb0458c Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 13:50:20 +0300 Subject: [PATCH 09/67] flatten code block, similar to AWS lambda --- aws/resource_aws_synthetics_canary.go | 90 +++++++--------------- aws/resource_aws_synthetics_canary_test.go | 6 +- 2 files changed, 31 insertions(+), 65 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index cd61a0cb433c..5f3941f46367 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -39,39 +39,29 @@ func resourceAwsSyntheticsCanary() *schema.Resource { return strings.TrimPrefix(new, "s3://") == old }, }, - "code": { - Type: schema.TypeList, - MaxItems: 1, + "handler": { + Type: schema.TypeString, Required: true, - ForceNew: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "handler": { - Type: schema.TypeString, - Required: true, - }, - "s3_bucket": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"code.0.zip_file"}, - }, - "s3_key": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"code.0.zip_file"}, - }, - "s3_version": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"code.0.zip_file"}, - }, - "zip_file": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"code.0.s3_bucket", "code.0.s3_key", "code.0.s3_version"}, - }, - }, - }, + }, + "s3_bucket": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"zip_file"}, + }, + "s3_key": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"zip_file"}, + }, + "s3_version": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"zip_file"}, + }, + "zip_file": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"s3_bucket", "s3_key", "s3_version"}, }, "execution_role_arn": { Type: schema.TypeString, @@ -99,7 +89,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { "timeout_in_seconds": { Type: schema.TypeInt, Optional: true, - Default: 840, }, }, }, @@ -175,7 +164,7 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) input.Tags = keyvaluetags.New(v).IgnoreAws().SyntheticsTags() } - code, err := expandAwsSyntheticsCanaryCode(d.Get("code").([]interface{})) + code, err := expandAwsSyntheticsCanaryCode(d) if err != nil { return err } @@ -233,6 +222,7 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e d.Set("artifact_s3_location", canary.ArtifactS3Location) d.Set("failure_retention_period", canary.FailureRetentionPeriodInDays) d.Set("success_retention_period", canary.SuccessRetentionPeriodInDays) + d.Set("handler", canary.Code.Handler) arn := arn.ARN{ Partition: meta.(*AWSClient).partition, @@ -244,10 +234,6 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e d.Set("arn", arn) - if err := d.Set("code", flattenAwsSyntheticsCanaryCode(canary.Code)); err != nil { - return fmt.Errorf("error setting code: %s", err) - } - if err := d.Set("vpc_config", flattenAwsSyntheticsCanaryVpcConfig(canary.VpcConfig)); err != nil { return fmt.Errorf("error setting vpc config: %s", err) } @@ -281,8 +267,8 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) updateFlag = true } - if d.HasChange("code") { - code, err := expandAwsSyntheticsCanaryCode(d.Get("code").([]interface{})) + if d.HasChanges("handler", "zip_file", "s3_bucket", "s3_key", "s3_version") { + code, err := expandAwsSyntheticsCanaryCode(d) if err != nil { return err } @@ -354,43 +340,25 @@ func resourceAwsSyntheticsCanaryDelete(d *schema.ResourceData, meta interface{}) return nil } -func expandAwsSyntheticsCanaryCode(l []interface{}) (*synthetics.CanaryCodeInput, error) { - if len(l) == 0 || l[0] == nil { - return nil, nil - } - - m := l[0].(map[string]interface{}) +func expandAwsSyntheticsCanaryCode(d *schema.ResourceData) (*synthetics.CanaryCodeInput, error) { codeConfig := &synthetics.CanaryCodeInput{ - Handler: aws.String(m["handler"].(string)), + Handler: aws.String(d.Get("handler").(string)), } - if v, ok := m["zip_file"]; ok { + if v, ok := d.GetOk("zip_file"); ok { awsMutexKV.Lock(awsMutexCanary) defer awsMutexKV.Unlock(awsMutexCanary) file, err := loadFileContent(v.(string)) if err != nil { return nil, fmt.Errorf("Unable to load %q: %s", v.(string), err) } - codeConfig.ZipFile = file } return codeConfig, nil } -func flattenAwsSyntheticsCanaryCode(canaryCodeOut *synthetics.CanaryCodeOutput) []interface{} { - if canaryCodeOut == nil { - return []interface{}{} - } - - m := map[string]interface{}{ - "handler": aws.StringValue(canaryCodeOut.Handler), - } - - return []interface{}{m} -} - func expandAwsSyntheticsCanarySchedule(l []interface{}) *synthetics.CanaryScheduleInput { if len(l) == 0 || l[0] == nil { return nil diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 89b211266d77..4087a8ecfbd2 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -225,10 +225,8 @@ resource "aws_synthetics_canary" "test" { artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" execution_role_arn = aws_iam_role.test.arn - code { - handler = "exports.handler" - zip_file = "test-fixtures/lambdatest.zip" - } + handler = "exports.handler" + zip_file = "test-fixtures/lambdatest.zip" schedule { expression = "rate(0 minute)" From c00fe9ede580d5651cb53a9c7aac72aeb2f1f6b5 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 15:18:28 +0300 Subject: [PATCH 10/67] tests passing --- aws/resource_aws_synthetics_canary.go | 5 +++++ aws/resource_aws_synthetics_canary_test.go | 26 ++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 5f3941f46367..e1b7b1910515 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -84,11 +84,13 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Type: schema.TypeList, MaxItems: 1, Optional: true, + Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "timeout_in_seconds": { Type: schema.TypeInt, Optional: true, + Computed: true, }, }, }, @@ -102,6 +104,9 @@ func resourceAwsSyntheticsCanary() *schema.Resource { "expression": { Type: schema.TypeString, Required: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return new == "rate(0 minute)" && old == "rate(0 hour)" + }, }, "duration_in_seconds": { Type: schema.TypeInt, diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 4087a8ecfbd2..bc8db3ea3209 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -36,7 +36,7 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"code"}, + ImportStateVerifyIgnore: []string{"zip_file"}, }, }, }) @@ -62,9 +62,10 @@ func TestAccAWSSyntheticsCanary_tags(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file"}, }, { Config: testAccAWSSyntheticsCanaryConfigTags2(rName, "key1", "value1updated", "key2", "value2"), @@ -224,7 +225,6 @@ 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.zip" @@ -236,9 +236,13 @@ resource "aws_synthetics_canary" "test" { } func testAccAWSSyntheticsCanaryConfigTags1(rName, tagKey1, tagValue1 string) string { - return fmt.Sprintf(` + return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { - name = %[1]q + 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.zip" tags = { %[2]q = %[3]q @@ -248,9 +252,13 @@ resource "aws_synthetics_canary" "test" { } func testAccAWSSyntheticsCanaryConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return fmt.Sprintf(` + return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { - name = %[1]q + 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.zip" tags = { %[2]q = %[3]q From 6804ce6d0f6d62239932bc6d109f034180f345b8 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 15:56:55 +0300 Subject: [PATCH 11/67] add waiter for canary create canary from S3 code --- .../service/synthetics/waiter/status.go | 23 ++++++++++++++ .../service/synthetics/waiter/waiter.go | 31 +++++++++++++++++++ aws/resource_aws_synthetics_canary.go | 28 +++++++++++++++-- 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 aws/internal/service/synthetics/waiter/status.go create mode 100644 aws/internal/service/synthetics/waiter/waiter.go diff --git a/aws/internal/service/synthetics/waiter/status.go b/aws/internal/service/synthetics/waiter/status.go new file mode 100644 index 000000000000..bb12ba9affd1 --- /dev/null +++ b/aws/internal/service/synthetics/waiter/status.go @@ -0,0 +1,23 @@ +package waiter + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/synthetics" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func CanaryStatus(conn *synthetics.Synthetics, name string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &synthetics.GetCanaryInput{ + Name: aws.String(name), + } + + output, err := conn.GetCanary(input) + + if err != nil { + return nil, synthetics.CanaryStateError, err + } + + return output, aws.StringValue(output.Canary.Status.State), nil + } +} diff --git a/aws/internal/service/synthetics/waiter/waiter.go b/aws/internal/service/synthetics/waiter/waiter.go new file mode 100644 index 000000000000..ea95e75965ed --- /dev/null +++ b/aws/internal/service/synthetics/waiter/waiter.go @@ -0,0 +1,31 @@ +package waiter + +import ( + "time" + + "github.com/aws/aws-sdk-go/service/synthetics" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +const ( + // Maximum amount of time to wait for a Canary to return Ready + CanaryCreatedTimeout = 5 * time.Minute +) + +// CanaryReady waits for a Canary to return Ready +func CanaryReady(conn *synthetics.Synthetics, name string) (*synthetics.GetCanaryOutput, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{synthetics.CanaryStateCreating}, + Target: []string{synthetics.CanaryStateReady}, + Refresh: CanaryStatus(conn, name), + Timeout: CanaryCreatedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*synthetics.GetCanaryOutput); ok { + return v, err + } + + return nil, err +} diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index e1b7b1910515..1d1540743e54 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -1,6 +1,7 @@ package aws import ( + "errors" "fmt" "strings" @@ -10,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/synthetics/waiter" ) const awsMutexCanary = `aws_synthetics_canary` @@ -203,6 +205,10 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) d.SetId(aws.StringValue(resp.Canary.Name)) + if _, err := waiter.CanaryReady(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Synthetics Canary (%s) creation: %s", d.Id(), err) + } + return resourceAwsSyntheticsCanaryRead(d, meta) } @@ -351,14 +357,30 @@ func expandAwsSyntheticsCanaryCode(d *schema.ResourceData) (*synthetics.CanaryCo Handler: aws.String(d.Get("handler").(string)), } - if v, ok := d.GetOk("zip_file"); ok { + zipFile, hasZipFile := d.GetOk("zip_file") + s3Bucket, bucketOk := d.GetOk("s3_bucket") + s3Key, keyOk := d.GetOk("s3_key") + s3Version, s3VersionOk := d.GetOk("s3_version") + + if hasZipFile { awsMutexKV.Lock(awsMutexCanary) defer awsMutexKV.Unlock(awsMutexCanary) - file, err := loadFileContent(v.(string)) + file, err := loadFileContent(zipFile.(string)) if err != nil { - return nil, fmt.Errorf("Unable to load %q: %s", v.(string), err) + return nil, fmt.Errorf("Unable to load %q: %s", zipFile.(string), err) } codeConfig.ZipFile = file + } else { + if !bucketOk || !keyOk { + return nil, errors.New("s3_bucket and s3_key must all be set while using S3 code source") + } + + codeConfig.S3Bucket = aws.String(s3Bucket.(string)) + codeConfig.S3Key = aws.String(s3Key.(string)) + + if s3VersionOk { + codeConfig.S3Version = aws.String(s3Version.(string)) + } } return codeConfig, nil From 87045b32bd364767f47e4c4750ed11f9f60c55e7 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 16:08:02 +0300 Subject: [PATCH 12/67] add waiter for update --- aws/internal/service/synthetics/waiter/waiter.go | 2 +- aws/resource_aws_synthetics_canary.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/aws/internal/service/synthetics/waiter/waiter.go b/aws/internal/service/synthetics/waiter/waiter.go index ea95e75965ed..e02648a387a8 100644 --- a/aws/internal/service/synthetics/waiter/waiter.go +++ b/aws/internal/service/synthetics/waiter/waiter.go @@ -15,7 +15,7 @@ const ( // CanaryReady waits for a Canary to return Ready func CanaryReady(conn *synthetics.Synthetics, name string) (*synthetics.GetCanaryOutput, error) { stateConf := &resource.StateChangeConf{ - Pending: []string{synthetics.CanaryStateCreating}, + Pending: []string{synthetics.CanaryStateCreating, synthetics.CanaryStateUpdating}, Target: []string{synthetics.CanaryStateReady}, Refresh: CanaryStatus(conn, name), Timeout: CanaryCreatedTimeout, diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 1d1540743e54..9a2674164d64 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -320,6 +320,10 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) if err != nil { return fmt.Errorf("error updating Synthetics Canary: %s", err) } + + if _, err := waiter.CanaryReady(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Synthetics Canary (%s) updating: %s", d.Id(), err) + } } if d.HasChange("tags") { From b1182409bda4cdf8807285e01be147c40da21e17 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 16:51:24 +0300 Subject: [PATCH 13/67] fix arn and tagging --- aws/resource_aws_synthetics_canary.go | 2 +- aws/resource_aws_synthetics_canary_test.go | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 9a2674164d64..16f99b62999b 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -240,7 +240,7 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e Service: "synthetics", Region: meta.(*AWSClient).region, AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("canary/%s", aws.StringValue(canary.Name)), + Resource: fmt.Sprintf("canary:%s", aws.StringValue(canary.Name)), }.String() d.Set("arn", arn) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index bc8db3ea3209..ed47e4e99de5 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -27,7 +27,7 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { Config: testAccAWSSyntheticsCanaryBasicConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsSyntheticsCanaryExists(resourceName), - testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary/.+`)), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), @@ -244,6 +244,10 @@ resource "aws_synthetics_canary" "test" { handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + schedule { + expression = "rate(0 minute)" + } + tags = { %[2]q = %[3]q } @@ -260,6 +264,10 @@ resource "aws_synthetics_canary" "test" { handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + schedule { + expression = "rate(0 minute)" + } + tags = { %[2]q = %[3]q %[4]q = %[5]q From ebf1a06cd231f7fcaf1db18bd13854e7a8d7a6bc Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 4 May 2020 19:57:40 +0300 Subject: [PATCH 14/67] add `source_location_arn` set `vpc_id` to computed add tests for VPC --- .../service/synthetics/waiter/status.go | 6 + aws/resource_aws_synthetics_canary.go | 7 +- aws/resource_aws_synthetics_canary_test.go | 184 ++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) diff --git a/aws/internal/service/synthetics/waiter/status.go b/aws/internal/service/synthetics/waiter/status.go index bb12ba9affd1..b3404ba4175d 100644 --- a/aws/internal/service/synthetics/waiter/status.go +++ b/aws/internal/service/synthetics/waiter/status.go @@ -1,6 +1,8 @@ package waiter import ( + "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/synthetics" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -18,6 +20,10 @@ func CanaryStatus(conn *synthetics.Synthetics, name string) resource.StateRefres return nil, synthetics.CanaryStateError, err } + if aws.StringValue(output.Canary.Status.State) == synthetics.CanaryStateError { + return output, synthetics.CanaryStateError, fmt.Errorf("%s: %s", aws.StringValue(output.Canary.Status.StateReasonCode), aws.StringValue(output.Canary.Status.StateReason)) + } + return output, aws.StringValue(output.Canary.Status.State), nil } } diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 16f99b62999b..957593814a34 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -65,6 +65,10 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Optional: true, ConflictsWith: []string{"s3_bucket", "s3_key", "s3_version"}, }, + "source_location_arn": { + Type: schema.TypeString, + Computed: true, + }, "execution_role_arn": { Type: schema.TypeString, Required: true, @@ -135,7 +139,7 @@ func resourceAwsSyntheticsCanary() *schema.Resource { }, "vpc_id": { Type: schema.TypeString, - Optional: true, + Computed: true, }, }, }, @@ -234,6 +238,7 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e d.Set("failure_retention_period", canary.FailureRetentionPeriodInDays) d.Set("success_retention_period", canary.SuccessRetentionPeriodInDays) d.Set("handler", canary.Code.Handler) + d.Set("source_location_arn", canary.Code.SourceLocationArn) arn := arn.ARN{ Partition: meta.(*AWSClient).partition, diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index ed47e4e99de5..79ffc50f494f 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -42,6 +42,52 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { }) } +func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { + 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: testAccAWSSyntheticsCanaryVPCConfig1(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "vpc_config.0.vpc_config.0.vpc_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file"}, + }, + { + Config: testAccAWSSyntheticsCanaryVPCConfig2(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "2"), + ), + }, + { + Config: testAccAWSSyntheticsCanaryVPCConfig3(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), + ), + }, + }, + }) +} + func TestAccAWSSyntheticsCanary_tags(t *testing.T) { rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -235,6 +281,144 @@ resource "aws_synthetics_canary" "test" { `, rName) } +func testAccAWSSyntheticsCanaryVPCConfigBase(rName string) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.1.0.0/16" + + tags = { + Name = %[1]q + } +} + +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +resource "aws_subnet" "test1" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "${cidrsubnet(aws_vpc.test.cidr_block, 2, 0)}" + availability_zone = "${data.aws_availability_zones.available.names[0]}" + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test2" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "${cidrsubnet(aws_vpc.test.cidr_block, 2, 1)}" + availability_zone = "${data.aws_availability_zones.available.names[1]}" + + tags = { + Name = %[1]q + } +} + +resource "aws_security_group" "test1" { + vpc_id = "${aws_vpc.test.id}" + + tags = { + Name = %[1]q + } +} + +resource "aws_security_group" "test2" { + vpc_id = "${aws_vpc.test.id}" + + tags = { + Name = %[1]q + } +} + +resource "aws_iam_role_policy_attachment" "test" { + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + role = "${aws_iam_role.test.name}" +} +`, rName) +} + +func testAccAWSSyntheticsCanaryVPCConfig1(rName string) string { + return testAccAWSSyntheticsCanaryConfigBase(rName) + + testAccAWSSyntheticsCanaryVPCConfigBase(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.zip" + + schedule { + expression = "rate(0 minute)" + } + + vpc_config { + subnet_ids = ["${aws_subnet.test1.id}"] + security_group_ids = ["${aws_security_group.test1.id}"] + } + + depends_on = ["aws_iam_role_policy_attachment.test"] +} +`, rName) +} + +func testAccAWSSyntheticsCanaryVPCConfig2(rName string) string { + return testAccAWSSyntheticsCanaryConfigBase(rName) + + testAccAWSSyntheticsCanaryVPCConfigBase(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.zip" + + schedule { + expression = "rate(0 minute)" + } + + vpc_config { + subnet_ids = ["${aws_subnet.test1.id}", "${aws_subnet.test2.id}"] + security_group_ids = ["${aws_security_group.test1.id}", "${aws_security_group.test2.id}"] + } + + depends_on = ["aws_iam_role_policy_attachment.test"] + +} +`, rName) +} + +func testAccAWSSyntheticsCanaryVPCConfig3(rName string) string { + return testAccAWSSyntheticsCanaryConfigBase(rName) + + testAccAWSSyntheticsCanaryVPCConfigBase(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.zip" + + schedule { + expression = "rate(0 minute)" + } + + vpc_config { + subnet_ids = ["${aws_subnet.test2.id}"] + security_group_ids = ["${aws_security_group.test2.id}"] + } + + depends_on = ["aws_iam_role_policy_attachment.test"] +} +`, rName) +} + func testAccAWSSyntheticsCanaryConfigTags1(rName, tagKey1, tagValue1 string) string { return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { From 67d04c94f8c817c8bd817b2f6bb2ed2b95e37eec Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 18 Jul 2020 20:20:49 +0300 Subject: [PATCH 15/67] add disappears test --- aws/resource_aws_synthetics_canary.go | 38 ++++++++++-------- aws/resource_aws_synthetics_canary_test.go | 45 +++++++++++++++++++++- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 957593814a34..a5e308e22a06 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -3,6 +3,7 @@ package aws import ( "errors" "fmt" + "log" "strings" "github.com/aws/aws-sdk-go/aws" @@ -65,10 +66,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Optional: true, ConflictsWith: []string{"s3_bucket", "s3_key", "s3_version"}, }, - "source_location_arn": { - Type: schema.TypeString, - Computed: true, - }, "execution_role_arn": { Type: schema.TypeString, Required: true, @@ -148,6 +145,10 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "source_location_arn": { + Type: schema.TypeString, + Computed: true, + }, "runtime_version": { Type: schema.TypeString, Computed: true, @@ -204,13 +205,13 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) resp, err := conn.CreateCanary(input) if err != nil { - return fmt.Errorf("error creating Synthetics Canary: %s", err) + return fmt.Errorf("error creating Synthetics Canary: %w", err) } d.SetId(aws.StringValue(resp.Canary.Name)) if _, err := waiter.CanaryReady(conn, d.Id()); err != nil { - return fmt.Errorf("error waiting for Synthetics Canary (%s) creation: %s", d.Id(), err) + return fmt.Errorf("error waiting for Synthetics Canary (%s) creation: %w", d.Id(), err) } return resourceAwsSyntheticsCanaryRead(d, meta) @@ -226,7 +227,12 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e resp, err := conn.GetCanary(input) if err != nil { - return fmt.Errorf("error reading Synthetics Canary: %s", err) + if isAWSErr(err, synthetics.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] CodeCommit Repository (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("error reading Synthetics Canary: %w", err) } canary := resp.Canary @@ -251,19 +257,19 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e d.Set("arn", arn) if err := d.Set("vpc_config", flattenAwsSyntheticsCanaryVpcConfig(canary.VpcConfig)); err != nil { - return fmt.Errorf("error setting vpc config: %s", err) + return fmt.Errorf("error setting vpc config: %w", err) } if err := d.Set("run_config", flattenAwsSyntheticsCanaryRunConfig(canary.RunConfig)); err != nil { - return fmt.Errorf("error setting run config: %s", err) + return fmt.Errorf("error setting run config: %w", err) } if err := d.Set("schedule", flattenAwsSyntheticsCanarySchedule(canary.Schedule)); err != nil { - return fmt.Errorf("error setting schedule: %s", err) + return fmt.Errorf("error setting schedule: %w", err) } if err := d.Set("tags", keyvaluetags.SyntheticsKeyValueTags(canary.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 @@ -323,11 +329,11 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) if updateFlag { _, err := conn.UpdateCanary(input) if err != nil { - return fmt.Errorf("error updating Synthetics Canary: %s", err) + 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: %s", d.Id(), err) + return fmt.Errorf("error waiting for Synthetics Canary (%s) updating: %w", d.Id(), err) } } @@ -335,7 +341,7 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) o, n := d.GetChange("tags") if err := keyvaluetags.SyntheticsUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating Synthetics Canary (%s) tags: %s", d.Id(), err) + return fmt.Errorf("error updating Synthetics Canary (%s) tags: %w", d.Id(), err) } } @@ -354,7 +360,7 @@ func resourceAwsSyntheticsCanaryDelete(d *schema.ResourceData, meta interface{}) if isAWSErr(err, synthetics.ErrCodeResourceNotFoundException, "") { return nil } - return fmt.Errorf("error deleting Synthetics Canary: %s", err) + return fmt.Errorf("error deleting Synthetics Canary: %w", err) } return nil @@ -376,7 +382,7 @@ func expandAwsSyntheticsCanaryCode(d *schema.ResourceData) (*synthetics.CanaryCo defer awsMutexKV.Unlock(awsMutexCanary) file, err := loadFileContent(zipFile.(string)) if err != nil { - return nil, fmt.Errorf("Unable to load %q: %s", zipFile.(string), err) + return nil, fmt.Errorf("unable to load %q: %w", zipFile.(string), err) } codeConfig.ZipFile = file } else { diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 79ffc50f494f..f770914aba4c 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -29,7 +29,17 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { testAccCheckAwsSyntheticsCanaryExists(resourceName), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-1.0"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), + resource.TestCheckResourceAttr(resourceName, "failure_retention_period", "31"), + resource.TestCheckResourceAttr(resourceName, "success_retention_period", "31"), + resource.TestCheckResourceAttr(resourceName, "handler", "exports.handler"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "schedule.0.duration_in_seconds", "0"), + resource.TestCheckResourceAttr(resourceName, "schedule.0.expression", "rate(0 hour)"), + //todo: add lambda arns, role arn, s3 validations ), }, { @@ -134,6 +144,29 @@ func TestAccAWSSyntheticsCanary_tags(t *testing.T) { }) } +func TestAccAWSSyntheticsCanary_disappears(t *testing.T) { + 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: testAccAWSSyntheticsCanaryBasicConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsSyntheticsCanary(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccCheckAwsSyntheticsCanaryDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).syntheticsconn @@ -189,6 +222,10 @@ func testAccAWSSyntheticsCanaryConfigBase(rName string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "test" { bucket = %[1]q + + tags = { + Name = %[1]q + } } resource "aws_iam_role" "test" { @@ -209,6 +246,10 @@ resource "aws_iam_role" "test" { ] } EOF + + tags = { + Name = %[1]q + } } data "aws_partition" "current" {} @@ -271,8 +312,8 @@ 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.zip" + handler = "exports.handler" + zip_file = "test-fixtures/lambdatest.zip" schedule { expression = "rate(0 minute)" From 4b5deb9c800ce4731737e1d7d64dc118ad378354 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 18 Jul 2020 20:35:24 +0300 Subject: [PATCH 16/67] add docs --- website/docs/index.html.markdown | 7 +- website/docs/r/synthetics_canary.markdown | 82 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 website/docs/r/synthetics_canary.markdown diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 581650db962a..34971ccb69c4 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -325,11 +325,14 @@ for more information about connecting to alternate AWS endpoints or AWS compatib - [`aws_ses_template` resource](/docs/providers/aws/r/ses_template.html) - [`aws_ssm_document` data source](/docs/providers/aws/d/ssm_document.html) - [`aws_ssm_document` resource](/docs/providers/aws/r/ssm_document.html) - - [`aws_vpc` data source](/docs/providers/aws/d/vpc.html) - - [`aws_vpc` resource](/docs/providers/aws/r/vpc.html) + - [`aws_ssm_parameter` data source](/docs/providers/aws/d/ssm_parameter.html) + - [`aws_ssm_parameter` resource](/docs/providers/aws/r/ssm_parameter.html) + - [`aws_synthetics_canary` resource](/docs/providers/aws/r/synthetics_canary.html) - [`aws_vpc_dhcp_options` data source](/docs/providers/aws/d/vpc_dhcp_options.html) - [`aws_vpc_dhcp_options` resource](/docs/providers/aws/r/vpc_dhcp_options.html) - [`aws_vpc_endpoint` data source](/docs/providers/aws/d/vpc_endpoint.html) + - [`aws_vpc` data source](/docs/providers/aws/d/vpc.html) + - [`aws_vpc` resource](/docs/providers/aws/r/vpc.html) - [`aws_vpc_endpoint` resource](/docs/providers/aws/r/vpc_endpoint.html) - [`aws_vpc_endpoint_service` data source](/docs/providers/aws/d/vpc_endpoint_service.html) - [`aws_vpc_endpoint_service` resource](/docs/providers/aws/r/vpc_endpoint_service.html) diff --git a/website/docs/r/synthetics_canary.markdown b/website/docs/r/synthetics_canary.markdown new file mode 100644 index 000000000000..12793400fd83 --- /dev/null +++ b/website/docs/r/synthetics_canary.markdown @@ -0,0 +1,82 @@ +--- +subcategory: "Synthetics" +layout: "aws" +page_title: "AWS: aws_synthetics_canary" +description: |- + Provides a Synthetics Canary resource +--- + +# Resource: aws_synthetics_canary + +Provides an SWF Domain resource. + +## Example Usage + +```hcl +resource "aws_synthetics_canary" "some" { + name = "some-canary" + artifact_s3_location = "s3://some-bucket/" + execution_role_arn = "some-role" + handler = "exports.handler" + zip_file = "test-fixtures/lambdatest.zip" + + schedule { + expression = "rate(0 minute)" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name for this canary. +* `artifact_s3_location` - (Required) The location in Amazon S3 where Synthetics stores artifacts from the test runs of this canary. +* `schedule` - (Required) Information about how often the canary is to run and when these test runs are to stop. See [Schedule](#schedule) below. +* `handler` - (Required) The domain description. +* `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. +* `s3_bucket` - (Optional) +* `s3_key` - (Optional) +* `zip_file` - (Optional) +* `failure_retention_period` - (Optional) The number of days to retain data about failed runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. +* `success_retention_period` - (Optional) The number of days to retain data about successful runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. +* `run_config` - (Optional) Configuration for individual canary runs. See [Run Config](#run-config) below. +* `vpc_config` - (Optional) If this canary is to test an endpoint in a VPC, this structure contains information about the subnet and security groups of the VPC endpoint. For more information, see [Running a Canary in a VPC](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_VPC.html). See [VPC Config](#vpc-config) below. +* `tags` - (Optional) Key-value map of resource tags + +### Schedule + +* `expression` - (Required) A rate expression that defines how often the canary is to run. The syntax is rate(number unit). unit can be minute, minutes, or hour. +* `duration_in_seconds` - (Optional) Duration in seconds, for the canary to continue making regular runs according to the schedule in the Expression value. + +### Run Config + +* `timeout_in_seconds` - (Optional) How long the canary is allowed to run before it must stop. If you omit this field, the frequency of the canary is used as this value, up to a maximum of 14 minutes. +* `memory_in_mb` - (Optional) The maximum amount of memory available to the canary while it is running, in MB. The value you specify must be a multiple of 64. + +### VPC Config + +* `subnet_ids` - (Required) The IDs of the subnets where this canary is to run. +* `security_group_ids` - (Required) The IDs of the security groups for this canary. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The name for this canary. +* `arn` - Amazon Resource Name (ARN) of the Canary. +* `source_location_arn` - The ARN of the Lambda layer where Synthetics stores the canary script code. +* `engine_arn` - The ARN of the Lambda function that is used as your canary's engine. +* `runtime_version` - Specifies the runtime version to use for the canary. + +### VPC Config + +* `vpc_id` - The ID of the VPC where this canary is to run. + +## Import + +Synthetics Canaries can be imported using the `name`, e.g. + +``` +$ terraform import aws_synthetics_canary.some some-canary +``` From b5e73e5119be6a416a7d7af2017879c29a7cb57c Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 18 Jul 2020 20:48:49 +0300 Subject: [PATCH 17/67] fill code part of docs --- website/docs/r/synthetics_canary.markdown | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/website/docs/r/synthetics_canary.markdown b/website/docs/r/synthetics_canary.markdown index 12793400fd83..b83fab5186c7 100644 --- a/website/docs/r/synthetics_canary.markdown +++ b/website/docs/r/synthetics_canary.markdown @@ -35,9 +35,10 @@ The following arguments are supported: * `schedule` - (Required) Information about how often the canary is to run and when these test runs are to stop. See [Schedule](#schedule) below. * `handler` - (Required) The domain description. * `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. -* `s3_bucket` - (Optional) -* `s3_key` - (Optional) -* `zip_file` - (Optional) +* `s3_bucket` - (Optional) If your canary script is located in S3, specify the full bucket name here. The bucket must already exist. Specify the full bucket name, including s3:// as the start of the bucket name. **Conflicts with `zip_file`** +* `s3_key` - (Optional) The S3 key of your script. **Conflicts with `zip_file`** +* `s3_version` - (Optional) The S3 version ID of your script. **Conflicts with `zip_file`** +* `zip_file` - (Optional) If you input your canary script directly into the canary instead of referring to an S3 location, the value of this parameter is the .zip file that contains the script. It can be up to 5 MB. **Conflicts with `s3_bucket`, `s3_key`, and `s3_version`** * `failure_retention_period` - (Optional) The number of days to retain data about failed runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. * `success_retention_period` - (Optional) The number of days to retain data about successful runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. * `run_config` - (Optional) Configuration for individual canary runs. See [Run Config](#run-config) below. From 5040381edff7b2e9ad4c6797a91572ce752f3aaa Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 18 Jul 2020 20:54:17 +0300 Subject: [PATCH 18/67] add lambda deletion logic for in vpc tests --- aws/resource_aws_synthetics_canary_test.go | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index f770914aba4c..aa6cf0be8371 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "github.com/aws/aws-sdk-go/service/lambda" "github.com/aws/aws-sdk-go/service/synthetics" "regexp" "testing" @@ -92,6 +93,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { testAccCheckAwsSyntheticsCanaryExists(resourceName), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), + testAccCheckAwsSyntheticsCanaryDeleteLambda(resourceName), ), }, }, @@ -218,6 +220,43 @@ func testAccCheckAwsSyntheticsCanaryExists(n string) resource.TestCheckFunc { } } +func testAccCheckAwsSyntheticsCanaryDeleteLambda(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("synthetics Canary not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("synthetics Canary name not set") + } + + name := rs.Primary.ID + conn := testAccProvider.Meta().(*AWSClient).syntheticsconn + lambdaConn := testAccProvider.Meta().(*AWSClient).lambdaconn + + input := &synthetics.GetCanaryInput{ + Name: aws.String(name), + } + + res, err := conn.GetCanary(input) + if err != nil { + return fmt.Errorf("syntherics Canary %s not found", name) + } + + lambdaInput := &lambda.DeleteFunctionInput{ + FunctionName: res.Canary.EngineArn, + } + + _, err = lambdaConn.DeleteFunction(lambdaInput) + if err != nil { + return fmt.Errorf("syntherics Canary Lambda %s could not be deleted", name) + } + + return nil + } +} + func testAccAWSSyntheticsCanaryConfigBase(rName string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "test" { From 014915be58050a25ebc0789922e45ae57f179ff1 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 18 Jul 2020 21:15:25 +0300 Subject: [PATCH 19/67] waiter + run config changes --- .../service/synthetics/waiter/waiter.go | 19 ++++ aws/resource_aws_synthetics_canary.go | 29 ++++-- aws/resource_aws_synthetics_canary_test.go | 89 ++++++++++++++++++- 3 files changed, 131 insertions(+), 6 deletions(-) diff --git a/aws/internal/service/synthetics/waiter/waiter.go b/aws/internal/service/synthetics/waiter/waiter.go index e02648a387a8..b09cb0b5720e 100644 --- a/aws/internal/service/synthetics/waiter/waiter.go +++ b/aws/internal/service/synthetics/waiter/waiter.go @@ -10,6 +10,7 @@ import ( const ( // Maximum amount of time to wait for a Canary to return Ready CanaryCreatedTimeout = 5 * time.Minute + CanaryDeletedTimeout = 5 * time.Minute ) // CanaryReady waits for a Canary to return Ready @@ -29,3 +30,21 @@ func CanaryReady(conn *synthetics.Synthetics, name string) (*synthetics.GetCanar return nil, err } + +// CanaryReady waits for a Canary to return Ready +func CanaryDeleted(conn *synthetics.Synthetics, name string) (*synthetics.GetCanaryOutput, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{synthetics.CanaryStateDeleting}, + Target: []string{}, + Refresh: CanaryStatus(conn, name), + Timeout: CanaryDeletedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*synthetics.GetCanaryOutput); ok { + return v, err + } + + return nil, err +} diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index a5e308e22a06..fbff32754a9c 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -90,10 +90,20 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "timeout_in_seconds": { + "memory_in_mb": { Type: schema.TypeInt, Optional: true, Computed: true, + ValidateFunc: validation.All( + validation.IntDivisibleBy(64), + validation.IntAtLeast(960), + ), + }, + "timeout_in_seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(60, 14*60), + Default: 840, }, }, }, @@ -172,10 +182,6 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) RuntimeVersion: aws.String("syn-1.0"), } - if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { - input.Tags = keyvaluetags.New(v).IgnoreAws().SyntheticsTags() - } - code, err := expandAwsSyntheticsCanaryCode(d) if err != nil { return err @@ -183,6 +189,10 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) input.Code = code + if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { + input.Tags = keyvaluetags.New(v).IgnoreAws().SyntheticsTags() + } + if v, ok := d.GetOk("run_config"); ok { input.RunConfig = expandAwsSyntheticsCanaryRunConfig(v.([]interface{})) } @@ -363,6 +373,10 @@ func resourceAwsSyntheticsCanaryDelete(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error deleting Synthetics Canary: %w", err) } + if _, err := waiter.CanaryDeleted(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Synthetics Canary (%s) deletion: %w", d.Id(), err) + } + return nil } @@ -443,6 +457,10 @@ func expandAwsSyntheticsCanaryRunConfig(l []interface{}) *synthetics.CanaryRunCo TimeoutInSeconds: aws.Int64(int64(m["timeout_in_seconds"].(int))), } + if v, ok := m["memory_in_mb"]; ok { + codeConfig.MemoryInMB = aws.Int64(int64(v.(int))) + } + return codeConfig } @@ -453,6 +471,7 @@ func flattenAwsSyntheticsCanaryRunConfig(canaryCodeOut *synthetics.CanaryRunConf m := map[string]interface{}{ "timeout_in_seconds": aws.Int64Value(canaryCodeOut.TimeoutInSeconds), + "memory_in_mb": aws.Int64Value(canaryCodeOut.MemoryInMB), } return []interface{}{m} diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index aa6cf0be8371..b70210b47b5d 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -53,6 +53,52 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { }) } +func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { + 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: testAccAWSSyntheticsCanaryRunConfigConfig1(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "60"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file"}, + }, + { + Config: testAccAWSSyntheticsCanaryRunConfigConfig2(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "960"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "120"), + ), + }, + { + Config: testAccAWSSyntheticsCanaryRunConfigConfig1(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "60"), + ), + }, + }, + }) +} + func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -70,7 +116,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { testAccCheckAwsSyntheticsCanaryExists(resourceName), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), - resource.TestCheckResourceAttrSet(resourceName, "vpc_config.0.vpc_config.0.vpc_id"), + resource.TestMatchResourceAttr(resourceName, "vpc_config.0.vpc_config.0.vpc_id", regexp.MustCompile(`vpc-`)), ), }, { @@ -345,6 +391,47 @@ EOF `, rName) } +func testAccAWSSyntheticsCanaryRunConfigConfig1(rName string) string { + return 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.zip" + + schedule { + expression = "rate(0 minute)" + } + + run_config { + timeout_in_seconds = 60 + } +} +`, rName) +} + +func testAccAWSSyntheticsCanaryRunConfigConfig2(rName string) string { + return 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.zip" + + schedule { + expression = "rate(0 minute)" + } + + run_config { + timeout_in_seconds = 120 + memory_in_mb = 960 + } +} +`, rName) +} + func testAccAWSSyntheticsCanaryBasicConfig(rName string) string { return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { From 0ae32c09cd5b10e6c88cc713ca67b3eb30c93f4b Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 18 Jul 2020 22:48:47 +0300 Subject: [PATCH 20/67] fix setting `memory_in_mb` --- aws/resource_aws_synthetics_canary.go | 9 ++++++-- aws/resource_aws_synthetics_canary_test.go | 27 ++++++++-------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index fbff32754a9c..7715ddbbd5b4 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -213,6 +213,8 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) input.SuccessRetentionPeriodInDays = aws.Int64(int64(v.(int))) } + log.Printf("[DEBUG] creating Synthetics Canary: %#v", input) + resp, err := conn.CreateCanary(input) if err != nil { return fmt.Errorf("error creating Synthetics Canary: %w", err) @@ -374,6 +376,9 @@ func resourceAwsSyntheticsCanaryDelete(d *schema.ResourceData, meta interface{}) } if _, err := waiter.CanaryDeleted(conn, d.Id()); err != nil { + if isAWSErr(err, synthetics.ErrCodeResourceNotFoundException, "") { + return nil + } return fmt.Errorf("error waiting for Synthetics Canary (%s) deletion: %w", d.Id(), err) } @@ -457,8 +462,8 @@ func expandAwsSyntheticsCanaryRunConfig(l []interface{}) *synthetics.CanaryRunCo TimeoutInSeconds: aws.Int64(int64(m["timeout_in_seconds"].(int))), } - if v, ok := m["memory_in_mb"]; ok { - codeConfig.MemoryInMB = aws.Int64(int64(v.(int))) + if v, ok := m["memory_in_mb"].(int); ok && v > 0 { + codeConfig.MemoryInMB = aws.Int64(int64(v)) } return codeConfig diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index b70210b47b5d..19a1cb6f60ce 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/aws/aws-sdk-go/service/lambda" "github.com/aws/aws-sdk-go/service/synthetics" + "log" "regexp" "testing" @@ -91,7 +92,7 @@ func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { Config: testAccAWSSyntheticsCanaryRunConfigConfig1(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsSyntheticsCanaryExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "960"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "60"), ), }, @@ -116,7 +117,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { testAccCheckAwsSyntheticsCanaryExists(resourceName), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), - resource.TestMatchResourceAttr(resourceName, "vpc_config.0.vpc_config.0.vpc_id", regexp.MustCompile(`vpc-`)), + resource.TestMatchResourceAttr(resourceName, "vpc_config.0.vpc_id", regexp.MustCompile(`vpc-`)), ), }, { @@ -277,26 +278,18 @@ func testAccCheckAwsSyntheticsCanaryDeleteLambda(n string) resource.TestCheckFun return fmt.Errorf("synthetics Canary name not set") } - name := rs.Primary.ID - conn := testAccProvider.Meta().(*AWSClient).syntheticsconn - lambdaConn := testAccProvider.Meta().(*AWSClient).lambdaconn - - input := &synthetics.GetCanaryInput{ - Name: aws.String(name), - } + conn := testAccProvider.Meta().(*AWSClient).lambdaconn - res, err := conn.GetCanary(input) - if err != nil { - return fmt.Errorf("syntherics Canary %s not found", name) + input := &lambda.DeleteFunctionInput{ + FunctionName: aws.String(rs.Primary.Attributes["engine_arn"]), } - lambdaInput := &lambda.DeleteFunctionInput{ - FunctionName: res.Canary.EngineArn, - } + log.Printf("delete sythetics Canary Lambda function request: %#v", input) - _, err = lambdaConn.DeleteFunction(lambdaInput) + _, err := conn.DeleteFunction(input) if err != nil { - return fmt.Errorf("syntherics Canary Lambda %s could not be deleted", name) + return fmt.Errorf("sythetics Canary Lambda (%s) could not be deleted: %w", + rs.Primary.Attributes["engine_arn"], err) } return nil From 29c205b44f13fe5ff949ec2982fcc084d108dd25 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 18 Jul 2020 23:25:24 +0300 Subject: [PATCH 21/67] add docs regarding implicit resources --- website/docs/r/synthetics_canary.markdown | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/docs/r/synthetics_canary.markdown b/website/docs/r/synthetics_canary.markdown index b83fab5186c7..1e2eecae4f7e 100644 --- a/website/docs/r/synthetics_canary.markdown +++ b/website/docs/r/synthetics_canary.markdown @@ -81,3 +81,7 @@ Synthetics Canaries can be imported using the `name`, e.g. ``` $ terraform import aws_synthetics_canary.some some-canary ``` + +**Note about leftover implicit resources** - When a canary is created a set of resources are created implicitly, + see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_DeleteCanary.html) for full list. +Terraform will not delete these resources automatically and require manual deletion or by using shell commands in terraform. From e83ba39000f38eead0d6aa11b665bab1e27c1bce Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 18 Jul 2020 23:30:28 +0300 Subject: [PATCH 22/67] add docs regarding permissions needed for role --- website/docs/r/synthetics_canary.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/synthetics_canary.markdown b/website/docs/r/synthetics_canary.markdown index 1e2eecae4f7e..500bc6f0d274 100644 --- a/website/docs/r/synthetics_canary.markdown +++ b/website/docs/r/synthetics_canary.markdown @@ -34,7 +34,7 @@ The following arguments are supported: * `artifact_s3_location` - (Required) The location in Amazon S3 where Synthetics stores artifacts from the test runs of this canary. * `schedule` - (Required) Information about how often the canary is to run and when these test runs are to stop. See [Schedule](#schedule) below. * `handler` - (Required) The domain description. -* `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. +* `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_CreateCanary.html#API_CreateCanary_RequestSyntax) for permissions needs for IAM Role. * `s3_bucket` - (Optional) If your canary script is located in S3, specify the full bucket name here. The bucket must already exist. Specify the full bucket name, including s3:// as the start of the bucket name. **Conflicts with `zip_file`** * `s3_key` - (Optional) The S3 key of your script. **Conflicts with `zip_file`** * `s3_version` - (Optional) The S3 version ID of your script. **Conflicts with `zip_file`** From 4a17a240a07808416b87121e5dcce9c6715ccffc Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 18 Jul 2020 23:44:57 +0300 Subject: [PATCH 23/67] fix name of file --- ...synthetics_canary.markdown => synthetics_canary.html.markdown} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename website/docs/r/{synthetics_canary.markdown => synthetics_canary.html.markdown} (100%) diff --git a/website/docs/r/synthetics_canary.markdown b/website/docs/r/synthetics_canary.html.markdown similarity index 100% rename from website/docs/r/synthetics_canary.markdown rename to website/docs/r/synthetics_canary.html.markdown From e9d40a4dccb31e7d78a25b3144e7d0532e311a63 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sun, 19 Jul 2020 10:55:16 +0300 Subject: [PATCH 24/67] delete vpc config gracefully --- aws/resource_aws_synthetics_canary_test.go | 74 +++++++++++++--------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 19a1cb6f60ce..12c2884af119 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -2,9 +2,7 @@ package aws import ( "fmt" - "github.com/aws/aws-sdk-go/service/lambda" "github.com/aws/aws-sdk-go/service/synthetics" - "log" "regexp" "testing" @@ -140,9 +138,11 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { testAccCheckAwsSyntheticsCanaryExists(resourceName), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), - testAccCheckAwsSyntheticsCanaryDeleteLambda(resourceName), ), }, + { + Config: testAccAWSSyntheticsCanaryBasicConfig(rName), + }, }, }) } @@ -267,34 +267,46 @@ func testAccCheckAwsSyntheticsCanaryExists(n string) resource.TestCheckFunc { } } -func testAccCheckAwsSyntheticsCanaryDeleteLambda(n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("synthetics Canary not found: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("synthetics Canary name not set") - } - - conn := testAccProvider.Meta().(*AWSClient).lambdaconn - - input := &lambda.DeleteFunctionInput{ - FunctionName: aws.String(rs.Primary.Attributes["engine_arn"]), - } - - log.Printf("delete sythetics Canary Lambda function request: %#v", input) - - _, err := conn.DeleteFunction(input) - if err != nil { - return fmt.Errorf("sythetics Canary Lambda (%s) could not be deleted: %w", - rs.Primary.Attributes["engine_arn"], err) - } - - return nil - } -} +//func testAccCheckAwsSyntheticsCanaryDeleteLambda(n string) resource.TestCheckFunc { +// return func(s *terraform.State) error { +// rs, ok := s.RootModule().Resources[n] +// if !ok { +// return fmt.Errorf("synthetics Canary not found: %s", n) +// } +// +// if rs.Primary.ID == "" { +// return fmt.Errorf("synthetics Canary name not set") +// } +// +// conn := testAccProvider.Meta().(*AWSClient).lambdaconn +// lambdaArn := fmt.Sprintf("%s:%s", rs.Primary.Attributes["engine_arn"], "3") +// +// deleteInput := &lambda.DeleteFunctionInput{ +// FunctionName: aws.String(lambdaArn), +// } +// +// log.Printf("delete sythetics Canary Lambda function request: %#v", deleteInput) +// +// _, err := conn.DeleteFunction(deleteInput) +// if err != nil { +// return fmt.Errorf("sythetics Canary Lambda (%s) could not be deleted: %w", lambdaArn, err) +// } +// +// getInput := &lambda.GetFunctionInput{ +// FunctionName: aws.String(lambdaArn), +// } +// +// _, err = conn.GetFunction(getInput) +// if err != nil { +// if isAWSErr(err, lambda.ErrCodeResourceNotFoundException, "") { +// return nil +// } +// return fmt.Errorf("sythetics Canary Lambda (%s) could not be read: %w", lambdaArn, err) +// } +// +// return fmt.Errorf("sythetics Canary Lambda (%s) still exists", lambdaArn) +// } +//} func testAccAWSSyntheticsCanaryConfigBase(rName string) string { return fmt.Sprintf(` From 1291728fc34c522483eb620df71cec4af7851b50 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Fri, 14 Aug 2020 00:24:01 +0300 Subject: [PATCH 25/67] use v2 --- aws/internal/service/synthetics/waiter/status.go | 2 +- aws/internal/service/synthetics/waiter/waiter.go | 2 +- aws/resource_aws_synthetics_canary.go | 4 ++-- aws/resource_aws_synthetics_canary_test.go | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/aws/internal/service/synthetics/waiter/status.go b/aws/internal/service/synthetics/waiter/status.go index b3404ba4175d..3f54d396853b 100644 --- a/aws/internal/service/synthetics/waiter/status.go +++ b/aws/internal/service/synthetics/waiter/status.go @@ -5,7 +5,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/synthetics" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) func CanaryStatus(conn *synthetics.Synthetics, name string) resource.StateRefreshFunc { diff --git a/aws/internal/service/synthetics/waiter/waiter.go b/aws/internal/service/synthetics/waiter/waiter.go index b09cb0b5720e..09598e543b14 100644 --- a/aws/internal/service/synthetics/waiter/waiter.go +++ b/aws/internal/service/synthetics/waiter/waiter.go @@ -4,7 +4,7 @@ import ( "time" "github.com/aws/aws-sdk-go/service/synthetics" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) const ( diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 7715ddbbd5b4..dda82e8b8315 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -9,8 +9,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/synthetics" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "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/service/synthetics/waiter" ) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 12c2884af119..58a093642102 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -2,14 +2,14 @@ package aws import ( "fmt" - "github.com/aws/aws-sdk-go/service/synthetics" "regexp" "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/aws/aws-sdk-go/service/synthetics" + "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" ) func TestAccAWSSyntheticsCanary_basic(t *testing.T) { From e4f111acd113f0e8cf54659c97064562e00e22b9 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Fri, 14 Aug 2020 09:47:58 +0300 Subject: [PATCH 26/67] tf12 syntax test --- aws/resource_aws_synthetics_canary_test.go | 103 ++++++--------------- 1 file changed, 26 insertions(+), 77 deletions(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 58a093642102..e15284b155e1 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -267,47 +267,6 @@ func testAccCheckAwsSyntheticsCanaryExists(n string) resource.TestCheckFunc { } } -//func testAccCheckAwsSyntheticsCanaryDeleteLambda(n string) resource.TestCheckFunc { -// return func(s *terraform.State) error { -// rs, ok := s.RootModule().Resources[n] -// if !ok { -// return fmt.Errorf("synthetics Canary not found: %s", n) -// } -// -// if rs.Primary.ID == "" { -// return fmt.Errorf("synthetics Canary name not set") -// } -// -// conn := testAccProvider.Meta().(*AWSClient).lambdaconn -// lambdaArn := fmt.Sprintf("%s:%s", rs.Primary.Attributes["engine_arn"], "3") -// -// deleteInput := &lambda.DeleteFunctionInput{ -// FunctionName: aws.String(lambdaArn), -// } -// -// log.Printf("delete sythetics Canary Lambda function request: %#v", deleteInput) -// -// _, err := conn.DeleteFunction(deleteInput) -// if err != nil { -// return fmt.Errorf("sythetics Canary Lambda (%s) could not be deleted: %w", lambdaArn, err) -// } -// -// getInput := &lambda.GetFunctionInput{ -// FunctionName: aws.String(lambdaArn), -// } -// -// _, err = conn.GetFunction(getInput) -// if err != nil { -// if isAWSErr(err, lambda.ErrCodeResourceNotFoundException, "") { -// return nil -// } -// return fmt.Errorf("sythetics Canary Lambda (%s) could not be read: %w", lambdaArn, err) -// } -// -// return fmt.Errorf("sythetics Canary Lambda (%s) still exists", lambdaArn) -// } -//} - func testAccAWSSyntheticsCanaryConfigBase(rName string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "test" { @@ -346,7 +305,7 @@ data "aws_partition" "current" {} resource "aws_iam_role_policy" "test" { name = %[1]q - role = "${aws_iam_role.test.id}" + role = aws_iam_role.test.id policy = < Date: Fri, 14 Aug 2020 09:50:00 +0300 Subject: [PATCH 27/67] typo --- website/docs/r/synthetics_canary.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index 500bc6f0d274..c07e7f292ac8 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -8,7 +8,7 @@ description: |- # Resource: aws_synthetics_canary -Provides an SWF Domain resource. +Provides a Synthetics Canary resource. ## Example Usage From d085db9f9c51451ad0c30a7708265b46fb9a3d10 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Thu, 27 Aug 2020 18:37:26 +0300 Subject: [PATCH 28/67] nit for arn var name --- aws/resource_aws_synthetics_canary.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index dda82e8b8315..8b2cdc873214 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -258,7 +258,7 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e d.Set("handler", canary.Code.Handler) d.Set("source_location_arn", canary.Code.SourceLocationArn) - arn := arn.ARN{ + canaryArn := arn.ARN{ Partition: meta.(*AWSClient).partition, Service: "synthetics", Region: meta.(*AWSClient).region, @@ -266,7 +266,7 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e Resource: fmt.Sprintf("canary:%s", aws.StringValue(canary.Name)), }.String() - d.Set("arn", arn) + d.Set("arn", canaryArn) if err := d.Set("vpc_config", flattenAwsSyntheticsCanaryVpcConfig(canary.VpcConfig)); err != nil { return fmt.Errorf("error setting vpc config: %w", err) From 364da7746333b71d557da0afedc657dc4394179d Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Thu, 27 Aug 2020 18:53:08 +0300 Subject: [PATCH 29/67] add checks for lambda, lambda layer, role and s3 location --- aws/resource_aws_synthetics_canary_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index e15284b155e1..06efd6c852b9 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -39,7 +39,10 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "vpc_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "schedule.0.duration_in_seconds", "0"), resource.TestCheckResourceAttr(resourceName, "schedule.0.expression", "rate(0 hour)"), - //todo: add lambda arns, role arn, s3 validations + testAccMatchResourceAttrRegionalARN(resourceName, "engine_arn", "lambda", regexp.MustCompile(fmt.Sprintf(`function:cwsyn-%s.+`, rName))), + testAccMatchResourceAttrRegionalARN(resourceName, "source_location_arn", "lambda", regexp.MustCompile(fmt.Sprintf(`layer:cwsyn-%s.+`, rName))), + resource.TestCheckResourceAttrPair(resourceName, "execution_role_arn", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "artifact_s3_location", fmt.Sprintf("%s/", rName)), ), }, { From 293642a82a7679db3328d3a6b88c4e17f722f234 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Thu, 27 Aug 2020 19:34:12 +0300 Subject: [PATCH 30/67] simplify s3 code artifact + test --- aws/resource_aws_synthetics_canary.go | 26 +++----- aws/resource_aws_synthetics_canary_test.go | 78 +++++++++++++++++++++- 2 files changed, 86 insertions(+), 18 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 8b2cdc873214..153356b5e7a5 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -1,7 +1,6 @@ package aws import ( - "errors" "fmt" "log" "strings" @@ -50,11 +49,13 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Type: schema.TypeString, Optional: true, ConflictsWith: []string{"zip_file"}, + RequiredWith: []string{"s3_key"}, }, "s3_key": { Type: schema.TypeString, Optional: true, ConflictsWith: []string{"zip_file"}, + RequiredWith: []string{"s3_bucket"}, }, "s3_version": { Type: schema.TypeString, @@ -391,29 +392,20 @@ func expandAwsSyntheticsCanaryCode(d *schema.ResourceData) (*synthetics.CanaryCo Handler: aws.String(d.Get("handler").(string)), } - zipFile, hasZipFile := d.GetOk("zip_file") - s3Bucket, bucketOk := d.GetOk("s3_bucket") - s3Key, keyOk := d.GetOk("s3_key") - s3Version, s3VersionOk := d.GetOk("s3_version") - - if hasZipFile { + if v, ok := d.GetOk("zip_file"); ok { awsMutexKV.Lock(awsMutexCanary) defer awsMutexKV.Unlock(awsMutexCanary) - file, err := loadFileContent(zipFile.(string)) + file, err := loadFileContent(v.(string)) if err != nil { - return nil, fmt.Errorf("unable to load %q: %w", zipFile.(string), err) + return nil, fmt.Errorf("unable to load %q: %w", v.(string), err) } codeConfig.ZipFile = file } else { - if !bucketOk || !keyOk { - return nil, errors.New("s3_bucket and s3_key must all be set while using S3 code source") - } - - codeConfig.S3Bucket = aws.String(s3Bucket.(string)) - codeConfig.S3Key = aws.String(s3Key.(string)) + codeConfig.S3Bucket = aws.String(d.Get("s3_bucket").(string)) + codeConfig.S3Key = aws.String(d.Get("s3_key").(string)) - if s3VersionOk { - codeConfig.S3Version = aws.String(s3Version.(string)) + if v, ok := d.GetOk("s3_version"); ok { + codeConfig.S3Version = aws.String(v.(string)) } } diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 06efd6c852b9..2ca99ec1e94a 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -55,6 +55,49 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { }) } +func TestAccAWSSyntheticsCanary_s3(t *testing.T) { + 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: testAccAWSSyntheticsCanaryBasicS3CodeConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary:.+`)), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-1.0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), + resource.TestCheckResourceAttr(resourceName, "failure_retention_period", "31"), + resource.TestCheckResourceAttr(resourceName, "success_retention_period", "31"), + resource.TestCheckResourceAttr(resourceName, "handler", "exports.handler"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "schedule.0.duration_in_seconds", "0"), + resource.TestCheckResourceAttr(resourceName, "schedule.0.expression", "rate(0 hour)"), + testAccMatchResourceAttrRegionalARN(resourceName, "engine_arn", "lambda", regexp.MustCompile(fmt.Sprintf(`function:cwsyn-%s.+`, rName))), + testAccMatchResourceAttrRegionalARN(resourceName, "source_location_arn", "lambda", regexp.MustCompile(fmt.Sprintf(`layer:cwsyn-%s.+`, rName))), + resource.TestCheckResourceAttrPair(resourceName, "execution_role_arn", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "artifact_s3_location", fmt.Sprintf("%s/", rName)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file"}, + }, + }, + }) +} + func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -273,7 +316,13 @@ func testAccCheckAwsSyntheticsCanaryExists(n string) resource.TestCheckFunc { func testAccAWSSyntheticsCanaryConfigBase(rName string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "test" { - bucket = %[1]q + bucket = %[1]q + acl = "private" + force_destroy = true + + versioning { + enabled = true + } tags = { Name = %[1]q @@ -415,6 +464,33 @@ resource "aws_synthetics_canary" "test" { `, rName) } +func testAccAWSSyntheticsCanaryBasicS3CodeConfig(rName string) string { + return 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" + s3_bucket = aws_s3_bucket_object.test.bucket + s3_key = aws_s3_bucket_object.test.key + s3_version = aws_s3_bucket_object.test.version_id + + + schedule { + expression = "rate(0 minute)" + } +} + +resource "aws_s3_bucket_object" "test" { + bucket = aws_s3_bucket.test.bucket + key = %[1]q + source = "test-fixtures/lambdatest.zip" + etag = filemd5("test-fixtures/lambdatest.zip") +} + +`, rName) +} + func testAccAWSSyntheticsCanaryVPCConfigBase(rName string) string { return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(` resource "aws_vpc" "test" { From 09d31b5580cf038d7d96b18a1d183e27bd73034f Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Thu, 27 Aug 2020 22:30:43 +0300 Subject: [PATCH 31/67] gnarly implicit lambda handling --- aws/resource_aws_synthetics_canary_test.go | 62 ++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 2ca99ec1e94a..f2b24cdea0a2 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -3,9 +3,12 @@ package aws import ( "fmt" "regexp" + "strings" "testing" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/lambda" "github.com/aws/aws-sdk-go/service/synthetics" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -161,7 +164,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { testAccCheckAwsSyntheticsCanaryExists(resourceName), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), - resource.TestMatchResourceAttr(resourceName, "vpc_config.0.vpc_id", regexp.MustCompile(`vpc-`)), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", "id"), ), }, { @@ -176,6 +179,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { testAccCheckAwsSyntheticsCanaryExists(resourceName), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "2"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", "id"), ), }, { @@ -184,11 +188,10 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { testAccCheckAwsSyntheticsCanaryExists(resourceName), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", "id"), + testAccCheckAwsSyntheticsCanaryDeleteImplicitResources(resourceName), ), }, - { - Config: testAccAWSSyntheticsCanaryBasicConfig(rName), - }, }, }) } @@ -313,6 +316,57 @@ func testAccCheckAwsSyntheticsCanaryExists(n string) resource.TestCheckFunc { } } +func testAccCheckAwsSyntheticsCanaryDeleteImplicitResources(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("synthetics Canary not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("synthetics Canary name not set") + } + + lambdaConn := testAccProvider.Meta().(*AWSClient).lambdaconn + + layerArn := rs.Primary.Attributes["source_location_arn"] + layerArnObj, err := arn.Parse(layerArn) + if err != nil { + return fmt.Errorf("synthetics Canary Lambda Layer %s incorrect arn format: %w", layerArn, err) + } + + layerName := strings.Split(layerArnObj.Resource, ":") + + deleteLayerVersionInput := &lambda.DeleteLayerVersionInput{ + LayerName: aws.String(layerName[1]), + VersionNumber: aws.Int64(1), + } + + _, err = lambdaConn.DeleteLayerVersion(deleteLayerVersionInput) + if err != nil { + return fmt.Errorf("synthetics Canary Lambda Layer %s could not be deleted: %w", layerArn, err) + } + + lambdaArn := rs.Primary.Attributes["engine_arn"] + lambdaArnObj, err := arn.Parse(layerArn) + if err != nil { + return fmt.Errorf("synthetics Canary Lambda %s incorrect arn format: %w", lambdaArn, err) + } + lambdaArnParts := strings.Split(lambdaArnObj.Resource, ":") + + deleteLambdaInput := &lambda.DeleteFunctionInput{ + FunctionName: aws.String(lambdaArnParts[1]), + } + + _, err = lambdaConn.DeleteFunction(deleteLambdaInput) + if err != nil { + return fmt.Errorf("synthetics Canary Lambda %s could not be deleted: %w", lambdaArn, err) + } + + return nil + } +} + func testAccAWSSyntheticsCanaryConfigBase(rName string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "test" { From 38bf316a0b73320b7470fad323258fc9066ed6ee Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 19 Sep 2020 22:45:40 +0300 Subject: [PATCH 32/67] lint --- website/docs/r/synthetics_canary.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index c07e7f292ac8..a41835374f61 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -34,7 +34,7 @@ The following arguments are supported: * `artifact_s3_location` - (Required) The location in Amazon S3 where Synthetics stores artifacts from the test runs of this canary. * `schedule` - (Required) Information about how often the canary is to run and when these test runs are to stop. See [Schedule](#schedule) below. * `handler` - (Required) The domain description. -* `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_CreateCanary.html#API_CreateCanary_RequestSyntax) for permissions needs for IAM Role. +* `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_CreateCanary.html#API_CreateCanary_RequestSyntax) for permissions needs for IAM Role. * `s3_bucket` - (Optional) If your canary script is located in S3, specify the full bucket name here. The bucket must already exist. Specify the full bucket name, including s3:// as the start of the bucket name. **Conflicts with `zip_file`** * `s3_key` - (Optional) The S3 key of your script. **Conflicts with `zip_file`** * `s3_version` - (Optional) The S3 version ID of your script. **Conflicts with `zip_file`** @@ -42,7 +42,7 @@ The following arguments are supported: * `failure_retention_period` - (Optional) The number of days to retain data about failed runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. * `success_retention_period` - (Optional) The number of days to retain data about successful runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. * `run_config` - (Optional) Configuration for individual canary runs. See [Run Config](#run-config) below. -* `vpc_config` - (Optional) If this canary is to test an endpoint in a VPC, this structure contains information about the subnet and security groups of the VPC endpoint. For more information, see [Running a Canary in a VPC](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_VPC.html). See [VPC Config](#vpc-config) below. +* `vpc_config` - (Optional) If this canary is to test an endpoint in a VPC, this structure contains information about the subnet and security groups of the VPC endpoint. For more information, see [Running a Canary in a VPC](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_VPC.html). See [VPC Config](#vpc-config) below. * `tags` - (Optional) Key-value map of resource tags ### Schedule From 67680f2b7690baff5e083e2a4b8556d61be0622b Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 19 Sep 2020 22:58:50 +0300 Subject: [PATCH 33/67] lint --- website/docs/index.html.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 34971ccb69c4..b3b49c13a097 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -330,12 +330,12 @@ for more information about connecting to alternate AWS endpoints or AWS compatib - [`aws_synthetics_canary` resource](/docs/providers/aws/r/synthetics_canary.html) - [`aws_vpc_dhcp_options` data source](/docs/providers/aws/d/vpc_dhcp_options.html) - [`aws_vpc_dhcp_options` resource](/docs/providers/aws/r/vpc_dhcp_options.html) + - [`aws_vpc_endpoint_service` data source](/docs/providers/aws/d/vpc_endpoint_service.html) + - [`aws_vpc_endpoint_service` resource](/docs/providers/aws/r/vpc_endpoint_service.html) - [`aws_vpc_endpoint` data source](/docs/providers/aws/d/vpc_endpoint.html) + - [`aws_vpc_endpoint` resource](/docs/providers/aws/r/vpc_endpoint.html) - [`aws_vpc` data source](/docs/providers/aws/d/vpc.html) - [`aws_vpc` resource](/docs/providers/aws/r/vpc.html) - - [`aws_vpc_endpoint` resource](/docs/providers/aws/r/vpc_endpoint.html) - - [`aws_vpc_endpoint_service` data source](/docs/providers/aws/d/vpc_endpoint_service.html) - - [`aws_vpc_endpoint_service` resource](/docs/providers/aws/r/vpc_endpoint_service.html) - [`aws_vpn_connection` resource](/docs/providers/aws/r/vpn_connection.html) - [`aws_vpn_gateway` data source](/docs/providers/aws/d/vpn_gateway.html) - [`aws_vpn_gateway` resource](/docs/providers/aws/r/vpn_gateway.html) From 1e3ae2d1c1606b2d2bec97f1d397e1e047a6e912 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 19 Sep 2020 23:00:28 +0300 Subject: [PATCH 34/67] start canary docs --- website/docs/r/synthetics_canary.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index a41835374f61..07e666942f42 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -35,6 +35,7 @@ The following arguments are supported: * `schedule` - (Required) Information about how often the canary is to run and when these test runs are to stop. See [Schedule](#schedule) below. * `handler` - (Required) The domain description. * `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_CreateCanary.html#API_CreateCanary_RequestSyntax) for permissions needs for IAM Role. +* `start_canary` - (Optional) Whether to run or stop the canary. * `s3_bucket` - (Optional) If your canary script is located in S3, specify the full bucket name here. The bucket must already exist. Specify the full bucket name, including s3:// as the start of the bucket name. **Conflicts with `zip_file`** * `s3_key` - (Optional) The S3 key of your script. **Conflicts with `zip_file`** * `s3_version` - (Optional) The S3 version ID of your script. **Conflicts with `zip_file`** From e26a6743d04febffcefd74d479cba206e1beaabf Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sat, 19 Sep 2020 23:07:15 +0300 Subject: [PATCH 35/67] start/stop canary --- .../service/synthetics/waiter/waiter.go | 42 +++++++++- aws/resource_aws_synthetics_canary.go | 77 +++++++++++++++++++ aws/resource_aws_synthetics_canary_test.go | 66 ++++++++++++++-- 3 files changed, 179 insertions(+), 6 deletions(-) diff --git a/aws/internal/service/synthetics/waiter/waiter.go b/aws/internal/service/synthetics/waiter/waiter.go index 09598e543b14..39501896d6f0 100644 --- a/aws/internal/service/synthetics/waiter/waiter.go +++ b/aws/internal/service/synthetics/waiter/waiter.go @@ -10,6 +10,8 @@ import ( const ( // Maximum amount of time to wait for a Canary to return Ready CanaryCreatedTimeout = 5 * time.Minute + CanaryRunningTimeout = 5 * time.Minute + CanaryStoppedTimeout = 5 * time.Minute CanaryDeletedTimeout = 5 * time.Minute ) @@ -31,10 +33,48 @@ func CanaryReady(conn *synthetics.Synthetics, name string) (*synthetics.GetCanar return nil, err } +// CanaryReady waits for a Canary to return Stopped +func CanaryStopped(conn *synthetics.Synthetics, name string) (*synthetics.GetCanaryOutput, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{synthetics.CanaryStateStopping, synthetics.CanaryStateUpdating, + synthetics.CanaryStateRunning, synthetics.CanaryStateReady, synthetics.CanaryStateStarting}, + Target: []string{synthetics.CanaryStateStopped}, + Refresh: CanaryStatus(conn, name), + Timeout: CanaryStoppedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*synthetics.GetCanaryOutput); ok { + return v, err + } + + return nil, err +} + +// CanaryReady waits for a Canary to return Running +func CanaryRunning(conn *synthetics.Synthetics, name string) (*synthetics.GetCanaryOutput, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{synthetics.CanaryStateStarting, synthetics.CanaryStateUpdating, + synthetics.CanaryStateStarting, synthetics.CanaryStateReady}, + Target: []string{synthetics.CanaryStateRunning}, + Refresh: CanaryStatus(conn, name), + Timeout: CanaryRunningTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*synthetics.GetCanaryOutput); ok { + return v, err + } + + return nil, err +} + // CanaryReady waits for a Canary to return Ready func CanaryDeleted(conn *synthetics.Synthetics, name string) (*synthetics.GetCanaryOutput, error) { stateConf := &resource.StateChangeConf{ - Pending: []string{synthetics.CanaryStateDeleting}, + Pending: []string{synthetics.CanaryStateDeleting, synthetics.CanaryStateStopped}, Target: []string{}, Refresh: CanaryStatus(conn, name), Timeout: CanaryDeletedTimeout, diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 153356b5e7a5..83fbaebf9187 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -72,6 +72,11 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Required: true, ValidateFunc: validateArn, }, + "start_canary": { + Type: schema.TypeBool, + Default: false, + Optional: true, + }, "failure_retention_period": { Type: schema.TypeInt, Optional: true, @@ -168,6 +173,10 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "status": { + Type: schema.TypeString, + Computed: true, + }, "tags": tagsSchema(), }, } @@ -227,6 +236,12 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error waiting for Synthetics Canary (%s) creation: %w", d.Id(), err) } + if v := d.Get("start_canary"); v.(bool) { + if err := syntheticsStartCanary(d.Id(), conn); err != nil { + return err + } + } + return resourceAwsSyntheticsCanaryRead(d, meta) } @@ -251,6 +266,7 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e canary := resp.Canary d.Set("name", canary.Name) d.Set("engine_arn", canary.EngineArn) + d.Set("status", canary.Status.State) d.Set("execution_role_arn", canary.ExecutionRoleArn) d.Set("runtime_version", canary.RuntimeVersion) d.Set("artifact_s3_location", canary.ArtifactS3Location) @@ -340,6 +356,12 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) } if updateFlag { + if status := d.Get("status").(string); status == synthetics.CanaryStateRunning { + if err := syntheticsStopCanary(d.Id(), conn); err != nil { + return err + } + } + _, err := conn.UpdateCanary(input) if err != nil { return fmt.Errorf("error updating Synthetics Canary: %w", err) @@ -350,6 +372,21 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) } } + 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 + } + } + } + if d.HasChange("tags") { o, n := d.GetChange("tags") @@ -364,6 +401,12 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) func resourceAwsSyntheticsCanaryDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).syntheticsconn + if status := d.Get("status").(string); status == synthetics.CanaryStateRunning { + if err := syntheticsStopCanary(d.Id(), conn); err != nil { + return err + } + } + input := &synthetics.DeleteCanaryInput{ Name: aws.String(d.Id()), } @@ -502,3 +545,37 @@ func expandAwsSyntheticsCanaryVpcConfig(l []interface{}) *synthetics.VpcConfigIn return codeConfig } + +func syntheticsStartCanary(name string, conn *synthetics.Synthetics) error { + startInput := &synthetics.StartCanaryInput{ + Name: aws.String(name), + } + + _, err := conn.StartCanary(startInput) + if err != nil { + return fmt.Errorf("error starting Synthetics Canary: %w", err) + } + + if _, err := waiter.CanaryRunning(conn, name); err != nil { + return fmt.Errorf("error waiting for Synthetics Canary (%s) to be running: %w", name, err) + } + + return nil +} + +func syntheticsStopCanary(name string, conn *synthetics.Synthetics) error { + stopInput := &synthetics.StopCanaryInput{ + Name: aws.String(name), + } + + _, err := conn.StopCanary(stopInput) + if err != nil { + return fmt.Errorf("error stopping Synthetics Canary: %w", err) + } + + if _, err := waiter.CanaryStopped(conn, name); err != nil { + return fmt.Errorf("error waiting for Synthetics Canary (%s) to be stopped: %w", name, err) + } + + return nil +} diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index f2b24cdea0a2..23a4542dc740 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -52,7 +52,46 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"zip_file"}, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, + }, + }, + }) +} + +func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { + 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), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, + }, + { + Config: testAccAWSSyntheticsCanaryStartCanaryConfig(rName, false), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + ), + }, + { + Config: testAccAWSSyntheticsCanaryStartCanaryConfig(rName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName), + ), }, }, }) @@ -95,7 +134,7 @@ func TestAccAWSSyntheticsCanary_s3(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"zip_file"}, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, }, }, }) @@ -125,7 +164,7 @@ func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"zip_file"}, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, }, { Config: testAccAWSSyntheticsCanaryRunConfigConfig2(rName), @@ -171,7 +210,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"zip_file"}, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, }, { Config: testAccAWSSyntheticsCanaryVPCConfig2(rName), @@ -219,7 +258,7 @@ func TestAccAWSSyntheticsCanary_tags(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"zip_file"}, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, }, { Config: testAccAWSSyntheticsCanaryConfigTags2(rName, "key1", "value1updated", "key2", "value2"), @@ -518,6 +557,23 @@ resource "aws_synthetics_canary" "test" { `, rName) } +func testAccAWSSyntheticsCanaryStartCanaryConfig(rName string, state bool) string { + return 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.zip" + start_canary = %[2]t + + schedule { + expression = "rate(0 minute)" + } +} +`, rName, state) +} + func testAccAWSSyntheticsCanaryBasicS3CodeConfig(rName string) string { return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { From 96c546f103d5a8b834124ed3057dd83022f0a846 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 21 Sep 2020 20:05:50 +0300 Subject: [PATCH 36/67] added `timeline` check for update with local code --- aws/resource_aws_synthetics_canary.go | 53 ++++++++++++ aws/resource_aws_synthetics_canary_test.go | 99 ++++++++++++++++++---- 2 files changed, 135 insertions(+), 17 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 83fbaebf9187..569a8515f51c 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "strings" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" @@ -157,6 +158,30 @@ func resourceAwsSyntheticsCanary() *schema.Resource { }, }, }, + "timeline": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "created": { + Type: schema.TypeString, + Computed: true, + }, + "last_modified": { + Type: schema.TypeString, + Computed: true, + }, + "last_started": { + Type: schema.TypeString, + Computed: true, + }, + "last_stopped": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, "engine_arn": { Type: schema.TypeString, Computed: true, @@ -297,6 +322,10 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("error setting schedule: %w", err) } + if err := d.Set("timeline", flattenAwsSyntheticsCanaryTimeline(canary.Timeline)); err != nil { + return fmt.Errorf("error setting schedule: %w", err) + } + if err := d.Set("tags", keyvaluetags.SyntheticsKeyValueTags(canary.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return fmt.Errorf("error setting tags: %w", err) } @@ -546,6 +575,30 @@ func expandAwsSyntheticsCanaryVpcConfig(l []interface{}) *synthetics.VpcConfigIn return codeConfig } +func flattenAwsSyntheticsCanaryTimeline(timeline *synthetics.CanaryTimeline) []interface{} { + if timeline == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "created": aws.TimeValue(timeline.Created).Format(time.RFC3339), + } + + if timeline.LastModified != nil { + m["last_modified"] = aws.TimeValue(timeline.LastModified).Format(time.RFC3339) + } + + if timeline.LastStarted != nil { + m["last_started"] = aws.TimeValue(timeline.LastStarted).Format(time.RFC3339) + } + + if timeline.LastStopped != nil { + m["last_stopped"] = aws.TimeValue(timeline.LastStopped).Format(time.RFC3339) + } + + return []interface{}{m} +} + func syntheticsStartCanary(name string, conn *synthetics.Synthetics) error { startInput := &synthetics.StartCanaryInput{ Name: aws.String(name), diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 23a4542dc740..7920c1a8dd31 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -16,6 +16,7 @@ import ( ) func TestAccAWSSyntheticsCanary_basic(t *testing.T) { + var conf1, conf2 synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -29,7 +30,7 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { { Config: testAccAWSSyntheticsCanaryBasicConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf1), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-1.0"), @@ -46,6 +47,8 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { testAccMatchResourceAttrRegionalARN(resourceName, "source_location_arn", "lambda", regexp.MustCompile(fmt.Sprintf(`layer:cwsyn-%s.+`, rName))), resource.TestCheckResourceAttrPair(resourceName, "execution_role_arn", "aws_iam_role.test", "arn"), resource.TestCheckResourceAttr(resourceName, "artifact_s3_location", fmt.Sprintf("%s/", rName)), + resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.created"), ), }, { @@ -54,11 +57,38 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, }, + { + Config: testAccAWSSyntheticsCanaryZipUpdatedConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf2), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary:.+`)), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-1.0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), + resource.TestCheckResourceAttr(resourceName, "failure_retention_period", "31"), + resource.TestCheckResourceAttr(resourceName, "success_retention_period", "31"), + resource.TestCheckResourceAttr(resourceName, "handler", "exports.handler"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "schedule.0.duration_in_seconds", "0"), + resource.TestCheckResourceAttr(resourceName, "schedule.0.expression", "rate(0 hour)"), + testAccMatchResourceAttrRegionalARN(resourceName, "engine_arn", "lambda", regexp.MustCompile(fmt.Sprintf(`function:cwsyn-%s.+`, rName))), + testAccMatchResourceAttrRegionalARN(resourceName, "source_location_arn", "lambda", regexp.MustCompile(fmt.Sprintf(`layer:cwsyn-%s.+`, rName))), + resource.TestCheckResourceAttrPair(resourceName, "execution_role_arn", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "artifact_s3_location", fmt.Sprintf("%s/", rName)), + resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.created"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_modified"), + testAccCheckAwsSyntheticsCanaryIsUpdated(&conf1, &conf2), + ), + }, }, }) } func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { + var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -72,7 +102,7 @@ func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { { Config: testAccAWSSyntheticsCanaryStartCanaryConfig(rName, true), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), ), }, { @@ -84,13 +114,13 @@ func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { { Config: testAccAWSSyntheticsCanaryStartCanaryConfig(rName, false), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), ), }, { Config: testAccAWSSyntheticsCanaryStartCanaryConfig(rName, true), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), ), }, }, @@ -98,6 +128,7 @@ func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { } func TestAccAWSSyntheticsCanary_s3(t *testing.T) { + var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -111,7 +142,7 @@ func TestAccAWSSyntheticsCanary_s3(t *testing.T) { { Config: testAccAWSSyntheticsCanaryBasicS3CodeConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-1.0"), @@ -141,6 +172,7 @@ func TestAccAWSSyntheticsCanary_s3(t *testing.T) { } func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { + var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -154,7 +186,7 @@ func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { { Config: testAccAWSSyntheticsCanaryRunConfigConfig1(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "60"), @@ -169,7 +201,7 @@ func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { { Config: testAccAWSSyntheticsCanaryRunConfigConfig2(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "960"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "120"), ), @@ -177,7 +209,7 @@ func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { { Config: testAccAWSSyntheticsCanaryRunConfigConfig1(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "960"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "60"), ), @@ -187,6 +219,7 @@ func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { } func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { + var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -200,7 +233,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { { Config: testAccAWSSyntheticsCanaryVPCConfig1(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", "id"), @@ -215,7 +248,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { { Config: testAccAWSSyntheticsCanaryVPCConfig2(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "2"), resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", "id"), @@ -224,7 +257,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { { Config: testAccAWSSyntheticsCanaryVPCConfig3(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", "id"), @@ -236,6 +269,7 @@ func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { } func TestAccAWSSyntheticsCanary_tags(t *testing.T) { + var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -249,7 +283,7 @@ func TestAccAWSSyntheticsCanary_tags(t *testing.T) { { Config: testAccAWSSyntheticsCanaryConfigTags1(rName, "key1", "value1"), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), @@ -263,7 +297,7 @@ func TestAccAWSSyntheticsCanary_tags(t *testing.T) { { Config: testAccAWSSyntheticsCanaryConfigTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -272,7 +306,7 @@ func TestAccAWSSyntheticsCanary_tags(t *testing.T) { { Config: testAccAWSSyntheticsCanaryConfigTags1(rName, "key2", "value2"), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), @@ -282,6 +316,7 @@ func TestAccAWSSyntheticsCanary_tags(t *testing.T) { } func TestAccAWSSyntheticsCanary_disappears(t *testing.T) { + var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -295,7 +330,7 @@ func TestAccAWSSyntheticsCanary_disappears(t *testing.T) { { Config: testAccAWSSyntheticsCanaryBasicConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), testAccCheckResourceDisappears(testAccProvider, resourceAwsSyntheticsCanary(), resourceName), ), ExpectNonEmptyPlan: true, @@ -329,7 +364,7 @@ func testAccCheckAwsSyntheticsCanaryDestroy(s *terraform.State) error { return nil } -func testAccCheckAwsSyntheticsCanaryExists(n string) resource.TestCheckFunc { +func testAccCheckAwsSyntheticsCanaryExists(n string, canary *synthetics.Canary) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -347,10 +382,13 @@ func testAccCheckAwsSyntheticsCanaryExists(n string) resource.TestCheckFunc { Name: aws.String(name), } - _, err := conn.GetCanary(input) + out, err := conn.GetCanary(input) if err != nil { return fmt.Errorf("syntherics Canary %s not found in AWS", name) } + + *canary = *out.Canary + return nil } } @@ -406,6 +444,17 @@ func testAccCheckAwsSyntheticsCanaryDeleteImplicitResources(n string) resource.T } } +func testAccCheckAwsSyntheticsCanaryIsUpdated(first, second *synthetics.Canary) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !second.Timeline.LastModified.After(*first.Timeline.LastModified) { + return fmt.Errorf("synthetics Canary not updated") + + } + + return nil + } +} + func testAccAWSSyntheticsCanaryConfigBase(rName string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "test" { @@ -557,6 +606,22 @@ resource "aws_synthetics_canary" "test" { `, rName) } +func testAccAWSSyntheticsCanaryZipUpdatedConfig(rName string) string { + return 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" + + schedule { + expression = "rate(0 minute)" + } +} +`, rName) +} + func testAccAWSSyntheticsCanaryStartCanaryConfig(rName string, state bool) string { return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { From f2df36299c2695d280cb32e5031827da65629f87 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 21 Sep 2020 20:09:27 +0300 Subject: [PATCH 37/67] added timeline docs --- website/docs/r/synthetics_canary.html.markdown | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index 07e666942f42..9d3acaea75d1 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -31,7 +31,7 @@ resource "aws_synthetics_canary" "some" { The following arguments are supported: * `name` - (Required) The name for this canary. -* `artifact_s3_location` - (Required) The location in Amazon S3 where Synthetics stores artifacts from the test runs of this canary. +* `artifact_s3_location` - (Required) The location on Amazon S3 where Synthetics stores artifacts from the test runs of this canary. * `schedule` - (Required) Information about how often the canary is to run and when these test runs are to stop. See [Schedule](#schedule) below. * `handler` - (Required) The domain description. * `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_CreateCanary.html#API_CreateCanary_RequestSyntax) for permissions needs for IAM Role. @@ -70,11 +70,19 @@ In addition to all arguments above, the following attributes are exported: * `source_location_arn` - The ARN of the Lambda layer where Synthetics stores the canary script code. * `engine_arn` - The ARN of the Lambda function that is used as your canary's engine. * `runtime_version` - Specifies the runtime version to use for the canary. +* `timeline` - A structure that contains information about when the canary was created, modified, and most recently run. see [Timeline](#timeline). ### VPC Config * `vpc_id` - The ID of the VPC where this canary is to run. +### Timeline + +* `created` - The date and time the canary was created. +* `last_modified` - The date and time the canary was most recently modified. +* `last_started` - The date and time that the canary's most recent run started. +* `last_stopped` - The date and time that the canary's most recent run ended. + ## Import Synthetics Canaries can be imported using the `name`, e.g. From f4f2af7bdc7bf8727385bc1689dd5dff8c653099 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 21 Sep 2020 22:03:10 +0300 Subject: [PATCH 38/67] start test more, check for start --- aws/resource_aws_synthetics_canary.go | 13 ++++++----- aws/resource_aws_synthetics_canary_test.go | 27 ++++++++++++++++++---- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 569a8515f51c..b173ff118863 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -385,7 +385,7 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) } if updateFlag { - if status := d.Get("status").(string); status == synthetics.CanaryStateRunning { + if status := d.Get("status"); status.(string) == synthetics.CanaryStateRunning { if err := syntheticsStopCanary(d.Id(), conn); err != nil { return err } @@ -401,15 +401,15 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) } } - status := d.Get("status").(string) + status := d.Get("status") if v := d.Get("start_canary"); v.(bool) { - if status != synthetics.CanaryStateRunning { + if status.(string) != synthetics.CanaryStateRunning { if err := syntheticsStartCanary(d.Id(), conn); err != nil { return err } } } else { - if status == synthetics.CanaryStateRunning { + if status.(string) == synthetics.CanaryStateRunning { if err := syntheticsStopCanary(d.Id(), conn); err != nil { return err } @@ -430,9 +430,9 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) func resourceAwsSyntheticsCanaryDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).syntheticsconn - if status := d.Get("status").(string); status == synthetics.CanaryStateRunning { + if status := d.Get("status"); status.(string) == synthetics.CanaryStateRunning { if err := syntheticsStopCanary(d.Id(), conn); err != nil { - return err + log.Print("could not stop canary before delete") } } @@ -604,6 +604,7 @@ func syntheticsStartCanary(name string, conn *synthetics.Synthetics) error { Name: aws.String(name), } + log.Print("starting Canary") _, err := conn.StartCanary(startInput) if err != nil { return fmt.Errorf("error starting Synthetics Canary: %w", err) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 7920c1a8dd31..e3d564cc4a5d 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -88,7 +88,7 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { } func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { - var conf synthetics.Canary + var conf1, conf2, conf3 synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) resourceName := "aws_synthetics_canary.test" @@ -102,7 +102,9 @@ func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { { Config: testAccAWSSyntheticsCanaryStartCanaryConfig(rName, true), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf1), + resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_started"), ), }, { @@ -114,13 +116,19 @@ func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { { Config: testAccAWSSyntheticsCanaryStartCanaryConfig(rName, false), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf2), + resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_started"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_stopped"), ), }, { Config: testAccAWSSyntheticsCanaryStartCanaryConfig(rName, true), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf3), + resource.TestCheckResourceAttr(resourceName, "timeline.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "timeline.0.last_started"), + testAccCheckAwsSyntheticsCanaryIsStartedAfter(&conf2, &conf3), ), }, }, @@ -455,6 +463,17 @@ func testAccCheckAwsSyntheticsCanaryIsUpdated(first, second *synthetics.Canary) } } +func testAccCheckAwsSyntheticsCanaryIsStartedAfter(first, second *synthetics.Canary) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !second.Timeline.LastStarted.After(*first.Timeline.LastStarted) { + return fmt.Errorf("synthetics Canary not updated") + + } + + return nil + } +} + func testAccAWSSyntheticsCanaryConfigBase(rName string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "test" { From 6248a2fcd17aad89cadb71f5b50024577f3c7899 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 21 Sep 2020 22:37:05 +0300 Subject: [PATCH 39/67] add sweeper + also deletes implict resources --- aws/resource_aws_synthetics_canary_test.go | 59 ++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index e3d564cc4a5d..f2164e453aee 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "log" "regexp" "strings" "testing" @@ -10,11 +11,69 @@ import ( "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/lambda" "github.com/aws/aws-sdk-go/service/synthetics" + "github.com/hashicorp/go-multierror" "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" ) +func init() { + resource.AddTestSweepers("aws_synthetics_canary", &resource.Sweeper{ + Name: "aws_synthetics_canary", + F: testSweepSyntheticsCanaries, + Dependencies: []string{ + "aws_lambda_function", + "aws_lambda_layer_version", + "aws_cloudwatch_log_group", + "aws_cloudwatch_metric_alarm", + }, + }) +} + +func testSweepSyntheticsCanaries(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).syntheticsconn + input := &synthetics.DescribeCanariesInput{} + var sweeperErrs *multierror.Error + + for { + output, err := conn.DescribeCanaries(input) + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping Synthetics Canary sweep for %s: %s", region, err) + return nil + } + if err != nil { + return fmt.Errorf("error retrieving Synthetics Canaries: %w", err) + } + + for _, canary := range output.Canaries { + name := aws.StringValue(canary.Name) + log.Printf("[INFO] Deleting Synthetics Canary: %s", name) + + r := resourceAwsSyntheticsCanary() + d := r.Data(nil) + d.SetId(name) + err := r.Delete(d, client) + + if err != nil { + log.Printf("[ERROR] %s", err) + sweeperErrs = multierror.Append(sweeperErrs, err) + continue + } + } + + if aws.StringValue(output.NextToken) == "" { + break + } + input.NextToken = output.NextToken + } + + return sweeperErrs.ErrorOrNil() +} + func TestAccAWSSyntheticsCanary_basic(t *testing.T) { var conf1, conf2 synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) From 6a03cad5955774f6de5a5279e3cd21d20e5b81f4 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Mon, 21 Sep 2020 22:38:15 +0300 Subject: [PATCH 40/67] remove non existent sweeper --- aws/resource_aws_synthetics_canary_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index f2164e453aee..03528da85f66 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -25,7 +25,6 @@ func init() { "aws_lambda_function", "aws_lambda_layer_version", "aws_cloudwatch_log_group", - "aws_cloudwatch_metric_alarm", }, }) } From b2e6cc6e8fcea843f7f9362b29b1f479e29d34bb Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 3 Oct 2020 16:59:40 +0300 Subject: [PATCH 41/67] allow changing runtime version --- aws/resource_aws_synthetics_canary.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index b173ff118863..88e513613288 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -34,6 +34,10 @@ func resourceAwsSyntheticsCanary() *schema.Resource { ForceNew: true, ValidateFunc: validation.StringLenBetween(1, 21), }, + "runtime_version": { + Type: schema.TypeString, + Required: true, + }, "artifact_s3_location": { Type: schema.TypeString, Required: true, @@ -190,10 +194,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "runtime_version": { - Type: schema.TypeString, - Computed: true, - }, "arn": { Type: schema.TypeString, Computed: true, @@ -214,7 +214,7 @@ func resourceAwsSyntheticsCanaryCreate(d *schema.ResourceData, meta interface{}) Name: aws.String(d.Get("name").(string)), ArtifactS3Location: aws.String(d.Get("artifact_s3_location").(string)), ExecutionRoleArn: aws.String(d.Get("execution_role_arn").(string)), - RuntimeVersion: aws.String("syn-1.0"), + RuntimeVersion: aws.String(d.Get("runtime_version").(string)), } code, err := expandAwsSyntheticsCanaryCode(d) @@ -347,6 +347,11 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) updateFlag = true } + if d.HasChange("runtime_version") { + input.RuntimeVersion = aws.String(d.Get("vpc_config").(string)) + updateFlag = true + } + if d.HasChanges("handler", "zip_file", "s3_bucket", "s3_key", "s3_version") { code, err := expandAwsSyntheticsCanaryCode(d) if err != nil { From 063120eae471510bce151874e6121cc4d4838dd6 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 3 Oct 2020 17:03:23 +0300 Subject: [PATCH 42/67] set runtime version in tests --- aws/resource_aws_synthetics_canary_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 03528da85f66..3d755550f352 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -634,6 +634,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -654,6 +655,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -675,6 +677,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -691,6 +694,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest_modified.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -708,6 +712,7 @@ resource "aws_synthetics_canary" "test" { handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" start_canary = %[2]t + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -726,6 +731,7 @@ resource "aws_synthetics_canary" "test" { s3_bucket = aws_s3_bucket_object.test.bucket s3_key = aws_s3_bucket_object.test.key s3_version = aws_s3_bucket_object.test.version_id + runtime_version = "syn-1.0" schedule { @@ -806,6 +812,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -831,6 +838,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -856,6 +864,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -879,6 +888,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -899,6 +909,7 @@ resource "aws_synthetics_canary" "test" { execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" From 470ce43872ffb375933b973510be321582166cbb Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 3 Oct 2020 17:06:35 +0300 Subject: [PATCH 43/67] runtime versiom docs --- website/docs/r/synthetics_canary.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index 9d3acaea75d1..22b60dbb172d 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -19,6 +19,7 @@ resource "aws_synthetics_canary" "some" { execution_role_arn = "some-role" handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" + runtime_version = "syn-1.0" schedule { expression = "rate(0 minute)" @@ -31,6 +32,7 @@ resource "aws_synthetics_canary" "some" { The following arguments are supported: * `name` - (Required) The name for this canary. +* `runtime_version` - (Required) Specifies the runtime version to use for the canary. Currently, the only valid values are `syn-nodejs-2.0`, `syn-nodejs-2.0-beta`, and `syn-1.0`. For more information about runtime versions, see [Canary Runtime Versions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html). * `artifact_s3_location` - (Required) The location on Amazon S3 where Synthetics stores artifacts from the test runs of this canary. * `schedule` - (Required) Information about how often the canary is to run and when these test runs are to stop. See [Schedule](#schedule) below. * `handler` - (Required) The domain description. From 2ec3a705e46d2bd82a054c1e9e3409a9a8da0e54 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 3 Oct 2020 17:21:45 +0300 Subject: [PATCH 44/67] runtime version fix+ test --- aws/resource_aws_synthetics_canary.go | 2 +- aws/resource_aws_synthetics_canary_test.go | 53 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 88e513613288..67cc45cf3502 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -348,7 +348,7 @@ func resourceAwsSyntheticsCanaryUpdate(d *schema.ResourceData, meta interface{}) } if d.HasChange("runtime_version") { - input.RuntimeVersion = aws.String(d.Get("vpc_config").(string)) + input.RuntimeVersion = aws.String(d.Get("runtime_version").(string)) updateFlag = true } diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 3d755550f352..3cc0bcd664ea 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -145,6 +145,42 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { }) } +func TestAccAWSSyntheticsCanary_runtimeVersion(t *testing.T) { + var conf1 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: testAccAWSSyntheticsCanaryRuntimeVersionConfig(rName, "syn-nodejs-2.0"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf1), + resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-nodejs-2.0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, + }, + { + Config: testAccAWSSyntheticsCanaryRuntimeVersionConfig(rName, "syn-nodejs-2.0-beta"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf1), + resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-nodejs-2.0-beta"), + ), + }, + }, + }) +} + func TestAccAWSSyntheticsCanary_startCanary(t *testing.T) { var conf1, conf2, conf3 synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) @@ -686,6 +722,23 @@ resource "aws_synthetics_canary" "test" { `, rName) } +func testAccAWSSyntheticsCanaryRuntimeVersionConfig(rName, version string) string { + return 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.zip" + runtime_version = %[2]q + + schedule { + expression = "rate(0 minute)" + } +} +`, rName, version) +} + func testAccAWSSyntheticsCanaryZipUpdatedConfig(rName string) string { return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { From 4f94a2fc5a5ae4d97f0ec23b6784624778e5bc1f Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 3 Oct 2020 17:43:56 +0300 Subject: [PATCH 45/67] active tracing --- aws/resource_aws_synthetics_canary.go | 9 +++++++++ aws/resource_aws_synthetics_canary_test.go | 1 + 2 files changed, 10 insertions(+) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 67cc45cf3502..c9cca983714b 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -116,6 +116,10 @@ func resourceAwsSyntheticsCanary() *schema.Resource { ValidateFunc: validation.IntBetween(60, 14*60), Default: 840, }, + "active_tracing": { + Type: schema.TypeBool, + Optional: true, + }, }, }, }, @@ -535,6 +539,10 @@ func expandAwsSyntheticsCanaryRunConfig(l []interface{}) *synthetics.CanaryRunCo codeConfig.MemoryInMB = aws.Int64(int64(v)) } + if v, ok := m["active_tracing"].(bool); ok { + codeConfig.ActiveTracing = aws.Bool(v) + } + return codeConfig } @@ -546,6 +554,7 @@ func flattenAwsSyntheticsCanaryRunConfig(canaryCodeOut *synthetics.CanaryRunConf m := map[string]interface{}{ "timeout_in_seconds": aws.Int64Value(canaryCodeOut.TimeoutInSeconds), "memory_in_mb": aws.Int64Value(canaryCodeOut.MemoryInMB), + "active_tracing": aws.BoolValue(canaryCodeOut.ActiveTracing), } return []interface{}{m} diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 3cc0bcd664ea..8e301e0cc42e 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -251,6 +251,7 @@ func TestAccAWSSyntheticsCanary_s3(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.active_tracing", "false"), resource.TestCheckResourceAttr(resourceName, "failure_retention_period", "31"), resource.TestCheckResourceAttr(resourceName, "success_retention_period", "31"), resource.TestCheckResourceAttr(resourceName, "handler", "exports.handler"), From cf0446c352b621f069a19f6997b7e03810fc7575 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 3 Oct 2020 17:52:26 +0300 Subject: [PATCH 46/67] add tracing test --- aws/resource_aws_synthetics_canary_test.go | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 8e301e0cc42e..1d9b1dd89752 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -321,6 +321,49 @@ func TestAccAWSSyntheticsCanary_runConfig(t *testing.T) { }) } +func TestAccAWSSyntheticsCanary_runConfigTracing(t *testing.T) { + var conf 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: testAccAWSSyntheticsCanaryRunConfigTracingConfig(rName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "run_config.0.active_tracing", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, + }, + { + Config: testAccAWSSyntheticsCanaryRunConfigTracingConfig(rName, false), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "run_config.0.active_tracing", "false"), + ), + }, + { + Config: testAccAWSSyntheticsCanaryRunConfigTracingConfig(rName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "run_config.0.active_tracing", "true"), + ), + }, + }, + }) +} + func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) @@ -706,6 +749,28 @@ resource "aws_synthetics_canary" "test" { `, rName) } +func testAccAWSSyntheticsCanaryRunConfigTracingConfig(rName string, tracing bool) string { + return 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.zip" + runtime_version = "syn-nodejs-2.0" + + schedule { + expression = "rate(0 minute)" + } + + run_config { + active_tracing = %[2]t + timeout_in_seconds = 60 + } +} +`, rName, tracing) +} + func testAccAWSSyntheticsCanaryBasicConfig(rName string) string { return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { From 9ccd3c9910216692d009c519d7e4377680f28dda Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 3 Oct 2020 17:54:33 +0300 Subject: [PATCH 47/67] tracing docs --- website/docs/r/synthetics_canary.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index 22b60dbb172d..076f276c16fa 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -57,6 +57,7 @@ The following arguments are supported: * `timeout_in_seconds` - (Optional) How long the canary is allowed to run before it must stop. If you omit this field, the frequency of the canary is used as this value, up to a maximum of 14 minutes. * `memory_in_mb` - (Optional) The maximum amount of memory available to the canary while it is running, in MB. The value you specify must be a multiple of 64. +* `active_tracing` - (Optional) Specifies whether this canary is to use active AWS X-Ray tracing when it runs. You can enable active tracing only for canaries that use version syn-nodejs-2.0 or later for their canary runtime. ### VPC Config @@ -71,7 +72,6 @@ In addition to all arguments above, the following attributes are exported: * `arn` - Amazon Resource Name (ARN) of the Canary. * `source_location_arn` - The ARN of the Lambda layer where Synthetics stores the canary script code. * `engine_arn` - The ARN of the Lambda function that is used as your canary's engine. -* `runtime_version` - Specifies the runtime version to use for the canary. * `timeline` - A structure that contains information about when the canary was created, modified, and most recently run. see [Timeline](#timeline). ### VPC Config From 23b10f5db6646ea53e64f17e55d874f07cf2cb3b Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 3 Oct 2020 17:57:39 +0300 Subject: [PATCH 48/67] lint --- aws/resource_aws_synthetics_canary_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 1d9b1dd89752..bb659b7b1367 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -758,7 +758,7 @@ resource "aws_synthetics_canary" "test" { handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" runtime_version = "syn-nodejs-2.0" - + schedule { expression = "rate(0 minute)" } From 341a85de606a9fdf46d8b719e08ef70fb80b0a56 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 3 Oct 2020 17:58:19 +0300 Subject: [PATCH 49/67] lint --- aws/resource_aws_synthetics_canary_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index bb659b7b1367..3dbc0617ab54 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -758,13 +758,13 @@ resource "aws_synthetics_canary" "test" { handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" runtime_version = "syn-nodejs-2.0" - + schedule { expression = "rate(0 minute)" } run_config { - active_tracing = %[2]t + active_tracing = %[2]t timeout_in_seconds = 60 } } From bc309ff3f1abfcc35829fee293bf7b355e2a6045 Mon Sep 17 00:00:00 2001 From: Ilia Lazebnik Date: Thu, 29 Oct 2020 22:20:23 +0200 Subject: [PATCH 50/67] Apply suggestions from code review Co-authored-by: Kit Ewbank --- website/docs/r/synthetics_canary.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index 076f276c16fa..b6367e48a763 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -33,9 +33,9 @@ The following arguments are supported: * `name` - (Required) The name for this canary. * `runtime_version` - (Required) Specifies the runtime version to use for the canary. Currently, the only valid values are `syn-nodejs-2.0`, `syn-nodejs-2.0-beta`, and `syn-1.0`. For more information about runtime versions, see [Canary Runtime Versions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html). -* `artifact_s3_location` - (Required) The location on Amazon S3 where Synthetics stores artifacts from the test runs of this canary. +* `artifact_s3_location` - (Required) The location in Amazon S3 where Synthetics stores artifacts from the test runs of this canary. * `schedule` - (Required) Information about how often the canary is to run and when these test runs are to stop. See [Schedule](#schedule) below. -* `handler` - (Required) The domain description. +* `handler` - (Required) The entry point to use for the source code when running the canary. This value must end with the string `.handler` . * `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_CreateCanary.html#API_CreateCanary_RequestSyntax) for permissions needs for IAM Role. * `start_canary` - (Optional) Whether to run or stop the canary. * `s3_bucket` - (Optional) If your canary script is located in S3, specify the full bucket name here. The bucket must already exist. Specify the full bucket name, including s3:// as the start of the bucket name. **Conflicts with `zip_file`** From aff1052460365fa46baa1b83887b411bccd8c8b3 Mon Sep 17 00:00:00 2001 From: Ilia Lazebnik Date: Tue, 17 Nov 2020 17:16:29 +0200 Subject: [PATCH 51/67] Update aws/resource_aws_synthetics_canary.go Co-authored-by: Robert Stettner --- aws/resource_aws_synthetics_canary.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index c9cca983714b..882c4aae0147 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -285,7 +285,7 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e resp, err := conn.GetCanary(input) if err != nil { if isAWSErr(err, synthetics.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] CodeCommit Repository (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] Synthetics Canary (%s) not found, removing from state", d.Id()) d.SetId("") return nil } From ac6156164455fd1adb9050e6e8b5968343ec64f6 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Tue, 17 Nov 2020 20:24:40 +0200 Subject: [PATCH 52/67] add more validation for names --- aws/resource_aws_synthetics_canary.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 882c4aae0147..4a0cf147a761 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "regexp" "strings" "time" @@ -29,10 +30,13 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringLenBetween(1, 21), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 21), + validation.StringMatch(regexp.MustCompile(`^[0-9a-z_\-]+$`), "must contain only alphanumeric, hyphen, underscore."), + ), }, "runtime_version": { Type: schema.TypeString, From a1f769455652752552b0027f30aecd96f48f79a0 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Tue, 17 Nov 2020 20:24:46 +0200 Subject: [PATCH 53/67] fix lint --- aws/resource_aws_synthetics_canary_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 3dbc0617ab54..d69029bd6da8 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -870,6 +870,8 @@ resource "aws_s3_bucket_object" "test" { func testAccAWSSyntheticsCanaryVPCConfigBase(rName string) string { return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(` +data "aws_partition" "current" {} + resource "aws_vpc" "test" { cidr_block = "10.1.0.0/16" @@ -915,7 +917,7 @@ resource "aws_security_group" "test2" { } resource "aws_iam_role_policy_attachment" "test" { - policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" role = aws_iam_role.test.name } `, rName) From cc23b08b07b9ba18a3a339682b06cb997ba8ca13 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Tue, 17 Nov 2020 21:42:32 +0200 Subject: [PATCH 54/67] use finder --- .../service/synthetics/finder/finder.go | 20 +++++++++++++++++++ aws/resource_aws_synthetics_canary.go | 7 ++----- aws/resource_aws_synthetics_canary_test.go | 13 +++--------- 3 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 aws/internal/service/synthetics/finder/finder.go diff --git a/aws/internal/service/synthetics/finder/finder.go b/aws/internal/service/synthetics/finder/finder.go new file mode 100644 index 000000000000..c7ed5179088a --- /dev/null +++ b/aws/internal/service/synthetics/finder/finder.go @@ -0,0 +1,20 @@ +package finder + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/synthetics" +) + +// CanaryByName returns the Canary corresponding to the specified Name. +func CanaryByName(conn *synthetics.Synthetics, name string) (*synthetics.GetCanaryOutput, error) { + input := &synthetics.GetCanaryInput{ + Name: aws.String(name), + } + + output, err := conn.GetCanary(input) + if err != nil { + return nil, err + } + + return output, nil +} diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 4a0cf147a761..08456ed3b19b 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "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/service/synthetics/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/synthetics/waiter" ) @@ -282,11 +283,7 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e conn := meta.(*AWSClient).syntheticsconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - input := &synthetics.GetCanaryInput{ - Name: aws.String(d.Id()), - } - - resp, err := conn.GetCanary(input) + resp, err := finder.CanaryByName(conn, d.Id()) if err != nil { if isAWSErr(err, synthetics.ErrCodeResourceNotFoundException, "") { log.Printf("[WARN] Synthetics Canary (%s) not found, removing from state", d.Id()) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index d69029bd6da8..5913a64cd9e6 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -15,6 +15,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/service/synthetics/finder" ) func init() { @@ -494,11 +495,7 @@ func testAccCheckAwsSyntheticsCanaryDestroy(s *terraform.State) error { } name := rs.Primary.ID - input := &synthetics.GetCanaryInput{ - Name: aws.String(name), - } - - _, err := conn.GetCanary(input) + _, err := finder.CanaryByName(conn, name) if err != nil { if isAWSErr(err, synthetics.ErrCodeResourceNotFoundException, "") { return nil @@ -524,11 +521,7 @@ func testAccCheckAwsSyntheticsCanaryExists(n string, canary *synthetics.Canary) name := rs.Primary.ID conn := testAccProvider.Meta().(*AWSClient).syntheticsconn - input := &synthetics.GetCanaryInput{ - Name: aws.String(name), - } - - out, err := conn.GetCanary(input) + out, err := finder.CanaryByName(conn, name) if err != nil { return fmt.Errorf("syntherics Canary %s not found in AWS", name) } From 7e7414b0de0b3adf8542018921591d831c72c85f Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 18 Nov 2020 10:03:19 +0200 Subject: [PATCH 55/67] fix --- aws/resource_aws_synthetics_canary_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 5913a64cd9e6..3e01df3fa80a 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -24,7 +24,7 @@ func init() { F: testSweepSyntheticsCanaries, Dependencies: []string{ "aws_lambda_function", - "aws_lambda_layer_version", + "aws_lambda_layer", "aws_cloudwatch_log_group", }, }) @@ -863,8 +863,6 @@ resource "aws_s3_bucket_object" "test" { func testAccAWSSyntheticsCanaryVPCConfigBase(rName string) string { return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(` -data "aws_partition" "current" {} - resource "aws_vpc" "test" { cidr_block = "10.1.0.0/16" From a48ad23276e85d443a4cdd3651cf4117918a7d8f Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 18 Nov 2020 14:55:56 +0200 Subject: [PATCH 56/67] ignore s3 arguments --- aws/resource_aws_synthetics_canary_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 3e01df3fa80a..7e2676f540d6 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -269,7 +269,7 @@ func TestAccAWSSyntheticsCanary_s3(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, + ImportStateVerifyIgnore: []string{"s3_bucket", "s3_key", "s3_version", "start_canary"}, }, }, }) From 1b43d1bc0f3d61c8aa99267db8a090808469d453 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Thu, 19 Nov 2020 10:19:15 +0200 Subject: [PATCH 57/67] env vars --- aws/resource_aws_synthetics_canary.go | 9 ++ aws/resource_aws_synthetics_canary_test.go | 98 ++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 08456ed3b19b..c8cea0ce4aff 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -115,6 +115,11 @@ func resourceAwsSyntheticsCanary() *schema.Resource { validation.IntAtLeast(960), ), }, + "environment_variables": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "timeout_in_seconds": { Type: schema.TypeInt, Optional: true, @@ -540,6 +545,10 @@ func expandAwsSyntheticsCanaryRunConfig(l []interface{}) *synthetics.CanaryRunCo codeConfig.MemoryInMB = aws.Int64(int64(v)) } + if v, ok := m["environment_variables"].(map[string]interface{}); ok && len(v) > 0 { + codeConfig.EnvironmentVariables = stringMapToPointers(v) + } + if v, ok := m["active_tracing"].(bool); ok { codeConfig.ActiveTracing = aws.Bool(v) } diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 7e2676f540d6..11019fca8046 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -365,6 +365,53 @@ func TestAccAWSSyntheticsCanary_runConfigTracing(t *testing.T) { }) } +func TestAccAWSSyntheticsCanary_runConfigEnvVars(t *testing.T) { + var conf 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: testAccAWSSyntheticsCanaryRunConfigEnvVarConfig1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.%", "1"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary", "run_config.0.environment_variables"}, + }, + { + Config: testAccAWSSyntheticsCanaryRunConfigEnvVarConfig2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.%", "2"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.key2", "value2"), + ), + }, + { + Config: testAccAWSSyntheticsCanaryRunConfigEnvVarConfig1(rName, "key2", "value2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.%", "1"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.key2", "value2"), + ), + }, + }, + }) +} + func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) @@ -764,6 +811,57 @@ resource "aws_synthetics_canary" "test" { `, rName, tracing) } +func testAccAWSSyntheticsCanaryRunConfigEnvVarConfig1(rName, key1, value1 string) string { + return 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.zip" + runtime_version = "syn-nodejs-2.0" + + schedule { + expression = "rate(0 minute)" + } + + run_config { + timeout_in_seconds = 60 + + environment_variables = { + %[2]q = %[3]q + } + } +} +`, rName, key1, value1) +} + +func testAccAWSSyntheticsCanaryRunConfigEnvVarConfig2(rName, key1, value1, key2, value2 string) string { + return 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.zip" + runtime_version = "syn-nodejs-2.0" + + schedule { + expression = "rate(0 minute)" + } + + run_config { + timeout_in_seconds = 60 + + environment_variables = { + %[2]q = %[3]q + %[4]q = %[5]q + } + } +} +`, rName, key1, value1, key2, value2) +} + func testAccAWSSyntheticsCanaryBasicConfig(rName string) string { return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { From c357b30bc8c7e33baa70a20652056838de146aa2 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 16 Jan 2021 20:46:27 +0200 Subject: [PATCH 58/67] revert env var --- aws/resource_aws_synthetics_canary.go | 9 -- aws/resource_aws_synthetics_canary_test.go | 98 ---------------------- 2 files changed, 107 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index c8cea0ce4aff..08456ed3b19b 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -115,11 +115,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { validation.IntAtLeast(960), ), }, - "environment_variables": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, "timeout_in_seconds": { Type: schema.TypeInt, Optional: true, @@ -545,10 +540,6 @@ func expandAwsSyntheticsCanaryRunConfig(l []interface{}) *synthetics.CanaryRunCo codeConfig.MemoryInMB = aws.Int64(int64(v)) } - if v, ok := m["environment_variables"].(map[string]interface{}); ok && len(v) > 0 { - codeConfig.EnvironmentVariables = stringMapToPointers(v) - } - if v, ok := m["active_tracing"].(bool); ok { codeConfig.ActiveTracing = aws.Bool(v) } diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 11019fca8046..7e2676f540d6 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -365,53 +365,6 @@ func TestAccAWSSyntheticsCanary_runConfigTracing(t *testing.T) { }) } -func TestAccAWSSyntheticsCanary_runConfigEnvVars(t *testing.T) { - var conf 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: testAccAWSSyntheticsCanaryRunConfigEnvVarConfig1(rName, "key1", "value1"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.%", "1"), - resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.key1", "value1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"zip_file", "start_canary", "run_config.0.environment_variables"}, - }, - { - Config: testAccAWSSyntheticsCanaryRunConfigEnvVarConfig2(rName, "key1", "value1updated", "key2", "value2"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.%", "2"), - resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.key1", "value1updated"), - resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.key2", "value2"), - ), - }, - { - Config: testAccAWSSyntheticsCanaryRunConfigEnvVarConfig1(rName, "key2", "value2"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.%", "1"), - resource.TestCheckResourceAttr(resourceName, "run_config.0.environment_variables.key2", "value2"), - ), - }, - }, - }) -} - func TestAccAWSSyntheticsCanary_vpc(t *testing.T) { var conf synthetics.Canary rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(8)) @@ -811,57 +764,6 @@ resource "aws_synthetics_canary" "test" { `, rName, tracing) } -func testAccAWSSyntheticsCanaryRunConfigEnvVarConfig1(rName, key1, value1 string) string { - return 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.zip" - runtime_version = "syn-nodejs-2.0" - - schedule { - expression = "rate(0 minute)" - } - - run_config { - timeout_in_seconds = 60 - - environment_variables = { - %[2]q = %[3]q - } - } -} -`, rName, key1, value1) -} - -func testAccAWSSyntheticsCanaryRunConfigEnvVarConfig2(rName, key1, value1, key2, value2 string) string { - return 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.zip" - runtime_version = "syn-nodejs-2.0" - - schedule { - expression = "rate(0 minute)" - } - - run_config { - timeout_in_seconds = 60 - - environment_variables = { - %[2]q = %[3]q - %[4]q = %[5]q - } - } -} -`, rName, key1, value1, key2, value2) -} - func testAccAWSSyntheticsCanaryBasicConfig(rName string) string { return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { From ae3e310a0517b94bea157531b9817042c97dec06 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 12 Feb 2021 09:43:00 -0500 Subject: [PATCH 59/67] resource/synthetics_canary: Use finder in waiter status --- aws/internal/service/synthetics/waiter/status.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/aws/internal/service/synthetics/waiter/status.go b/aws/internal/service/synthetics/waiter/status.go index 3f54d396853b..b25b869f6ba6 100644 --- a/aws/internal/service/synthetics/waiter/status.go +++ b/aws/internal/service/synthetics/waiter/status.go @@ -6,15 +6,12 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/synthetics" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/synthetics/finder" ) func CanaryStatus(conn *synthetics.Synthetics, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - input := &synthetics.GetCanaryInput{ - Name: aws.String(name), - } - - output, err := conn.GetCanary(input) + output, err := finder.CanaryByName(conn, name) if err != nil { return nil, synthetics.CanaryStateError, err From 5dfe396dde43d2cd9968804ad354d4eb52a29ca0 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 12 Feb 2021 10:10:23 -0500 Subject: [PATCH 60/67] docs/synthetics_canary: Minor changes to docs --- .../docs/r/synthetics_canary.html.markdown | 80 ++++++++++--------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index b6367e48a763..77fba73498dc 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -10,6 +10,8 @@ description: |- Provides a Synthetics Canary resource. +~> **NOTE:** When you create a canary, AWS creates supporting implicit resources. See the Amazon CloudWatch Synthetics documentation on [DeleteCanary](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_DeleteCanary.html) for a full list. Neither AWS nor Terraform deletes these implicit resources automatically when the canary is deleted. Before deleting a canary, ensure you have all the information about the canary that you need to delete the implicit resources using Terraform shell commands, the AWS Console, or AWS CLI. + ## Example Usage ```hcl @@ -29,61 +31,67 @@ resource "aws_synthetics_canary" "some" { ## Argument Reference -The following arguments are supported: +The following arguments are required: -* `name` - (Required) The name for this canary. +* `artifact_s3_location` - (Required) Location in Amazon S3 where Synthetics stores artifacts from the test runs of this canary. +* `execution_role_arn` - (Required) ARN of the IAM role to be used to run the canary. see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_CreateCanary.html#API_CreateCanary_RequestSyntax) for permissions needs for IAM Role. +* `handler` - (Required) Entry point to use for the source code when running the canary. This value must end with the string `.handler` . +* `name` - (Required) Name for this canary. * `runtime_version` - (Required) Specifies the runtime version to use for the canary. Currently, the only valid values are `syn-nodejs-2.0`, `syn-nodejs-2.0-beta`, and `syn-1.0`. For more information about runtime versions, see [Canary Runtime Versions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html). -* `artifact_s3_location` - (Required) The location in Amazon S3 where Synthetics stores artifacts from the test runs of this canary. -* `schedule` - (Required) Information about how often the canary is to run and when these test runs are to stop. See [Schedule](#schedule) below. -* `handler` - (Required) The entry point to use for the source code when running the canary. This value must end with the string `.handler` . -* `execution_role_arn` - (Required) The ARN of the IAM role to be used to run the canary. see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_CreateCanary.html#API_CreateCanary_RequestSyntax) for permissions needs for IAM Role. +* `schedule` - (Required) Configuration block providing how often the canary is to run and when these test runs are to stop. Detailed below. + +The following arguments are optional: + +* `failure_retention_period` - (Optional) Number of days to retain data about failed runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. +* `run_config` - (Optional) Configuration block for individual canary runs. Detailed below. +* `s3_bucket` - (Optional) Full bucket name which is used if your canary script is located in S3. The bucket must already exist. Specify the full bucket name including s3:// as the start of the bucket name. **Conflicts with `zip_file`.** +* `s3_key` - (Optional) S3 key of your script. **Conflicts with `zip_file`.** +* `s3_version` - (Optional) S3 version ID of your script. **Conflicts with `zip_file`.** * `start_canary` - (Optional) Whether to run or stop the canary. -* `s3_bucket` - (Optional) If your canary script is located in S3, specify the full bucket name here. The bucket must already exist. Specify the full bucket name, including s3:// as the start of the bucket name. **Conflicts with `zip_file`** -* `s3_key` - (Optional) The S3 key of your script. **Conflicts with `zip_file`** -* `s3_version` - (Optional) The S3 version ID of your script. **Conflicts with `zip_file`** -* `zip_file` - (Optional) If you input your canary script directly into the canary instead of referring to an S3 location, the value of this parameter is the .zip file that contains the script. It can be up to 5 MB. **Conflicts with `s3_bucket`, `s3_key`, and `s3_version`** -* `failure_retention_period` - (Optional) The number of days to retain data about failed runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. -* `success_retention_period` - (Optional) The number of days to retain data about successful runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. -* `run_config` - (Optional) Configuration for individual canary runs. See [Run Config](#run-config) below. -* `vpc_config` - (Optional) If this canary is to test an endpoint in a VPC, this structure contains information about the subnet and security groups of the VPC endpoint. For more information, see [Running a Canary in a VPC](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_VPC.html). See [VPC Config](#vpc-config) below. +* `success_retention_period` - (Optional) Number of days to retain data about successful runs of this canary. If you omit this field, the default of 31 days is used. The valid range is 1 to 455 days. * `tags` - (Optional) Key-value map of resource tags +* `vpc_config` - (Optional) Configuration block. Detailed below. +* `zip_file` - (Optional) ZIP file that contains the script, if you input your canary script directly into the canary instead of referring to an S3 location. It can be up to 5 MB. **Conflicts with `s3_bucket`, `s3_key`, and `s3_version`.** -### Schedule +### schedule -* `expression` - (Required) A rate expression that defines how often the canary is to run. The syntax is rate(number unit). unit can be minute, minutes, or hour. +* `expression` - (Required) Rate expression that defines how often the canary is to run. The syntax is rate(number unit). unit can be minute, minutes, or hour. * `duration_in_seconds` - (Optional) Duration in seconds, for the canary to continue making regular runs according to the schedule in the Expression value. -### Run Config +### run_config -* `timeout_in_seconds` - (Optional) How long the canary is allowed to run before it must stop. If you omit this field, the frequency of the canary is used as this value, up to a maximum of 14 minutes. -* `memory_in_mb` - (Optional) The maximum amount of memory available to the canary while it is running, in MB. The value you specify must be a multiple of 64. -* `active_tracing` - (Optional) Specifies whether this canary is to use active AWS X-Ray tracing when it runs. You can enable active tracing only for canaries that use version syn-nodejs-2.0 or later for their canary runtime. +* `timeout_in_seconds` - (Optional) Number of seconds the canary is allowed to run before it must stop. If you omit this field, the frequency of the canary is used, up to a maximum of 840 (14 minutes). +* `memory_in_mb` - (Optional) Maximum amount of memory available to the canary while it is running, in MB. The value you specify must be a multiple of 64. +* `active_tracing` - (Optional) Whether this canary is to use active AWS X-Ray tracing when it runs. You can enable active tracing only for canaries that use version syn-nodejs-2.0 or later for their canary runtime. -### VPC Config +### vpc_config -* `subnet_ids` - (Required) The IDs of the subnets where this canary is to run. -* `security_group_ids` - (Required) The IDs of the security groups for this canary. +If this canary tests an endpoint in a VPC, this structure contains information about the subnet and security groups of the VPC endpoint. For more information, see [Running a Canary in a VPC](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_VPC.html). + +* `subnet_ids` - (Required) IDs of the subnets where this canary is to run. +* `security_group_ids` - (Required) IDs of the security groups for this canary. ## Attributes Reference In addition to all arguments above, the following attributes are exported: -* `id` - The name for this canary. * `arn` - Amazon Resource Name (ARN) of the Canary. -* `source_location_arn` - The ARN of the Lambda layer where Synthetics stores the canary script code. -* `engine_arn` - The ARN of the Lambda function that is used as your canary's engine. -* `timeline` - A structure that contains information about when the canary was created, modified, and most recently run. see [Timeline](#timeline). +* `engine_arn` - ARN of the Lambda function that is used as your canary's engine. +* `id` - Name for this canary. +* `source_location_arn` - ARN of the Lambda layer where Synthetics stores the canary script code. +* `status` - Canary status. +* `timeline` - Structure that contains information about when the canary was created, modified, and most recently run. see [Timeline](#timeline). -### VPC Config +### vpc_config -* `vpc_id` - The ID of the VPC where this canary is to run. +* `vpc_id` - ID of the VPC where this canary is to run. -### Timeline +### timeline -* `created` - The date and time the canary was created. -* `last_modified` - The date and time the canary was most recently modified. -* `last_started` - The date and time that the canary's most recent run started. -* `last_stopped` - The date and time that the canary's most recent run ended. +* `created` - Date and time the canary was created. +* `last_modified` - Date and time the canary was most recently modified. +* `last_started` - Date and time that the canary's most recent run started. +* `last_stopped` - Date and time that the canary's most recent run ended. ## Import @@ -92,7 +100,3 @@ Synthetics Canaries can be imported using the `name`, e.g. ``` $ terraform import aws_synthetics_canary.some some-canary ``` - -**Note about leftover implicit resources** - When a canary is created a set of resources are created implicitly, - see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_DeleteCanary.html) for full list. -Terraform will not delete these resources automatically and require manual deletion or by using shell commands in terraform. From 4881bb611b7c466832637d98f4247d2438e94891 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 12 Feb 2021 10:22:22 -0500 Subject: [PATCH 61/67] resource/synthetics_canary: Sort arguments --- aws/resource_aws_synthetics_canary.go | 162 +++++++++++++------------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 08456ed3b19b..9146fda84751 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -30,18 +30,9 @@ func resourceAwsSyntheticsCanary() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 21), - validation.StringMatch(regexp.MustCompile(`^[0-9a-z_\-]+$`), "must contain only alphanumeric, hyphen, underscore."), - ), - }, - "runtime_version": { + "arn": { Type: schema.TypeString, - Required: true, + Computed: true, }, "artifact_s3_location": { Type: schema.TypeString, @@ -51,53 +42,33 @@ func resourceAwsSyntheticsCanary() *schema.Resource { return strings.TrimPrefix(new, "s3://") == old }, }, - "handler": { + "engine_arn": { Type: schema.TypeString, - Required: true, - }, - "s3_bucket": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"zip_file"}, - RequiredWith: []string{"s3_key"}, - }, - "s3_key": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"zip_file"}, - RequiredWith: []string{"s3_bucket"}, - }, - "s3_version": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"zip_file"}, - }, - "zip_file": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"s3_bucket", "s3_key", "s3_version"}, + Computed: true, }, "execution_role_arn": { Type: schema.TypeString, Required: true, ValidateFunc: validateArn, }, - "start_canary": { - Type: schema.TypeBool, - Default: false, - Optional: true, - }, "failure_retention_period": { Type: schema.TypeInt, Optional: true, Default: 31, ValidateFunc: validation.IntBetween(1, 455), }, - "success_retention_period": { - Type: schema.TypeInt, - Optional: true, - Default: 31, - ValidateFunc: validation.IntBetween(1, 455), + "handler": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 21), + validation.StringMatch(regexp.MustCompile(`^[0-9a-z_\-]+$`), "must contain only alphanumeric, hyphen, underscore."), + ), }, "run_config": { Type: schema.TypeList, @@ -128,6 +99,27 @@ func resourceAwsSyntheticsCanary() *schema.Resource { }, }, }, + "runtime_version": { + Type: schema.TypeString, + Required: true, + }, + "s3_bucket": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"zip_file"}, + RequiredWith: []string{"s3_key"}, + }, + "s3_key": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"zip_file"}, + RequiredWith: []string{"s3_bucket"}, + }, + "s3_version": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"zip_file"}, + }, "schedule": { Type: schema.TypeList, MaxItems: 1, @@ -148,29 +140,26 @@ func resourceAwsSyntheticsCanary() *schema.Resource { }, }, }, - "vpc_config": { - Type: schema.TypeList, - MaxItems: 1, + "source_location_arn": { + Type: schema.TypeString, + Computed: true, + }, + "start_canary": { + Type: schema.TypeBool, + Default: false, Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "security_group_ids": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - "subnet_ids": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - "vpc_id": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "success_retention_period": { + Type: schema.TypeInt, + Optional: true, + Default: 31, + ValidateFunc: validation.IntBetween(1, 455), + }, + "tags": tagsSchema(), "timeline": { Type: schema.TypeList, Computed: true, @@ -195,23 +184,34 @@ func resourceAwsSyntheticsCanary() *schema.Resource { }, }, }, - "engine_arn": { - Type: schema.TypeString, - Computed: true, - }, - "source_location_arn": { - Type: schema.TypeString, - Computed: true, - }, - "arn": { - Type: schema.TypeString, - Computed: true, + "vpc_config": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "security_group_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "subnet_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, }, - "status": { - Type: schema.TypeString, - Computed: true, + "zip_file": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"s3_bucket", "s3_key", "s3_version"}, }, - "tags": tagsSchema(), }, } } From 0da88b53a0d4b496b1bad2d4d0f16c1c3bf0acec Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 12 Feb 2021 10:26:49 -0500 Subject: [PATCH 62/67] resource/synthetics_canary: Minor fixes --- aws/resource_aws_synthetics_canary.go | 13 ++++++------- aws/resource_aws_synthetics_canary_test.go | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 9146fda84751..0c9a9e845194 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -294,20 +294,20 @@ func resourceAwsSyntheticsCanaryRead(d *schema.ResourceData, meta interface{}) e } canary := resp.Canary - d.Set("name", canary.Name) + d.Set("artifact_s3_location", canary.ArtifactS3Location) d.Set("engine_arn", canary.EngineArn) - d.Set("status", canary.Status.State) d.Set("execution_role_arn", canary.ExecutionRoleArn) - d.Set("runtime_version", canary.RuntimeVersion) - d.Set("artifact_s3_location", canary.ArtifactS3Location) d.Set("failure_retention_period", canary.FailureRetentionPeriodInDays) - d.Set("success_retention_period", canary.SuccessRetentionPeriodInDays) d.Set("handler", canary.Code.Handler) + d.Set("name", canary.Name) + d.Set("runtime_version", canary.RuntimeVersion) d.Set("source_location_arn", canary.Code.SourceLocationArn) + d.Set("status", canary.Status.State) + d.Set("success_retention_period", canary.SuccessRetentionPeriodInDays) canaryArn := arn.ARN{ Partition: meta.(*AWSClient).partition, - Service: "synthetics", + Service: synthetics.ServiceName, Region: meta.(*AWSClient).region, AccountID: meta.(*AWSClient).accountid, Resource: fmt.Sprintf("canary:%s", aws.StringValue(canary.Name)), @@ -469,7 +469,6 @@ func resourceAwsSyntheticsCanaryDelete(d *schema.ResourceData, meta interface{}) } func expandAwsSyntheticsCanaryCode(d *schema.ResourceData) (*synthetics.CanaryCodeInput, error) { - codeConfig := &synthetics.CanaryCodeInput{ Handler: aws.String(d.Get("handler").(string)), } diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 7e2676f540d6..15a9920708c1 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -90,7 +90,7 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { Config: testAccAWSSyntheticsCanaryBasicConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf1), - testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary:.+`)), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", synthetics.ServiceName, regexp.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-1.0"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), @@ -120,7 +120,7 @@ func TestAccAWSSyntheticsCanary_basic(t *testing.T) { Config: testAccAWSSyntheticsCanaryZipUpdatedConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf2), - testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary:.+`)), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", synthetics.ServiceName, regexp.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-1.0"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), @@ -246,7 +246,7 @@ func TestAccAWSSyntheticsCanary_s3(t *testing.T) { Config: testAccAWSSyntheticsCanaryBasicS3CodeConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf), - testAccMatchResourceAttrRegionalARN(resourceName, "arn", "synthetics", regexp.MustCompile(`canary:.+`)), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", synthetics.ServiceName, regexp.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-1.0"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), From 2a53c4840e2f98dc31b512c59b3b0ddb25a6137d Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 12 Feb 2021 10:30:05 -0500 Subject: [PATCH 63/67] resource/synthetics_canary: Sort config block args --- aws/resource_aws_synthetics_canary.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/aws/resource_aws_synthetics_canary.go b/aws/resource_aws_synthetics_canary.go index 0c9a9e845194..ee730dabcd92 100644 --- a/aws/resource_aws_synthetics_canary.go +++ b/aws/resource_aws_synthetics_canary.go @@ -77,6 +77,10 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "active_tracing": { + Type: schema.TypeBool, + Optional: true, + }, "memory_in_mb": { Type: schema.TypeInt, Optional: true, @@ -92,10 +96,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { ValidateFunc: validation.IntBetween(60, 14*60), Default: 840, }, - "active_tracing": { - Type: schema.TypeBool, - Optional: true, - }, }, }, }, @@ -126,6 +126,10 @@ func resourceAwsSyntheticsCanary() *schema.Resource { Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "duration_in_seconds": { + Type: schema.TypeInt, + Optional: true, + }, "expression": { Type: schema.TypeString, Required: true, @@ -133,10 +137,6 @@ func resourceAwsSyntheticsCanary() *schema.Resource { return new == "rate(0 minute)" && old == "rate(0 hour)" }, }, - "duration_in_seconds": { - Type: schema.TypeInt, - Optional: true, - }, }, }, }, From 93badf3f56c408af3883258149287a7164b5ae56 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 12 Feb 2021 10:42:24 -0500 Subject: [PATCH 64/67] tests/resource/synthetics_canary: Fix deprecated versions --- aws/resource_aws_synthetics_canary_test.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 15a9920708c1..4f8a383594b1 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -152,17 +152,15 @@ func TestAccAWSSyntheticsCanary_runtimeVersion(t *testing.T) { resourceName := "aws_synthetics_canary.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - }, + PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsSyntheticsCanaryDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSyntheticsCanaryRuntimeVersionConfig(rName, "syn-nodejs-2.0"), + Config: testAccAWSSyntheticsCanaryRuntimeVersionConfig(rName, "syn-nodejs-2.1"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf1), - resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-nodejs-2.0"), + resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-nodejs-2.1"), ), }, { @@ -172,10 +170,10 @@ func TestAccAWSSyntheticsCanary_runtimeVersion(t *testing.T) { ImportStateVerifyIgnore: []string{"zip_file", "start_canary"}, }, { - Config: testAccAWSSyntheticsCanaryRuntimeVersionConfig(rName, "syn-nodejs-2.0-beta"), + Config: testAccAWSSyntheticsCanaryRuntimeVersionConfig(rName, "syn-nodejs-2.2"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAwsSyntheticsCanaryExists(resourceName, &conf1), - resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-nodejs-2.0-beta"), + resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-nodejs-2.2"), ), }, }, @@ -845,7 +843,6 @@ resource "aws_synthetics_canary" "test" { s3_version = aws_s3_bucket_object.test.version_id runtime_version = "syn-1.0" - schedule { expression = "rate(0 minute)" } From 2bf734ab9f66410343c915ce5356500f7109c331 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 12 Feb 2021 10:49:36 -0500 Subject: [PATCH 65/67] tests/resource/sythentics_canary: Use composeConfig() --- aws/resource_aws_synthetics_canary_test.go | 65 +++++++++++----------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/aws/resource_aws_synthetics_canary_test.go b/aws/resource_aws_synthetics_canary_test.go index 4f8a383594b1..5202928fe732 100644 --- a/aws/resource_aws_synthetics_canary_test.go +++ b/aws/resource_aws_synthetics_canary_test.go @@ -698,7 +698,7 @@ EOF } func testAccAWSSyntheticsCanaryRunConfigConfig1(rName string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -715,11 +715,11 @@ resource "aws_synthetics_canary" "test" { timeout_in_seconds = 60 } } -`, rName) +`, rName)) } func testAccAWSSyntheticsCanaryRunConfigConfig2(rName string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -737,11 +737,11 @@ resource "aws_synthetics_canary" "test" { memory_in_mb = 960 } } -`, rName) +`, rName)) } func testAccAWSSyntheticsCanaryRunConfigTracingConfig(rName string, tracing bool) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -759,11 +759,11 @@ resource "aws_synthetics_canary" "test" { timeout_in_seconds = 60 } } -`, rName, tracing) +`, rName, tracing)) } func testAccAWSSyntheticsCanaryBasicConfig(rName string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -776,11 +776,11 @@ resource "aws_synthetics_canary" "test" { expression = "rate(0 minute)" } } -`, rName) +`, rName)) } func testAccAWSSyntheticsCanaryRuntimeVersionConfig(rName, version string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -793,11 +793,11 @@ resource "aws_synthetics_canary" "test" { expression = "rate(0 minute)" } } -`, rName, version) +`, rName, version)) } func testAccAWSSyntheticsCanaryZipUpdatedConfig(rName string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -810,11 +810,11 @@ resource "aws_synthetics_canary" "test" { expression = "rate(0 minute)" } } -`, rName) +`, rName)) } func testAccAWSSyntheticsCanaryStartCanaryConfig(rName string, state bool) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -828,11 +828,11 @@ resource "aws_synthetics_canary" "test" { expression = "rate(0 minute)" } } -`, rName, state) +`, rName, state)) } func testAccAWSSyntheticsCanaryBasicS3CodeConfig(rName string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -855,11 +855,11 @@ resource "aws_s3_bucket_object" "test" { etag = filemd5("test-fixtures/lambdatest.zip") } -`, rName) +`, rName)) } func testAccAWSSyntheticsCanaryVPCConfigBase(rName string) string { - return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(` + return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.1.0.0/16" @@ -908,12 +908,13 @@ resource "aws_iam_role_policy_attachment" "test" { policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" role = aws_iam_role.test.name } -`, rName) +`, rName)) } func testAccAWSSyntheticsCanaryVPCConfig1(rName string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + - testAccAWSSyntheticsCanaryVPCConfigBase(rName) + + return composeConfig( + testAccAWSSyntheticsCanaryConfigBase(rName), + testAccAWSSyntheticsCanaryVPCConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q @@ -934,12 +935,13 @@ resource "aws_synthetics_canary" "test" { depends_on = [aws_iam_role_policy_attachment.test] } -`, rName) +`, rName)) } func testAccAWSSyntheticsCanaryVPCConfig2(rName string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + - testAccAWSSyntheticsCanaryVPCConfigBase(rName) + + return composeConfig( + testAccAWSSyntheticsCanaryConfigBase(rName), + testAccAWSSyntheticsCanaryVPCConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q @@ -960,12 +962,13 @@ resource "aws_synthetics_canary" "test" { depends_on = [aws_iam_role_policy_attachment.test] } -`, rName) +`, rName)) } func testAccAWSSyntheticsCanaryVPCConfig3(rName string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + - testAccAWSSyntheticsCanaryVPCConfigBase(rName) + + return composeConfig( + testAccAWSSyntheticsCanaryConfigBase(rName), + testAccAWSSyntheticsCanaryVPCConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q @@ -986,11 +989,11 @@ resource "aws_synthetics_canary" "test" { depends_on = [aws_iam_role_policy_attachment.test] } -`, rName) +`, rName)) } func testAccAWSSyntheticsCanaryConfigTags1(rName, tagKey1, tagValue1 string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -1007,11 +1010,11 @@ resource "aws_synthetics_canary" "test" { %[2]q = %[3]q } } -`, rName, tagKey1, tagValue1) +`, rName, tagKey1, tagValue1)) } func testAccAWSSyntheticsCanaryConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccAWSSyntheticsCanaryConfigBase(rName) + fmt.Sprintf(` + return composeConfig(testAccAWSSyntheticsCanaryConfigBase(rName), fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -1029,5 +1032,5 @@ resource "aws_synthetics_canary" "test" { %[4]q = %[5]q } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } From 617a9c218521e4b3002209d66a7526fd14391c80 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 12 Feb 2021 11:03:15 -0500 Subject: [PATCH 66/67] resource/synthetics_canary: Changelog --- .changelog/13140.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/13140.txt diff --git a/.changelog/13140.txt b/.changelog/13140.txt new file mode 100644 index 000000000000..c15a0e0c7525 --- /dev/null +++ b/.changelog/13140.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_synthetics_canary +``` \ No newline at end of file From 6568886e11ff9a5f72dd7b46ac1cd182b2aaa7c1 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 12 Feb 2021 11:18:02 -0500 Subject: [PATCH 67/67] docs/synthetics_canary: Update runtime versions --- website/docs/r/synthetics_canary.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index 77fba73498dc..745322205fe4 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -37,7 +37,7 @@ The following arguments are required: * `execution_role_arn` - (Required) ARN of the IAM role to be used to run the canary. see [AWS Docs](https://docs.aws.amazon.com/AmazonSynthetics/latest/APIReference/API_CreateCanary.html#API_CreateCanary_RequestSyntax) for permissions needs for IAM Role. * `handler` - (Required) Entry point to use for the source code when running the canary. This value must end with the string `.handler` . * `name` - (Required) Name for this canary. -* `runtime_version` - (Required) Specifies the runtime version to use for the canary. Currently, the only valid values are `syn-nodejs-2.0`, `syn-nodejs-2.0-beta`, and `syn-1.0`. For more information about runtime versions, see [Canary Runtime Versions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html). +* `runtime_version` - (Required) Runtime version to use for the canary. Versions change often so consult the [Amazon CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html) for the latest valid versions. Values include `syn-python-selenium-1.0`, `syn-nodejs-puppeteer-3.0`, `syn-nodejs-2.2`, `syn-nodejs-2.1`, `syn-nodejs-2.0`, and `syn-1.0`. * `schedule` - (Required) Configuration block providing how often the canary is to run and when these test runs are to stop. Detailed below. The following arguments are optional: