From 9475b29e0bc269a27509d32744b30484a1a59fa3 Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Fri, 6 Sep 2024 00:38:59 -0400 Subject: [PATCH 1/8] feat: Add new aws_synthetics_runtime_versons data source --- .changelog/39180.txt | 3 + internal/service/synthetics/find.go | 15 ++++ .../runtime_versions_data_source.go | 90 +++++++++++++++++++ .../runtime_versions_data_source_test.go | 76 ++++++++++++++++ .../service/synthetics/service_package_gen.go | 7 +- .../synthetics_runtime_versions.html.markdown | 34 +++++++ 6 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 .changelog/39180.txt create mode 100644 internal/service/synthetics/runtime_versions_data_source.go create mode 100644 internal/service/synthetics/runtime_versions_data_source_test.go create mode 100644 website/docs/d/synthetics_runtime_versions.html.markdown diff --git a/.changelog/39180.txt b/.changelog/39180.txt new file mode 100644 index 00000000000..833449dbd68 --- /dev/null +++ b/.changelog/39180.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_synthetics_runtime_versions +``` \ No newline at end of file diff --git a/internal/service/synthetics/find.go b/internal/service/synthetics/find.go index 3d8619e7a22..f5d419cf28e 100644 --- a/internal/service/synthetics/find.go +++ b/internal/service/synthetics/find.go @@ -102,3 +102,18 @@ func FindAssociatedGroup(ctx context.Context, conn *synthetics.Client, canaryArn return &group, nil } + +func FindRuntimeVersions(ctx context.Context, conn *synthetics.Client) ([]awstypes.RuntimeVersion, error) { + input := &synthetics.DescribeRuntimeVersionsInput{} + output, err := conn.DescribeRuntimeVersions(ctx, input) + + if err != nil { + return nil, err + } + + if output == nil || output.RuntimeVersions == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.RuntimeVersions, nil +} diff --git a/internal/service/synthetics/runtime_versions_data_source.go b/internal/service/synthetics/runtime_versions_data_source.go new file mode 100644 index 00000000000..47b4b8e5a5c --- /dev/null +++ b/internal/service/synthetics/runtime_versions_data_source.go @@ -0,0 +1,90 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package synthetics + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkDataSource(name="Runtime Versions") +func newDataSourceRuntimeVersions(context.Context) (datasource.DataSourceWithConfigure, error) { + return &dataSourceRuntimeVersions{}, nil +} + +const ( + DSNameRuntimeVersions = "Runtime Versions Data Source" +) + +type dataSourceRuntimeVersions struct { + framework.DataSourceWithConfigure +} + +func (d *dataSourceRuntimeVersions) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { // nosemgrep:ci.meta-in-func-name + resp.TypeName = "aws_synthetics_runtime_versions" +} + +func (d *dataSourceRuntimeVersions) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + names.AttrID: framework.IDAttribute(), + "skip_deprecated": schema.BoolAttribute{ + Optional: true, + }, + "version_names": schema.ListAttribute{ + CustomType: fwtypes.ListOfStringType, + ElementType: types.StringType, + Computed: true, + }, + }, + } +} + +func (d *dataSourceRuntimeVersions) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + conn := d.Meta().SyntheticsClient(ctx) + + var data dataSourceRuntimeVersionsModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + skipDeprecated := data.SkipDeprecated.ValueBool() + out, err := FindRuntimeVersions(ctx, conn) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Synthetics, create.ErrActionReading, DSNameRuntimeVersions, "", err), + err.Error(), + ) + return + } + + var versionNames []string + + for _, v := range out { + if !skipDeprecated || v.DeprecationDate == nil { + versionNames = append(versionNames, aws.ToString(v.VersionName)) + } + } + + data.ID = flex.StringToFramework(ctx, &d.Meta().Region) + data.VersionNames = flex.FlattenFrameworkStringValueListOfString(ctx, versionNames) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +type dataSourceRuntimeVersionsModel struct { + ID types.String `tfsdk:"id"` + SkipDeprecated types.Bool `tfsdk:"skip_deprecated"` + VersionNames fwtypes.ListValueOf[types.String] `tfsdk:"version_names"` +} diff --git a/internal/service/synthetics/runtime_versions_data_source_test.go b/internal/service/synthetics/runtime_versions_data_source_test.go new file mode 100644 index 00000000000..c797ccf23ca --- /dev/null +++ b/internal/service/synthetics/runtime_versions_data_source_test.go @@ -0,0 +1,76 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package synthetics_test + +import ( + "fmt" + "testing" + + "github.com/YakDriver/regexache" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccSyntheticsRuntimeVersionsDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + dataSourceName := "data.aws_synthetics_runtime_versions.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SyntheticsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccRuntimeVersionsDataSourceConfig_basic(), + Check: resource.ComposeTestCheckFunc( + acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "version_names.#", 0), + resource.TestCheckTypeSetElemAttr(dataSourceName, "version_names.*", "syn-1.0"), + ), + }, + }, + }) +} + +func TestAccSyntheticsRuntimeVersionsDataSource_skipDeprecated(t *testing.T) { + ctx := acctest.Context(t) + dataSourceName := "data.aws_synthetics_runtime_versions.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SyntheticsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccRuntimeVersionsDataSourceConfig_skipDeprecated(false), + Check: resource.ComposeTestCheckFunc( + acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "version_names.#", 0), + resource.TestCheckTypeSetElemAttr(dataSourceName, "version_names.*", "syn-1.0"), + ), + }, + { + Config: testAccRuntimeVersionsDataSourceConfig_skipDeprecated(true), + Check: resource.ComposeTestCheckFunc( + acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "version_names.#", 0), + resource.TestCheckTypeSetElemAttr(dataSourceName, "version_names.*", "syn-1.0"), + ), + ExpectError: regexache.MustCompile(`no TypeSet element "version_names.*", with value "syn-1.0" in state`), + }, + }, + }) +} + +func testAccRuntimeVersionsDataSourceConfig_basic() string { + return ` +data "aws_synthetics_runtime_versions" "test" {} +` +} + +func testAccRuntimeVersionsDataSourceConfig_skipDeprecated(skipDeprecated bool) string { + return fmt.Sprintf(` +data "aws_synthetics_runtime_versions" "test" { + skip_deprecated = %[1]t +} +`, skipDeprecated) +} diff --git a/internal/service/synthetics/service_package_gen.go b/internal/service/synthetics/service_package_gen.go index 4c7cc2c2e56..37532c8abfa 100644 --- a/internal/service/synthetics/service_package_gen.go +++ b/internal/service/synthetics/service_package_gen.go @@ -15,7 +15,12 @@ import ( type servicePackage struct{} func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.ServicePackageFrameworkDataSource { - return []*types.ServicePackageFrameworkDataSource{} + return []*types.ServicePackageFrameworkDataSource{ + { + Factory: newDataSourceRuntimeVersions, + Name: "Runtime Versions", + }, + } } func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { diff --git a/website/docs/d/synthetics_runtime_versions.html.markdown b/website/docs/d/synthetics_runtime_versions.html.markdown new file mode 100644 index 00000000000..3bad39a4557 --- /dev/null +++ b/website/docs/d/synthetics_runtime_versions.html.markdown @@ -0,0 +1,34 @@ +--- +subcategory: "CloudWatch Synthetics" +layout: "aws" +page_title: "AWS: aws_synthetics_runtime_versions" +description: |- + Terraform data source for managing an AWS CloudWatch Synthetics Runtime Versions. +--- + +# Data Source: aws_synthetics_runtime_versions + +Terraform data source for managing an AWS CloudWatch Synthetics Runtime Versions. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_synthetics_runtime_versions" "example" { + skip_deprecated = true +} +``` + +## Argument Reference + +The following arguments are optional: + +* `skip_deprecated` - (Optional) Whether deprecated runtime versions should be skipped. Default to `false`. + +## Attribute Reference + +This data source exports the following attributes in addition to the arguments above: + +* `id` - Name of the AWS region from which runtime versions are fetched. +* `version_names` - List of runtime version names. From aaa0654fca67b0f3b984f7efd488d182003a87aa Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Fri, 6 Sep 2024 13:01:22 -0400 Subject: [PATCH 2/8] feat: Add new aws_synthetics_runtime_version data source --- .changelog/39180.txt | 3 + .../synthetics/runtime_version_data_source.go | 185 ++++++++++++++++++ .../runtime_version_data_source_test.go | 80 ++++++++ .../service/synthetics/service_package_gen.go | 4 + .../synthetics_runtime_version.html.markdown | 51 +++++ 5 files changed, 323 insertions(+) create mode 100644 internal/service/synthetics/runtime_version_data_source.go create mode 100644 internal/service/synthetics/runtime_version_data_source_test.go create mode 100644 website/docs/d/synthetics_runtime_version.html.markdown diff --git a/.changelog/39180.txt b/.changelog/39180.txt index 833449dbd68..a01b0938240 100644 --- a/.changelog/39180.txt +++ b/.changelog/39180.txt @@ -1,3 +1,6 @@ ```release-note:new-data-source +aws_synthetics_runtime_version +``` +```release-note:new-data-source aws_synthetics_runtime_versions ``` \ No newline at end of file diff --git a/internal/service/synthetics/runtime_version_data_source.go b/internal/service/synthetics/runtime_version_data_source.go new file mode 100644 index 00000000000..5b4a11b3388 --- /dev/null +++ b/internal/service/synthetics/runtime_version_data_source.go @@ -0,0 +1,185 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package synthetics + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/synthetics/types" + "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + "github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkDataSource(name="Runtime Version") +func newDataSourceRuntimeVersion(context.Context) (datasource.DataSourceWithConfigure, error) { + return &dataSourceRuntimeVersion{}, nil +} + +const ( + DSNameRuntimeVersion = "Runtime Version Data Source" +) + +type dataSourceRuntimeVersion struct { + framework.DataSourceWithConfigure +} + +func (d *dataSourceRuntimeVersion) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { // nosemgrep:ci.meta-in-func-name + resp.TypeName = "aws_synthetics_runtime_version" +} + +func (d *dataSourceRuntimeVersion) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "deprecation_date": schema.StringAttribute{ + CustomType: timetypes.RFC3339Type{}, + Computed: true, + }, + names.AttrDescription: schema.StringAttribute{ + Computed: true, + }, + names.AttrID: framework.IDAttribute(), + "release_date": schema.StringAttribute{ + CustomType: timetypes.RFC3339Type{}, + Computed: true, + }, + "latest": schema.BoolAttribute{ + Optional: true, + Validators: []validator.Bool{ + boolValueEqualsValidator{ + Value: true, + }, + boolvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("latest"), + path.MatchRoot(names.AttrVersion), + }...), + }, + }, + names.AttrPrefix: schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^.*[^-]$`), "must not end with a hyphen"), + }, + }, + names.AttrVersion: schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^\d.*$`), "must start with a digit"), + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("latest"), + path.MatchRoot(names.AttrVersion), + }...), + }, + }, + "version_name": schema.StringAttribute{ + Computed: true, + }, + }, + } +} + +func (d *dataSourceRuntimeVersion) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + conn := d.Meta().SyntheticsClient(ctx) + + var data dataSourceRuntimeVersionModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + latest := data.Latest.ValueBool() + prefix := data.Prefix.ValueString() + version := data.Version.ValueString() + + out, err := FindRuntimeVersions(ctx, conn) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Synthetics, create.ErrActionReading, DSNameRuntimeVersion, "", err), + err.Error(), + ) + return + } + + var runtimeVersion *awstypes.RuntimeVersion + var latestReleaseDate *time.Time + + for _, v := range out { + if strings.HasPrefix(aws.ToString(v.VersionName), prefix) { + if latest { + if latestReleaseDate == nil || aws.ToTime(v.ReleaseDate).After(*latestReleaseDate) { + latestReleaseDate = v.ReleaseDate + runtimeVersion = &v + } + } else { + if aws.ToString(v.VersionName) == fmt.Sprintf("%s-%s", prefix, version) { + runtimeVersion = &v + break + } + } + } + } + + if runtimeVersion == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Synthetics, create.ErrActionReading, DSNameRuntimeVersion, "", err), + "Query returned no results.", + ) + return + } + + data.ID = flex.StringToFramework(ctx, runtimeVersion.VersionName) + resp.Diagnostics.Append(flex.Flatten(ctx, runtimeVersion, &data, flex.WithFieldNamePrefix("{{ .Resource }}"))...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +type dataSourceRuntimeVersionModel struct { + DeprecationDate timetypes.RFC3339 `tfsdk:"deprecation_date"` + Description types.String `tfsdk:"description"` + ID types.String `tfsdk:"id"` + Latest types.Bool `tfsdk:"latest"` + Prefix types.String `tfsdk:"prefix"` + ReleaseDate timetypes.RFC3339 `tfsdk:"release_date"` + Version types.String `tfsdk:"version"` + VersionName types.String `tfsdk:"version_name"` +} + +type boolValueEqualsValidator struct { + Value bool +} + +func (v boolValueEqualsValidator) Description(ctx context.Context) string { + return fmt.Sprintf("Value must be equal to %t", v.Value) +} + +func (v boolValueEqualsValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v boolValueEqualsValidator) ValidateBool(ctx context.Context, req validator.BoolRequest, resp *validator.BoolResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + if req.ConfigValue.ValueBool() != v.Value { + resp.Diagnostics.AddAttributeError(req.Path, "Invalid Bool Value", fmt.Sprintf("Value must be equal to %t", v.Value)) + } +} diff --git a/internal/service/synthetics/runtime_version_data_source_test.go b/internal/service/synthetics/runtime_version_data_source_test.go new file mode 100644 index 00000000000..c51dbdcb20c --- /dev/null +++ b/internal/service/synthetics/runtime_version_data_source_test.go @@ -0,0 +1,80 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package synthetics_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccSyntheticsRuntimeVersionDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + dataSourceName := "data.aws_synthetics_runtime_version.test" + prefix := "syn-nodejs-puppeteer" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SyntheticsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccRuntimeVersionDataSourceConfig_basic(prefix), + Check: resource.ComposeTestCheckFunc( + acctest.CheckResourceAttrHasPrefix(dataSourceName, "version_name", prefix), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrID, dataSourceName, "version_name"), + resource.TestCheckNoResourceAttr(dataSourceName, "deprecation_date"), + resource.TestCheckResourceAttrSet(dataSourceName, names.AttrDescription), + resource.TestCheckResourceAttrSet(dataSourceName, "release_date"), + ), + }, + }, + }) +} + +func TestAccSyntheticsRuntimeVersionDataSource_deprecatedVersion(t *testing.T) { + ctx := acctest.Context(t) + dataSourceName := "data.aws_synthetics_runtime_version.test" + prefix := "syn-nodejs-puppeteer" + version := "3.0" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SyntheticsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccRuntimeVersionDataSourceConfig_version(prefix, version), + Check: resource.ComposeTestCheckFunc( + acctest.CheckResourceAttrHasPrefix(dataSourceName, "version_name", fmt.Sprintf("%s-%s", prefix, version)), + resource.TestCheckResourceAttrPair(dataSourceName, names.AttrID, dataSourceName, "version_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "deprecation_date"), + resource.TestCheckResourceAttrSet(dataSourceName, names.AttrDescription), + resource.TestCheckResourceAttrSet(dataSourceName, "release_date"), + ), + }, + }, + }) +} + +func testAccRuntimeVersionDataSourceConfig_basic(prefix string) string { + return fmt.Sprintf(` +data "aws_synthetics_runtime_version" "test" { + prefix = %[1]q + latest = true +} +`, prefix) +} + +func testAccRuntimeVersionDataSourceConfig_version(prefix, version string) string { + return fmt.Sprintf(` +data "aws_synthetics_runtime_version" "test" { + prefix = %[1]q + version = %[2]q +} +`, prefix, version) +} diff --git a/internal/service/synthetics/service_package_gen.go b/internal/service/synthetics/service_package_gen.go index 37532c8abfa..7dfe81a556c 100644 --- a/internal/service/synthetics/service_package_gen.go +++ b/internal/service/synthetics/service_package_gen.go @@ -16,6 +16,10 @@ type servicePackage struct{} func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.ServicePackageFrameworkDataSource { return []*types.ServicePackageFrameworkDataSource{ + { + Factory: newDataSourceRuntimeVersion, + Name: "Runtime Version", + }, { Factory: newDataSourceRuntimeVersions, Name: "Runtime Versions", diff --git a/website/docs/d/synthetics_runtime_version.html.markdown b/website/docs/d/synthetics_runtime_version.html.markdown new file mode 100644 index 00000000000..1c1ff79b746 --- /dev/null +++ b/website/docs/d/synthetics_runtime_version.html.markdown @@ -0,0 +1,51 @@ +--- +subcategory: "CloudWatch Synthetics" +layout: "aws" +page_title: "AWS: aws_synthetics_runtime_version" +description: |- + Terraform data source for managing an AWS CloudWatch Synthetics Runtime Version. +--- +# Data Source: aws_synthetics_runtime_version + +Terraform data source for managing an AWS CloudWatch Synthetics Runtime Version. + +## Example Usage + +### Latest Runtime Version + +```terraform +data "aws_synthetics_runtime_version" "example" { + prefix = "syn-nodejs-puppeteer" + latest = true +} +``` + +### Specific Runtime Version + +```terraform +data "aws_synthetics_runtime_version" "example" { + prefix = "syn-nodejs-puppeteer" + version = "9.0" +} +``` + +## Argument Reference + +The following arguments are required: + +* `prefix` - (Required) Name prefix of the runtime version (for example, `syn-nodejs-puppeteer`). + +The following arguments are optional: + +* `latest` - (Optional) Whether the latest version of the runtime should be fetched. Conflicts with `version`. Valid values: `true`. +* `version` - (Optional) Version of the runtime to be fetched (for example, `9.0`). Conflicts with `latest`. + +## Attribute Reference + +This data source exports the following attributes in addition to the arguments above: + +* `deprecation_date` - Date of deprecation if the runtme version is deprecated. +* `description` - Description of the runtime version, created by Amazon. +* `id` - Name of the runtime version. For a list of valid runtime versions, see [Canary Runtime Versions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html). +* `release_date` - Date that the runtime version was released. +* `version_name` - Name of the runtime version. For a list of valid runtime versions, see [Canary Runtime Versions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html). From 04ffe0ef482bb4ab3628032891f3dc8733559835 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Tue, 17 Sep 2024 16:11:07 -0400 Subject: [PATCH 3/8] internal/framework/validators: add BoolEquals validator ```console % go test -count=1 ./internal/framework/validators/... ok github.com/hashicorp/terraform-provider-aws/internal/framework/validators 0.492s ``` --- internal/framework/validators/bool_equals.go | 51 ++++++++++++++ .../framework/validators/bool_equals_test.go | 70 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 internal/framework/validators/bool_equals.go create mode 100644 internal/framework/validators/bool_equals_test.go diff --git a/internal/framework/validators/bool_equals.go b/internal/framework/validators/bool_equals.go new file mode 100644 index 00000000000..fb601629be3 --- /dev/null +++ b/internal/framework/validators/bool_equals.go @@ -0,0 +1,51 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ validator.Bool = boolEqualsValidator{} + +type boolEqualsValidator struct { + value types.Bool +} + +func (v boolEqualsValidator) Description(ctx context.Context) string { + return fmt.Sprintf("Value must be %q", v.value) +} + +func (v boolEqualsValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v boolEqualsValidator) ValidateBool(ctx context.Context, req validator.BoolRequest, resp *validator.BoolResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + configValue := req.ConfigValue + + if !configValue.Equal(v.value) { + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic( + req.Path, + v.Description(ctx), + configValue.String(), + )) + } +} + +// BoolEquals checks that the Bool held in the attribute matches the +// given `value` +func BoolEquals(value bool) validator.Bool { + return boolEqualsValidator{ + value: types.BoolValue(value), + } +} diff --git a/internal/framework/validators/bool_equals_test.go b/internal/framework/validators/bool_equals_test.go new file mode 100644 index 00000000000..bbbd176d5f2 --- /dev/null +++ b/internal/framework/validators/bool_equals_test.go @@ -0,0 +1,70 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" +) + +func TestBoolEqualsValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in types.Bool + validator validator.Bool + expErrors int + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.BoolValue(true), + validator: fwvalidators.BoolEquals(true), + expErrors: 0, + }, + "simple-mismatch": { + in: types.BoolValue(false), + validator: fwvalidators.BoolEquals(true), + expErrors: 1, + }, + "skip-validation-on-null": { + in: types.BoolNull(), + validator: fwvalidators.BoolEquals(true), + expErrors: 0, + }, + "skip-validation-on-unknown": { + in: types.BoolUnknown(), + validator: fwvalidators.BoolEquals(true), + expErrors: 0, + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + t.Parallel() + req := validator.BoolRequest{ + ConfigValue: test.in, + } + res := validator.BoolResponse{} + test.validator.ValidateBool(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != res.Diagnostics.ErrorsCount() { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, res.Diagnostics.ErrorsCount(), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", res.Diagnostics.ErrorsCount(), res.Diagnostics) + } + }) + } +} From 5ac826734816f2711dd362fd48675e5cc8e35348 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Tue, 17 Sep 2024 16:12:28 -0400 Subject: [PATCH 4/8] d/aws_synthetics_runtime_version: use BoolEquals validator --- .../synthetics/runtime_version_data_source.go | 29 ++----------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/internal/service/synthetics/runtime_version_data_source.go b/internal/service/synthetics/runtime_version_data_source.go index 5b4a11b3388..d4274a247ba 100644 --- a/internal/service/synthetics/runtime_version_data_source.go +++ b/internal/service/synthetics/runtime_version_data_source.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -61,9 +62,7 @@ func (d *dataSourceRuntimeVersion) Schema(ctx context.Context, req datasource.Sc "latest": schema.BoolAttribute{ Optional: true, Validators: []validator.Bool{ - boolValueEqualsValidator{ - Value: true, - }, + fwvalidators.BoolEquals(true), boolvalidator.ExactlyOneOf(path.Expressions{ path.MatchRoot("latest"), path.MatchRoot(names.AttrVersion), @@ -143,7 +142,7 @@ func (d *dataSourceRuntimeVersion) Read(ctx context.Context, req datasource.Read } data.ID = flex.StringToFramework(ctx, runtimeVersion.VersionName) - resp.Diagnostics.Append(flex.Flatten(ctx, runtimeVersion, &data, flex.WithFieldNamePrefix("{{ .Resource }}"))...) + resp.Diagnostics.Append(flex.Flatten(ctx, runtimeVersion, &data)...) if resp.Diagnostics.HasError() { return } @@ -161,25 +160,3 @@ type dataSourceRuntimeVersionModel struct { Version types.String `tfsdk:"version"` VersionName types.String `tfsdk:"version_name"` } - -type boolValueEqualsValidator struct { - Value bool -} - -func (v boolValueEqualsValidator) Description(ctx context.Context) string { - return fmt.Sprintf("Value must be equal to %t", v.Value) -} - -func (v boolValueEqualsValidator) MarkdownDescription(ctx context.Context) string { - return v.Description(ctx) -} - -func (v boolValueEqualsValidator) ValidateBool(ctx context.Context, req validator.BoolRequest, resp *validator.BoolResponse) { - if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { - return - } - - if req.ConfigValue.ValueBool() != v.Value { - resp.Diagnostics.AddAttributeError(req.Path, "Invalid Bool Value", fmt.Sprintf("Value must be equal to %t", v.Value)) - } -} From 0c04d798925d5b03d9b29ba20a6e951d44d58d65 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Tue, 17 Sep 2024 16:24:33 -0400 Subject: [PATCH 5/8] d/aws_synthetics_runtime_version: keep finder private --- internal/service/synthetics/find.go | 2 +- internal/service/synthetics/runtime_version_data_source.go | 2 +- internal/service/synthetics/runtime_versions_data_source.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/service/synthetics/find.go b/internal/service/synthetics/find.go index f5d419cf28e..5f4fbb3d24c 100644 --- a/internal/service/synthetics/find.go +++ b/internal/service/synthetics/find.go @@ -103,7 +103,7 @@ func FindAssociatedGroup(ctx context.Context, conn *synthetics.Client, canaryArn return &group, nil } -func FindRuntimeVersions(ctx context.Context, conn *synthetics.Client) ([]awstypes.RuntimeVersion, error) { +func findRuntimeVersions(ctx context.Context, conn *synthetics.Client) ([]awstypes.RuntimeVersion, error) { input := &synthetics.DescribeRuntimeVersionsInput{} output, err := conn.DescribeRuntimeVersions(ctx, input) diff --git a/internal/service/synthetics/runtime_version_data_source.go b/internal/service/synthetics/runtime_version_data_source.go index d4274a247ba..0c3f14dbe77 100644 --- a/internal/service/synthetics/runtime_version_data_source.go +++ b/internal/service/synthetics/runtime_version_data_source.go @@ -105,7 +105,7 @@ func (d *dataSourceRuntimeVersion) Read(ctx context.Context, req datasource.Read prefix := data.Prefix.ValueString() version := data.Version.ValueString() - out, err := FindRuntimeVersions(ctx, conn) + out, err := findRuntimeVersions(ctx, conn) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.Synthetics, create.ErrActionReading, DSNameRuntimeVersion, "", err), diff --git a/internal/service/synthetics/runtime_versions_data_source.go b/internal/service/synthetics/runtime_versions_data_source.go index 47b4b8e5a5c..ffab5f9add2 100644 --- a/internal/service/synthetics/runtime_versions_data_source.go +++ b/internal/service/synthetics/runtime_versions_data_source.go @@ -60,7 +60,7 @@ func (d *dataSourceRuntimeVersions) Read(ctx context.Context, req datasource.Rea } skipDeprecated := data.SkipDeprecated.ValueBool() - out, err := FindRuntimeVersions(ctx, conn) + out, err := findRuntimeVersions(ctx, conn) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.Synthetics, create.ErrActionReading, DSNameRuntimeVersions, "", err), From 22448f5a86ddafaa1e590e640d276dce3bf8df41 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Tue, 17 Sep 2024 16:58:45 -0400 Subject: [PATCH 6/8] d/aws_synthetics_runtime_versions: add runtime_versions, rm skip_deprecated This change returns a data structure more closely matching the `DescribeRuntimeVersions` API result. It also removes the `skip_deprecated` argument, as the ability to use the `latest` argument on the `aws_synthetics_runtime_version` data source eliminates much of the need to exclude deprecated versions from the result set. ```console % make testacc PKG=synthetics TESTS=TestAccSyntheticsRuntimeVersion make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.6 test ./internal/service/synthetics/... -v -count 1 -parallel 20 -run='TestAccSyntheticsRuntimeVersion' -timeout 360m --- PASS: TestAccSyntheticsRuntimeVersionsDataSource_basic (10.21s) --- PASS: TestAccSyntheticsRuntimeVersionDataSource_deprecatedVersion (10.21s) --- PASS: TestAccSyntheticsRuntimeVersionDataSource_basic (10.25s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/synthetics 16.489s ``` --- .../runtime_versions_data_source.go | 57 +++++++++++-------- .../runtime_versions_data_source_test.go | 44 ++------------ .../synthetics_runtime_versions.html.markdown | 18 +++--- 3 files changed, 49 insertions(+), 70 deletions(-) diff --git a/internal/service/synthetics/runtime_versions_data_source.go b/internal/service/synthetics/runtime_versions_data_source.go index ffab5f9add2..3de433a5c74 100644 --- a/internal/service/synthetics/runtime_versions_data_source.go +++ b/internal/service/synthetics/runtime_versions_data_source.go @@ -6,7 +6,7 @@ package synthetics import ( "context" - "github.com/aws/aws-sdk-go-v2/aws" + "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/types" @@ -38,13 +38,28 @@ func (d *dataSourceRuntimeVersions) Schema(ctx context.Context, req datasource.S resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ names.AttrID: framework.IDAttribute(), - "skip_deprecated": schema.BoolAttribute{ - Optional: true, - }, - "version_names": schema.ListAttribute{ - CustomType: fwtypes.ListOfStringType, - ElementType: types.StringType, - Computed: true, + }, + Blocks: map[string]schema.Block{ + "runtime_versions": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[runtimeVersionModel](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "deprecation_date": schema.StringAttribute{ + CustomType: timetypes.RFC3339Type{}, + Computed: true, + }, + names.AttrDescription: schema.StringAttribute{ + Computed: true, + }, + "release_date": schema.StringAttribute{ + CustomType: timetypes.RFC3339Type{}, + Computed: true, + }, + "version_name": schema.StringAttribute{ + Computed: true, + }, + }, + }, }, }, } @@ -59,7 +74,6 @@ func (d *dataSourceRuntimeVersions) Read(ctx context.Context, req datasource.Rea return } - skipDeprecated := data.SkipDeprecated.ValueBool() out, err := findRuntimeVersions(ctx, conn) if err != nil { resp.Diagnostics.AddError( @@ -69,22 +83,19 @@ func (d *dataSourceRuntimeVersions) Read(ctx context.Context, req datasource.Rea return } - var versionNames []string - - for _, v := range out { - if !skipDeprecated || v.DeprecationDate == nil { - versionNames = append(versionNames, aws.ToString(v.VersionName)) - } - } - - data.ID = flex.StringToFramework(ctx, &d.Meta().Region) - data.VersionNames = flex.FlattenFrameworkStringValueListOfString(ctx, versionNames) - + data.ID = flex.StringValueToFramework(ctx, d.Meta().Region) + resp.Diagnostics.Append(flex.Flatten(ctx, out, &data.RuntimeVersions)...) resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } type dataSourceRuntimeVersionsModel struct { - ID types.String `tfsdk:"id"` - SkipDeprecated types.Bool `tfsdk:"skip_deprecated"` - VersionNames fwtypes.ListValueOf[types.String] `tfsdk:"version_names"` + ID types.String `tfsdk:"id"` + RuntimeVersions fwtypes.ListNestedObjectValueOf[runtimeVersionModel] `tfsdk:"runtime_versions"` +} + +type runtimeVersionModel struct { + DeprecationDate timetypes.RFC3339 `tfsdk:"deprecation_date"` + Description types.String `tfsdk:"description"` + ReleaseDate timetypes.RFC3339 `tfsdk:"release_date"` + VersionName types.String `tfsdk:"version_name"` } diff --git a/internal/service/synthetics/runtime_versions_data_source_test.go b/internal/service/synthetics/runtime_versions_data_source_test.go index c797ccf23ca..489be4e84af 100644 --- a/internal/service/synthetics/runtime_versions_data_source_test.go +++ b/internal/service/synthetics/runtime_versions_data_source_test.go @@ -4,10 +4,8 @@ package synthetics_test import ( - "fmt" "testing" - "github.com/YakDriver/regexache" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/names" @@ -25,52 +23,18 @@ func TestAccSyntheticsRuntimeVersionsDataSource_basic(t *testing.T) { { Config: testAccRuntimeVersionsDataSourceConfig_basic(), Check: resource.ComposeTestCheckFunc( - acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "version_names.#", 0), - resource.TestCheckTypeSetElemAttr(dataSourceName, "version_names.*", "syn-1.0"), + acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "runtime_versions.#", 0), + resource.TestCheckTypeSetElemNestedAttrs(dataSourceName, "runtime_versions.*", map[string]string{ + "version_name": "syn-1.0", + }), ), }, }, }) } -func TestAccSyntheticsRuntimeVersionsDataSource_skipDeprecated(t *testing.T) { - ctx := acctest.Context(t) - dataSourceName := "data.aws_synthetics_runtime_versions.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, names.SyntheticsServiceID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccRuntimeVersionsDataSourceConfig_skipDeprecated(false), - Check: resource.ComposeTestCheckFunc( - acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "version_names.#", 0), - resource.TestCheckTypeSetElemAttr(dataSourceName, "version_names.*", "syn-1.0"), - ), - }, - { - Config: testAccRuntimeVersionsDataSourceConfig_skipDeprecated(true), - Check: resource.ComposeTestCheckFunc( - acctest.CheckResourceAttrGreaterThanValue(dataSourceName, "version_names.#", 0), - resource.TestCheckTypeSetElemAttr(dataSourceName, "version_names.*", "syn-1.0"), - ), - ExpectError: regexache.MustCompile(`no TypeSet element "version_names.*", with value "syn-1.0" in state`), - }, - }, - }) -} - func testAccRuntimeVersionsDataSourceConfig_basic() string { return ` data "aws_synthetics_runtime_versions" "test" {} ` } - -func testAccRuntimeVersionsDataSourceConfig_skipDeprecated(skipDeprecated bool) string { - return fmt.Sprintf(` -data "aws_synthetics_runtime_versions" "test" { - skip_deprecated = %[1]t -} -`, skipDeprecated) -} diff --git a/website/docs/d/synthetics_runtime_versions.html.markdown b/website/docs/d/synthetics_runtime_versions.html.markdown index 3bad39a4557..921e592e5d3 100644 --- a/website/docs/d/synthetics_runtime_versions.html.markdown +++ b/website/docs/d/synthetics_runtime_versions.html.markdown @@ -15,20 +15,24 @@ Terraform data source for managing an AWS CloudWatch Synthetics Runtime Versions ### Basic Usage ```terraform -data "aws_synthetics_runtime_versions" "example" { - skip_deprecated = true -} +data "aws_synthetics_runtime_versions" "example" {} ``` ## Argument Reference -The following arguments are optional: - -* `skip_deprecated` - (Optional) Whether deprecated runtime versions should be skipped. Default to `false`. +There are no arguments available for this data source. ## Attribute Reference This data source exports the following attributes in addition to the arguments above: * `id` - Name of the AWS region from which runtime versions are fetched. -* `version_names` - List of runtime version names. +* `runtime_versions` - List of runtime versions. See [`runtime_versions` attribute reference](#runtime_versions-attribute-reference). + +### `runtime_versions` Attribute Reference + +* `deprecation_date` - Date of deprecation if the runtme version is deprecated. +* `description` - Description of the runtime version, created by Amazon. +* `release_date` - Date that the runtime version was released. +* `version_name` - Name of the runtime version. +For a list of valid runtime versions, see [Canary Runtime Versions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html). From 21884f69df8d87ef1bf394300ba02459f9a36c7b Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 18 Sep 2024 08:54:36 -0400 Subject: [PATCH 7/8] chore: fix copyloopvar lint --- internal/framework/validators/bool_equals_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/framework/validators/bool_equals_test.go b/internal/framework/validators/bool_equals_test.go index bbbd176d5f2..86360415fca 100644 --- a/internal/framework/validators/bool_equals_test.go +++ b/internal/framework/validators/bool_equals_test.go @@ -45,7 +45,6 @@ func TestBoolEqualsValidator(t *testing.T) { } for name, test := range testCases { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() req := validator.BoolRequest{ From 6d178c087aaa7c8b4ffe7471013dca4aa1c4259e Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 18 Sep 2024 09:25:45 -0400 Subject: [PATCH 8/8] r/aws_synthetics_canary(test): use runtime version data source This eliminates the dependency on hardcoded runtime versions which become deprecated over time and can cause test failures. ```console % make testacc PKG=synthetics TESTS=TestAccSyntheticsCanary_ make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.22.6 test ./internal/service/synthetics/... -v -count 1 -parallel 20 -run='TestAccSyntheticsCanary_' -timeout 360m --- PASS: TestAccSyntheticsCanary_tags (94.55s) --- PASS: TestAccSyntheticsCanary_runtimeVersion (129.64s) --- PASS: TestAccSyntheticsCanary_runTracing (144.68s) --- PASS: TestAccSyntheticsCanary_startCanary (194.92s) --- PASS: TestAccSyntheticsCanary_run (216.00s) --- PASS: TestAccSyntheticsCanary_runEnvironmentVariables (219.09s) --- PASS: TestAccSyntheticsCanary_artifactEncryption (237.15s) --- PASS: TestAccSyntheticsCanary_disappears (470.10s) --- PASS: TestAccSyntheticsCanary_s3 (559.86s) --- PASS: TestAccSyntheticsCanary_StartCanary_codeChanges (586.04s) --- PASS: TestAccSyntheticsCanary_basic (606.08s) --- PASS: TestAccSyntheticsCanary_rate (678.13s) --- PASS: TestAccSyntheticsCanary_vpc (2180.95s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/synthetics 2187.415s ``` --- internal/service/synthetics/canary_test.go | 101 +++++++++++++-------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/internal/service/synthetics/canary_test.go b/internal/service/synthetics/canary_test.go index c6410a6006d..d7d57e6ac32 100644 --- a/internal/service/synthetics/canary_test.go +++ b/internal/service/synthetics/canary_test.go @@ -25,6 +25,7 @@ func TestAccSyntheticsCanary_basic(t *testing.T) { var conf1, conf2 awstypes.Canary rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(8)) resourceName := "aws_synthetics_canary.test" + runtimeVersionDataSourceName := "data.aws_synthetics_runtime_version.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -38,7 +39,7 @@ func TestAccSyntheticsCanary_basic(t *testing.T) { testAccCheckCanaryExists(ctx, resourceName, &conf1), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "synthetics", regexache.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), - resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-nodejs-puppeteer-9.0"), + resource.TestCheckResourceAttrPair(resourceName, "runtime_version", runtimeVersionDataSourceName, "version_name"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), @@ -70,7 +71,7 @@ func TestAccSyntheticsCanary_basic(t *testing.T) { testAccCheckCanaryExists(ctx, resourceName, &conf2), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "synthetics", regexache.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), - resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-nodejs-puppeteer-9.0"), + resource.TestCheckResourceAttrPair(resourceName, "runtime_version", runtimeVersionDataSourceName, "version_name"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), @@ -313,6 +314,7 @@ func TestAccSyntheticsCanary_s3(t *testing.T) { var conf awstypes.Canary rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(8)) resourceName := "aws_synthetics_canary.test" + runtimeVersionDataSourceName := "data.aws_synthetics_runtime_version.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -326,7 +328,7 @@ func TestAccSyntheticsCanary_s3(t *testing.T) { testAccCheckCanaryExists(ctx, resourceName, &conf), acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "synthetics", regexache.MustCompile(`canary:.+`)), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), - resource.TestCheckResourceAttr(resourceName, "runtime_version", "syn-nodejs-puppeteer-9.0"), + resource.TestCheckResourceAttrPair(resourceName, "runtime_version", runtimeVersionDataSourceName, "version_name"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), @@ -782,6 +784,11 @@ resource "aws_iam_role_policy" "test" { } EOF } + +data "aws_synthetics_runtime_version" "test" { + prefix = "syn-nodejs-puppeteer" + latest = true +} `, rName) } @@ -793,7 +800,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -817,7 +824,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -842,7 +849,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -867,7 +874,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -886,14 +893,15 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_runEnvVariables2(rName string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(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-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -913,7 +921,9 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_basic(rName string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { # Must have bucket versioning enabled first depends_on = [aws_s3_bucket_versioning.test, aws_iam_role.test, aws_iam_role_policy.test] @@ -923,7 +933,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -934,7 +944,9 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_rate(rName string, rate string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { # Must have bucket versioning enabled first depends_on = [aws_s3_bucket_versioning.test, aws_iam_role.test, aws_iam_role_policy.test] @@ -944,7 +956,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true run_config { @@ -960,14 +972,16 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_artifactEncryption(rName string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(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-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true artifact_config { @@ -986,7 +1000,9 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_artifactEncryptionKMS(rName string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + fmt.Sprintf(` resource "aws_kms_key" "test" { description = %[1]q deletion_window_in_days = 7 @@ -998,7 +1014,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true artifact_config { @@ -1018,7 +1034,9 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_runtimeVersion(rName, version string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -1038,14 +1056,16 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_zipUpdated(rName string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/test/" execution_role_arn = aws_iam_role.test.arn handler = "exports.handler" zip_file = "test-fixtures/lambdatest_modified.zip" - runtime_version = "syn-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -1058,7 +1078,9 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_start(rName string, state bool) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -1066,7 +1088,7 @@ resource "aws_synthetics_canary" "test" { handler = "exports.handler" zip_file = "test-fixtures/lambdatest.zip" start_canary = %[2]t - runtime_version = "syn-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -1079,7 +1101,9 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_startZipUpdated(rName string, state bool) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -1087,7 +1111,7 @@ resource "aws_synthetics_canary" "test" { handler = "exports.handler" zip_file = "test-fixtures/lambdatest_modified.zip" start_canary = %[2]t - runtime_version = "syn-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -1100,7 +1124,9 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_basicS3Code(rName string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + fmt.Sprintf(` resource "aws_synthetics_canary" "test" { name = %[1]q artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" @@ -1109,7 +1135,7 @@ resource "aws_synthetics_canary" "test" { s3_bucket = aws_s3_object.test.bucket s3_key = aws_s3_object.test.key s3_version = aws_s3_object.test.version_id - runtime_version = "syn-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -1128,12 +1154,11 @@ resource "aws_s3_object" "test" { source = "test-fixtures/lambdatest.zip" etag = filemd5("test-fixtures/lambdatest.zip") } - `, rName)) } func testAccCanarySecurityGroupBaseConfig(rName string, count int) string { - return acctest.ConfigCompose(fmt.Sprintf(` + return fmt.Sprintf(` resource "aws_security_group" "test" { count = %[2]d vpc_id = aws_vpc.test.id @@ -1147,7 +1172,7 @@ 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, count)) +`, rName, count) } func testAccCanaryConfig_vpc1(rName string) string { @@ -1162,7 +1187,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -1191,7 +1216,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -1220,7 +1245,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-nodejs-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -1238,14 +1263,16 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_tags1(rName, tagKey1, tagValue1 string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(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-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule { @@ -1260,14 +1287,16 @@ resource "aws_synthetics_canary" "test" { } func testAccCanaryConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return acctest.ConfigCompose(testAccCanaryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose( + testAccCanaryConfig_base(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-puppeteer-9.0" + runtime_version = data.aws_synthetics_runtime_version.test.version_name delete_lambda = true schedule {