From 5026b0bd50fe5668b8d2479af4c670497a25b8fb Mon Sep 17 00:00:00 2001 From: Terence Ho Date: Sun, 13 Oct 2024 01:19:18 -0700 Subject: [PATCH 1/9] initial cloudfront data source key group --- .../cloudfront/key_group_data_source.go | 201 +++++++++++++++++ .../cloudfront/key_group_data_source_test.go | 210 ++++++++++++++++++ .../docs/d/cloudfront_key_group.html.markdown | 45 ++++ 3 files changed, 456 insertions(+) create mode 100644 internal/service/cloudfront/key_group_data_source.go create mode 100644 internal/service/cloudfront/key_group_data_source_test.go create mode 100644 website/docs/d/cloudfront_key_group.html.markdown diff --git a/internal/service/cloudfront/key_group_data_source.go b/internal/service/cloudfront/key_group_data_source.go new file mode 100644 index 000000000000..0edbda2b1021 --- /dev/null +++ b/internal/service/cloudfront/key_group_data_source.go @@ -0,0 +1,201 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cloudfront +// **PLEASE DELETE THIS AND ALL TIP COMMENTS BEFORE SUBMITTING A PR FOR REVIEW!** +// +// TIP: ==== INTRODUCTION ==== +// Thank you for trying the skaff tool! +// +// You have opted to include these helpful comments. They all include "TIP:" +// to help you find and remove them when you're done with them. +// +// While some aspects of this file are customized to your input, the +// scaffold tool does *not* look at the AWS API and ensure it has correct +// function, structure, and variable names. It makes guesses based on +// commonalities. You will need to make significant adjustments. +// +// In other words, as generated, this is a rough outline of the work you will +// need to do. If something doesn't make sense for your situation, get rid of +// it. + +import ( + // TIP: ==== IMPORTS ==== + // This is a common set of imports but not customized to your code since + // your code hasn't been written yet. Make sure you, your IDE, or + // goimports -w fixes these imports. + // + // The provider linter wants your imports to be in two groups: first, + // standard library (i.e., "fmt" or "strings"), second, everything else. + // + // Also, AWS Go SDK v2 may handle nested structures differently than v1, + // using the services/cloudfront/types package. If so, you'll + // need to import types and reference the nested types, e.g., as + // awstypes.. + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudfront" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" + "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" +) + +// TIP: ==== FILE STRUCTURE ==== +// All data sources should follow this basic outline. Improve this data source's +// maintainability by sticking to it. +// +// 1. Package declaration +// 2. Imports +// 3. Main data source struct with schema method +// 4. Read method +// 5. Other functions (flatteners, expanders, waiters, finders, etc.) + +// Function annotations are used for datasource registration to the Provider. DO NOT EDIT. +// @FrameworkDataSource(name="Key Group") +func newDataSourceKeyGroup(context.Context) (datasource.DataSourceWithConfigure, error) { + return &dataSourceKeyGroup{}, nil +} + +const ( + DSNameKeyGroup = "Key Group Data Source" +) + +type dataSourceKeyGroup struct { + framework.DataSourceWithConfigure +} + +func (d *dataSourceKeyGroup) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { // nosemgrep:ci.meta-in-func-name + resp.TypeName = "aws_cloudfront_key_group" +} + +// TIP: ==== SCHEMA ==== +// In the schema, add each of the arguments and attributes in snake +// case (e.g., delete_automated_backups). +// * Alphabetize arguments to make them easier to find. +// * Do not add a blank line between arguments/attributes. +// +// Users can configure argument values while attribute values cannot be +// configured and are used as output. Arguments have either: +// Required: true, +// Optional: true, +// +// All attributes will be computed and some arguments. If users will +// want to read updated information or detect drift for an argument, +// it should be computed: +// Computed: true, +// +// You will typically find arguments in the input struct +// (e.g., CreateDBInstanceInput) for the create operation. Sometimes +// they are only in the input struct (e.g., ModifyDBInstanceInput) for +// the modify operation. +// +// For more about schema options, visit +// https://developer.hashicorp.com/terraform/plugin/framework/handling-data/schemas?page=schemas +func (d *dataSourceKeyGroup) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "etag": schema.StringAttribute{ + Computed: true, + }, + names.AttrID: { + Type: schema.TypeString, + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "key_group_config": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "items": schema.ListAttribute{ + ElementType: types.StringType, + Required: true, + }, + names.AttrName: schema.StringAttribute{ + Computed: true, + }, + "comment": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + } +} +// TIP: ==== ASSIGN CRUD METHODS ==== +// Data sources only have a read method. +func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + // TIP: ==== DATA SOURCE READ ==== + // Generally, the Read function should do the following things. Make + // sure there is a good reason if you don't do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the config + // 3. Get information about a resource from AWS + // 4. Set the ID, arguments, and attributes + // 5. Set the tags + // 6. Set the state + // TIP: -- 1. Get a client connection to the relevant service + conn := d.Meta().CloudFrontClient(ctx) + + // TIP: -- 2. Fetch the config + var data dataSourceKeyGroupModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Get information about a resource from AWS + out, err := findKeyGroupById(ctx, conn, data.Name.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.CloudFront, create.ErrActionReading, DSNameKeyGroup, data.Name.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 4. Set the ID, arguments, and attributes + // Using a field name prefix allows mapping fields such as `KeyGroupId` to `ID` + resp.Diagnostics.Append(flex.Flatten(ctx, out, &data, flex.WithFieldNamePrefix("KeyGroup"))...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 5. Set the tags + data.Etag = flex.StringToFramework(ctx, out.ETag) + + // TIP: -- 6. Set the state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + + +// TIP: ==== DATA STRUCTURES ==== +// With Terraform Plugin-Framework configurations are deserialized into +// Go types, providing type safety without the need for type assertions. +// These structs should match the schema definition exactly, and the `tfsdk` +// tag value should match the attribute name. +// +// Nested objects are represented in their own data struct. These will +// also have a corresponding attribute type mapping for use inside flex +// functions. +// +// See more: +// https://developer.hashicorp.com/terraform/plugin/framework/handling-data/accessing-values +type dataSourceKeyGroupModel struct { + Etag types.String `tfsdk:"etag"` + Items types.List `tfsdk:"items"` + Comment types.String `tfsdk:"comment"` + Name types.String `tfsdk:"name"` + Type types.String `tfsdk:"type"` +} + +type complexArgumentModel struct { + NestedRequired types.String `tfsdk:"nested_required"` + NestedOptional types.String `tfsdk:"nested_optional"` +} diff --git a/internal/service/cloudfront/key_group_data_source_test.go b/internal/service/cloudfront/key_group_data_source_test.go new file mode 100644 index 000000000000..50a89f3d37c7 --- /dev/null +++ b/internal/service/cloudfront/key_group_data_source_test.go @@ -0,0 +1,210 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cloudfront_test +// **PLEASE DELETE THIS AND ALL TIP COMMENTS BEFORE SUBMITTING A PR FOR REVIEW!** +// +// TIP: ==== INTRODUCTION ==== +// Thank you for trying the skaff tool! +// +// You have opted to include these helpful comments. They all include "TIP:" +// to help you find and remove them when you're done with them. +// +// While some aspects of this file are customized to your input, the +// scaffold tool does *not* look at the AWS API and ensure it has correct +// function, structure, and variable names. It makes guesses based on +// commonalities. You will need to make significant adjustments. +// +// In other words, as generated, this is a rough outline of the work you will +// need to do. If something doesn't make sense for your situation, get rid of +// it. + +import ( + // TIP: ==== IMPORTS ==== + // This is a common set of imports but not customized to your code since + // your code hasn't been written yet. Make sure you, your IDE, or + // goimports -w fixes these imports. + // + // The provider linter wants your imports to be in two groups: first, + // standard library (i.e., "fmt" or "strings"), second, everything else. + // + // Also, AWS Go SDK v2 may handle nested structures differently than v1, + // using the services/cloudfront/types package. If so, you'll + // need to import types and reference the nested types, e.g., as + // types.. + "fmt" + "strings" + "testing" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudfront" + "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + + // TIP: You will often need to import the package that this test file lives + // in. Since it is in the "test" context, it must import the package to use + // any normal context constants, variables, or functions. + tfcloudfront "github.com/hashicorp/terraform-provider-aws/internal/service/cloudfront" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// TIP: File Structure. The basic outline for all test files should be as +// follows. Improve this data source's maintainability by following this +// outline. +// +// 1. Package declaration (add "_test" since this is a test file) +// 2. Imports +// 3. Unit tests +// 4. Basic test +// 5. Disappears test +// 6. All the other tests +// 7. Helper functions (exists, destroy, check, etc.) +// 8. Functions that return Terraform configurations + + +// TIP: ==== UNIT TESTS ==== +// This is an example of a unit test. Its name is not prefixed with +// "TestAcc" like an acceptance test. +// +// Unlike acceptance tests, unit tests do not access AWS and are focused on a +// function (or method). Because of this, they are quick and cheap to run. +// +// In designing a data source's implementation, isolate complex bits from AWS bits +// so that they can be tested through a unit test. We encourage more unit tests +// in the provider. +// +// Cut and dry functions using well-used patterns, like typical flatteners and +// expanders, don't need unit testing. However, if they are complex or +// intricate, they should be unit tested. +func TestKeyGroupExampleUnitTest(t *testing.T) { + t.Parallel() + + testCases := []struct { + TestName string + Input string + Expected string + Error bool + }{ + { + TestName: "empty", + Input: "", + Expected: "", + Error: true, + }, + { + TestName: "descriptive name", + Input: "some input", + Expected: "some output", + Error: false, + }, + { + TestName: "another descriptive name", + Input: "more input", + Expected: "more output", + Error: false, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.TestName, func(t *testing.T) { + t.Parallel() + got, err := tfcloudfront.FunctionFromDataSource(testCase.Input) + + if err != nil && !testCase.Error { + t.Errorf("got error (%s), expected no error", err) + } + + if err == nil && testCase.Error { + t.Errorf("got (%s) and no error, expected error", got) + } + + if got != testCase.Expected { + t.Errorf("got %s, expected %s", got, testCase.Expected) + } + }) + } +} + + +// TIP: ==== ACCEPTANCE TESTS ==== +// This is an example of a basic acceptance test. This should test as much of +// standard functionality of the data source as possible, and test importing, if +// applicable. We prefix its name with "TestAcc", the service, and the +// data source name. +// +// Acceptance test access AWS and cost money to run. +func TestAccCloudFrontKeyGroupDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + // TIP: This is a long-running test guard for tests that run longer than + // 300s (5 min) generally. + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var keygroup cloudfront.DescribeKeyGroupResponse + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_cloudfront_key_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.CloudFrontEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.CloudFrontServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckKeyGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccKeyGroupDataSourceConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeyGroupExists(ctx, dataSourceName, &keygroup), + resource.TestCheckResourceAttr(dataSourceName, "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttrSet(dataSourceName, "maintenance_window_start_time.0.day_of_week"), + resource.TestCheckTypeSetElemNestedAttrs(dataSourceName, "user.*", map[string]string{ + "console_access": "false", + "groups.#": "0", + "username": "Test", + "password": "TestTest1234", + }), + acctest.MatchResourceAttrRegionalARN(dataSourceName, "arn", "cloudfront", regexache.MustCompile(`keygroup:+.`)), + ), + }, + }, + }) +} + +func testAccKeyGroupDataSourceConfig_basic(rName, version string) string { + return fmt.Sprintf(` +data "aws_security_group" "test" { + name = %[1]q +} + +data "aws_cloudfront_key_group" "test" { + key_group_name = %[1]q + engine_type = "ActiveCloudFront" + engine_version = %[2]q + host_instance_type = "cloudfront.t2.micro" + security_groups = [aws_security_group.test.id] + authentication_strategy = "simple" + storage_type = "efs" + + logs { + general = true + } + + user { + username = "Test" + password = "TestTest1234" + } +} +`, rName, version) +} diff --git a/website/docs/d/cloudfront_key_group.html.markdown b/website/docs/d/cloudfront_key_group.html.markdown new file mode 100644 index 000000000000..ef8c435bb685 --- /dev/null +++ b/website/docs/d/cloudfront_key_group.html.markdown @@ -0,0 +1,45 @@ +--- +subcategory: "CloudFront" +layout: "aws" +page_title: "AWS: aws_cloudfront_key_group" +description: |- + Terraform data source for managing an AWS CloudFront Key Group. +--- + + +# Data Source: aws_cloudfront_key_group + +Terraform data source for managing an AWS CloudFront Key Group. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_cloudfront_key_group" "example" { +} +``` + +## Argument Reference + +The following arguments are required: + +* `example_arg` - (Required) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +The following arguments are optional: + +* `optional_arg` - (Optional) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Attribute Reference + +This data source exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Key Group. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `example_attribute` - Concise description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. From 0ebe9990b68c080cc6a530aa8473cd8d722d2fb5 Mon Sep 17 00:00:00 2001 From: Terence Ho Date: Mon, 14 Oct 2024 13:29:56 -0700 Subject: [PATCH 2/9] remove key group id as it duplicates name --- .../cloudfront/key_group_data_source.go | 43 +++++++++---------- .../cloudfront/key_group_data_source_test.go | 11 +++-- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/internal/service/cloudfront/key_group_data_source.go b/internal/service/cloudfront/key_group_data_source.go index 0edbda2b1021..37b26616911b 100644 --- a/internal/service/cloudfront/key_group_data_source.go +++ b/internal/service/cloudfront/key_group_data_source.go @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MPL-2.0 package cloudfront + // **PLEASE DELETE THIS AND ALL TIP COMMENTS BEFORE SUBMITTING A PR FOR REVIEW!** // // TIP: ==== INTRODUCTION ==== @@ -34,16 +35,16 @@ import ( // awstypes.. "context" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudfront" - awstypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" + /// "github.com/aws/aws-sdk-go-v2/aws" + /// "github.com/aws/aws-sdk-go-v2/service/cloudfront" + /// awstypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" "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" + /// fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -104,22 +105,18 @@ func (d *dataSourceKeyGroup) Schema(ctx context.Context, req datasource.SchemaRe "etag": schema.StringAttribute{ Computed: true, }, - names.AttrID: { - Type: schema.TypeString, - Required: true, - }, }, Blocks: map[string]schema.Block{ "key_group_config": schema.SingleNestedBlock{ Attributes: map[string]schema.Attribute{ "items": schema.ListAttribute{ - ElementType: types.StringType, - Required: true, + ElementType: types.StringType, + Computed: true, }, names.AttrName: schema.StringAttribute{ - Computed: true, + Required: true, }, - "comment": schema.StringAttribute{ + names.AttrComment: schema.StringAttribute{ Optional: true, }, }, @@ -127,6 +124,7 @@ func (d *dataSourceKeyGroup) Schema(ctx context.Context, req datasource.SchemaRe }, } } + // TIP: ==== ASSIGN CRUD METHODS ==== // Data sources only have a read method. func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { @@ -142,16 +140,16 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques // 6. Set the state // TIP: -- 1. Get a client connection to the relevant service conn := d.Meta().CloudFrontClient(ctx) - + // TIP: -- 2. Fetch the config var data dataSourceKeyGroupModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } - + // TIP: -- 3. Get information about a resource from AWS - out, err := findKeyGroupById(ctx, conn, data.Name.ValueString()) + out, err := findKeyGroupByID(ctx, conn, data.Name.ValueString()) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.CloudFront, create.ErrActionReading, DSNameKeyGroup, data.Name.String(), err), @@ -174,25 +172,24 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } - // TIP: ==== DATA STRUCTURES ==== // With Terraform Plugin-Framework configurations are deserialized into // Go types, providing type safety without the need for type assertions. // These structs should match the schema definition exactly, and the `tfsdk` -// tag value should match the attribute name. +// tag value should match the attribute name. // -// Nested objects are represented in their own data struct. These will +// Nested objects are represented in their own data struct. These will // also have a corresponding attribute type mapping for use inside flex // functions. // // See more: // https://developer.hashicorp.com/terraform/plugin/framework/handling-data/accessing-values type dataSourceKeyGroupModel struct { - Etag types.String `tfsdk:"etag"` - Items types.List `tfsdk:"items"` - Comment types.String `tfsdk:"comment"` - Name types.String `tfsdk:"name"` - Type types.String `tfsdk:"type"` + Etag types.String `tfsdk:"etag"` + Items types.List `tfsdk:"items"` + Comment types.String `tfsdk:"comment"` + Name types.String `tfsdk:"name"` + Type types.String `tfsdk:"type"` } type complexArgumentModel struct { diff --git a/internal/service/cloudfront/key_group_data_source_test.go b/internal/service/cloudfront/key_group_data_source_test.go index 50a89f3d37c7..b96e4b19628d 100644 --- a/internal/service/cloudfront/key_group_data_source_test.go +++ b/internal/service/cloudfront/key_group_data_source_test.go @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MPL-2.0 package cloudfront_test + // **PLEASE DELETE THIS AND ALL TIP COMMENTS BEFORE SUBMITTING A PR FOR REVIEW!** // // TIP: ==== INTRODUCTION ==== @@ -50,8 +51,8 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" // TIP: You will often need to import the package that this test file lives - // in. Since it is in the "test" context, it must import the package to use - // any normal context constants, variables, or functions. + // in. Since it is in the "test" context, it must import the package to use + // any normal context constants, variables, or functions. tfcloudfront "github.com/hashicorp/terraform-provider-aws/internal/service/cloudfront" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -69,7 +70,6 @@ import ( // 7. Helper functions (exists, destroy, check, etc.) // 8. Functions that return Terraform configurations - // TIP: ==== UNIT TESTS ==== // This is an example of a unit test. Its name is not prefixed with // "TestAcc" like an acceptance test. @@ -133,7 +133,6 @@ func TestKeyGroupExampleUnitTest(t *testing.T) { } } - // TIP: ==== ACCEPTANCE TESTS ==== // This is an example of a basic acceptance test. This should test as much of // standard functionality of the data source as possible, and test importing, if @@ -143,8 +142,8 @@ func TestKeyGroupExampleUnitTest(t *testing.T) { // Acceptance test access AWS and cost money to run. func TestAccCloudFrontKeyGroupDataSource_basic(t *testing.T) { ctx := acctest.Context(t) - // TIP: This is a long-running test guard for tests that run longer than - // 300s (5 min) generally. + // TIP: This is a long-running test guard for tests that run longer than + // 300s (5 min) generally. if testing.Short() { t.Skip("skipping long-running test in short mode") } From 0957c8c77d24ba51d65868549c2120cc9b753f40 Mon Sep 17 00:00:00 2001 From: Terence Ho Date: Mon, 14 Oct 2024 13:30:29 -0700 Subject: [PATCH 3/9] register key group data source to aws provider --- internal/service/cloudfront/service_package_gen.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/service/cloudfront/service_package_gen.go b/internal/service/cloudfront/service_package_gen.go index 3f8995858abb..6ce488339ce7 100644 --- a/internal/service/cloudfront/service_package_gen.go +++ b/internal/service/cloudfront/service_package_gen.go @@ -16,6 +16,10 @@ type servicePackage struct{} func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.ServicePackageFrameworkDataSource { return []*types.ServicePackageFrameworkDataSource{ + { + Factory: newDataSourceKeyGroup, + Name: "Key Group", + }, { Factory: newDataSourceOriginAccessControl, Name: "Origin Access Control", From 6875073f9cd3c04101ffa14f2b2986e0e9cb2202 Mon Sep 17 00:00:00 2001 From: Terence Ho Date: Mon, 14 Oct 2024 14:51:55 -0700 Subject: [PATCH 4/9] lookup by id, flatten data structure --- .../cloudfront/key_group_data_source.go | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/internal/service/cloudfront/key_group_data_source.go b/internal/service/cloudfront/key_group_data_source.go index 37b26616911b..bb08b00e2085 100644 --- a/internal/service/cloudfront/key_group_data_source.go +++ b/internal/service/cloudfront/key_group_data_source.go @@ -105,21 +105,18 @@ func (d *dataSourceKeyGroup) Schema(ctx context.Context, req datasource.SchemaRe "etag": schema.StringAttribute{ Computed: true, }, - }, - Blocks: map[string]schema.Block{ - "key_group_config": schema.SingleNestedBlock{ - Attributes: map[string]schema.Attribute{ - "items": schema.ListAttribute{ - ElementType: types.StringType, - Computed: true, - }, - names.AttrName: schema.StringAttribute{ - Required: true, - }, - names.AttrComment: schema.StringAttribute{ - Optional: true, - }, - }, + names.AttrID: schema.StringAttribute{ + Required: true, + }, + names.AttrName: schema.StringAttribute{ + Computed: true, + }, + "items": schema.ListAttribute{ + ElementType: types.StringType, + Computed: true, + }, + names.AttrComment: schema.StringAttribute{ + Optional: true, }, }, } @@ -149,7 +146,7 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques } // TIP: -- 3. Get information about a resource from AWS - out, err := findKeyGroupByID(ctx, conn, data.Name.ValueString()) + out, err := findKeyGroupByID(ctx, conn, data.ID.ValueString()) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.CloudFront, create.ErrActionReading, DSNameKeyGroup, data.Name.String(), err), @@ -160,7 +157,7 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques // TIP: -- 4. Set the ID, arguments, and attributes // Using a field name prefix allows mapping fields such as `KeyGroupId` to `ID` - resp.Diagnostics.Append(flex.Flatten(ctx, out, &data, flex.WithFieldNamePrefix("KeyGroup"))...) + resp.Diagnostics.Append(flex.Flatten(ctx, out.KeyGroup.KeyGroupConfig, &data, flex.WithFieldNamePrefix("KeyGroup"))...) if resp.Diagnostics.HasError() { return } @@ -188,8 +185,8 @@ type dataSourceKeyGroupModel struct { Etag types.String `tfsdk:"etag"` Items types.List `tfsdk:"items"` Comment types.String `tfsdk:"comment"` + ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` - Type types.String `tfsdk:"type"` } type complexArgumentModel struct { From 8132d2062eababa4f6a252599bc5eb42943c5d74 Mon Sep 17 00:00:00 2001 From: Terence Ho Date: Tue, 15 Oct 2024 00:51:52 -0700 Subject: [PATCH 5/9] can search by id or name --- .../cloudfront/key_group_data_source.go | 62 +++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/internal/service/cloudfront/key_group_data_source.go b/internal/service/cloudfront/key_group_data_source.go index bb08b00e2085..544584f589ee 100644 --- a/internal/service/cloudfront/key_group_data_source.go +++ b/internal/service/cloudfront/key_group_data_source.go @@ -34,12 +34,16 @@ import ( // need to import types and reference the nested types, e.g., as // awstypes.. "context" + "fmt" - /// "github.com/aws/aws-sdk-go-v2/aws" - /// "github.com/aws/aws-sdk-go-v2/service/cloudfront" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudfront" /// awstypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" + "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" @@ -106,9 +110,17 @@ func (d *dataSourceKeyGroup) Schema(ctx context.Context, req datasource.SchemaRe Computed: true, }, names.AttrID: schema.StringAttribute{ - Required: true, + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.ExactlyOneOf( + path.MatchRelative().AtParent().AtName(names.AttrID), + path.MatchRelative().AtParent().AtName(names.AttrName), + ), + }, }, names.AttrName: schema.StringAttribute{ + Optional: true, Computed: true, }, "items": schema.ListAttribute{ @@ -145,8 +157,50 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques return } + var keyGroupID string + + if !data.ID.IsNull() && !data.ID.IsUnknown() { + keyGroupID = data.ID.ValueString() + } else { + name := data.Name.ValueString() + input := &cloudfront.ListKeyGroupsInput{} + + err := listKeyGroupsPages(ctx, conn, input, func(page *cloudfront.ListKeyGroupsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, policySummary := range page.KeyGroupList.Items { + if keyGroup := policySummary.KeyGroup; aws.ToString(keyGroup.KeyGroupConfig.Name) == name { + keyGroupID = aws.ToString(keyGroup.Id) + + return false + } + } + + return !lastPage + }) + + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.CloudFront, create.ErrActionReading, DSNameKeyGroup, data.Name.String(), err), + err.Error(), + ) + return + } + + if keyGroupID == "" { + err := fmt.Errorf("no matching CloudFront Key Group (%s)", name) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.CloudFront, create.ErrActionReading, DSNameKeyGroup, data.Name.String(), err), + err.Error(), + ) + return + } + } + // TIP: -- 3. Get information about a resource from AWS - out, err := findKeyGroupByID(ctx, conn, data.ID.ValueString()) + out, err := findKeyGroupByID(ctx, conn, keyGroupID) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.CloudFront, create.ErrActionReading, DSNameKeyGroup, data.Name.String(), err), From 2f7f990718146fcbf04439e0568935f9356b6288 Mon Sep 17 00:00:00 2001 From: Terence Ho Date: Tue, 15 Oct 2024 00:52:51 -0700 Subject: [PATCH 6/9] fix: map comment, name, id to data source attributes --- internal/service/cloudfront/key_group_data_source.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/service/cloudfront/key_group_data_source.go b/internal/service/cloudfront/key_group_data_source.go index 544584f589ee..420e597f21f3 100644 --- a/internal/service/cloudfront/key_group_data_source.go +++ b/internal/service/cloudfront/key_group_data_source.go @@ -217,6 +217,9 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques } // TIP: -- 5. Set the tags + data.ID = flex.StringToFramework(ctx, out.KeyGroup.Id) + data.Name = flex.StringToFramework(ctx, out.KeyGroup.KeyGroupConfig.Name) + data.Comment = flex.StringToFramework(ctx, out.KeyGroup.KeyGroupConfig.Comment) data.Etag = flex.StringToFramework(ctx, out.ETag) // TIP: -- 6. Set the state From 3f62f43eaf3c904265aa0aebca4b971a56483fb5 Mon Sep 17 00:00:00 2001 From: Terence Ho Date: Tue, 15 Oct 2024 01:21:56 -0700 Subject: [PATCH 7/9] handle last modified time --- .../cloudfront/key_group_data_source.go | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/internal/service/cloudfront/key_group_data_source.go b/internal/service/cloudfront/key_group_data_source.go index 420e597f21f3..2725fa0b4867 100644 --- a/internal/service/cloudfront/key_group_data_source.go +++ b/internal/service/cloudfront/key_group_data_source.go @@ -38,7 +38,8 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudfront" - /// awstypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" + /// awstypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" + "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" @@ -130,6 +131,10 @@ func (d *dataSourceKeyGroup) Schema(ctx context.Context, req datasource.SchemaRe names.AttrComment: schema.StringAttribute{ Optional: true, }, + "last_modified_time": schema.StringAttribute{ + CustomType: timetypes.RFC3339Type{}, + Computed: true, + }, }, } } @@ -220,6 +225,8 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques data.ID = flex.StringToFramework(ctx, out.KeyGroup.Id) data.Name = flex.StringToFramework(ctx, out.KeyGroup.KeyGroupConfig.Name) data.Comment = flex.StringToFramework(ctx, out.KeyGroup.KeyGroupConfig.Comment) + data.LastModifiedTime = flex.TimeToFramework(ctx, out.KeyGroup.LastModifiedTime) + data.Etag = flex.StringToFramework(ctx, out.ETag) // TIP: -- 6. Set the state @@ -239,11 +246,12 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques // See more: // https://developer.hashicorp.com/terraform/plugin/framework/handling-data/accessing-values type dataSourceKeyGroupModel struct { - Etag types.String `tfsdk:"etag"` - Items types.List `tfsdk:"items"` - Comment types.String `tfsdk:"comment"` - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` + Etag types.String `tfsdk:"etag"` + Items types.List `tfsdk:"items"` + Comment types.String `tfsdk:"comment"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + LastModifiedTime timetypes.RFC3339 `tfsdk:"last_modified_time"` } type complexArgumentModel struct { From 2b48073ce023d9bc4d30bc884941e454a21c1af7 Mon Sep 17 00:00:00 2001 From: Terence Ho Date: Tue, 15 Oct 2024 02:24:43 -0700 Subject: [PATCH 8/9] handle empty comments --- internal/service/cloudfront/key_group_data_source.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/service/cloudfront/key_group_data_source.go b/internal/service/cloudfront/key_group_data_source.go index 2725fa0b4867..5e03734bb071 100644 --- a/internal/service/cloudfront/key_group_data_source.go +++ b/internal/service/cloudfront/key_group_data_source.go @@ -224,7 +224,16 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques // TIP: -- 5. Set the tags data.ID = flex.StringToFramework(ctx, out.KeyGroup.Id) data.Name = flex.StringToFramework(ctx, out.KeyGroup.KeyGroupConfig.Name) - data.Comment = flex.StringToFramework(ctx, out.KeyGroup.KeyGroupConfig.Comment) + + var comment *string + if out.KeyGroup.KeyGroupConfig.Comment != nil { + comment = out.KeyGroup.KeyGroupConfig.Comment + } else { + emptyString := "" + comment = &emptyString + } + data.Comment = flex.StringToFramework(ctx, comment) + data.LastModifiedTime = flex.TimeToFramework(ctx, out.KeyGroup.LastModifiedTime) data.Etag = flex.StringToFramework(ctx, out.ETag) From 8cd9e1eaac68f9c1fccc371b5e4c406bc7b438e3 Mon Sep 17 00:00:00 2001 From: Terence Ho Date: Tue, 15 Oct 2024 02:37:24 -0700 Subject: [PATCH 9/9] explicitly handle key group items list --- internal/service/cloudfront/key_group_data_source.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/cloudfront/key_group_data_source.go b/internal/service/cloudfront/key_group_data_source.go index 5e03734bb071..fe5a4473100e 100644 --- a/internal/service/cloudfront/key_group_data_source.go +++ b/internal/service/cloudfront/key_group_data_source.go @@ -224,6 +224,7 @@ func (d *dataSourceKeyGroup) Read(ctx context.Context, req datasource.ReadReques // TIP: -- 5. Set the tags data.ID = flex.StringToFramework(ctx, out.KeyGroup.Id) data.Name = flex.StringToFramework(ctx, out.KeyGroup.KeyGroupConfig.Name) + data.Items = flex.FlattenFrameworkStringValueList(ctx, out.KeyGroup.KeyGroupConfig.Items) var comment *string if out.KeyGroup.KeyGroupConfig.Comment != nil {