From aaa0654fca67b0f3b984f7efd488d182003a87aa Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Fri, 6 Sep 2024 13:01:22 -0400 Subject: [PATCH] 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).