From 1fb75ee9a2242afc4092f65a678570bbfa1baa5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:56:54 +0000 Subject: [PATCH 01/42] Add index policy resource --- internal/service/logs/index_policy.go | 128 ++++++++ internal/service/logs/index_policy_test.go | 329 +++++++++++++++++++++ 2 files changed, 457 insertions(+) create mode 100644 internal/service/logs/index_policy.go create mode 100644 internal/service/logs/index_policy_test.go diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go new file mode 100644 index 00000000000..a44e24ef663 --- /dev/null +++ b/internal/service/logs/index_policy.go @@ -0,0 +1,128 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package logs + +import ( + "context" + "fmt" + "log" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKResource("aws_cloudwatch_log_index_policy") +func resourceIndexPolicy() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceIndexPolicyPut, + ReadWithoutTimeout: resourceIndexPolicyRead, + UpdateWithoutTimeout: resourceIndexPolicyPut, + DeleteWithoutTimeout: resourceIndexPolicyDelete, + + Importer: &schema.ResourceImporter{ + State: resourceIndexPolicyImport, + }, + + Schema: map[string]*schema.Schema{ + names.AttrLogGroupName: { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validLogGroupName, + }, + "fields": { + Type: schema.TypeList, + Optional: false, + Elem: schema.TypeString, + }, + }, + } +} + +func resourceIndexPolicyPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + + conn := meta.(*conns.AWSClient).LogsClient(ctx) + + name := d.Get(names.AttrName).(string) + logGroupName := d.Get(names.AttrLogGroupName).(string) + input := &cloudwatchlogs.PutIndexPolicyInput{ + LogGroupIdentifier: aws.String(logGroupName), + PolicyDocument: aws.String(fmt.Sprintf(`{"fields": %s}`, d.Get("fields").([]interface{}))), + } + + _, err := conn.PutIndexPolicy(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "putting CloudWatch Logs Index Policy (%s): %s", d.Id(), err) + } + + if d.IsNewResource() { + d.SetId(name) + } + + return append(diags, resourceIndexPolicyRead(ctx, d, meta)...) +} + +func resourceIndexPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + + conn := meta.(*conns.AWSClient).LogsClient(ctx) + + input := cloudwatchlogs.DescribeIndexPoliciesInput{ + LogGroupIdentifiers: []string{d.Get(names.AttrLogGroupName).(string)}, + } + + ip, err := conn.DescribeIndexPolicies(ctx, &input) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] CloudWatch Logs Index Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading CloudWatch Logs Index Policy (%s): %s", d.Id(), err) + } + + d.Set(names.AttrLogGroupName, ip.IndexPolicies[0].LogGroupIdentifier) + d.Set("indexPolicy", ip.IndexPolicies) + + return diags +} + +func resourceIndexPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + + conn := meta.(*conns.AWSClient).LogsClient(ctx) + + log.Printf("[INFO] Deleting CloudWatch Logs Index Policy: %s", d.Id()) + _, err := conn.DeleteIndexPolicy(ctx, &cloudwatchlogs.DeleteIndexPolicyInput{ + LogGroupIdentifier: aws.String(d.Get(names.AttrLogGroupName).(string)), + }) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting CloudWatch Logs Index Policy (%s): %s", d.Id(), err) + } + + return diags +} + +func resourceIndexPolicyImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + logGroupName := d.Get(names.AttrLogGroupName).(string) + d.Set(names.AttrLogGroupName, logGroupName) + return []*schema.ResourceData{d}, nil +} diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go new file mode 100644 index 00000000000..48646c064cc --- /dev/null +++ b/internal/service/logs/index_policy_test.go @@ -0,0 +1,329 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package logs_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/cloudwatchlogs/types package. If so, you'll + // need to import types and reference the nested types, e.g., as + // types.. + "context" + "errors" + "fmt" + "testing" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + 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" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/names" + + // 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. + tflogs "github.com/hashicorp/terraform-provider-aws/internal/service/logs" +) + +// TIP: File Structure. The basic outline for all test files should be as +// follows. Improve this resource'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 resource'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 TestFieldIndexExampleUnitTest(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 := tflogs.FunctionFromResource(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 resource as possible, and test importing, if +// applicable. We prefix its name with "TestAcc", the service, and the +// resource name. +// +// Acceptance test access AWS and cost money to run. +func TestAccLogsFieldIndex_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 fieldindex cloudwatchlogs.DescribeFieldIndexResponse + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_logs_field_index.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LogsEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFieldIndexDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFieldIndexConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFieldIndexExists(ctx, resourceName, &fieldindex), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ + "console_access": "false", + "groups.#": "0", + "username": "Test", + "password": "TestTest1234", + }), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "logs", regexache.MustCompile(`fieldindex:+.`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + }, + }, + }) +} + +func TestAccLogsFieldIndex_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var fieldindex cloudwatchlogs.DescribeFieldIndexResponse + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_logs_field_index.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LogsEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFieldIndexDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFieldIndexConfig_basic(rName, testAccFieldIndexVersionNewer), + Check: resource.ComposeTestCheckFunc( + testAccCheckFieldIndexExists(ctx, resourceName, &fieldindex), + // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, + // but expects a new resource factory function as the third argument. To expose this + // private function to the testing package, you may need to add a line like the following + // to exports_test.go: + // + // var ResourceFieldIndex = newResourceFieldIndex + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflogs.ResourceFieldIndex, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckFieldIndexDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_logs_field_index" { + continue + } + + + // TIP: ==== FINDERS ==== + // The find function should be exported. Since it won't be used outside of the package, it can be exported + // in the `exports_test.go` file. + _, err := tflogs.FindFieldIndexByID(ctx, conn, rs.Primary.ID) + if tfresource.NotFound(err) { + return nil + } + if err != nil { + return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameFieldIndex, rs.Primary.ID, err) + } + + return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameFieldIndex, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} + +func testAccCheckFieldIndexExists(ctx context.Context, name string, fieldindex *cloudwatchlogs.DescribeFieldIndexResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameFieldIndex, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameFieldIndex, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) + + resp, err := tflogs.FindFieldIndexByID(ctx, conn, rs.Primary.ID) + if err != nil { + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameFieldIndex, rs.Primary.ID, err) + } + + *fieldindex = *resp + + return nil + } +} + +func testAccPreCheck(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) + + input := &cloudwatchlogs.ListFieldIndexsInput{} + + _, err := conn.ListFieldIndexs(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccCheckFieldIndexNotRecreated(before, after *cloudwatchlogs.DescribeFieldIndexResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.ToString(before.FieldIndexId), aws.ToString(after.FieldIndexId); before != after { + return create.Error(names.Logs, create.ErrActionCheckingNotRecreated, tflogs.ResNameFieldIndex, aws.ToString(before.FieldIndexId), errors.New("recreated")) + } + + return nil + } +} + +func testAccFieldIndexConfig_basic(rName, version string) string { + return fmt.Sprintf(` +resource "aws_security_group" "test" { + name = %[1]q +} + +resource "aws_logs_field_index" "test" { + field_index_name = %[1]q + engine_type = "ActiveLogs" + engine_version = %[2]q + host_instance_type = "logs.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) +} From c40cc6a9e34554b253801dd0803ae61a18b0de11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:05:24 +0000 Subject: [PATCH 02/42] Regiter resource to the provider --- internal/service/logs/index_policy_test.go | 58 ++++++++-------- internal/service/logs/service_package_gen.go | 4 ++ website/docs/r/logs_field_index.html.markdown | 69 +++++++++++++++++++ 3 files changed, 102 insertions(+), 29 deletions(-) create mode 100644 website/docs/r/logs_field_index.html.markdown diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 48646c064cc..14401649eda 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -83,7 +83,7 @@ import ( // 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 TestFieldIndexExampleUnitTest(t *testing.T) { +func TestIndexPolicyExampleUnitTest(t *testing.T) { t.Parallel() testCases := []struct { @@ -139,7 +139,7 @@ func TestFieldIndexExampleUnitTest(t *testing.T) { // resource name. // // Acceptance test access AWS and cost money to run. -func TestAccLogsFieldIndex_basic(t *testing.T) { +func TestAccLogsIndexPolicy_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. @@ -147,7 +147,7 @@ func TestAccLogsFieldIndex_basic(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var fieldindex cloudwatchlogs.DescribeFieldIndexResponse + var fieldindex cloudwatchlogs.DescribeIndexPolicyResponse rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_logs_field_index.test" @@ -159,12 +159,12 @@ func TestAccLogsFieldIndex_basic(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckFieldIndexDestroy(ctx), + CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccFieldIndexConfig_basic(rName), + Config: testAccIndexPolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFieldIndexExists(ctx, resourceName, &fieldindex), + testAccCheckIndexPolicyExists(ctx, resourceName, &fieldindex), resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ @@ -186,13 +186,13 @@ func TestAccLogsFieldIndex_basic(t *testing.T) { }) } -func TestAccLogsFieldIndex_disappears(t *testing.T) { +func TestAccLogsIndexPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") } - var fieldindex cloudwatchlogs.DescribeFieldIndexResponse + var fieldindex cloudwatchlogs.DescribeIndexPolicyResponse rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_logs_field_index.test" @@ -204,19 +204,19 @@ func TestAccLogsFieldIndex_disappears(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckFieldIndexDestroy(ctx), + CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccFieldIndexConfig_basic(rName, testAccFieldIndexVersionNewer), + Config: testAccIndexPolicyConfig_basic(rName, testAccIndexPolicyVersionNewer), Check: resource.ComposeTestCheckFunc( - testAccCheckFieldIndexExists(ctx, resourceName, &fieldindex), + testAccCheckIndexPolicyExists(ctx, resourceName, &fieldindex), // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, // but expects a new resource factory function as the third argument. To expose this // private function to the testing package, you may need to add a line like the following // to exports_test.go: // - // var ResourceFieldIndex = newResourceFieldIndex - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflogs.ResourceFieldIndex, resourceName), + // var ResourceIndexPolicy = newResourceIndexPolicy + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflogs.ResourceIndexPolicy, resourceName), ), ExpectNonEmptyPlan: true, }, @@ -224,7 +224,7 @@ func TestAccLogsFieldIndex_disappears(t *testing.T) { }) } -func testAccCheckFieldIndexDestroy(ctx context.Context) resource.TestCheckFunc { +func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) @@ -233,41 +233,41 @@ func testAccCheckFieldIndexDestroy(ctx context.Context) resource.TestCheckFunc { continue } - + // TIP: ==== FINDERS ==== // The find function should be exported. Since it won't be used outside of the package, it can be exported // in the `exports_test.go` file. - _, err := tflogs.FindFieldIndexByID(ctx, conn, rs.Primary.ID) + _, err := tflogs.FindIndexPolicyByID(ctx, conn, rs.Primary.ID) if tfresource.NotFound(err) { return nil } if err != nil { - return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameFieldIndex, rs.Primary.ID, err) + return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) } - return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameFieldIndex, rs.Primary.ID, errors.New("not destroyed")) + return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameIndexPolicy, rs.Primary.ID, errors.New("not destroyed")) } return nil } } -func testAccCheckFieldIndexExists(ctx context.Context, name string, fieldindex *cloudwatchlogs.DescribeFieldIndexResponse) resource.TestCheckFunc { +func testAccCheckIndexPolicyExists(ctx context.Context, name string, fieldindex *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameFieldIndex, name, errors.New("not found")) + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, name, errors.New("not found")) } if rs.Primary.ID == "" { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameFieldIndex, name, errors.New("not set")) + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, name, errors.New("not set")) } conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - resp, err := tflogs.FindFieldIndexByID(ctx, conn, rs.Primary.ID) + resp, err := tflogs.FindIndexPolicyByID(ctx, conn, rs.Primary.ID) if err != nil { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameFieldIndex, rs.Primary.ID, err) + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) } *fieldindex = *resp @@ -279,9 +279,9 @@ func testAccCheckFieldIndexExists(ctx context.Context, name string, fieldindex * func testAccPreCheck(ctx context.Context, t *testing.T) { conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - input := &cloudwatchlogs.ListFieldIndexsInput{} + input := &cloudwatchlogs.ListIndexPolicysInput{} - _, err := conn.ListFieldIndexs(ctx, input) + _, err := conn.ListIndexPolicys(ctx, input) if acctest.PreCheckSkipError(err) { t.Skipf("skipping acceptance testing: %s", err) @@ -291,17 +291,17 @@ func testAccPreCheck(ctx context.Context, t *testing.T) { } } -func testAccCheckFieldIndexNotRecreated(before, after *cloudwatchlogs.DescribeFieldIndexResponse) resource.TestCheckFunc { +func testAccCheckIndexPolicyNotRecreated(before, after *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { return func(s *terraform.State) error { - if before, after := aws.ToString(before.FieldIndexId), aws.ToString(after.FieldIndexId); before != after { - return create.Error(names.Logs, create.ErrActionCheckingNotRecreated, tflogs.ResNameFieldIndex, aws.ToString(before.FieldIndexId), errors.New("recreated")) + if before, after := aws.ToString(before.IndexPolicyId), aws.ToString(after.IndexPolicyId); before != after { + return create.Error(names.Logs, create.ErrActionCheckingNotRecreated, tflogs.ResNameIndexPolicy, aws.ToString(before.IndexPolicyId), errors.New("recreated")) } return nil } } -func testAccFieldIndexConfig_basic(rName, version string) string { +func testAccIndexPolicyConfig_basic(rName, version string) string { return fmt.Sprintf(` resource "aws_security_group" "test" { name = %[1]q diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index 8a4105c1e72..00f1a3419be 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -82,6 +82,10 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka IdentifierAttribute: names.AttrARN, }, }, + { + Factory: resourceIndexPolicy, + TypeName: "aws_cloudwatch_log_index_policy", + }, { Factory: resourceMetricFilter, TypeName: "aws_cloudwatch_log_metric_filter", diff --git a/website/docs/r/logs_field_index.html.markdown b/website/docs/r/logs_field_index.html.markdown new file mode 100644 index 00000000000..8a6205dd0df --- /dev/null +++ b/website/docs/r/logs_field_index.html.markdown @@ -0,0 +1,69 @@ +--- +subcategory: "CloudWatch Logs" +layout: "aws" +page_title: "AWS: aws_logs_index_policy" +description: |- + Terraform resource for managing an AWS CloudWatch Logs Field Index. +--- +` +# Resource: aws_logs_index_policy + +Terraform resource for managing an AWS CloudWatch Logs Field Index. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_logs_index_policy" "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 resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Field Index. 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. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `60m`) +* `update` - (Default `180m`) +* `delete` - (Default `90m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Field Index using the `example_id_arg`. For example: + +```terraform +import { + to = aws_logs_index_policy.example + id = "index_policy-id-12345678" +} +``` + +Using `terraform import`, import CloudWatch Logs Field Index using the `example_id_arg`. For example: + +```console +% terraform import aws_logs_index_policy.example index_policy-id-12345678 +``` From bdfed7577ddabd2f45f568f38e1db4f21c8d4380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:13:05 +0000 Subject: [PATCH 03/42] Set up tests --- internal/service/logs/exports_test.go | 2 + internal/service/logs/index_policy.go | 26 ++- internal/service/logs/index_policy_test.go | 226 ++++----------------- 3 files changed, 58 insertions(+), 196 deletions(-) diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index a96b911e119..3e4284a4911 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -11,6 +11,7 @@ var ( ResourceDestinationPolicy = resourceDestinationPolicy ResourceGroup = resourceGroup ResourceMetricFilter = resourceMetricFilter + ResourceIndexPolicy = resourceIndexPolicy ResourceQueryDefinition = resourceQueryDefinition ResourceResourcePolicy = resourceResourcePolicy ResourceStream = resourceStream @@ -22,6 +23,7 @@ var ( FindLogGroupByName = findLogGroupByName FindLogStreamByTwoPartKey = findLogStreamByTwoPartKey // nosemgrep:ci.logs-in-var-name FindMetricFilterByTwoPartKey = findMetricFilterByTwoPartKey + FindIndexPolicyByLogGroupName = findIndexPolicyByLogGroupName FindQueryDefinitionByTwoPartKey = findQueryDefinitionByTwoPartKey FindResourcePolicyByName = findResourcePolicyByName FindSubscriptionFilterByTwoPartKey = findSubscriptionFilterByTwoPartKey diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index a44e24ef663..fb7919180d0 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -39,10 +39,9 @@ func resourceIndexPolicy() *schema.Resource { ForceNew: true, ValidateFunc: validLogGroupName, }, - "fields": { - Type: schema.TypeList, + "policyDocument": { + Type: schema.TypeString, Optional: false, - Elem: schema.TypeString, }, }, } @@ -53,11 +52,11 @@ func resourceIndexPolicyPut(ctx context.Context, d *schema.ResourceData, meta in conn := meta.(*conns.AWSClient).LogsClient(ctx) - name := d.Get(names.AttrName).(string) logGroupName := d.Get(names.AttrLogGroupName).(string) + policyDocument := d.Get("policyDocument").(string) input := &cloudwatchlogs.PutIndexPolicyInput{ LogGroupIdentifier: aws.String(logGroupName), - PolicyDocument: aws.String(fmt.Sprintf(`{"fields": %s}`, d.Get("fields").([]interface{}))), + PolicyDocument: aws.String(policyDocument), } _, err := conn.PutIndexPolicy(ctx, input) @@ -67,7 +66,7 @@ func resourceIndexPolicyPut(ctx context.Context, d *schema.ResourceData, meta in } if d.IsNewResource() { - d.SetId(name) + d.SetId(fmt.Sprintf("%s:index-policy", logGroupName)) } return append(diags, resourceIndexPolicyRead(ctx, d, meta)...) @@ -95,7 +94,7 @@ func resourceIndexPolicyRead(ctx context.Context, d *schema.ResourceData, meta i } d.Set(names.AttrLogGroupName, ip.IndexPolicies[0].LogGroupIdentifier) - d.Set("indexPolicy", ip.IndexPolicies) + d.Set("policyDocument", ip.IndexPolicies[0].PolicyDocument) return diags } @@ -126,3 +125,16 @@ func resourceIndexPolicyImport(d *schema.ResourceData, meta interface{}) ([]*sch d.Set(names.AttrLogGroupName, logGroupName) return []*schema.ResourceData{d}, nil } + +func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName string) ([]types.IndexPolicy, error) { + input := cloudwatchlogs.DescribeIndexPoliciesInput{ + LogGroupIdentifiers: []string{logGroupName}, + } + + ip, err := conn.DescribeIndexPolicies(ctx, &input) + if err != nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return ip.IndexPolicies, nil +} diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 14401649eda..c699410708b 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -2,42 +2,13 @@ // SPDX-License-Identifier: MPL-2.0 package logs_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/cloudwatchlogs/types package. If so, you'll - // need to import types and reference the nested types, e.g., as - // types.. "context" "errors" "fmt" "testing" - "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" @@ -47,91 +18,12 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" - // 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. tflogs "github.com/hashicorp/terraform-provider-aws/internal/service/logs" ) -// TIP: File Structure. The basic outline for all test files should be as -// follows. Improve this resource'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 resource'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 TestIndexPolicyExampleUnitTest(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 := tflogs.FunctionFromResource(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 resource as possible, and test importing, if @@ -141,46 +33,35 @@ func TestIndexPolicyExampleUnitTest(t *testing.T) { // Acceptance test access AWS and cost money to run. func TestAccLogsIndexPolicy_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 fieldindex cloudwatchlogs.DescribeIndexPolicyResponse + var indexPolicy types.IndexPolicy + resourceName := "aws_cloudwatch_log_index_policy.test" + logGroupResourceName := "aws_cloudwatch_log_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_logs_field_index.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.LogsEndpointID) - testAccPreCheck(ctx, t) - }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIndexPolicyConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckIndexPolicyExists(ctx, resourceName, &fieldindex), - resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), - resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ - "console_access": "false", - "groups.#": "0", - "username": "Test", - "password": "TestTest1234", - }), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "logs", regexache.MustCompile(`fieldindex:+.`)), + Config: testAccIndexPolicy_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIndexPolicyExists(ctx, resourceName, &indexPolicy), + resource.TestCheckResourceAttrPair(resourceName, names.AttrLogGroupName, logGroupResourceName, names.AttrName), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + resource.TestCheckResourceAttr(resourceName, "fields", ""), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccIndexPolicyImportStateIdFunc(resourceName), + ImportStateVerify: true, }, }, }) @@ -188,35 +69,21 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { func TestAccLogsIndexPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - - var fieldindex cloudwatchlogs.DescribeIndexPolicyResponse + var mf types.IndexPolicy + resourceName := "aws_cloudwatch_log_index_policy.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_logs_field_index.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.LogsEndpointID) - testAccPreCheck(ctx, t) - }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIndexPolicyConfig_basic(rName, testAccIndexPolicyVersionNewer), + Config: testAccIndexPolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckIndexPolicyExists(ctx, resourceName, &fieldindex), - // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, - // but expects a new resource factory function as the third argument. To expose this - // private function to the testing package, you may need to add a line like the following - // to exports_test.go: - // - // var ResourceIndexPolicy = newResourceIndexPolicy - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflogs.ResourceIndexPolicy, resourceName), + testAccCheckIndexPolicyExists(ctx, resourceName, &mf), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tflogs.ResourceIndexPolicy(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -229,32 +96,31 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_logs_field_index" { + if rs.Type != "aws_cloudwatch_log_index_policy" { continue } + _, err := tflogs.FindMetricFilterByTwoPartKey(ctx, conn, rs.Primary.Attributes[names.AttrLogGroupName], rs.Primary.ID) + _, err := tflogs. - // TIP: ==== FINDERS ==== - // The find function should be exported. Since it won't be used outside of the package, it can be exported - // in the `exports_test.go` file. - _, err := tflogs.FindIndexPolicyByID(ctx, conn, rs.Primary.ID) if tfresource.NotFound(err) { - return nil + continue } + if err != nil { - return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) + return err } - return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameIndexPolicy, rs.Primary.ID, errors.New("not destroyed")) + return fmt.Errorf("CloudWatch Logs Metric Filter still exists: %s", rs.Primary.ID) } return nil } } -func testAccCheckIndexPolicyExists(ctx context.Context, name string, fieldindex *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { +func testAccCheckIndexPolicyExists(ctx context.Context, logGroupName string, indexpolicy *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[logGroupName] if !ok { return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, name, errors.New("not found")) } @@ -265,12 +131,12 @@ func testAccCheckIndexPolicyExists(ctx context.Context, name string, fieldindex conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - resp, err := tflogs.FindIndexPolicyByID(ctx, conn, rs.Primary.ID) + resp, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, logGroupName) if err != nil { return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) } - *fieldindex = *resp + *indexpolicy = *resp return nil } @@ -301,29 +167,11 @@ func testAccCheckIndexPolicyNotRecreated(before, after *cloudwatchlogs.DescribeI } } -func testAccIndexPolicyConfig_basic(rName, version string) string { +func testAccIndexPolicyConfig_basic(rName, policyDocument string) string { return fmt.Sprintf(` -resource "aws_security_group" "test" { - name = %[1]q -} - -resource "aws_logs_field_index" "test" { - field_index_name = %[1]q - engine_type = "ActiveLogs" - engine_version = %[2]q - host_instance_type = "logs.t2.micro" - security_groups = [aws_security_group.test.id] - authentication_strategy = "simple" - storage_type = "efs" - - logs { - general = true - } - - user { - username = "Test" - password = "TestTest1234" - } +resource "aws_cloudwatch_log_index_policy" "test" { + log_group_name = %[1]q + policyDocument = %[2]q } -`, rName, version) +`, rName, policyDocument) } From f4b183fd1dddb04958c13251c9e2743d34109b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:21:30 +0000 Subject: [PATCH 04/42] Generate docs --- ...x.html.markdown => logs_index_policy.html.markdown} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename website/docs/r/{logs_field_index.html.markdown => logs_index_policy.html.markdown} (79%) diff --git a/website/docs/r/logs_field_index.html.markdown b/website/docs/r/logs_index_policy.html.markdown similarity index 79% rename from website/docs/r/logs_field_index.html.markdown rename to website/docs/r/logs_index_policy.html.markdown index 8a6205dd0df..c85e3948705 100644 --- a/website/docs/r/logs_field_index.html.markdown +++ b/website/docs/r/logs_index_policy.html.markdown @@ -3,7 +3,7 @@ subcategory: "CloudWatch Logs" layout: "aws" page_title: "AWS: aws_logs_index_policy" description: |- - Terraform resource for managing an AWS CloudWatch Logs Field Index. + Terraform resource for managing an AWS CloudWatch Logs Index Policy. --- ` # Resource: aws_logs_index_policy -Terraform resource for managing an AWS CloudWatch Logs Field Index. +Terraform resource for managing an AWS CloudWatch Logs Index Policy. ## Example Usage @@ -40,7 +40,7 @@ The following arguments are optional: This resource exports the following attributes in addition to the arguments above: -* `arn` - ARN of the Field Index. 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. +* `arn` - ARN of the Index Policy. 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. ## Timeouts @@ -53,7 +53,7 @@ This resource exports the following attributes in addition to the arguments abov ## Import -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Field Index using the `example_id_arg`. For example: +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Index Policy using the `example_id_arg`. For example: ```terraform import { @@ -62,7 +62,7 @@ import { } ``` -Using `terraform import`, import CloudWatch Logs Field Index using the `example_id_arg`. For example: +Using `terraform import`, import CloudWatch Logs Index Policy using the `example_id_arg`. For example: ```console % terraform import aws_logs_index_policy.example index_policy-id-12345678 From b9ff589592db7dbf284be138fc8ab68aa4509e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:02:48 +0000 Subject: [PATCH 05/42] Fix policy document parsing --- internal/service/logs/index_policy.go | 22 +++++++++++---- internal/service/logs/index_policy_test.go | 32 +++++++--------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index fb7919180d0..3b7cd61172b 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -13,6 +13,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" @@ -20,6 +21,10 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) +const ( + ResNameIndexPolicy = "Index Policy" +) + // @SDKResource("aws_cloudwatch_log_index_policy") func resourceIndexPolicy() *schema.Resource { return &schema.Resource{ @@ -39,9 +44,9 @@ func resourceIndexPolicy() *schema.Resource { ForceNew: true, ValidateFunc: validLogGroupName, }, - "policyDocument": { + "policy_document": { Type: schema.TypeString, - Optional: false, + Required: true, }, }, } @@ -53,20 +58,25 @@ func resourceIndexPolicyPut(ctx context.Context, d *schema.ResourceData, meta in conn := meta.(*conns.AWSClient).LogsClient(ctx) logGroupName := d.Get(names.AttrLogGroupName).(string) - policyDocument := d.Get("policyDocument").(string) + + policyDocument, err := structure.NormalizeJsonString(d.Get("policy_document").(string)) + if err != nil { + return sdkdiag.AppendErrorf(diags, "policy (%s) is invalid JSON: %s", policyDocument, err) + } + input := &cloudwatchlogs.PutIndexPolicyInput{ LogGroupIdentifier: aws.String(logGroupName), PolicyDocument: aws.String(policyDocument), } - _, err := conn.PutIndexPolicy(ctx, input) + output, err := conn.PutIndexPolicy(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "putting CloudWatch Logs Index Policy (%s): %s", d.Id(), err) } if d.IsNewResource() { - d.SetId(fmt.Sprintf("%s:index-policy", logGroupName)) + d.SetId(fmt.Sprintf("%s:index-policy", *output.IndexPolicy.LogGroupIdentifier)) } return append(diags, resourceIndexPolicyRead(ctx, d, meta)...) @@ -94,7 +104,7 @@ func resourceIndexPolicyRead(ctx context.Context, d *schema.ResourceData, meta i } d.Set(names.AttrLogGroupName, ip.IndexPolicies[0].LogGroupIdentifier) - d.Set("policyDocument", ip.IndexPolicies[0].PolicyDocument) + d.Set("policy_document", ip.IndexPolicies[0].PolicyDocument) return diags } diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index c699410708b..1f81c21ee36 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -100,8 +100,7 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc continue } - _, err := tflogs.FindMetricFilterByTwoPartKey(ctx, conn, rs.Primary.Attributes[names.AttrLogGroupName], rs.Primary.ID) - _, err := tflogs. + _, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.Attributes[names.AttrLogGroupName]) if tfresource.NotFound(err) { continue @@ -118,15 +117,15 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc } } -func testAccCheckIndexPolicyExists(ctx context.Context, logGroupName string, indexpolicy *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { +func testAccCheckIndexPolicyExists(ctx context.Context, logGroupName string, indexpolicy []types.IndexPolicy) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[logGroupName] if !ok { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, name, errors.New("not found")) + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, logGroupName, errors.New("not found")) } if rs.Primary.ID == "" { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, name, errors.New("not set")) + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, logGroupName, errors.New("not set")) } conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) @@ -136,27 +135,12 @@ func testAccCheckIndexPolicyExists(ctx context.Context, logGroupName string, ind return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) } - *indexpolicy = *resp + indexpolicy = resp return nil } } -func testAccPreCheck(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - - input := &cloudwatchlogs.ListIndexPolicysInput{} - - _, err := conn.ListIndexPolicys(ctx, input) - - if acctest.PreCheckSkipError(err) { - t.Skipf("skipping acceptance testing: %s", err) - } - if err != nil { - t.Fatalf("unexpected PreCheck error: %s", err) - } -} - func testAccCheckIndexPolicyNotRecreated(before, after *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { return func(s *terraform.State) error { if before, after := aws.ToString(before.IndexPolicyId), aws.ToString(after.IndexPolicyId); before != after { @@ -169,8 +153,12 @@ func testAccCheckIndexPolicyNotRecreated(before, after *cloudwatchlogs.DescribeI func testAccIndexPolicyConfig_basic(rName, policyDocument string) string { return fmt.Sprintf(` +resource "aws_cloudwatch_log_group" "test" { + name = %[1]q +} + resource "aws_cloudwatch_log_index_policy" "test" { - log_group_name = %[1]q + log_group_name = aws_cloudwatch_log_group.test.name policyDocument = %[2]q } `, rName, policyDocument) From 5728203efd1b907b2914ca7bc643fc013aa0fcc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:59:27 +0000 Subject: [PATCH 06/42] Fix import and docs --- internal/service/logs/index_policy.go | 17 ++----- .../docs/r/logs_index_policy.html.markdown | 46 ++++++++----------- 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index 3b7cd61172b..f3efb023eb8 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -34,7 +34,7 @@ func resourceIndexPolicy() *schema.Resource { DeleteWithoutTimeout: resourceIndexPolicyDelete, Importer: &schema.ResourceImporter{ - State: resourceIndexPolicyImport, + StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ @@ -75,9 +75,7 @@ func resourceIndexPolicyPut(ctx context.Context, d *schema.ResourceData, meta in return sdkdiag.AppendErrorf(diags, "putting CloudWatch Logs Index Policy (%s): %s", d.Id(), err) } - if d.IsNewResource() { - d.SetId(fmt.Sprintf("%s:index-policy", *output.IndexPolicy.LogGroupIdentifier)) - } + d.SetId(fmt.Sprintf("%s:index-policy", *output.IndexPolicy.LogGroupIdentifier)) return append(diags, resourceIndexPolicyRead(ctx, d, meta)...) } @@ -87,8 +85,9 @@ func resourceIndexPolicyRead(ctx context.Context, d *schema.ResourceData, meta i conn := meta.(*conns.AWSClient).LogsClient(ctx) + logGroupName := d.Id() input := cloudwatchlogs.DescribeIndexPoliciesInput{ - LogGroupIdentifiers: []string{d.Get(names.AttrLogGroupName).(string)}, + LogGroupIdentifiers: []string{logGroupName}, } ip, err := conn.DescribeIndexPolicies(ctx, &input) @@ -130,12 +129,6 @@ func resourceIndexPolicyDelete(ctx context.Context, d *schema.ResourceData, meta return diags } -func resourceIndexPolicyImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - logGroupName := d.Get(names.AttrLogGroupName).(string) - d.Set(names.AttrLogGroupName, logGroupName) - return []*schema.ResourceData{d}, nil -} - func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName string) ([]types.IndexPolicy, error) { input := cloudwatchlogs.DescribeIndexPoliciesInput{ LogGroupIdentifiers: []string{logGroupName}, @@ -143,7 +136,7 @@ func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Cli ip, err := conn.DescribeIndexPolicies(ctx, &input) if err != nil { - return nil, tfresource.NewEmptyResultError(input) + return nil, err } return ip.IndexPolicies, nil diff --git a/website/docs/r/logs_index_policy.html.markdown b/website/docs/r/logs_index_policy.html.markdown index c85e3948705..555b8ae1dec 100644 --- a/website/docs/r/logs_index_policy.html.markdown +++ b/website/docs/r/logs_index_policy.html.markdown @@ -1,19 +1,11 @@ --- subcategory: "CloudWatch Logs" layout: "aws" -page_title: "AWS: aws_logs_index_policy" +page_title: "AWS: aws_cloudwatch_log_index_policy" description: |- Terraform resource for managing an AWS CloudWatch Logs Index Policy. --- -` -# Resource: aws_logs_index_policy +# Resource: aws_cloudwatch_log_index_policy Terraform resource for managing an AWS CloudWatch Logs Index Policy. @@ -22,7 +14,15 @@ Terraform resource for managing an AWS CloudWatch Logs Index Policy. ### Basic Usage ```terraform -resource "aws_logs_index_policy" "example" { +resource "aws_cloudwatch_log_group" "example" { + name = "example" +} + +resource "aws_cloudwatch_log_index_policy" "example" { + log_group_name = aws_cloudwatch_log_group.example.name + policy_document = jsonencode({ + Field = ["eventName"] + }) } ``` @@ -30,18 +30,8 @@ resource "aws_logs_index_policy" "example" { 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 resource exports the following attributes in addition to the arguments above: - -* `arn` - ARN of the Index Policy. 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. +* `log_group_name` - (Required) Log group name to attach index policy to. +* `policy_document` - (Required) Index policy document in the form of `{"Fields": ["field1", "field2", ...]}` ## Timeouts @@ -53,17 +43,17 @@ This resource exports the following attributes in addition to the arguments abov ## Import -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Index Policy using the `example_id_arg`. For example: +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Index Policy using the `log_group_name`. For example: ```terraform import { - to = aws_logs_index_policy.example - id = "index_policy-id-12345678" + to = aws_cloudwatch_logs_index_policy.example + id = "/aws/log-group/name" } ``` -Using `terraform import`, import CloudWatch Logs Index Policy using the `example_id_arg`. For example: +Using `terraform import`, import CloudWatch Logs Index Policy using the `log_group_name`. For example: ```console -% terraform import aws_logs_index_policy.example index_policy-id-12345678 +% terraform import aws_cloudwatch_logs_index_policy.example /aws/log-group/name ``` From 64634f717af0331d8a05944b7eb536b18507b1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:59:34 +0000 Subject: [PATCH 07/42] Improve acceptance tests --- internal/service/logs/index_policy.go | 6 +-- internal/service/logs/index_policy_test.go | 51 +++++++++++----------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index f3efb023eb8..2ec2dd8b9e6 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -129,15 +129,15 @@ func resourceIndexPolicyDelete(ctx context.Context, d *schema.ResourceData, meta return diags } -func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName string) ([]types.IndexPolicy, error) { +func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName string) (*types.IndexPolicy, error) { input := cloudwatchlogs.DescribeIndexPoliciesInput{ LogGroupIdentifiers: []string{logGroupName}, } ip, err := conn.DescribeIndexPolicies(ctx, &input) if err != nil { - return nil, err + return &types.IndexPolicy{}, err } - return ip.IndexPolicies, nil + return &ip.IndexPolicies[0], nil } diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 1f81c21ee36..3b407cd3827 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -9,8 +9,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -38,9 +36,10 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { } var indexPolicy types.IndexPolicy + var logGroup types.LogGroup resourceName := "aws_cloudwatch_log_index_policy.test" - logGroupResourceName := "aws_cloudwatch_log_group.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyDocument := "{\"Fields\":[\"eventName\"]}" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -49,12 +48,11 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIndexPolicy_basic(rName), + Config: testAccIndexPolicyConfig_basic(rName, policyDocument), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckIndexPolicyExists(ctx, resourceName, &indexPolicy), - resource.TestCheckResourceAttrPair(resourceName, names.AttrLogGroupName, logGroupResourceName, names.AttrName), - resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), - resource.TestCheckResourceAttr(resourceName, "fields", ""), + testAccCheckLogGroupExists(ctx, t, "aws_cloudwatch_log_group.test", &logGroup), + testAccCheckIndexPolicyExists(ctx, t, resourceName, &indexPolicy), + resource.TestCheckResourceAttr(resourceName, "policy_document", policyDocument), ), }, { @@ -69,9 +67,10 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { func TestAccLogsIndexPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) - var mf types.IndexPolicy + var ip types.IndexPolicy resourceName := "aws_cloudwatch_log_index_policy.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyDocument := "{\"Fields\":[\"eventName\"]}" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -80,9 +79,9 @@ func TestAccLogsIndexPolicy_disappears(t *testing.T) { CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIndexPolicyConfig_basic(rName), + Config: testAccIndexPolicyConfig_basic(rName, policyDocument), Check: resource.ComposeTestCheckFunc( - testAccCheckIndexPolicyExists(ctx, resourceName, &mf), + testAccCheckIndexPolicyExists(ctx, t, resourceName, &ip), acctest.CheckResourceDisappears(ctx, acctest.Provider, tflogs.ResourceIndexPolicy(), resourceName), ), ExpectNonEmptyPlan: true, @@ -110,44 +109,44 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc return err } - return fmt.Errorf("CloudWatch Logs Metric Filter still exists: %s", rs.Primary.ID) + return fmt.Errorf("CloudWatch Logs Index Policy still exists: %s", rs.Primary.ID) } return nil } } -func testAccCheckIndexPolicyExists(ctx context.Context, logGroupName string, indexpolicy []types.IndexPolicy) resource.TestCheckFunc { +func testAccCheckIndexPolicyExists(ctx context.Context, t *testing.T, resourceName string, indexPolicy *types.IndexPolicy) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[logGroupName] + rs, ok := s.RootModule().Resources[resourceName] if !ok { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, logGroupName, errors.New("not found")) + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, resourceName, errors.New("not found")) } if rs.Primary.ID == "" { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, logGroupName, errors.New("not set")) + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, resourceName, errors.New("not set")) } - conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) + conn := acctest.ProviderMeta(ctx, t).LogsClient(ctx) - resp, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, logGroupName) + resp, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.Attributes[names.AttrLogGroupName]) if err != nil { return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) } - indexpolicy = resp - + *indexPolicy = *resp return nil } } -func testAccCheckIndexPolicyNotRecreated(before, after *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { - return func(s *terraform.State) error { - if before, after := aws.ToString(before.IndexPolicyId), aws.ToString(after.IndexPolicyId); before != after { - return create.Error(names.Logs, create.ErrActionCheckingNotRecreated, tflogs.ResNameIndexPolicy, aws.ToString(before.IndexPolicyId), errors.New("recreated")) +func testAccIndexPolicyImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not found: %s", resourceName) } - return nil + return rs.Primary.Attributes[names.AttrLogGroupName] + ":" + rs.Primary.Attributes[names.AttrName], nil } } @@ -159,7 +158,7 @@ resource "aws_cloudwatch_log_group" "test" { resource "aws_cloudwatch_log_index_policy" "test" { log_group_name = aws_cloudwatch_log_group.test.name - policyDocument = %[2]q + policy_document = %[2]q } `, rName, policyDocument) } From 7a633a060d085b7b9b47089ee671891f6ff8975c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:35:34 +0000 Subject: [PATCH 08/42] Refactor to use plugin framework --- internal/service/logs/exports_test.go | 1 - internal/service/logs/index_policy.go | 279 +++++++++++++----- internal/service/logs/index_policy_test.go | 261 +++++++++++++--- internal/service/logs/service_package_gen.go | 8 +- ...loudwatch_log_account_policy.html.markdown | 1 + website/docs/r/logs_fake_res.html.markdown | 69 +++++ .../docs/r/logs_index_policy.html.markdown | 46 +-- 7 files changed, 518 insertions(+), 147 deletions(-) create mode 100644 website/docs/r/logs_fake_res.html.markdown diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index 3e4284a4911..52e96ab2c87 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -11,7 +11,6 @@ var ( ResourceDestinationPolicy = resourceDestinationPolicy ResourceGroup = resourceGroup ResourceMetricFilter = resourceMetricFilter - ResourceIndexPolicy = resourceIndexPolicy ResourceQueryDefinition = resourceQueryDefinition ResourceResourcePolicy = resourceResourcePolicy ResourceStream = resourceStream diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index 2ec2dd8b9e6..bf596ec361e 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -5,139 +5,266 @@ package logs import ( "context" + "errors" "fmt" - "log" + "time" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" - "github.com/hashicorp/terraform-provider-aws/internal/conns" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" - "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) +// @FrameworkResource("aws_cloudwatch_logs_index_policy", name="Index Policy") +func newResourceIndexPolicy(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &resourceIndexPolicy{} + + r.SetDefaultCreateTimeout(30 * time.Minute) + r.SetDefaultUpdateTimeout(30 * time.Minute) + r.SetDefaultDeleteTimeout(30 * time.Minute) + + return r, nil +} + const ( ResNameIndexPolicy = "Index Policy" ) -// @SDKResource("aws_cloudwatch_log_index_policy") -func resourceIndexPolicy() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceIndexPolicyPut, - ReadWithoutTimeout: resourceIndexPolicyRead, - UpdateWithoutTimeout: resourceIndexPolicyPut, - DeleteWithoutTimeout: resourceIndexPolicyDelete, +type resourceIndexPolicy struct { + framework.ResourceWithConfigure + framework.WithTimeouts +} - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, +func (r *resourceIndexPolicy) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_logs_index_policy" +} - Schema: map[string]*schema.Schema{ - names.AttrLogGroupName: { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validLogGroupName, - }, - "policy_document": { - Type: schema.TypeString, +func (r *resourceIndexPolicy) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": framework.IDAttribute(), + names.AttrLogGroupName: schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "policy_document": schema.StringAttribute{ + Required: true, + Description: "Field index filter policy, in JSON", }, }, + Blocks: map[string]schema.Block{ + "timeouts": timeouts.Block(ctx, timeouts.Opts{ + Create: true, + Update: true, + Delete: true, + }), + }, } } -func resourceIndexPolicyPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - - conn := meta.(*conns.AWSClient).LogsClient(ctx) - - logGroupName := d.Get(names.AttrLogGroupName).(string) +func (r *resourceIndexPolicy) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().LogsClient(ctx) - policyDocument, err := structure.NormalizeJsonString(d.Get("policy_document").(string)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "policy (%s) is invalid JSON: %s", policyDocument, err) + var plan resourceIndexPolicyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return } - input := &cloudwatchlogs.PutIndexPolicyInput{ - LogGroupIdentifier: aws.String(logGroupName), - PolicyDocument: aws.String(policyDocument), + input := cloudwatchlogs.PutIndexPolicyInput{ + LogGroupIdentifier: plan.LogGroupName.ValueStringPointer(), + PolicyDocument: plan.PolicyDocument.ValueStringPointer(), } - output, err := conn.PutIndexPolicy(ctx, input) + resp.Diagnostics.Append(flex.Expand(ctx, plan, &input, flex.WithFieldNamePrefix("IndexPolicy"))...) + if resp.Diagnostics.HasError() { + return + } + out, err := conn.PutIndexPolicy(ctx, &input) if err != nil { - return sdkdiag.AppendErrorf(diags, "putting CloudWatch Logs Index Policy (%s): %s", d.Id(), err) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Logs, create.ErrActionCreating, ResNameIndexPolicy, plan.LogGroupName.String(), err), + err.Error(), + ) + return + } + if out == nil || out.IndexPolicy == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Logs, create.ErrActionCreating, ResNameIndexPolicy, plan.LogGroupName.String(), nil), + errors.New("empty output").Error(), + ) + return } - d.SetId(fmt.Sprintf("%s:index-policy", *output.IndexPolicy.LogGroupIdentifier)) + // Set resource ID + id := fmt.Sprintf("%s:%s", out.IndexPolicy.LogGroupIdentifier, "index-policy") + plan.ID = flex.StringToFramework(ctx, &id) - return append(diags, resourceIndexPolicyRead(ctx, d, meta)...) -} + resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) + if resp.Diagnostics.HasError() { + return + } -func resourceIndexPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} - conn := meta.(*conns.AWSClient).LogsClient(ctx) +func (r *resourceIndexPolicy) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().LogsClient(ctx) - logGroupName := d.Id() - input := cloudwatchlogs.DescribeIndexPoliciesInput{ - LogGroupIdentifiers: []string{logGroupName}, + var state resourceIndexPolicyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return } - ip, err := conn.DescribeIndexPolicies(ctx, &input) + out, err := findIndexPolicyByLogGroupName(ctx, conn, state.LogGroupName.ValueString()) - if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] CloudWatch Logs Index Policy (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return } - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading CloudWatch Logs Index Policy (%s): %s", d.Id(), err) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Logs, create.ErrActionSetting, ResNameIndexPolicy, state.ID.String(), err), + err.Error(), + ) + return } - d.Set(names.AttrLogGroupName, ip.IndexPolicies[0].LogGroupIdentifier) - d.Set("policy_document", ip.IndexPolicies[0].PolicyDocument) + state.ID = flex.StringToFramework(ctx, state.ID.ValueStringPointer()) + state.LogGroupName = flex.StringToFramework(ctx, out.LogGroupIdentifier) + state.PolicyDocument = flex.StringToFramework(ctx, out.PolicyDocument) - return diags + resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func resourceIndexPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics +func (r *resourceIndexPolicy) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + conn := r.Meta().LogsClient(ctx) - conn := meta.(*conns.AWSClient).LogsClient(ctx) + var plan, state resourceIndexPolicyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if !plan.PolicyDocument.Equal(state.PolicyDocument) { + input := cloudwatchlogs.PutIndexPolicyInput{ + LogGroupIdentifier: plan.LogGroupName.ValueStringPointer(), + } + + resp.Diagnostics.Append(flex.Expand(ctx, plan, &input, flex.WithFieldNamePrefix("Test"))...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 4. Call the AWS modify/update function + out, err := conn.PutIndexPolicy(ctx, &input) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Logs, create.ErrActionUpdating, ResNameIndexPolicy, plan.LogGroupName.String(), err), + err.Error(), + ) + return + } + if out == nil || out.IndexPolicy == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Logs, create.ErrActionUpdating, ResNameIndexPolicy, plan.ID.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} - log.Printf("[INFO] Deleting CloudWatch Logs Index Policy: %s", d.Id()) - _, err := conn.DeleteIndexPolicy(ctx, &cloudwatchlogs.DeleteIndexPolicyInput{ - LogGroupIdentifier: aws.String(d.Get(names.AttrLogGroupName).(string)), - }) +func (r *resourceIndexPolicy) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().LogsClient(ctx) - if errs.IsA[*types.ResourceNotFoundException](err) { - return diags + // TIP: -- 2. Fetch the state + var state resourceIndexPolicyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return } + // TIP: -- 3. Populate a delete input structure + input := cloudwatchlogs.DeleteIndexPolicyInput{ + LogGroupIdentifier: state.LogGroupName.ValueStringPointer(), + } + + _, err := conn.DeleteIndexPolicy(ctx, &input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting CloudWatch Logs Index Policy (%s): %s", d.Id(), err) + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return + } + + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Logs, create.ErrActionDeleting, ResNameIndexPolicy, state.ID.String(), err), + err.Error(), + ) + return } +} - return diags +func (r *resourceIndexPolicy) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root(names.AttrLogGroupName), req, resp) } -func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName string) (*types.IndexPolicy, error) { - input := cloudwatchlogs.DescribeIndexPoliciesInput{ +func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName string) (*awstypes.IndexPolicy, error) { + in := &cloudwatchlogs.DescribeIndexPoliciesInput{ LogGroupIdentifiers: []string{logGroupName}, } - ip, err := conn.DescribeIndexPolicies(ctx, &input) + out, err := conn.DescribeIndexPolicies(ctx, in) if err != nil { - return &types.IndexPolicy{}, err + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + return nil, err } - return &ip.IndexPolicies[0], nil + if out == nil || out.IndexPolicies == nil || len(out.IndexPolicies) == 0 { + return nil, tfresource.NewEmptyResultError(in) + } + + return &out.IndexPolicies[0], nil +} + +type resourceIndexPolicyModel struct { + ID types.String `tfsdk:"id"` + LogGroupName types.String `tfsdk:"log_group_name"` + PolicyDocument types.String `tfsdk:"policy_document"` + Timeouts timeouts.Value `tfsdk:"timeouts"` } diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 3b407cd3827..25ee67e9dae 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -3,12 +3,44 @@ package logs_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/cloudwatchlogs/types package. If so, you'll + // need to import types and reference the nested types, e.g., as + // types.. "context" "errors" "fmt" "testing" + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -16,12 +48,91 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/names" + // 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. tflogs "github.com/hashicorp/terraform-provider-aws/internal/service/logs" ) +// TIP: File Structure. The basic outline for all test files should be as +// follows. Improve this resource'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 resource'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 TestIndexPolicyExampleUnitTest(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 := tflogs.FunctionFromResource(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 resource as possible, and test importing, if @@ -31,35 +142,46 @@ import ( // Acceptance test access AWS and cost money to run. func TestAccLogsIndexPolicy_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 indexPolicy types.IndexPolicy - var logGroup types.LogGroup - resourceName := "aws_cloudwatch_log_index_policy.test" + var indexpolicy cloudwatchlogs.DescribeIndexPolicyResponse rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyDocument := "{\"Fields\":[\"eventName\"]}" + resourceName := "aws_logs_index_policy.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LogsEndpointID) + testAccPreCheck(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIndexPolicyConfig_basic(rName, policyDocument), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckLogGroupExists(ctx, t, "aws_cloudwatch_log_group.test", &logGroup), - testAccCheckIndexPolicyExists(ctx, t, resourceName, &indexPolicy), - resource.TestCheckResourceAttr(resourceName, "policy_document", policyDocument), + Config: testAccIndexPolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckIndexPolicyExists(ctx, resourceName, &indexpolicy), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ + "console_access": "false", + "groups.#": "0", + "username": "Test", + "password": "TestTest1234", + }), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "logs", regexache.MustCompile(`indexpolicy:+.`)), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccIndexPolicyImportStateIdFunc(resourceName), - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, }, }, }) @@ -67,22 +189,35 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { func TestAccLogsIndexPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) - var ip types.IndexPolicy - resourceName := "aws_cloudwatch_log_index_policy.test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var indexpolicy cloudwatchlogs.DescribeIndexPolicyResponse rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyDocument := "{\"Fields\":[\"eventName\"]}" + resourceName := "aws_logs_index_policy.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LogsEndpointID) + testAccPreCheck(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIndexPolicyConfig_basic(rName, policyDocument), + Config: testAccIndexPolicyConfig_basic(rName, testAccIndexPolicyVersionNewer), Check: resource.ComposeTestCheckFunc( - testAccCheckIndexPolicyExists(ctx, t, resourceName, &ip), - acctest.CheckResourceDisappears(ctx, acctest.Provider, tflogs.ResourceIndexPolicy(), resourceName), + testAccCheckIndexPolicyExists(ctx, resourceName, &indexpolicy), + // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, + // but expects a new resource factory function as the third argument. To expose this + // private function to the testing package, you may need to add a line like the following + // to exports_test.go: + // + // var ResourceIndexPolicy = newResourceIndexPolicy + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflogs.ResourceIndexPolicy, resourceName), ), ExpectNonEmptyPlan: true, }, @@ -95,70 +230,100 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_cloudwatch_log_index_policy" { + if rs.Type != "aws_logs_index_policy" { continue } - _, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.Attributes[names.AttrLogGroupName]) - + // TIP: ==== FINDERS ==== + // The find function should be exported. Since it won't be used outside of the package, it can be exported + // in the `exports_test.go` file. + _, err := tflogs.FindIndexPolicyByID(ctx, conn, rs.Primary.ID) if tfresource.NotFound(err) { - continue + return nil } - if err != nil { - return err + return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) } - return fmt.Errorf("CloudWatch Logs Index Policy still exists: %s", rs.Primary.ID) + return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameIndexPolicy, rs.Primary.ID, errors.New("not destroyed")) } return nil } } -func testAccCheckIndexPolicyExists(ctx context.Context, t *testing.T, resourceName string, indexPolicy *types.IndexPolicy) resource.TestCheckFunc { +func testAccCheckIndexPolicyExists(ctx context.Context, name string, indexpolicy *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[name] if !ok { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, resourceName, errors.New("not found")) + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, name, errors.New("not found")) } if rs.Primary.ID == "" { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, resourceName, errors.New("not set")) + return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, name, errors.New("not set")) } - conn := acctest.ProviderMeta(ctx, t).LogsClient(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - resp, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.Attributes[names.AttrLogGroupName]) + resp, err := tflogs.FindIndexPolicyByID(ctx, conn, rs.Primary.ID) if err != nil { return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) } - *indexPolicy = *resp + *indexpolicy = *resp + return nil } } -func testAccIndexPolicyImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return "", fmt.Errorf("Not found: %s", resourceName) +func testAccPreCheck(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) + + input := &cloudwatchlogs.ListIndexPolicysInput{} + + _, err := conn.ListIndexPolicys(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccCheckIndexPolicyNotRecreated(before, after *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.ToString(before.IndexPolicyId), aws.ToString(after.IndexPolicyId); before != after { + return create.Error(names.Logs, create.ErrActionCheckingNotRecreated, tflogs.ResNameIndexPolicy, aws.ToString(before.IndexPolicyId), errors.New("recreated")) } - return rs.Primary.Attributes[names.AttrLogGroupName] + ":" + rs.Primary.Attributes[names.AttrName], nil + return nil } } -func testAccIndexPolicyConfig_basic(rName, policyDocument string) string { +func testAccIndexPolicyConfig_basic(rName, version string) string { return fmt.Sprintf(` -resource "aws_cloudwatch_log_group" "test" { +resource "aws_security_group" "test" { name = %[1]q } -resource "aws_cloudwatch_log_index_policy" "test" { - log_group_name = aws_cloudwatch_log_group.test.name - policy_document = %[2]q +resource "aws_logs_index_policy" "test" { + index_policy_name = %[1]q + engine_type = "ActiveLogs" + engine_version = %[2]q + host_instance_type = "logs.t2.micro" + security_groups = [aws_security_group.test.id] + authentication_strategy = "simple" + storage_type = "efs" + + logs { + general = true + } + + user { + username = "Test" + password = "TestTest1234" + } } -`, rName, policyDocument) +`, rName, version) } diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index 00f1a3419be..3d470352c80 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -27,6 +27,10 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic IdentifierAttribute: names.AttrARN, }, }, + { + Factory: newResourceIndexPolicy, + Name: "Index Policy", + }, } } @@ -82,10 +86,6 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka IdentifierAttribute: names.AttrARN, }, }, - { - Factory: resourceIndexPolicy, - TypeName: "aws_cloudwatch_log_index_policy", - }, { Factory: resourceMetricFilter, TypeName: "aws_cloudwatch_log_metric_filter", diff --git a/website/docs/r/cloudwatch_log_account_policy.html.markdown b/website/docs/r/cloudwatch_log_account_policy.html.markdown index 035d8f6761b..1069a1d678b 100644 --- a/website/docs/r/cloudwatch_log_account_policy.html.markdown +++ b/website/docs/r/cloudwatch_log_account_policy.html.markdown @@ -109,3 +109,4 @@ Using `terraform import`, import this resource using the `policy_name` and `poli ```console % terraform import aws_cloudwatch_log_account_policy.example "my-account-policy:SUBSCRIPTION_FILTER_POLICY" ``` + diff --git a/website/docs/r/logs_fake_res.html.markdown b/website/docs/r/logs_fake_res.html.markdown new file mode 100644 index 00000000000..73f34eedc9f --- /dev/null +++ b/website/docs/r/logs_fake_res.html.markdown @@ -0,0 +1,69 @@ +--- +subcategory: "CloudWatch Logs" +layout: "aws" +page_title: "AWS: aws_logs_fake_res" +description: |- + Terraform resource for managing an AWS CloudWatch Logs Fake Res. +--- +` +# Resource: aws_logs_fake_res + +Terraform resource for managing an AWS CloudWatch Logs Fake Res. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_logs_fake_res" "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 resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Fake Res. 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. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `60m`) +* `update` - (Default `180m`) +* `delete` - (Default `90m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Fake Res using the `example_id_arg`. For example: + +```terraform +import { + to = aws_logs_fake_res.example + id = "fake_res-id-12345678" +} +``` + +Using `terraform import`, import CloudWatch Logs Fake Res using the `example_id_arg`. For example: + +```console +% terraform import aws_logs_fake_res.example fake_res-id-12345678 +``` diff --git a/website/docs/r/logs_index_policy.html.markdown b/website/docs/r/logs_index_policy.html.markdown index 555b8ae1dec..c85e3948705 100644 --- a/website/docs/r/logs_index_policy.html.markdown +++ b/website/docs/r/logs_index_policy.html.markdown @@ -1,11 +1,19 @@ --- subcategory: "CloudWatch Logs" layout: "aws" -page_title: "AWS: aws_cloudwatch_log_index_policy" +page_title: "AWS: aws_logs_index_policy" description: |- Terraform resource for managing an AWS CloudWatch Logs Index Policy. --- -# Resource: aws_cloudwatch_log_index_policy +` +# Resource: aws_logs_index_policy Terraform resource for managing an AWS CloudWatch Logs Index Policy. @@ -14,15 +22,7 @@ Terraform resource for managing an AWS CloudWatch Logs Index Policy. ### Basic Usage ```terraform -resource "aws_cloudwatch_log_group" "example" { - name = "example" -} - -resource "aws_cloudwatch_log_index_policy" "example" { - log_group_name = aws_cloudwatch_log_group.example.name - policy_document = jsonencode({ - Field = ["eventName"] - }) +resource "aws_logs_index_policy" "example" { } ``` @@ -30,8 +30,18 @@ resource "aws_cloudwatch_log_index_policy" "example" { The following arguments are required: -* `log_group_name` - (Required) Log group name to attach index policy to. -* `policy_document` - (Required) Index policy document in the form of `{"Fields": ["field1", "field2", ...]}` +* `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 resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Index Policy. 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. ## Timeouts @@ -43,17 +53,17 @@ The following arguments are required: ## Import -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Index Policy using the `log_group_name`. For example: +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Index Policy using the `example_id_arg`. For example: ```terraform import { - to = aws_cloudwatch_logs_index_policy.example - id = "/aws/log-group/name" + to = aws_logs_index_policy.example + id = "index_policy-id-12345678" } ``` -Using `terraform import`, import CloudWatch Logs Index Policy using the `log_group_name`. For example: +Using `terraform import`, import CloudWatch Logs Index Policy using the `example_id_arg`. For example: ```console -% terraform import aws_cloudwatch_logs_index_policy.example /aws/log-group/name +% terraform import aws_logs_index_policy.example index_policy-id-12345678 ``` From 340a5e5cebec50ed0c540c692b6c095418ea9c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:03:22 +0000 Subject: [PATCH 09/42] Fix log group name and remove timeouts --- internal/service/logs/arn.go | 9 ++++++ internal/service/logs/index_policy.go | 35 ++++++++-------------- internal/service/logs/index_policy_test.go | 35 ++++++++-------------- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/internal/service/logs/arn.go b/internal/service/logs/arn.go index 77e238ca8dd..c28c19850b7 100644 --- a/internal/service/logs/arn.go +++ b/internal/service/logs/arn.go @@ -4,6 +4,7 @@ package logs import ( + "errors" "strings" ) @@ -15,3 +16,11 @@ const ( func TrimLogGroupARNWildcardSuffix(arn string) string { return strings.TrimSuffix(arn, logGroupARNWildcardSuffix) } + +func LogGroupArnToName(arn string) (string, error) { + parts := strings.SplitN(arn, "log-group:", 2) + if len(parts) > 1 { + return parts[1], nil + } + return "", errors.New("invalid log group ARN") +} diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index bf596ec361e..73998875483 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -7,11 +7,9 @@ import ( "context" "errors" "fmt" - "time" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -27,14 +25,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @FrameworkResource("aws_cloudwatch_logs_index_policy", name="Index Policy") +// @FrameworkResource("aws_cloudwatch_log_index_policy", name="Index Policy") func newResourceIndexPolicy(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourceIndexPolicy{} - - r.SetDefaultCreateTimeout(30 * time.Minute) - r.SetDefaultUpdateTimeout(30 * time.Minute) - r.SetDefaultDeleteTimeout(30 * time.Minute) - return r, nil } @@ -44,11 +37,10 @@ const ( type resourceIndexPolicy struct { framework.ResourceWithConfigure - framework.WithTimeouts } func (r *resourceIndexPolicy) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "aws_logs_index_policy" + resp.TypeName = "aws_cloudwatch_log_index_policy" } func (r *resourceIndexPolicy) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -66,13 +58,6 @@ func (r *resourceIndexPolicy) Schema(ctx context.Context, req resource.SchemaReq Description: "Field index filter policy, in JSON", }, }, - Blocks: map[string]schema.Block{ - "timeouts": timeouts.Block(ctx, timeouts.Opts{ - Create: true, - Update: true, - Delete: true, - }), - }, } } @@ -112,7 +97,7 @@ func (r *resourceIndexPolicy) Create(ctx context.Context, req resource.CreateReq } // Set resource ID - id := fmt.Sprintf("%s:%s", out.IndexPolicy.LogGroupIdentifier, "index-policy") + id := fmt.Sprintf("%s:%s", *out.IndexPolicy.LogGroupIdentifier, "index-policy") plan.ID = flex.StringToFramework(ctx, &id) resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) @@ -147,7 +132,12 @@ func (r *resourceIndexPolicy) Read(ctx context.Context, req resource.ReadRequest } state.ID = flex.StringToFramework(ctx, state.ID.ValueStringPointer()) - state.LogGroupName = flex.StringToFramework(ctx, out.LogGroupIdentifier) + + logGroupName, err := LogGroupArnToName(*out.LogGroupIdentifier) + if err != nil { + resp.Diagnostics.AddError("failed to parse log group name", err.Error()) + } + state.LogGroupName = flex.StringToFramework(ctx, &logGroupName) state.PolicyDocument = flex.StringToFramework(ctx, out.PolicyDocument) resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...) @@ -263,8 +253,7 @@ func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Cli } type resourceIndexPolicyModel struct { - ID types.String `tfsdk:"id"` - LogGroupName types.String `tfsdk:"log_group_name"` - PolicyDocument types.String `tfsdk:"policy_document"` - Timeouts timeouts.Value `tfsdk:"timeouts"` + ID types.String `tfsdk:"id"` + LogGroupName types.String `tfsdk:"log_group_name"` + PolicyDocument types.String `tfsdk:"policy_document"` } diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 25ee67e9dae..3872ef8ae68 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -41,14 +41,13 @@ import ( "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" 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" - "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" // TIP: You will often need to import the package that this test file lives @@ -150,7 +149,7 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { var indexpolicy cloudwatchlogs.DescribeIndexPolicyResponse rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_logs_index_policy.test" + resourceName := "aws_cloudwatch_log_index_policy.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { @@ -195,7 +194,7 @@ func TestAccLogsIndexPolicy_disappears(t *testing.T) { var indexpolicy cloudwatchlogs.DescribeIndexPolicyResponse rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_logs_index_policy.test" + resourceName := "aws_cloudwatch_log_index_policy.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { @@ -230,7 +229,7 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_logs_index_policy" { + if rs.Type != "aws_cloudwatch_log_index_policy" { continue } @@ -303,27 +302,17 @@ func testAccCheckIndexPolicyNotRecreated(before, after *cloudwatchlogs.DescribeI func testAccIndexPolicyConfig_basic(rName, version string) string { return fmt.Sprintf(` -resource "aws_security_group" "test" { +resource "aws_cloudwatch_log_group" "test" { name = %[1]q } -resource "aws_logs_index_policy" "test" { - index_policy_name = %[1]q - engine_type = "ActiveLogs" - engine_version = %[2]q - host_instance_type = "logs.t2.micro" - security_groups = [aws_security_group.test.id] - authentication_strategy = "simple" - storage_type = "efs" - - logs { - general = true - } - - user { - username = "Test" - password = "TestTest1234" - } +resource "aws_cloudwatch_log_index_policy" "test" { + log_group_name = aws_cloudwatch_log_group.test.name + policy_document = jsonencode({ + Fields = [ + "eventName" + ] + }) } `, rName, version) } From b284c6425918dffab0cf2eb79b8fa6d3e6ac2e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:05:29 +0000 Subject: [PATCH 10/42] Remove tips --- internal/service/logs/index_policy.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index 73998875483..acdbc342e00 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -168,7 +168,6 @@ func (r *resourceIndexPolicy) Update(ctx context.Context, req resource.UpdateReq return } - // TIP: -- 4. Call the AWS modify/update function out, err := conn.PutIndexPolicy(ctx, &input) if err != nil { resp.Diagnostics.AddError( @@ -197,14 +196,12 @@ func (r *resourceIndexPolicy) Update(ctx context.Context, req resource.UpdateReq func (r *resourceIndexPolicy) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { conn := r.Meta().LogsClient(ctx) - // TIP: -- 2. Fetch the state var state resourceIndexPolicyModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - // TIP: -- 3. Populate a delete input structure input := cloudwatchlogs.DeleteIndexPolicyInput{ LogGroupIdentifier: state.LogGroupName.ValueStringPointer(), } From d22be84c1fff16993feaedc4b83723082e245172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:12:41 +0000 Subject: [PATCH 11/42] Fix basic config --- internal/service/logs/index_policy_test.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 3872ef8ae68..311b78368bf 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -300,7 +300,7 @@ func testAccCheckIndexPolicyNotRecreated(before, after *cloudwatchlogs.DescribeI } } -func testAccIndexPolicyConfig_basic(rName, version string) string { +func testAccIndexPolicyConfig_basic(logGroupName string, policyDocument string) string { return fmt.Sprintf(` resource "aws_cloudwatch_log_group" "test" { name = %[1]q @@ -308,11 +308,7 @@ resource "aws_cloudwatch_log_group" "test" { resource "aws_cloudwatch_log_index_policy" "test" { log_group_name = aws_cloudwatch_log_group.test.name - policy_document = jsonencode({ - Fields = [ - "eventName" - ] - }) + policy_document = jsonencode(%[2]q) } -`, rName, version) +`, logGroupName, policyDocument) } From fc2477bfb96490858efc88e45ac53c501431f59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:25:57 +0000 Subject: [PATCH 12/42] Format terraform --- internal/service/logs/index_policy_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 311b78368bf..93e582efa00 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -307,7 +307,7 @@ resource "aws_cloudwatch_log_group" "test" { } resource "aws_cloudwatch_log_index_policy" "test" { - log_group_name = aws_cloudwatch_log_group.test.name + log_group_name = aws_cloudwatch_log_group.test.name policy_document = jsonencode(%[2]q) } `, logGroupName, policyDocument) From ddd41409b1f6318df640621ec9c6b33bfc332c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:38:31 +0000 Subject: [PATCH 13/42] Delete files --- website/docs/r/logs_fake_res.html.markdown | 69 ------------------- .../docs/r/logs_index_policy.html.markdown | 69 ------------------- 2 files changed, 138 deletions(-) delete mode 100644 website/docs/r/logs_fake_res.html.markdown delete mode 100644 website/docs/r/logs_index_policy.html.markdown diff --git a/website/docs/r/logs_fake_res.html.markdown b/website/docs/r/logs_fake_res.html.markdown deleted file mode 100644 index 73f34eedc9f..00000000000 --- a/website/docs/r/logs_fake_res.html.markdown +++ /dev/null @@ -1,69 +0,0 @@ ---- -subcategory: "CloudWatch Logs" -layout: "aws" -page_title: "AWS: aws_logs_fake_res" -description: |- - Terraform resource for managing an AWS CloudWatch Logs Fake Res. ---- -` -# Resource: aws_logs_fake_res - -Terraform resource for managing an AWS CloudWatch Logs Fake Res. - -## Example Usage - -### Basic Usage - -```terraform -resource "aws_logs_fake_res" "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 resource exports the following attributes in addition to the arguments above: - -* `arn` - ARN of the Fake Res. 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. - -## Timeouts - -[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): - -* `create` - (Default `60m`) -* `update` - (Default `180m`) -* `delete` - (Default `90m`) - -## Import - -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Fake Res using the `example_id_arg`. For example: - -```terraform -import { - to = aws_logs_fake_res.example - id = "fake_res-id-12345678" -} -``` - -Using `terraform import`, import CloudWatch Logs Fake Res using the `example_id_arg`. For example: - -```console -% terraform import aws_logs_fake_res.example fake_res-id-12345678 -``` diff --git a/website/docs/r/logs_index_policy.html.markdown b/website/docs/r/logs_index_policy.html.markdown deleted file mode 100644 index c85e3948705..00000000000 --- a/website/docs/r/logs_index_policy.html.markdown +++ /dev/null @@ -1,69 +0,0 @@ ---- -subcategory: "CloudWatch Logs" -layout: "aws" -page_title: "AWS: aws_logs_index_policy" -description: |- - Terraform resource for managing an AWS CloudWatch Logs Index Policy. ---- -` -# Resource: aws_logs_index_policy - -Terraform resource for managing an AWS CloudWatch Logs Index Policy. - -## Example Usage - -### Basic Usage - -```terraform -resource "aws_logs_index_policy" "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 resource exports the following attributes in addition to the arguments above: - -* `arn` - ARN of the Index Policy. 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. - -## Timeouts - -[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): - -* `create` - (Default `60m`) -* `update` - (Default `180m`) -* `delete` - (Default `90m`) - -## Import - -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Index Policy using the `example_id_arg`. For example: - -```terraform -import { - to = aws_logs_index_policy.example - id = "index_policy-id-12345678" -} -``` - -Using `terraform import`, import CloudWatch Logs Index Policy using the `example_id_arg`. For example: - -```console -% terraform import aws_logs_index_policy.example index_policy-id-12345678 -``` From 3994627c5a3b564859826bd4f4e1df1e304d1f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:47:03 +0000 Subject: [PATCH 14/42] Make function private to package --- internal/service/logs/arn.go | 2 +- internal/service/logs/index_policy.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/logs/arn.go b/internal/service/logs/arn.go index c28c19850b7..7b536a311f3 100644 --- a/internal/service/logs/arn.go +++ b/internal/service/logs/arn.go @@ -17,7 +17,7 @@ func TrimLogGroupARNWildcardSuffix(arn string) string { return strings.TrimSuffix(arn, logGroupARNWildcardSuffix) } -func LogGroupArnToName(arn string) (string, error) { +func logGroupArnToName(arn string) (string, error) { parts := strings.SplitN(arn, "log-group:", 2) if len(parts) > 1 { return parts[1], nil diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index acdbc342e00..23d660fbb69 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -133,7 +133,7 @@ func (r *resourceIndexPolicy) Read(ctx context.Context, req resource.ReadRequest state.ID = flex.StringToFramework(ctx, state.ID.ValueStringPointer()) - logGroupName, err := LogGroupArnToName(*out.LogGroupIdentifier) + logGroupName, err := logGroupArnToName(*out.LogGroupIdentifier) if err != nil { resp.Diagnostics.AddError("failed to parse log group name", err.Error()) } From ec109b637e128fe73217e0782e8ef58ed956b1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:48:46 +0000 Subject: [PATCH 15/42] Remove extra white line --- website/docs/r/cloudwatch_log_account_policy.html.markdown | 1 - 1 file changed, 1 deletion(-) diff --git a/website/docs/r/cloudwatch_log_account_policy.html.markdown b/website/docs/r/cloudwatch_log_account_policy.html.markdown index 1069a1d678b..035d8f6761b 100644 --- a/website/docs/r/cloudwatch_log_account_policy.html.markdown +++ b/website/docs/r/cloudwatch_log_account_policy.html.markdown @@ -109,4 +109,3 @@ Using `terraform import`, import this resource using the `policy_name` and `poli ```console % terraform import aws_cloudwatch_log_account_policy.example "my-account-policy:SUBSCRIPTION_FILTER_POLICY" ``` - From a57b8170bc1c94ba6d6e2d49f47b9297fbdb4031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:00:10 +0000 Subject: [PATCH 16/42] Validate policy_document is valid JSON --- internal/service/logs/index_policy.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index 23d660fbb69..274d5412126 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -15,12 +15,14 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -56,6 +58,9 @@ func (r *resourceIndexPolicy) Schema(ctx context.Context, req resource.SchemaReq "policy_document": schema.StringAttribute{ Required: true, Description: "Field index filter policy, in JSON", + Validators: []validator.String{ + validators.JSON(), + }, }, }, } From 4f267d3ed6446e386ce72b168a026e205a8b2728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:05:23 +0000 Subject: [PATCH 17/42] Remove ID attribute --- internal/service/logs/index_policy.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index 274d5412126..c6e99da758c 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -6,7 +6,6 @@ package logs import ( "context" "errors" - "fmt" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" @@ -48,7 +47,6 @@ func (r *resourceIndexPolicy) Metadata(_ context.Context, req resource.MetadataR func (r *resourceIndexPolicy) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "id": framework.IDAttribute(), names.AttrLogGroupName: schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -102,8 +100,8 @@ func (r *resourceIndexPolicy) Create(ctx context.Context, req resource.CreateReq } // Set resource ID - id := fmt.Sprintf("%s:%s", *out.IndexPolicy.LogGroupIdentifier, "index-policy") - plan.ID = flex.StringToFramework(ctx, &id) + //id := fmt.Sprintf("%s:%s", *out.IndexPolicy.LogGroupIdentifier, "index-policy") + //plan.ID = flex.StringToFramework(ctx, &id) resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) if resp.Diagnostics.HasError() { @@ -130,13 +128,13 @@ func (r *resourceIndexPolicy) Read(ctx context.Context, req resource.ReadRequest } if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionSetting, ResNameIndexPolicy, state.ID.String(), err), + create.ProblemStandardMessage(names.Logs, create.ErrActionSetting, ResNameIndexPolicy, "", err), err.Error(), ) return } - state.ID = flex.StringToFramework(ctx, state.ID.ValueStringPointer()) + //state.ID = flex.StringToFramework(ctx, state.ID.ValueStringPointer()) logGroupName, err := logGroupArnToName(*out.LogGroupIdentifier) if err != nil { @@ -183,7 +181,7 @@ func (r *resourceIndexPolicy) Update(ctx context.Context, req resource.UpdateReq } if out == nil || out.IndexPolicy == nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionUpdating, ResNameIndexPolicy, plan.ID.String(), nil), + create.ProblemStandardMessage(names.Logs, create.ErrActionUpdating, ResNameIndexPolicy, "", nil), errors.New("empty output").Error(), ) return @@ -219,7 +217,7 @@ func (r *resourceIndexPolicy) Delete(ctx context.Context, req resource.DeleteReq } resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionDeleting, ResNameIndexPolicy, state.ID.String(), err), + create.ProblemStandardMessage(names.Logs, create.ErrActionDeleting, ResNameIndexPolicy, "", err), err.Error(), ) return @@ -255,7 +253,6 @@ func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Cli } type resourceIndexPolicyModel struct { - ID types.String `tfsdk:"id"` LogGroupName types.String `tfsdk:"log_group_name"` PolicyDocument types.String `tfsdk:"policy_document"` } From b9a14d11c834ec91bb10b19d6407a769553118cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:10:51 +0000 Subject: [PATCH 18/42] Fix functiona name casing --- internal/service/logs/arn.go | 2 +- internal/service/logs/index_policy.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/logs/arn.go b/internal/service/logs/arn.go index 7b536a311f3..e94c7e78c79 100644 --- a/internal/service/logs/arn.go +++ b/internal/service/logs/arn.go @@ -17,7 +17,7 @@ func TrimLogGroupARNWildcardSuffix(arn string) string { return strings.TrimSuffix(arn, logGroupARNWildcardSuffix) } -func logGroupArnToName(arn string) (string, error) { +func logGroupARNToName(arn string) (string, error) { parts := strings.SplitN(arn, "log-group:", 2) if len(parts) > 1 { return parts[1], nil diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index c6e99da758c..8fc1871a2b5 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -136,7 +136,7 @@ func (r *resourceIndexPolicy) Read(ctx context.Context, req resource.ReadRequest //state.ID = flex.StringToFramework(ctx, state.ID.ValueStringPointer()) - logGroupName, err := logGroupArnToName(*out.LogGroupIdentifier) + logGroupName, err := logGroupARNToName(*out.LogGroupIdentifier) if err != nil { resp.Diagnostics.AddError("failed to parse log group name", err.Error()) } From d2d5e1963bf53ec0fc3c48aa469299ad348809c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:38:32 +0000 Subject: [PATCH 19/42] Fix acceptance tests --- internal/service/logs/exports_test.go | 1 + internal/service/logs/index_policy_test.go | 197 ++------------------- 2 files changed, 18 insertions(+), 180 deletions(-) diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index 52e96ab2c87..cc914ff82a5 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -16,6 +16,7 @@ var ( ResourceStream = resourceStream ResourceSubscriptionFilter = resourceSubscriptionFilter ResourceAnomalyDetector = newResourceAnomalyDetector + ResourceIndexPolicy = newResourceIndexPolicy FindAccountPolicyByTwoPartKey = findAccountPolicyByTwoPartKey FindDestinationByName = findDestinationByName diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 93e582efa00..396fe792e21 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -3,43 +3,12 @@ package logs_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/cloudwatchlogs/types package. If so, you'll - // need to import types and reference the nested types, e.g., as - // types.. "context" "errors" "fmt" "testing" - "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -47,133 +16,35 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" + tflogs "github.com/hashicorp/terraform-provider-aws/internal/service/logs" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" - - // 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. - tflogs "github.com/hashicorp/terraform-provider-aws/internal/service/logs" ) -// TIP: File Structure. The basic outline for all test files should be as -// follows. Improve this resource'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 resource'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 TestIndexPolicyExampleUnitTest(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 := tflogs.FunctionFromResource(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 resource as possible, and test importing, if -// applicable. We prefix its name with "TestAcc", the service, and the -// resource name. -// -// Acceptance test access AWS and cost money to run. func TestAccLogsIndexPolicy_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 indexpolicy cloudwatchlogs.DescribeIndexPolicyResponse - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + var indexPolicy cloudwatchlogs.DescribeIndexPoliciesOutput + logGroupName := "/aws/testacc/index-policy-" + sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyDocument := `{Fields:[\"eventName\"]}` resourceName := "aws_cloudwatch_log_index_policy.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.LogsEndpointID) - testAccPreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LogsServiceID) }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIndexPolicyConfig_basic(rName), + Config: testAccIndexPolicyConfig_basic(logGroupName, policyDocument), Check: resource.ComposeTestCheckFunc( - testAccCheckIndexPolicyExists(ctx, resourceName, &indexpolicy), - resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), - resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ - "console_access": "false", - "groups.#": "0", - "username": "Test", - "password": "TestTest1234", - }), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "logs", regexache.MustCompile(`indexpolicy:+.`)), + testAccCheckIndexPolicyExists(ctx, resourceName, &indexPolicy), ), }, { @@ -192,30 +63,24 @@ func TestAccLogsIndexPolicy_disappears(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var indexpolicy cloudwatchlogs.DescribeIndexPolicyResponse - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + var indexPolicy cloudwatchlogs.DescribeIndexPoliciesOutput + logGroupName := "/aws/testacc/index-policy-" + sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyDocument := `{Fields:[\"eventName\"]}` resourceName := "aws_cloudwatch_log_index_policy.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.LogsEndpointID) - testAccPreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.LogsServiceID) }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIndexPolicyConfig_basic(rName, testAccIndexPolicyVersionNewer), + Config: testAccIndexPolicyConfig_basic(logGroupName, policyDocument), Check: resource.ComposeTestCheckFunc( - testAccCheckIndexPolicyExists(ctx, resourceName, &indexpolicy), - // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, - // but expects a new resource factory function as the third argument. To expose this - // private function to the testing package, you may need to add a line like the following - // to exports_test.go: - // - // var ResourceIndexPolicy = newResourceIndexPolicy + testAccCheckIndexPolicyExists(ctx, resourceName, &indexPolicy), acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflogs.ResourceIndexPolicy, resourceName), ), ExpectNonEmptyPlan: true, @@ -233,10 +98,7 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc continue } - // TIP: ==== FINDERS ==== - // The find function should be exported. Since it won't be used outside of the package, it can be exported - // in the `exports_test.go` file. - _, err := tflogs.FindIndexPolicyByID(ctx, conn, rs.Primary.ID) + _, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.Attributes["log_group_name"]) if tfresource.NotFound(err) { return nil } @@ -251,7 +113,7 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc } } -func testAccCheckIndexPolicyExists(ctx context.Context, name string, indexpolicy *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { +func testAccCheckIndexPolicyExists(ctx context.Context, name string, indexPolicy *cloudwatchlogs.DescribeIndexPoliciesOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { @@ -264,37 +126,12 @@ func testAccCheckIndexPolicyExists(ctx context.Context, name string, indexpolicy conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - resp, err := tflogs.FindIndexPolicyByID(ctx, conn, rs.Primary.ID) + resp, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.ID) if err != nil { return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) } - *indexpolicy = *resp - - return nil - } -} - -func testAccPreCheck(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - - input := &cloudwatchlogs.ListIndexPolicysInput{} - - _, err := conn.ListIndexPolicys(ctx, input) - - if acctest.PreCheckSkipError(err) { - t.Skipf("skipping acceptance testing: %s", err) - } - if err != nil { - t.Fatalf("unexpected PreCheck error: %s", err) - } -} - -func testAccCheckIndexPolicyNotRecreated(before, after *cloudwatchlogs.DescribeIndexPolicyResponse) resource.TestCheckFunc { - return func(s *terraform.State) error { - if before, after := aws.ToString(before.IndexPolicyId), aws.ToString(after.IndexPolicyId); before != after { - return create.Error(names.Logs, create.ErrActionCheckingNotRecreated, tflogs.ResNameIndexPolicy, aws.ToString(before.IndexPolicyId), errors.New("recreated")) - } + indexPolicy.IndexPolicies = append(indexPolicy.IndexPolicies, *resp) return nil } From 311383e209c2cb21eb35089eaf99ee3b504d42b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:18:30 +0000 Subject: [PATCH 20/42] Fix semgrep findings --- internal/service/logs/index_policy_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 396fe792e21..3f490970c75 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -35,7 +35,7 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.LogsServiceID) + acctest.PreCheckPartitionHasService(t, names.CloudWatchEndpointID) }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -51,7 +51,7 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + ImportStateVerifyIgnore: []string{names.AttrApplyImmediately, "user"}, }, }, }) @@ -71,7 +71,7 @@ func TestAccLogsIndexPolicy_disappears(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.LogsServiceID) + acctest.PreCheckPartitionHasService(t, names.CloudWatchEndpointID) }, ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -98,7 +98,7 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc continue } - _, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.Attributes["log_group_name"]) + _, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.Attributes[names.AttrLogGroupName]) if tfresource.NotFound(err) { return nil } From 7075c2d207f199628b2b8edfa9d18c350f6cc2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Coelho?= <16445494+jcoelho93@users.noreply.github.com> Date: Thu, 19 Dec 2024 14:19:10 +0000 Subject: [PATCH 21/42] Add resource documentation --- .../cloudwatch_log_index_policy.html.markdown | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 website/docs/r/cloudwatch_log_index_policy.html.markdown diff --git a/website/docs/r/cloudwatch_log_index_policy.html.markdown b/website/docs/r/cloudwatch_log_index_policy.html.markdown new file mode 100644 index 00000000000..aca884a5361 --- /dev/null +++ b/website/docs/r/cloudwatch_log_index_policy.html.markdown @@ -0,0 +1,56 @@ +--- +subcategory: "CloudWatch Logs" +layout: "aws" +page_title: "AWS: aws_cloudwatch_log_index_policy" +description: |- + Terraform resource for managing an AWS CloudWatch Logs Index Policy. +--- + +# Resource: aws_cloudwatch_log_index_policy + +Terraform resource for managing an AWS CloudWatch Logs Index Policy. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_cloudwatch_log_group" "example" { + name = "example" +} + +resource "aws_cloudwatch_log_index_policy" "example" { + log_group_name = aws_cloudwatch_log_group.example.name + policy_document = jsonencode({ + Fields = ["eventName"] + }) +} +``` + +## Argument Reference + +The following arguments are required: + +* `log_group_name` - (Required) Log group name to set the policy for. +* `policy_document` - (Required) JSON policy document. This is a JSON formatted string. + +## Attribute Reference + +This resource exports no additional attributes. + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import CloudWatch Logs Index Policy using the `log_group_name`. For example: + +```terraform +import { + to = aws_cloudwatch_log_index_policy.example + id = "/aws/log/group/name" +} +``` + +Using `terraform import`, import CloudWatch Logs Index Policy using the `log_group_name`. For example: + +```console +% terraform import aws_cloudwatch_log_index_policy.example /aws/log/group/name +``` From 707ff74e4b07948e061c8e09a8b3f1ed5bd53d08 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 11:55:29 -0500 Subject: [PATCH 22/42] r/aws_cloudwatch_log_index_policy: Tidy up. --- .changelog/40594.txt | 3 + internal/service/logs/arn.go | 9 - internal/service/logs/exports_test.go | 6 +- internal/service/logs/generate.go | 1 + internal/service/logs/index_policy.go | 258 +++++++++---------- internal/service/logs/index_policy_test.go | 51 ++-- internal/service/logs/list_pages_gen.go | 27 ++ internal/service/logs/service_package_gen.go | 8 +- 8 files changed, 168 insertions(+), 195 deletions(-) create mode 100644 .changelog/40594.txt create mode 100644 internal/service/logs/list_pages_gen.go diff --git a/.changelog/40594.txt b/.changelog/40594.txt new file mode 100644 index 00000000000..d90f52f927d --- /dev/null +++ b/.changelog/40594.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_cloudwatch_log_index_policy +``` \ No newline at end of file diff --git a/internal/service/logs/arn.go b/internal/service/logs/arn.go index e94c7e78c79..77e238ca8dd 100644 --- a/internal/service/logs/arn.go +++ b/internal/service/logs/arn.go @@ -4,7 +4,6 @@ package logs import ( - "errors" "strings" ) @@ -16,11 +15,3 @@ const ( func TrimLogGroupARNWildcardSuffix(arn string) string { return strings.TrimSuffix(arn, logGroupARNWildcardSuffix) } - -func logGroupARNToName(arn string) (string, error) { - parts := strings.SplitN(arn, "log-group:", 2) - if len(parts) > 1 { - return parts[1], nil - } - return "", errors.New("invalid log group ARN") -} diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index cc914ff82a5..99452539f13 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -6,20 +6,21 @@ package logs // Exports for use in tests only. var ( ResourceAccountPolicy = resourceAccountPolicy + ResourceAnomalyDetector = newResourceAnomalyDetector ResourceDataProtectionPolicy = resourceDataProtectionPolicy ResourceDestination = resourceDestination ResourceDestinationPolicy = resourceDestinationPolicy ResourceGroup = resourceGroup + ResourceIndexPolicy = newIndexPolicyResource ResourceMetricFilter = resourceMetricFilter ResourceQueryDefinition = resourceQueryDefinition ResourceResourcePolicy = resourceResourcePolicy ResourceStream = resourceStream ResourceSubscriptionFilter = resourceSubscriptionFilter - ResourceAnomalyDetector = newResourceAnomalyDetector - ResourceIndexPolicy = newResourceIndexPolicy FindAccountPolicyByTwoPartKey = findAccountPolicyByTwoPartKey FindDestinationByName = findDestinationByName + FindLogAnomalyDetectorByARN = findLogAnomalyDetectorByARN FindLogGroupByName = findLogGroupByName FindLogStreamByTwoPartKey = findLogStreamByTwoPartKey // nosemgrep:ci.logs-in-var-name FindMetricFilterByTwoPartKey = findMetricFilterByTwoPartKey @@ -27,7 +28,6 @@ var ( FindQueryDefinitionByTwoPartKey = findQueryDefinitionByTwoPartKey FindResourcePolicyByName = findResourcePolicyByName FindSubscriptionFilterByTwoPartKey = findSubscriptionFilterByTwoPartKey - FindLogAnomalyDetectorByARN = findLogAnomalyDetectorByARN ValidLogGroupName = validLogGroupName ValidLogGroupNamePrefix = validLogGroupNamePrefix diff --git a/internal/service/logs/generate.go b/internal/service/logs/generate.go index e4084aad6d5..1e2e4a7e162 100644 --- a/internal/service/logs/generate.go +++ b/internal/service/logs/generate.go @@ -1,6 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 +//go:generate go run ../../generate/listpages/main.go -ListOps=DescribeIndexPolicies //go:generate go run ../../generate/tags/main.go -ListTags -ServiceTagsMap -UpdateTags -CreateTags -KVTValues //go:generate go run ../../generate/servicepackage/main.go //go:generate go run ../../generate/tagstests/main.go diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index 8fc1871a2b5..e328efad643 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -5,47 +5,45 @@ package logs import ( "context" - "errors" + "fmt" + "strings" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" "github.com/hashicorp/terraform-provider-aws/internal/framework" - "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" - "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) // @FrameworkResource("aws_cloudwatch_log_index_policy", name="Index Policy") -func newResourceIndexPolicy(_ context.Context) (resource.ResourceWithConfigure, error) { - r := &resourceIndexPolicy{} +func newIndexPolicyResource(context.Context) (resource.ResourceWithConfigure, error) { + r := &indexPolicyResource{} return r, nil } -const ( - ResNameIndexPolicy = "Index Policy" -) - -type resourceIndexPolicy struct { +type indexPolicyResource struct { framework.ResourceWithConfigure } -func (r *resourceIndexPolicy) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "aws_cloudwatch_log_index_policy" +func (*indexPolicyResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_cloudwatch_log_index_policy" } -func (r *resourceIndexPolicy) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ +func (r *indexPolicyResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ names.AttrLogGroupName: schema.StringAttribute{ Required: true, @@ -54,205 +52,179 @@ func (r *resourceIndexPolicy) Schema(ctx context.Context, req resource.SchemaReq }, }, "policy_document": schema.StringAttribute{ + CustomType: jsontypes.NormalizedType{}, Required: true, Description: "Field index filter policy, in JSON", - Validators: []validator.String{ - validators.JSON(), - }, }, }, } } -func (r *resourceIndexPolicy) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - conn := r.Meta().LogsClient(ctx) - - var plan resourceIndexPolicyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { +func (r *indexPolicyResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data indexPolicyResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } + conn := r.Meta().LogsClient(ctx) + input := cloudwatchlogs.PutIndexPolicyInput{ - LogGroupIdentifier: plan.LogGroupName.ValueStringPointer(), - PolicyDocument: plan.PolicyDocument.ValueStringPointer(), + LogGroupIdentifier: fwflex.StringFromFramework(ctx, data.LogGroupName), + PolicyDocument: fwflex.StringFromFramework(ctx, data.PolicyDocument), } - resp.Diagnostics.Append(flex.Expand(ctx, plan, &input, flex.WithFieldNamePrefix("IndexPolicy"))...) - if resp.Diagnostics.HasError() { - return - } + _, err := conn.PutIndexPolicy(ctx, &input) - out, err := conn.PutIndexPolicy(ctx, &input) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionCreating, ResNameIndexPolicy, plan.LogGroupName.String(), err), - err.Error(), - ) - return - } - if out == nil || out.IndexPolicy == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionCreating, ResNameIndexPolicy, plan.LogGroupName.String(), nil), - errors.New("empty output").Error(), - ) - return - } - - // Set resource ID - //id := fmt.Sprintf("%s:%s", *out.IndexPolicy.LogGroupIdentifier, "index-policy") - //plan.ID = flex.StringToFramework(ctx, &id) + response.Diagnostics.AddError(fmt.Sprintf("creating CloudWatch Logs Index Policy (%s)", data.LogGroupName.ValueString()), err.Error()) - resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) - if resp.Diagnostics.HasError() { return } - resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + response.Diagnostics.Append(response.State.Set(ctx, data)...) } -func (r *resourceIndexPolicy) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - conn := r.Meta().LogsClient(ctx) - - var state resourceIndexPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *indexPolicyResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data indexPolicyResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - out, err := findIndexPolicyByLogGroupName(ctx, conn, state.LogGroupName.ValueString()) + conn := r.Meta().LogsClient(ctx) + + output, err := findIndexPolicyByLogGroupName(ctx, conn, data.LogGroupName.ValueString()) if tfresource.NotFound(err) { - resp.State.RemoveResource(ctx) + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) + return } + if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionSetting, ResNameIndexPolicy, "", err), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("reading CloudWatch Logs Index Policy (%s)", data.LogGroupName.ValueString()), err.Error()) + return } - //state.ID = flex.StringToFramework(ctx, state.ID.ValueStringPointer()) + // Set attributes for import. + data.LogGroupName = fwflex.StringValueToFramework(ctx, logGroupIdentifierToName(aws.ToString(output.LogGroupIdentifier))) + data.PolicyDocument = jsontypes.NewNormalizedValue(aws.ToString(output.PolicyDocument)) - logGroupName, err := logGroupARNToName(*out.LogGroupIdentifier) - if err != nil { - resp.Diagnostics.AddError("failed to parse log group name", err.Error()) - } - state.LogGroupName = flex.StringToFramework(ctx, &logGroupName) - state.PolicyDocument = flex.StringToFramework(ctx, out.PolicyDocument) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) +} - resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...) - if resp.Diagnostics.HasError() { +func (r *indexPolicyResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var new indexPolicyResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &new)...) + if response.Diagnostics.HasError() { return } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *resourceIndexPolicy) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { conn := r.Meta().LogsClient(ctx) - var plan, state resourceIndexPolicyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return + input := cloudwatchlogs.PutIndexPolicyInput{ + LogGroupIdentifier: fwflex.StringFromFramework(ctx, new.LogGroupName), + PolicyDocument: fwflex.StringFromFramework(ctx, new.PolicyDocument), } - if !plan.PolicyDocument.Equal(state.PolicyDocument) { - input := cloudwatchlogs.PutIndexPolicyInput{ - LogGroupIdentifier: plan.LogGroupName.ValueStringPointer(), - } - - resp.Diagnostics.Append(flex.Expand(ctx, plan, &input, flex.WithFieldNamePrefix("Test"))...) - if resp.Diagnostics.HasError() { - return - } + _, err := conn.PutIndexPolicy(ctx, &input) - out, err := conn.PutIndexPolicy(ctx, &input) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionUpdating, ResNameIndexPolicy, plan.LogGroupName.String(), err), - err.Error(), - ) - return - } - if out == nil || out.IndexPolicy == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionUpdating, ResNameIndexPolicy, "", nil), - errors.New("empty output").Error(), - ) - return - } + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("updating CloudWatch Logs Index Policy (%s)", new.LogGroupName.ValueString()), err.Error()) - resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) - if resp.Diagnostics.HasError() { - return - } + return } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + response.Diagnostics.Append(response.State.Set(ctx, &new)...) } -func (r *resourceIndexPolicy) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - conn := r.Meta().LogsClient(ctx) - - var state resourceIndexPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *indexPolicyResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var data indexPolicyResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } + conn := r.Meta().LogsClient(ctx) + input := cloudwatchlogs.DeleteIndexPolicyInput{ - LogGroupIdentifier: state.LogGroupName.ValueStringPointer(), + LogGroupIdentifier: fwflex.StringFromFramework(ctx, data.LogGroupName), } _, err := conn.DeleteIndexPolicy(ctx, &input) + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return + } + if err != nil { - if errs.IsA[*awstypes.ResourceNotFoundException](err) { - return - } + response.Diagnostics.AddError(fmt.Sprintf("deleting CloudWatch Logs Index Policy (%s)", data.LogGroupName.ValueString()), err.Error()) - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionDeleting, ResNameIndexPolicy, "", err), - err.Error(), - ) return } } -func (r *resourceIndexPolicy) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root(names.AttrLogGroupName), req, resp) +func (r *indexPolicyResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root(names.AttrLogGroupName), request, response) } -func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName string) (*awstypes.IndexPolicy, error) { - in := &cloudwatchlogs.DescribeIndexPoliciesInput{ - LogGroupIdentifiers: []string{logGroupName}, +func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Client, name string) (*awstypes.IndexPolicy, error) { + input := cloudwatchlogs.DescribeIndexPoliciesInput{ + LogGroupIdentifiers: []string{name}, } - out, err := conn.DescribeIndexPolicies(ctx, in) + return findIndexPolicy(ctx, conn, &input) +} + +func findIndexPolicy(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeIndexPoliciesInput) (*awstypes.IndexPolicy, error) { + output, err := findIndexPolicies(ctx, conn, input) + if err != nil { - if errs.IsA[*awstypes.ResourceNotFoundException](err) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: in, - } + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findIndexPolicies(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeIndexPoliciesInput) ([]awstypes.IndexPolicy, error) { + var output []awstypes.IndexPolicy + + err := describeIndexPoliciesPages(ctx, conn, input, func(page *cloudwatchlogs.DescribeIndexPoliciesOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - return nil, err + output = append(output, page.IndexPolicies...) + + return !lastPage + }) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } } - if out == nil || out.IndexPolicies == nil || len(out.IndexPolicies) == 0 { - return nil, tfresource.NewEmptyResultError(in) + if err != nil { + return nil, err } - return &out.IndexPolicies[0], nil + return output, nil } -type resourceIndexPolicyModel struct { - LogGroupName types.String `tfsdk:"log_group_name"` - PolicyDocument types.String `tfsdk:"policy_document"` +type indexPolicyResourceModel struct { + LogGroupName types.String `tfsdk:"log_group_name"` + PolicyDocument jsontypes.Normalized `tfsdk:"policy_document"` +} + +func logGroupIdentifierToName(identifier string) string { + if !arn.IsARN(identifier) { + return identifier + } + + return strings.TrimPrefix(identifier, "log-group:") } diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 3f490970c75..533beda942b 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -5,17 +5,14 @@ package logs_test import ( "context" - "errors" "fmt" "testing" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" 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" tflogs "github.com/hashicorp/terraform-provider-aws/internal/service/logs" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -23,11 +20,6 @@ import ( func TestAccLogsIndexPolicy_basic(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - - var indexPolicy cloudwatchlogs.DescribeIndexPoliciesOutput logGroupName := "/aws/testacc/index-policy-" + sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) policyDocument := `{Fields:[\"eventName\"]}` resourceName := "aws_cloudwatch_log_index_policy.test" @@ -44,14 +36,13 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { { Config: testAccIndexPolicyConfig_basic(logGroupName, policyDocument), Check: resource.ComposeTestCheckFunc( - testAccCheckIndexPolicyExists(ctx, resourceName, &indexPolicy), + testAccCheckIndexPolicyExists(ctx, resourceName), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{names.AttrApplyImmediately, "user"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -59,11 +50,6 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { func TestAccLogsIndexPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - - var indexPolicy cloudwatchlogs.DescribeIndexPoliciesOutput logGroupName := "/aws/testacc/index-policy-" + sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) policyDocument := `{Fields:[\"eventName\"]}` resourceName := "aws_cloudwatch_log_index_policy.test" @@ -80,7 +66,7 @@ func TestAccLogsIndexPolicy_disappears(t *testing.T) { { Config: testAccIndexPolicyConfig_basic(logGroupName, policyDocument), Check: resource.ComposeTestCheckFunc( - testAccCheckIndexPolicyExists(ctx, resourceName, &indexPolicy), + testAccCheckIndexPolicyExists(ctx, resourceName), acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflogs.ResourceIndexPolicy, resourceName), ), ExpectNonEmptyPlan: true, @@ -99,41 +85,34 @@ func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc } _, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.Attributes[names.AttrLogGroupName]) + if tfresource.NotFound(err) { - return nil + continue } + if err != nil { - return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) + return err } - return create.Error(names.Logs, create.ErrActionCheckingDestroyed, tflogs.ResNameIndexPolicy, rs.Primary.ID, errors.New("not destroyed")) + return fmt.Errorf("CloudWatch Logs Index Policy still exists: %s", rs.Primary.ID) } return nil } } -func testAccCheckIndexPolicyExists(ctx context.Context, name string, indexPolicy *cloudwatchlogs.DescribeIndexPoliciesOutput) resource.TestCheckFunc { +func testAccCheckIndexPolicyExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, name, errors.New("not found")) - } - - if rs.Primary.ID == "" { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, name, errors.New("not set")) + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - resp, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.ID) - if err != nil { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameIndexPolicy, rs.Primary.ID, err) - } - - indexPolicy.IndexPolicies = append(indexPolicy.IndexPolicies, *resp) + _, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.ID) - return nil + return err } } diff --git a/internal/service/logs/list_pages_gen.go b/internal/service/logs/list_pages_gen.go new file mode 100644 index 00000000000..853281735e2 --- /dev/null +++ b/internal/service/logs/list_pages_gen.go @@ -0,0 +1,27 @@ +// Code generated by "internal/generate/listpages/main.go -ListOps=DescribeIndexPolicies"; DO NOT EDIT. + +package logs + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" +) + +func describeIndexPoliciesPages(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeIndexPoliciesInput, fn func(*cloudwatchlogs.DescribeIndexPoliciesOutput, bool) bool) error { + for { + output, err := conn.DescribeIndexPolicies(ctx, input) + if err != nil { + return err + } + + lastPage := aws.ToString(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index 3d470352c80..895dd558bed 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -20,6 +20,10 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { return []*types.ServicePackageFrameworkResource{ + { + Factory: newIndexPolicyResource, + Name: "Index Policy", + }, { Factory: newResourceAnomalyDetector, Name: "Anomaly Detector", @@ -27,10 +31,6 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic IdentifierAttribute: names.AttrARN, }, }, - { - Factory: newResourceIndexPolicy, - Name: "Index Policy", - }, } } From 7804b83e194e953b22fd584c9160707539025059 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 11:58:06 -0500 Subject: [PATCH 23/42] Add 'TestAccLogsIndexPolicy_update'. --- internal/service/logs/index_policy_test.go | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 533beda942b..5574cada54a 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -75,6 +75,45 @@ func TestAccLogsIndexPolicy_disappears(t *testing.T) { }) } +func TestAccLogsIndexPolicy_update(t *testing.T) { + ctx := acctest.Context(t) + logGroupName := "/aws/testacc/index-policy-" + sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyDocument1 := `{Fields:[\"eventName\"]}` + policyDocument2 := `{Fields:[\"requestId\"]}` + resourceName := "aws_cloudwatch_log_index_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.CloudWatchEndpointID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.LogsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIndexPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIndexPolicyConfig_basic(logGroupName, policyDocument1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIndexPolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "policy_document", policyDocument1), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccIndexPolicyConfig_basic(logGroupName, policyDocument2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIndexPolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "policy_document", policyDocument2), + ), + }, + }, + }) +} + func testAccCheckIndexPolicyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) From 810c631403a9ae97df5dd5beb4b27b383f476ac1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 12:17:03 -0500 Subject: [PATCH 24/42] Corrections. --- internal/service/logs/index_policy.go | 5 +-- internal/service/logs/index_policy_test.go | 36 +++++++++++++--------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index e328efad643..17ca1241ae4 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -222,9 +222,10 @@ type indexPolicyResourceModel struct { } func logGroupIdentifierToName(identifier string) string { - if !arn.IsARN(identifier) { + arn, err := arn.Parse(identifier) + if err != nil { return identifier } - return strings.TrimPrefix(identifier, "log-group:") + return strings.TrimPrefix(arn.Resource, "log-group:") } diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 5574cada54a..0d14232befa 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -21,7 +21,7 @@ import ( func TestAccLogsIndexPolicy_basic(t *testing.T) { ctx := acctest.Context(t) logGroupName := "/aws/testacc/index-policy-" + sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyDocument := `{Fields:[\"eventName\"]}` + policyDocument := `{"Fields":["eventName"]}` resourceName := "aws_cloudwatch_log_index_policy.test" resource.ParallelTest(t, resource.TestCase{ @@ -40,9 +40,11 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccIndexPolicyStateIdFunc(resourceName), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: names.AttrLogGroupName, }, }, }) @@ -51,7 +53,7 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { func TestAccLogsIndexPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) logGroupName := "/aws/testacc/index-policy-" + sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyDocument := `{Fields:[\"eventName\"]}` + policyDocument := `{"Fields":["eventName"]}` resourceName := "aws_cloudwatch_log_index_policy.test" resource.ParallelTest(t, resource.TestCase{ @@ -78,8 +80,8 @@ func TestAccLogsIndexPolicy_disappears(t *testing.T) { func TestAccLogsIndexPolicy_update(t *testing.T) { ctx := acctest.Context(t) logGroupName := "/aws/testacc/index-policy-" + sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyDocument1 := `{Fields:[\"eventName\"]}` - policyDocument2 := `{Fields:[\"requestId\"]}` + policyDocument1 := `{"Fields":["eventName"]}` + policyDocument2 := `{"Fields": ["eventName", "requestId"]}` resourceName := "aws_cloudwatch_log_index_policy.test" resource.ParallelTest(t, resource.TestCase{ @@ -98,11 +100,6 @@ func TestAccLogsIndexPolicy_update(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "policy_document", policyDocument1), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, { Config: testAccIndexPolicyConfig_basic(logGroupName, policyDocument2), Check: resource.ComposeTestCheckFunc( @@ -149,12 +146,23 @@ func testAccCheckIndexPolicyExists(ctx context.Context, n string) resource.TestC conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - _, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.ID) + _, err := tflogs.FindIndexPolicyByLogGroupName(ctx, conn, rs.Primary.Attributes[names.AttrLogGroupName]) return err } } +func testAccIndexPolicyStateIdFunc(n string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[n] + if !ok { + return "", fmt.Errorf("Not found: %s", n) + } + + return rs.Primary.Attributes[names.AttrLogGroupName], nil + } +} + func testAccIndexPolicyConfig_basic(logGroupName string, policyDocument string) string { return fmt.Sprintf(` resource "aws_cloudwatch_log_group" "test" { @@ -163,7 +171,7 @@ resource "aws_cloudwatch_log_group" "test" { resource "aws_cloudwatch_log_index_policy" "test" { log_group_name = aws_cloudwatch_log_group.test.name - policy_document = jsonencode(%[2]q) + policy_document = %[2]q } `, logGroupName, policyDocument) } From d11a07e33410d911091b5911b9eceafb63c8294d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 12:17:14 -0500 Subject: [PATCH 25/42] Acceptance test output: % make testacc TESTARGS='-run=TestAccLogsIndexPolicy_' PKG=logs make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... TF_ACC=1 go1.23.3 test ./internal/service/logs/... -v -count 1 -parallel 20 -run=TestAccLogsIndexPolicy_ -timeout 360m 2024/12/19 12:16:31 Initializing Terraform AWS Provider... === RUN TestAccLogsIndexPolicy_basic === PAUSE TestAccLogsIndexPolicy_basic === RUN TestAccLogsIndexPolicy_disappears === PAUSE TestAccLogsIndexPolicy_disappears === RUN TestAccLogsIndexPolicy_update === PAUSE TestAccLogsIndexPolicy_update === CONT TestAccLogsIndexPolicy_basic === CONT TestAccLogsIndexPolicy_update === CONT TestAccLogsIndexPolicy_disappears --- PASS: TestAccLogsIndexPolicy_disappears (13.21s) --- PASS: TestAccLogsIndexPolicy_basic (14.48s) --- PASS: TestAccLogsIndexPolicy_update (20.38s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/logs 25.705s From ef8ae8c53fc71f77236e52f1101709de70a800a2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 12:20:28 -0500 Subject: [PATCH 26/42] logs: Generate paginators. --- internal/service/logs/generate.go | 2 +- internal/service/logs/list_pages.go | 46 ------------------------- internal/service/logs/list_pages_gen.go | 34 +++++++++++++++++- 3 files changed, 34 insertions(+), 48 deletions(-) delete mode 100644 internal/service/logs/list_pages.go diff --git a/internal/service/logs/generate.go b/internal/service/logs/generate.go index 1e2e4a7e162..2d7579c2654 100644 --- a/internal/service/logs/generate.go +++ b/internal/service/logs/generate.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//go:generate go run ../../generate/listpages/main.go -ListOps=DescribeIndexPolicies +//go:generate go run ../../generate/listpages/main.go -ListOps=DescribeIndexPolicies,DescribeQueryDefinitions,DescribeResourcePolicies //go:generate go run ../../generate/tags/main.go -ListTags -ServiceTagsMap -UpdateTags -CreateTags -KVTValues //go:generate go run ../../generate/servicepackage/main.go //go:generate go run ../../generate/tagstests/main.go diff --git a/internal/service/logs/list_pages.go b/internal/service/logs/list_pages.go deleted file mode 100644 index a0dd2ac4b36..00000000000 --- a/internal/service/logs/list_pages.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -// Code generated by internal/generate/listpages/main.go and migrated to AWS SDK for Go v2. - -package logs - -import ( - "context" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" -) - -func describeQueryDefinitionsPages(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeQueryDefinitionsInput, fn func(*cloudwatchlogs.DescribeQueryDefinitionsOutput, bool) bool) error { - for { - output, err := conn.DescribeQueryDefinitions(ctx, input) - if err != nil { - return err - } - - lastPage := aws.ToString(output.NextToken) == "" - if !fn(output, lastPage) || lastPage { - break - } - - input.NextToken = output.NextToken - } - return nil -} -func describeResourcePoliciesPages(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeResourcePoliciesInput, fn func(*cloudwatchlogs.DescribeResourcePoliciesOutput, bool) bool) error { - for { - output, err := conn.DescribeResourcePolicies(ctx, input) - if err != nil { - return err - } - - lastPage := aws.ToString(output.NextToken) == "" - if !fn(output, lastPage) || lastPage { - break - } - - input.NextToken = output.NextToken - } - return nil -} diff --git a/internal/service/logs/list_pages_gen.go b/internal/service/logs/list_pages_gen.go index 853281735e2..b120a12165a 100644 --- a/internal/service/logs/list_pages_gen.go +++ b/internal/service/logs/list_pages_gen.go @@ -1,4 +1,4 @@ -// Code generated by "internal/generate/listpages/main.go -ListOps=DescribeIndexPolicies"; DO NOT EDIT. +// Code generated by "internal/generate/listpages/main.go -ListOps=DescribeIndexPolicies,DescribeQueryDefinitions,DescribeResourcePolicies"; DO NOT EDIT. package logs @@ -25,3 +25,35 @@ func describeIndexPoliciesPages(ctx context.Context, conn *cloudwatchlogs.Client } return nil } +func describeQueryDefinitionsPages(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeQueryDefinitionsInput, fn func(*cloudwatchlogs.DescribeQueryDefinitionsOutput, bool) bool) error { + for { + output, err := conn.DescribeQueryDefinitions(ctx, input) + if err != nil { + return err + } + + lastPage := aws.ToString(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} +func describeResourcePoliciesPages(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeResourcePoliciesInput, fn func(*cloudwatchlogs.DescribeResourcePoliciesOutput, bool) bool) error { + for { + output, err := conn.DescribeResourcePolicies(ctx, input) + if err != nil { + return err + } + + lastPage := aws.ToString(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} From e6d64e830613d90ba9a316d22279f5842dff43ab Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 12:27:44 -0500 Subject: [PATCH 27/42] Tweak 'findIndexPolicyByLogGroupName'. --- internal/service/logs/index_policy.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index 17ca1241ae4..01ad28a70cc 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -175,8 +175,17 @@ func findIndexPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Cli input := cloudwatchlogs.DescribeIndexPoliciesInput{ LogGroupIdentifiers: []string{name}, } + output, err := findIndexPolicy(ctx, conn, &input) - return findIndexPolicy(ctx, conn, &input) + if err != nil { + return nil, err + } + + if output.PolicyDocument == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, err } func findIndexPolicy(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeIndexPoliciesInput) (*awstypes.IndexPolicy, error) { From 94b9b52db17a28e7ac71511a424fc3d1822de847 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 12:32:34 -0500 Subject: [PATCH 28/42] r/aws_cloudwatch_log_account_policy: Tidy up. --- internal/service/logs/account_policy.go | 65 ++++++++++++++------ internal/service/logs/account_policy_test.go | 6 +- internal/service/logs/generate.go | 2 +- internal/service/logs/index_policy_test.go | 4 +- internal/service/logs/list_pages_gen.go | 18 +++++- 5 files changed, 70 insertions(+), 25 deletions(-) diff --git a/internal/service/logs/account_policy.go b/internal/service/logs/account_policy.go index ddfdb47a911..e161eecd4dc 100644 --- a/internal/service/logs/account_policy.go +++ b/internal/service/logs/account_policy.go @@ -11,7 +11,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -58,13 +58,13 @@ func resourceAccountPolicy() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateDiagFunc: enum.Validate[types.PolicyType](), + ValidateDiagFunc: enum.Validate[awstypes.PolicyType](), }, names.AttrScope: { Type: schema.TypeString, Optional: true, - Default: types.ScopeAll, - ValidateDiagFunc: enum.Validate[types.Scope](), + Default: awstypes.ScopeAll, + ValidateDiagFunc: enum.Validate[awstypes.Scope](), }, "selection_criteria": { Type: schema.TypeString, @@ -88,8 +88,8 @@ func resourceAccountPolicyPut(ctx context.Context, d *schema.ResourceData, meta input := &cloudwatchlogs.PutAccountPolicyInput{ PolicyDocument: aws.String(policy), PolicyName: aws.String(name), - PolicyType: types.PolicyType(d.Get("policy_type").(string)), - Scope: types.Scope(d.Get(names.AttrScope).(string)), + PolicyType: awstypes.PolicyType(d.Get("policy_type").(string)), + Scope: awstypes.Scope(d.Get(names.AttrScope).(string)), } if v, ok := d.GetOk("selection_criteria"); ok { @@ -111,7 +111,7 @@ func resourceAccountPolicyRead(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).LogsClient(ctx) - output, err := findAccountPolicyByTwoPartKey(ctx, conn, types.PolicyType(d.Get("policy_type").(string)), d.Id()) + output, err := findAccountPolicyByTwoPartKey(ctx, conn, awstypes.PolicyType(d.Get("policy_type").(string)), d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] CloudWatch Logs Account Policy (%s) not found, removing from state", d.Id()) @@ -149,10 +149,10 @@ func resourceAccountPolicyDelete(ctx context.Context, d *schema.ResourceData, me log.Printf("[DEBUG] Deleting CloudWatch Logs Account Policy: %s", d.Id()) _, err := conn.DeleteAccountPolicy(ctx, &cloudwatchlogs.DeleteAccountPolicyInput{ PolicyName: aws.String(d.Id()), - PolicyType: types.PolicyType(d.Get("policy_type").(string)), + PolicyType: awstypes.PolicyType(d.Get("policy_type").(string)), }) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diags } @@ -178,15 +178,48 @@ func resourceAccountPolicyImport(d *schema.ResourceData, meta interface{}) ([]*s return []*schema.ResourceData{d}, nil } -func findAccountPolicyByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, policyType types.PolicyType, policyName string) (*types.AccountPolicy, error) { - input := &cloudwatchlogs.DescribeAccountPoliciesInput{ +func findAccountPolicyByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, policyType awstypes.PolicyType, policyName string) (*awstypes.AccountPolicy, error) { + input := cloudwatchlogs.DescribeAccountPoliciesInput{ PolicyName: aws.String(policyName), PolicyType: policyType, } + output, err := findAccountPolicy(ctx, conn, &input) - output, err := conn.DescribeAccountPolicies(ctx, input) + if err != nil { + return nil, err + } + + if output.PolicyDocument == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, err +} + +func findAccountPolicy(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeAccountPoliciesInput) (*awstypes.AccountPolicy, error) { + output, err := findAccountPolicies(ctx, conn, input) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findAccountPolicies(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeAccountPoliciesInput) ([]awstypes.AccountPolicy, error) { + var output []awstypes.AccountPolicy + + err := describeAccountPoliciesPages(ctx, conn, input, func(page *cloudwatchlogs.DescribeAccountPoliciesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } - if errs.IsA[*types.ResourceNotFoundException](err) { + output = append(output, page.AccountPolicies...) + + return !lastPage + }) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, @@ -197,9 +230,5 @@ func findAccountPolicyByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Cli return nil, err } - if output == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - return tfresource.AssertSingleValueResult(output.AccountPolicies) + return output, nil } diff --git a/internal/service/logs/account_policy_test.go b/internal/service/logs/account_policy_test.go index db41081bb1e..9df784f496d 100644 --- a/internal/service/logs/account_policy_test.go +++ b/internal/service/logs/account_policy_test.go @@ -185,11 +185,11 @@ func testAccCheckAccountPolicyExists(ctx context.Context, n string, v *types.Acc } } -func testAccAccountPolicyImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { +func testAccAccountPolicyImportStateIDFunc(n string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return "", fmt.Errorf("Not found: %s", resourceName) + return "", fmt.Errorf("Not found: %s", n) } policyName := rs.Primary.ID diff --git a/internal/service/logs/generate.go b/internal/service/logs/generate.go index 2d7579c2654..77a88af01db 100644 --- a/internal/service/logs/generate.go +++ b/internal/service/logs/generate.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//go:generate go run ../../generate/listpages/main.go -ListOps=DescribeIndexPolicies,DescribeQueryDefinitions,DescribeResourcePolicies +//go:generate go run ../../generate/listpages/main.go -ListOps=DescribeAccountPolicies,DescribeIndexPolicies,DescribeQueryDefinitions,DescribeResourcePolicies //go:generate go run ../../generate/tags/main.go -ListTags -ServiceTagsMap -UpdateTags -CreateTags -KVTValues //go:generate go run ../../generate/servicepackage/main.go //go:generate go run ../../generate/tagstests/main.go diff --git a/internal/service/logs/index_policy_test.go b/internal/service/logs/index_policy_test.go index 0d14232befa..bb2cc3cfa71 100644 --- a/internal/service/logs/index_policy_test.go +++ b/internal/service/logs/index_policy_test.go @@ -42,7 +42,7 @@ func TestAccLogsIndexPolicy_basic(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccIndexPolicyStateIdFunc(resourceName), + ImportStateIdFunc: testAccIndexPolicyImportStateIDFunc(resourceName), ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: names.AttrLogGroupName, }, @@ -152,7 +152,7 @@ func testAccCheckIndexPolicyExists(ctx context.Context, n string) resource.TestC } } -func testAccIndexPolicyStateIdFunc(n string) resource.ImportStateIdFunc { +func testAccIndexPolicyImportStateIDFunc(n string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { rs, ok := s.RootModule().Resources[n] if !ok { diff --git a/internal/service/logs/list_pages_gen.go b/internal/service/logs/list_pages_gen.go index b120a12165a..e9e409f7bff 100644 --- a/internal/service/logs/list_pages_gen.go +++ b/internal/service/logs/list_pages_gen.go @@ -1,4 +1,4 @@ -// Code generated by "internal/generate/listpages/main.go -ListOps=DescribeIndexPolicies,DescribeQueryDefinitions,DescribeResourcePolicies"; DO NOT EDIT. +// Code generated by "internal/generate/listpages/main.go -ListOps=DescribeAccountPolicies,DescribeIndexPolicies,DescribeQueryDefinitions,DescribeResourcePolicies"; DO NOT EDIT. package logs @@ -9,6 +9,22 @@ import ( "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" ) +func describeAccountPoliciesPages(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeAccountPoliciesInput, fn func(*cloudwatchlogs.DescribeAccountPoliciesOutput, bool) bool) error { + for { + output, err := conn.DescribeAccountPolicies(ctx, input) + if err != nil { + return err + } + + lastPage := aws.ToString(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} func describeIndexPoliciesPages(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeIndexPoliciesInput, fn func(*cloudwatchlogs.DescribeIndexPoliciesOutput, bool) bool) error { for { output, err := conn.DescribeIndexPolicies(ctx, input) From f667dcb4e2ee11dd1c8bf436febfbabfd09999c7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 14:08:59 -0500 Subject: [PATCH 29/42] r/aws_cloudwatch_log_data_protection_policy: Tidy up. --- .../service/logs/data_protection_policy.go | 49 ++++++++++--------- .../logs/data_protection_policy_test.go | 8 +-- internal/service/logs/exports_test.go | 21 ++++---- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/internal/service/logs/data_protection_policy.go b/internal/service/logs/data_protection_policy.go index 2b41f342164..268bfea3e8c 100644 --- a/internal/service/logs/data_protection_policy.go +++ b/internal/service/logs/data_protection_policy.go @@ -5,18 +5,18 @@ package logs import ( "context" - "errors" "log" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -59,17 +59,14 @@ func resourceDataProtectionPolicy() *schema.Resource { func resourceDataProtectionPolicyPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) - logGroupName := d.Get(names.AttrLogGroupName).(string) - policy, err := structure.NormalizeJsonString(d.Get("policy_document").(string)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "policy (%s) is invalid JSON: %s", policy, err) + return sdkdiag.AppendFromErr(diags, err) } + logGroupName := d.Get(names.AttrLogGroupName).(string) input := &cloudwatchlogs.PutDataProtectionPolicyInput{ LogGroupIdentifier: aws.String(logGroupName), PolicyDocument: aws.String(policy), @@ -90,10 +87,9 @@ func resourceDataProtectionPolicyPut(ctx context.Context, d *schema.ResourceData func resourceDataProtectionPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) - output, err := FindDataProtectionPolicyByID(ctx, conn, d.Id()) + output, err := findDataProtectionPolicyByLogGroupName(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] CloudWatch Logs Data Protection Policy (%s) not found, removing from state", d.Id()) @@ -105,20 +101,17 @@ func resourceDataProtectionPolicyRead(ctx context.Context, d *schema.ResourceDat return sdkdiag.AppendErrorf(diags, "reading CloudWatch Logs Data Protection Policy (%s): %s", d.Id(), err) } - d.Set(names.AttrLogGroupName, output.LogGroupIdentifier) - policyToSet, err := verify.SecondJSONUnlessEquivalent(d.Get("policy_document").(string), aws.ToString(output.PolicyDocument)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "while setting policy (%s), encountered: %s", policyToSet, err) + return sdkdiag.AppendFromErr(diags, err) } policyToSet, err = structure.NormalizeJsonString(policyToSet) - if err != nil { - return sdkdiag.AppendErrorf(diags, "policy (%s) is invalid JSON: %s", policyToSet, err) + return sdkdiag.AppendFromErr(diags, err) } + d.Set(names.AttrLogGroupName, output.LogGroupIdentifier) d.Set("policy_document", policyToSet) return diags @@ -126,7 +119,6 @@ func resourceDataProtectionPolicyRead(ctx context.Context, d *schema.ResourceDat func resourceDataProtectionPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) log.Printf("[DEBUG] Deleting CloudWatch Logs Data Protection Policy: %s", d.Id()) @@ -134,7 +126,7 @@ func resourceDataProtectionPolicyDelete(ctx context.Context, d *schema.ResourceD LogGroupIdentifier: aws.String(d.Id()), }) - if nfe := (*types.ResourceNotFoundException)(nil); errors.As(err, &nfe) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diags } @@ -145,16 +137,29 @@ func resourceDataProtectionPolicyDelete(ctx context.Context, d *schema.ResourceD return diags } -func FindDataProtectionPolicyByID(ctx context.Context, conn *cloudwatchlogs.Client, id string) (*cloudwatchlogs.GetDataProtectionPolicyOutput, error) { - input := &cloudwatchlogs.GetDataProtectionPolicyInput{ - LogGroupIdentifier: aws.String(id), +func findDataProtectionPolicyByLogGroupName(ctx context.Context, conn *cloudwatchlogs.Client, name string) (*cloudwatchlogs.GetDataProtectionPolicyOutput, error) { + input := cloudwatchlogs.GetDataProtectionPolicyInput{ + LogGroupIdentifier: aws.String(name), + } + output, err := findDataProtectionPolicy(ctx, conn, &input) + + if err != nil { + return nil, err + } + + if output.PolicyDocument == nil { + return nil, tfresource.NewEmptyResultError(input) } + return output, err +} + +func findDataProtectionPolicy(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.GetDataProtectionPolicyInput) (*cloudwatchlogs.GetDataProtectionPolicyOutput, error) { output, err := conn.GetDataProtectionPolicy(ctx, input) - if nfe := (*types.ResourceNotFoundException)(nil); errors.As(err, &nfe) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return nil, &retry.NotFoundError{ - LastError: nfe, + LastError: err, LastRequest: input, } } diff --git a/internal/service/logs/data_protection_policy_test.go b/internal/service/logs/data_protection_policy_test.go index f487faa0ee3..d59d1158ada 100644 --- a/internal/service/logs/data_protection_policy_test.go +++ b/internal/service/logs/data_protection_policy_test.go @@ -221,7 +221,7 @@ func testAccCheckDataProtectionPolicyDestroy(ctx context.Context) resource.TestC continue } - _, err := tflogs.FindDataProtectionPolicyByID(ctx, conn, rs.Primary.ID) + _, err := tflogs.FindDataProtectionPolicyByLogGroupName(ctx, conn, rs.Primary.ID) if tfresource.NotFound(err) { continue @@ -245,13 +245,9 @@ func testAccCheckDataProtectionPolicyExists(ctx context.Context, n string, v *cl return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No CloudWatch Logs Data Protection Policy ID is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - output, err := tflogs.FindDataProtectionPolicyByID(ctx, conn, rs.Primary.ID) + output, err := tflogs.FindDataProtectionPolicyByLogGroupName(ctx, conn, rs.Primary.ID) if err != nil { return err diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index 99452539f13..27b0880c892 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -18,16 +18,17 @@ var ( ResourceStream = resourceStream ResourceSubscriptionFilter = resourceSubscriptionFilter - FindAccountPolicyByTwoPartKey = findAccountPolicyByTwoPartKey - FindDestinationByName = findDestinationByName - FindLogAnomalyDetectorByARN = findLogAnomalyDetectorByARN - FindLogGroupByName = findLogGroupByName - FindLogStreamByTwoPartKey = findLogStreamByTwoPartKey // nosemgrep:ci.logs-in-var-name - FindMetricFilterByTwoPartKey = findMetricFilterByTwoPartKey - FindIndexPolicyByLogGroupName = findIndexPolicyByLogGroupName - FindQueryDefinitionByTwoPartKey = findQueryDefinitionByTwoPartKey - FindResourcePolicyByName = findResourcePolicyByName - FindSubscriptionFilterByTwoPartKey = findSubscriptionFilterByTwoPartKey + FindAccountPolicyByTwoPartKey = findAccountPolicyByTwoPartKey + FindDataProtectionPolicyByLogGroupName = findDataProtectionPolicyByLogGroupName + FindDestinationByName = findDestinationByName + FindLogAnomalyDetectorByARN = findLogAnomalyDetectorByARN + FindLogGroupByName = findLogGroupByName + FindLogStreamByTwoPartKey = findLogStreamByTwoPartKey // nosemgrep:ci.logs-in-var-name + FindMetricFilterByTwoPartKey = findMetricFilterByTwoPartKey + FindIndexPolicyByLogGroupName = findIndexPolicyByLogGroupName + FindQueryDefinitionByTwoPartKey = findQueryDefinitionByTwoPartKey + FindResourcePolicyByName = findResourcePolicyByName + FindSubscriptionFilterByTwoPartKey = findSubscriptionFilterByTwoPartKey ValidLogGroupName = validLogGroupName ValidLogGroupNamePrefix = validLogGroupNamePrefix From c491d4e2accb31cebaa5bff518a53f1a936b8df9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 14:18:44 -0500 Subject: [PATCH 30/42] 'TrimLogGroupARNWildcardSuffix' -> 'trimLogGroupARNWildcardSuffix'. --- internal/service/logs/arn.go | 4 ++-- internal/service/logs/exports_test.go | 1 + internal/service/logs/group.go | 2 +- internal/service/logs/group_data_source.go | 2 +- internal/service/logs/groups_data_source.go | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/service/logs/arn.go b/internal/service/logs/arn.go index 77e238ca8dd..b9507c6890e 100644 --- a/internal/service/logs/arn.go +++ b/internal/service/logs/arn.go @@ -11,7 +11,7 @@ const ( logGroupARNWildcardSuffix = ":*" ) -// TrimLogGroupARNWildcardSuffix trims any wilcard suffix from a Log Group ARN. -func TrimLogGroupARNWildcardSuffix(arn string) string { +// trimLogGroupARNWildcardSuffix trims any wilcard suffix from a Log Group ARN. +func trimLogGroupARNWildcardSuffix(arn string) string { return strings.TrimSuffix(arn, logGroupARNWildcardSuffix) } diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index 27b0880c892..d805f04aeca 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -30,6 +30,7 @@ var ( FindResourcePolicyByName = findResourcePolicyByName FindSubscriptionFilterByTwoPartKey = findSubscriptionFilterByTwoPartKey + TrimLogGroupARNWildcardSuffix = trimLogGroupARNWildcardSuffix ValidLogGroupName = validLogGroupName ValidLogGroupNamePrefix = validLogGroupNamePrefix ValidLogMetricFilterName = validLogMetricFilterName diff --git a/internal/service/logs/group.go b/internal/service/logs/group.go index 698068bb374..b4291493a70 100644 --- a/internal/service/logs/group.go +++ b/internal/service/logs/group.go @@ -151,7 +151,7 @@ func resourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interfa return sdkdiag.AppendErrorf(diags, "reading CloudWatch Logs Log Group (%s): %s", d.Id(), err) } - d.Set(names.AttrARN, TrimLogGroupARNWildcardSuffix(aws.ToString(lg.Arn))) + d.Set(names.AttrARN, trimLogGroupARNWildcardSuffix(aws.ToString(lg.Arn))) d.Set(names.AttrKMSKeyID, lg.KmsKeyId) d.Set("log_group_class", lg.LogGroupClass) d.Set(names.AttrName, lg.LogGroupName) diff --git a/internal/service/logs/group_data_source.go b/internal/service/logs/group_data_source.go index bf757b2af28..b992a55f964 100644 --- a/internal/service/logs/group_data_source.go +++ b/internal/service/logs/group_data_source.go @@ -64,7 +64,7 @@ func dataSourceGroupRead(ctx context.Context, d *schema.ResourceData, meta inter } d.SetId(name) - d.Set(names.AttrARN, TrimLogGroupARNWildcardSuffix(aws.ToString(logGroup.Arn))) + d.Set(names.AttrARN, trimLogGroupARNWildcardSuffix(aws.ToString(logGroup.Arn))) d.Set(names.AttrCreationTime, logGroup.CreationTime) d.Set(names.AttrKMSKeyID, logGroup.KmsKeyId) d.Set("log_group_class", logGroup.LogGroupClass) diff --git a/internal/service/logs/groups_data_source.go b/internal/service/logs/groups_data_source.go index 93863336ce4..b033291878c 100644 --- a/internal/service/logs/groups_data_source.go +++ b/internal/service/logs/groups_data_source.go @@ -69,7 +69,7 @@ func dataSourceGroupsRead(ctx context.Context, d *schema.ResourceData, meta inte var arns, logGroupNames []string for _, r := range output { - arns = append(arns, TrimLogGroupARNWildcardSuffix(aws.ToString(r.Arn))) + arns = append(arns, trimLogGroupARNWildcardSuffix(aws.ToString(r.Arn))) logGroupNames = append(logGroupNames, aws.ToString(r.LogGroupName)) } From b7c3576f59454763660d725d359dfd692a0e67b8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Dec 2024 14:57:02 -0500 Subject: [PATCH 31/42] r/aws_cloudwatch_log_destination_policy: Tidy up. --- internal/service/logs/account_policy.go | 2 +- internal/service/logs/account_policy_test.go | 32 +++++++-------- internal/service/logs/destination_policy.go | 40 ++++++++++++++++--- .../service/logs/destination_policy_test.go | 2 +- internal/service/logs/exports_test.go | 1 + 5 files changed, 53 insertions(+), 24 deletions(-) diff --git a/internal/service/logs/account_policy.go b/internal/service/logs/account_policy.go index e161eecd4dc..f67254fef45 100644 --- a/internal/service/logs/account_policy.go +++ b/internal/service/logs/account_policy.go @@ -99,7 +99,7 @@ func resourceAccountPolicyPut(ctx context.Context, d *schema.ResourceData, meta output, err := conn.PutAccountPolicy(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "creating CloudWatch Logs Account Policy (%s): %s", name, err) + return sdkdiag.AppendErrorf(diags, "putting CloudWatch Logs Account Policy (%s): %s", name, err) } d.SetId(aws.ToString(output.AccountPolicy.PolicyName)) diff --git a/internal/service/logs/account_policy_test.go b/internal/service/logs/account_policy_test.go index 9df784f496d..381ee3dd009 100644 --- a/internal/service/logs/account_policy_test.go +++ b/internal/service/logs/account_policy_test.go @@ -185,21 +185,6 @@ func testAccCheckAccountPolicyExists(ctx context.Context, n string, v *types.Acc } } -func testAccAccountPolicyImportStateIDFunc(n string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[n] - if !ok { - return "", fmt.Errorf("Not found: %s", n) - } - - policyName := rs.Primary.ID - policyType := rs.Primary.Attributes["policy_type"] - stateID := fmt.Sprintf("%s:%s", policyName, policyType) - - return stateID, nil - } -} - func testAccCheckAccountPolicyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) @@ -219,13 +204,28 @@ func testAccCheckAccountPolicyDestroy(ctx context.Context) resource.TestCheckFun return err } - return fmt.Errorf("CloudWatch Logs Resource Policy still exists: %s", rs.Primary.ID) + return fmt.Errorf("CloudWatch Logs Account Policy still exists: %s", rs.Primary.ID) } return nil } } +func testAccAccountPolicyImportStateIDFunc(n string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[n] + if !ok { + return "", fmt.Errorf("Not found: %s", n) + } + + policyName := rs.Primary.ID + policyType := rs.Primary.Attributes["policy_type"] + stateID := fmt.Sprintf("%s:%s", policyName, policyType) + + return stateID, nil + } +} + func testAccCheckAccountHasSubscriptionFilterPolicy(ctx context.Context, resourceName string, rName string) resource.TestCheckFunc { return func(s *terraform.State) error { expectedJSONTemplate := `{ diff --git a/internal/service/logs/destination_policy.go b/internal/service/logs/destination_policy.go index 84e65546bfc..0f5e6a19575 100644 --- a/internal/service/logs/destination_policy.go +++ b/internal/service/logs/destination_policy.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" @@ -57,12 +58,16 @@ func resourceDestinationPolicy() *schema.Resource { func resourceDestinationPolicyPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) + policy, err := structure.NormalizeJsonString(d.Get("access_policy").(string)) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + name := d.Get("destination_name").(string) input := &cloudwatchlogs.PutDestinationPolicyInput{ - AccessPolicy: aws.String(d.Get("access_policy").(string)), + AccessPolicy: aws.String(policy), DestinationName: aws.String(name), } @@ -70,7 +75,7 @@ func resourceDestinationPolicyPut(ctx context.Context, d *schema.ResourceData, m input.ForceUpdate = aws.Bool(v.(bool)) } - _, err := conn.PutDestinationPolicy(ctx, input) + _, err = conn.PutDestinationPolicy(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "putting CloudWatch Logs Destination Policy (%s): %s", name, err) @@ -85,10 +90,9 @@ func resourceDestinationPolicyPut(ctx context.Context, d *schema.ResourceData, m func resourceDestinationPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) - destination, err := findDestinationByName(ctx, conn, d.Id()) + destination, err := findDestinationPolicyByName(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] CloudWatch Logs Destination Policy (%s) not found, removing from state", d.Id()) @@ -100,8 +104,32 @@ func resourceDestinationPolicyRead(ctx context.Context, d *schema.ResourceData, return sdkdiag.AppendErrorf(diags, "reading CloudWatch Logs Destination Policy (%s): %s", d.Id(), err) } - d.Set("access_policy", destination.AccessPolicy) + policyToSet, err := verify.SecondJSONUnlessEquivalent(d.Get("access_policy").(string), aws.ToString(destination.AccessPolicy)) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + policyToSet, err = structure.NormalizeJsonString(policyToSet) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + d.Set("access_policy", policyToSet) d.Set("destination_name", destination.DestinationName) return diags } + +func findDestinationPolicyByName(ctx context.Context, conn *cloudwatchlogs.Client, name string) (*awstypes.Destination, error) { + output, err := findDestinationByName(ctx, conn, name) + + if err != nil { + return nil, err + } + + if output.AccessPolicy == nil { + return nil, tfresource.NewEmptyResultError(name) + } + + return output, err +} diff --git a/internal/service/logs/destination_policy_test.go b/internal/service/logs/destination_policy_test.go index f5949298b89..d8e39bfbeb8 100644 --- a/internal/service/logs/destination_policy_test.go +++ b/internal/service/logs/destination_policy_test.go @@ -63,7 +63,7 @@ func testAccCheckDestinationPolicyExists(ctx context.Context, n string, v *strin conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - output, err := tflogs.FindDestinationByName(ctx, conn, rs.Primary.ID) + output, err := tflogs.FindDestinationPolicyByName(ctx, conn, rs.Primary.ID) if err != nil { return err diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index d805f04aeca..d3e4bf05dbd 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -21,6 +21,7 @@ var ( FindAccountPolicyByTwoPartKey = findAccountPolicyByTwoPartKey FindDataProtectionPolicyByLogGroupName = findDataProtectionPolicyByLogGroupName FindDestinationByName = findDestinationByName + FindDestinationPolicyByName = findDestinationPolicyByName FindLogAnomalyDetectorByARN = findLogAnomalyDetectorByARN FindLogGroupByName = findLogGroupByName FindLogStreamByTwoPartKey = findLogStreamByTwoPartKey // nosemgrep:ci.logs-in-var-name From 3f0098da1f52b47ed4bfecaeb01f21da428256bb Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 11:20:13 -0500 Subject: [PATCH 32/42] Add and use 'sdkv2.JSONDocumentSchemaRequired' and 'sdkv2.IAMPolicyDocumentSchemaRequired'. --- internal/sdkv2/schema.go | 22 +++++++++++++++ internal/sdkv2/suppress.go | 28 +++++++++++++++++++ internal/service/logs/account_policy.go | 13 ++------- .../service/logs/data_protection_policy.go | 14 ++-------- internal/service/logs/destination_policy.go | 13 ++------- internal/service/logs/validate.go | 12 -------- 6 files changed, 56 insertions(+), 46 deletions(-) diff --git a/internal/sdkv2/schema.go b/internal/sdkv2/schema.go index 5f87af00148..c085efe764d 100644 --- a/internal/sdkv2/schema.go +++ b/internal/sdkv2/schema.go @@ -4,7 +4,10 @@ package sdkv2 import ( + "sync" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // Adapted from https://github.com/hashicorp/terraform-provider-google/google/datasource_helpers.go. Thanks! @@ -61,3 +64,22 @@ func DataSourceSchemaFromResourceSchema(rs map[string]*schema.Schema) map[string return ds } + +// JSONDocumentSchemaRequired returns the standard schema for a required JSON document. +var JSONDocumentSchemaRequired = sync.OnceValue(jsonDocumentSchemaRequiredFunc(SuppressEquivalentJSONDocuments)) + +// IAMPolicyDocumentSchemaRequired returns the standard schema for a required IAM policy JSON document. +var IAMPolicyDocumentSchemaRequired = sync.OnceValue(jsonDocumentSchemaRequiredFunc(SuppressEquivalentIAMPolicyDocuments)) + +func jsonDocumentSchemaRequiredFunc(diffSuppressFunc schema.SchemaDiffSuppressFunc) func() *schema.Schema { + return func() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsJSON, + DiffSuppressFunc: diffSuppressFunc, + DiffSuppressOnRefresh: true, + StateFunc: NormalizeJsonStringSchemaStateFunc, + } + } +} diff --git a/internal/sdkv2/suppress.go b/internal/sdkv2/suppress.go index 97b1a813a86..276159166ba 100644 --- a/internal/sdkv2/suppress.go +++ b/internal/sdkv2/suppress.go @@ -6,7 +6,9 @@ package sdkv2 import ( "strings" + awspolicy "github.com/hashicorp/awspolicyequivalence" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/json" ) // SuppressEquivalentStringCaseInsensitive provides custom difference suppression @@ -14,3 +16,29 @@ import ( func SuppressEquivalentStringCaseInsensitive(k, old, new string, d *schema.ResourceData) bool { return strings.EqualFold(old, new) } + +// SuppressEquivalentJSONDocuments provides custom difference suppression +// for JSON documents in the given strings that are equivalent. +func SuppressEquivalentJSONDocuments(k, old, new string, d *schema.ResourceData) bool { + return json.EqualStrings(old, new) +} + +// SuppressEquivalentIAMPolicyDocuments provides custom difference suppression +// for IAM policy documents in the given strings that are equivalent. +func SuppressEquivalentIAMPolicyDocuments(k, old, new string, d *schema.ResourceData) bool { + if equalEmptyJSONStrings(old, new) { + return true + } + + equivalent, err := awspolicy.PoliciesAreEquivalent(old, new) + if err != nil { + return false + } + + return equivalent +} + +func equalEmptyJSONStrings(old, new string) bool { + old, new = strings.TrimSpace(old), strings.TrimSpace(new) + return (old == "" || old == "{}") && (new == "" || new == "{}") +} diff --git a/internal/service/logs/account_policy.go b/internal/service/logs/account_policy.go index f67254fef45..1e7f9ff5fe6 100644 --- a/internal/service/logs/account_policy.go +++ b/internal/service/logs/account_policy.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" @@ -38,17 +39,7 @@ func resourceAccountPolicy() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "policy_document": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validAccountPolicyDocument, - DiffSuppressFunc: verify.SuppressEquivalentJSONDiffs, - DiffSuppressOnRefresh: true, - StateFunc: func(v interface{}) string { - json, _ := structure.NormalizeJsonString(v) - return json - }, - }, + "policy_document": sdkv2.JSONDocumentSchemaRequired(), "policy_name": { Type: schema.TypeString, Required: true, diff --git a/internal/service/logs/data_protection_policy.go b/internal/service/logs/data_protection_policy.go index 268bfea3e8c..2ed777a6549 100644 --- a/internal/service/logs/data_protection_policy.go +++ b/internal/service/logs/data_protection_policy.go @@ -14,10 +14,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" @@ -42,17 +42,7 @@ func resourceDataProtectionPolicy() *schema.Resource { ForceNew: true, ValidateFunc: validLogGroupName, }, - "policy_document": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsJSON, - DiffSuppressFunc: verify.SuppressEquivalentJSONDiffs, - DiffSuppressOnRefresh: true, - StateFunc: func(v interface{}) string { - json, _ := structure.NormalizeJsonString(v) - return json - }, - }, + "policy_document": sdkv2.JSONDocumentSchemaRequired(), }, } } diff --git a/internal/service/logs/destination_policy.go b/internal/service/logs/destination_policy.go index 0f5e6a19575..425bdf0bfd9 100644 --- a/internal/service/logs/destination_policy.go +++ b/internal/service/logs/destination_policy.go @@ -13,9 +13,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -33,16 +33,7 @@ func resourceDestinationPolicy() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "access_policy": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsJSON, - DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, - StateFunc: func(v interface{}) string { - json, _ := structure.NormalizeJsonString(v) - return json - }, - }, + "access_policy": sdkv2.IAMPolicyDocumentSchemaRequired(), "destination_name": { Type: schema.TypeString, Required: true, diff --git a/internal/service/logs/validate.go b/internal/service/logs/validate.go index ee1b018cc56..1cf142c0d3c 100644 --- a/internal/service/logs/validate.go +++ b/internal/service/logs/validate.go @@ -22,18 +22,6 @@ func validResourcePolicyDocument(v interface{}, k string) (ws []string, errors [ return } -func validAccountPolicyDocument(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - // https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutAccountPolicy.html - if len(value) > 30720 || (len(value) == 0) { - errors = append(errors, fmt.Errorf("CloudWatch log account policy document must be between 1 and 30,720 characters.")) - } - if _, err := structure.NormalizeJsonString(v); err != nil { - errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) - } - return -} - func validLogGroupName(v interface{}, k string) (ws []string, errors []error) { value := v.(string) From 79d74c262553167685e879700878f801a9d1b038 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 14:07:20 -0500 Subject: [PATCH 33/42] r/aws_cloudwatch_log_destination: Tidy up. --- internal/service/logs/destination.go | 41 +++++++++++++++++++--------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/internal/service/logs/destination.go b/internal/service/logs/destination.go index c27ff37162e..95a8825a361 100644 --- a/internal/service/logs/destination.go +++ b/internal/service/logs/destination.go @@ -11,13 +11,14 @@ import ( "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -76,7 +77,6 @@ const ( func resourceDestinationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) name := d.Get(names.AttrName).(string) @@ -86,7 +86,7 @@ func resourceDestinationCreate(ctx context.Context, d *schema.ResourceData, meta TargetArn: aws.String(d.Get(names.AttrTargetARN).(string)), } - outputRaw, err := tfresource.RetryWhenIsA[*types.InvalidParameterException](ctx, propagationTimeout, func() (interface{}, error) { + outputRaw, err := tfresource.RetryWhenIsA[*awstypes.InvalidParameterException](ctx, propagationTimeout, func() (interface{}, error) { return conn.PutDestination(ctx, input) }) @@ -108,7 +108,6 @@ func resourceDestinationCreate(ctx context.Context, d *schema.ResourceData, meta func resourceDestinationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) destination, err := findDestinationByName(ctx, conn, d.Id()) @@ -133,7 +132,6 @@ func resourceDestinationRead(ctx context.Context, d *schema.ResourceData, meta i func resourceDestinationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { @@ -143,7 +141,7 @@ func resourceDestinationUpdate(ctx context.Context, d *schema.ResourceData, meta TargetArn: aws.String(d.Get(names.AttrTargetARN).(string)), } - _, err := tfresource.RetryWhenIsA[*types.InvalidParameterException](ctx, propagationTimeout, func() (interface{}, error) { + _, err := tfresource.RetryWhenIsA[*awstypes.InvalidParameterException](ctx, propagationTimeout, func() (interface{}, error) { return conn.PutDestination(ctx, input) }) @@ -157,7 +155,6 @@ func resourceDestinationUpdate(ctx context.Context, d *schema.ResourceData, meta func resourceDestinationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) log.Printf("[INFO] Deleting CloudWatch Logs Destination: %s", d.Id()) @@ -165,7 +162,7 @@ func resourceDestinationDelete(ctx context.Context, d *schema.ResourceData, meta DestinationName: aws.String(d.Id()), }) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diags } @@ -176,11 +173,29 @@ func resourceDestinationDelete(ctx context.Context, d *schema.ResourceData, meta return diags } -func findDestinationByName(ctx context.Context, conn *cloudwatchlogs.Client, name string) (*types.Destination, error) { - input := &cloudwatchlogs.DescribeDestinationsInput{ +func findDestinationByName(ctx context.Context, conn *cloudwatchlogs.Client, name string) (*awstypes.Destination, error) { + input := cloudwatchlogs.DescribeDestinationsInput{ DestinationNamePrefix: aws.String(name), } + return findDestination(ctx, conn, &input, func(v *awstypes.Destination) bool { + return aws.ToString(v.DestinationName) == name + }) +} + +func findDestination(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeDestinationsInput, filter tfslices.Predicate[*awstypes.Destination]) (*awstypes.Destination, error) { + output, err := findDestinations(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findDestinations(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeDestinationsInput, filter tfslices.Predicate[*awstypes.Destination]) ([]awstypes.Destination, error) { + var output []awstypes.Destination + pages := cloudwatchlogs.NewDescribeDestinationsPaginator(conn, input) for pages.HasMorePages() { page, err := pages.NextPage(ctx) @@ -190,11 +205,11 @@ func findDestinationByName(ctx context.Context, conn *cloudwatchlogs.Client, nam } for _, v := range page.Destinations { - if aws.ToString(v.DestinationName) == name { - return &v, nil + if filter(&v) { + output = append(output, v) } } } - return nil, tfresource.NewEmptyResultError(input) + return output, nil } From ac435371d55f8226a9ad3dc84cc636e245639029 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 15:06:59 -0500 Subject: [PATCH 34/42] r/aws_cloudwatch_log_group: Tidy up. --- internal/service/logs/group.go | 63 ++++++++++++++-------- internal/service/logs/group_data_source.go | 1 - 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/internal/service/logs/group.go b/internal/service/logs/group.go index b4291493a70..ec12e0116cd 100644 --- a/internal/service/logs/group.go +++ b/internal/service/logs/group.go @@ -10,7 +10,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -55,7 +56,7 @@ func resourceGroup() *schema.Resource { Optional: true, Computed: true, ForceNew: true, - ValidateDiagFunc: enum.Validate[types.LogGroupClass](), + ValidateDiagFunc: enum.Validate[awstypes.LogGroupClass](), }, names.AttrName: { Type: schema.TypeString, @@ -94,12 +95,11 @@ func resourceGroup() *schema.Resource { func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) name := create.Name(d.Get(names.AttrName).(string), d.Get(names.AttrNamePrefix).(string)) input := &cloudwatchlogs.CreateLogGroupInput{ - LogGroupClass: types.LogGroupClass(d.Get("log_group_class").(string)), + LogGroupClass: awstypes.LogGroupClass(d.Get("log_group_class").(string)), LogGroupName: aws.String(name), Tags: getTagsIn(ctx), } @@ -136,7 +136,6 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter func resourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) lg, err := findLogGroupByName(ctx, conn, d.Id()) @@ -165,7 +164,6 @@ func resourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interfa func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) if d.HasChange("retention_in_days") { @@ -183,9 +181,11 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter return sdkdiag.AppendErrorf(diags, "setting CloudWatch Logs Log Group (%s) retention policy: %s", d.Id(), err) } } else { - _, err := conn.DeleteRetentionPolicy(ctx, &cloudwatchlogs.DeleteRetentionPolicyInput{ + input := &cloudwatchlogs.DeleteRetentionPolicyInput{ LogGroupName: aws.String(d.Id()), - }) + } + + _, err := conn.DeleteRetentionPolicy(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "deleting CloudWatch Logs Log Group (%s) retention policy: %s", d.Id(), err) @@ -195,18 +195,22 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter if d.HasChange(names.AttrKMSKeyID) { if v, ok := d.GetOk(names.AttrKMSKeyID); ok { - _, err := conn.AssociateKmsKey(ctx, &cloudwatchlogs.AssociateKmsKeyInput{ + input := &cloudwatchlogs.AssociateKmsKeyInput{ KmsKeyId: aws.String(v.(string)), LogGroupName: aws.String(d.Id()), - }) + } + + _, err := conn.AssociateKmsKey(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "associating CloudWatch Logs Log Group (%s) KMS key: %s", d.Id(), err) } } else { - _, err := conn.DisassociateKmsKey(ctx, &cloudwatchlogs.DisassociateKmsKeyInput{ + input := &cloudwatchlogs.DisassociateKmsKeyInput{ LogGroupName: aws.String(d.Id()), - }) + } + + _, err := conn.DisassociateKmsKey(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "disassociating CloudWatch Logs Log Group (%s) KMS key: %s", d.Id(), err) @@ -219,22 +223,21 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter func resourceGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).LogsClient(ctx) if v, ok := d.GetOk(names.AttrSkipDestroy); ok && v.(bool) { log.Printf("[DEBUG] Retaining CloudWatch Logs Log Group: %s", d.Id()) return diags } - conn := meta.(*conns.AWSClient).LogsClient(ctx) - log.Printf("[INFO] Deleting CloudWatch Logs Log Group: %s", d.Id()) - _, err := tfresource.RetryWhenIsAErrorMessageContains[*types.OperationAbortedException](ctx, 1*time.Minute, func() (interface{}, error) { + _, err := tfresource.RetryWhenIsAErrorMessageContains[*awstypes.OperationAbortedException](ctx, 1*time.Minute, func() (interface{}, error) { return conn.DeleteLogGroup(ctx, &cloudwatchlogs.DeleteLogGroupInput{ LogGroupName: aws.String(d.Id()), }) }, "try again") - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diags } @@ -245,11 +248,29 @@ func resourceGroupDelete(ctx context.Context, d *schema.ResourceData, meta inter return diags } -func findLogGroupByName(ctx context.Context, conn *cloudwatchlogs.Client, name string) (*types.LogGroup, error) { - input := &cloudwatchlogs.DescribeLogGroupsInput{ +func findLogGroupByName(ctx context.Context, conn *cloudwatchlogs.Client, name string) (*awstypes.LogGroup, error) { + input := cloudwatchlogs.DescribeLogGroupsInput{ LogGroupNamePrefix: aws.String(name), } + return findLogGroup(ctx, conn, &input, func(v *awstypes.LogGroup) bool { + return aws.ToString(v.LogGroupName) == name + }) +} + +func findLogGroup(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeLogGroupsInput, filter tfslices.Predicate[*awstypes.LogGroup]) (*awstypes.LogGroup, error) { + output, err := findLogGroups(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findLogGroups(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeLogGroupsInput, filter tfslices.Predicate[*awstypes.LogGroup]) ([]awstypes.LogGroup, error) { + var output []awstypes.LogGroup + pages := cloudwatchlogs.NewDescribeLogGroupsPaginator(conn, input) for pages.HasMorePages() { page, err := pages.NextPage(ctx) @@ -259,11 +280,11 @@ func findLogGroupByName(ctx context.Context, conn *cloudwatchlogs.Client, name s } for _, v := range page.LogGroups { - if aws.ToString(v.LogGroupName) == name { - return &v, nil + if filter(&v) { + output = append(output, v) } } } - return nil, tfresource.NewEmptyResultError(input) + return output, nil } diff --git a/internal/service/logs/group_data_source.go b/internal/service/logs/group_data_source.go index b992a55f964..651c3f210c4 100644 --- a/internal/service/logs/group_data_source.go +++ b/internal/service/logs/group_data_source.go @@ -53,7 +53,6 @@ func dataSourceGroup() *schema.Resource { func dataSourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) name := d.Get(names.AttrName).(string) From 200004578cfe4b8790a3f328c6fe953fa04826c3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 15:14:12 -0500 Subject: [PATCH 35/42] d/aws_cloudwatch_log_groups: Tidy up. --- internal/service/logs/groups_data_source.go | 31 +++++++------------- internal/service/logs/service_package_gen.go | 1 + 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/internal/service/logs/groups_data_source.go b/internal/service/logs/groups_data_source.go index b033291878c..77bdb8c06be 100644 --- a/internal/service/logs/groups_data_source.go +++ b/internal/service/logs/groups_data_source.go @@ -8,15 +8,16 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_cloudwatch_log_groups") +// @SDKDataSource("aws_cloudwatch_log_groups", name="Log Groups") func dataSourceGroups() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceGroupsRead, @@ -42,37 +43,25 @@ func dataSourceGroups() *schema.Resource { func dataSourceGroupsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) - input := &cloudwatchlogs.DescribeLogGroupsInput{} - + input := cloudwatchlogs.DescribeLogGroupsInput{} if v, ok := d.GetOk("log_group_name_prefix"); ok { input.LogGroupNamePrefix = aws.String(v.(string)) } - var output []types.LogGroup - - pages := cloudwatchlogs.NewDescribeLogGroupsPaginator(conn, input) - for pages.HasMorePages() { - page, err := pages.NextPage(ctx) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading CloudWatch Log Groups: %s", err) - } + output, err := findLogGroups(ctx, conn, &input, tfslices.PredicateTrue[*awstypes.LogGroup]()) - output = append(output, page.LogGroups...) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading CloudWatch Log Groups: %s", err) } d.SetId(meta.(*conns.AWSClient).Region(ctx)) - var arns, logGroupNames []string - - for _, r := range output { - arns = append(arns, trimLogGroupARNWildcardSuffix(aws.ToString(r.Arn))) - logGroupNames = append(logGroupNames, aws.ToString(r.LogGroupName)) + for _, v := range output { + arns = append(arns, trimLogGroupARNWildcardSuffix(aws.ToString(v.Arn))) + logGroupNames = append(logGroupNames, aws.ToString(v.LogGroupName)) } - d.Set(names.AttrARNs, arns) d.Set("log_group_names", logGroupNames) diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index 895dd558bed..5f4be155063 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -51,6 +51,7 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac { Factory: dataSourceGroups, TypeName: "aws_cloudwatch_log_groups", + Name: "Log Groups", }, } } From 64a77349e35893020533d657c06cceee81a44753 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 15:20:01 -0500 Subject: [PATCH 36/42] r/aws_cloudwatch_log_metric_filter: Tidy up. --- internal/service/logs/metric_filter.go | 58 +++++++++++++------- internal/service/logs/service_package_gen.go | 1 + 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/internal/service/logs/metric_filter.go b/internal/service/logs/metric_filter.go index bb97e056a73..400a85827a9 100644 --- a/internal/service/logs/metric_filter.go +++ b/internal/service/logs/metric_filter.go @@ -12,7 +12,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -23,11 +23,12 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/sdkv2/types/nullable" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_cloudwatch_log_metric_filter") +// @SDKResource("aws_cloudwatch_log_metric_filter", name="Metric Filter") func resourceMetricFilter() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceMetricFilterPut, @@ -75,8 +76,8 @@ func resourceMetricFilter() *schema.Resource { names.AttrUnit: { Type: schema.TypeString, Optional: true, - Default: types.StandardUnitNone, - ValidateDiagFunc: enum.Validate[types.StandardUnit](), + Default: awstypes.StandardUnitNone, + ValidateDiagFunc: enum.Validate[awstypes.StandardUnit](), }, names.AttrValue: { Type: schema.TypeString, @@ -110,7 +111,6 @@ func resourceMetricFilter() *schema.Resource { func resourceMetricFilterPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) name := d.Get(names.AttrName).(string) @@ -144,7 +144,6 @@ func resourceMetricFilterPut(ctx context.Context, d *schema.ResourceData, meta i func resourceMetricFilterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) mf, err := findMetricFilterByTwoPartKey(ctx, conn, d.Get(names.AttrLogGroupName).(string), d.Id()) @@ -171,7 +170,6 @@ func resourceMetricFilterRead(ctx context.Context, d *schema.ResourceData, meta func resourceMetricFilterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) // Creating multiple filters on the same log group can sometimes cause @@ -187,7 +185,7 @@ func resourceMetricFilterDelete(ctx context.Context, d *schema.ResourceData, met LogGroupName: aws.String(d.Get(names.AttrLogGroupName).(string)), }) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diags } @@ -211,17 +209,35 @@ func resourceMetricFilterImport(d *schema.ResourceData, meta interface{}) ([]*sc return []*schema.ResourceData{d}, nil } -func findMetricFilterByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName, name string) (*types.MetricFilter, error) { - input := &cloudwatchlogs.DescribeMetricFiltersInput{ +func findMetricFilterByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName, name string) (*awstypes.MetricFilter, error) { + input := cloudwatchlogs.DescribeMetricFiltersInput{ FilterNamePrefix: aws.String(name), LogGroupName: aws.String(logGroupName), } + return findMetricFilter(ctx, conn, &input, func(v *awstypes.MetricFilter) bool { + return aws.ToString(v.FilterName) == name + }) +} + +func findMetricFilter(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeMetricFiltersInput, filter tfslices.Predicate[*awstypes.MetricFilter]) (*awstypes.MetricFilter, error) { + output, err := findMetricFilters(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findMetricFilters(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeMetricFiltersInput, filter tfslices.Predicate[*awstypes.MetricFilter]) ([]awstypes.MetricFilter, error) { + var output []awstypes.MetricFilter + pages := cloudwatchlogs.NewDescribeMetricFiltersPaginator(conn, input) for pages.HasMorePages() { page, err := pages.NextPage(ctx) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, @@ -233,21 +249,21 @@ func findMetricFilterByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Clie } for _, v := range page.MetricFilters { - if aws.ToString(v.FilterName) == name { - return &v, nil + if filter(&v) { + output = append(output, v) } } } - return nil, tfresource.NewEmptyResultError(input) + return output, nil } -func expandMetricTransformation(tfMap map[string]interface{}) *types.MetricTransformation { +func expandMetricTransformation(tfMap map[string]interface{}) *awstypes.MetricTransformation { if tfMap == nil { return nil } - apiObject := &types.MetricTransformation{} + apiObject := &awstypes.MetricTransformation{} if v, ok := tfMap[names.AttrDefaultValue].(string); ok { if v, null, _ := nullable.Float(v).ValueFloat64(); !null { @@ -268,7 +284,7 @@ func expandMetricTransformation(tfMap map[string]interface{}) *types.MetricTrans } if v, ok := tfMap[names.AttrUnit].(string); ok && v != "" { - apiObject.Unit = types.StandardUnit(v) + apiObject.Unit = awstypes.StandardUnit(v) } if v, ok := tfMap[names.AttrValue].(string); ok && v != "" { @@ -278,12 +294,12 @@ func expandMetricTransformation(tfMap map[string]interface{}) *types.MetricTrans return apiObject } -func expandMetricTransformations(tfList []interface{}) []types.MetricTransformation { +func expandMetricTransformations(tfList []interface{}) []awstypes.MetricTransformation { if len(tfList) == 0 { return nil } - var apiObjects []types.MetricTransformation + var apiObjects []awstypes.MetricTransformation for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) @@ -304,7 +320,7 @@ func expandMetricTransformations(tfList []interface{}) []types.MetricTransformat return apiObjects } -func flattenMetricTransformation(apiObject types.MetricTransformation) map[string]interface{} { +func flattenMetricTransformation(apiObject awstypes.MetricTransformation) map[string]interface{} { tfMap := map[string]interface{}{ names.AttrUnit: apiObject.Unit, } @@ -332,7 +348,7 @@ func flattenMetricTransformation(apiObject types.MetricTransformation) map[strin return tfMap } -func flattenMetricTransformations(apiObjects []types.MetricTransformation) []interface{} { +func flattenMetricTransformations(apiObjects []awstypes.MetricTransformation) []interface{} { if len(apiObjects) == 0 { return nil } diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index 5f4be155063..8125ed95517 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -90,6 +90,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka { Factory: resourceMetricFilter, TypeName: "aws_cloudwatch_log_metric_filter", + Name: "Metric Filter", }, { Factory: resourceResourcePolicy, From 75704f99cf1e2dfc051e57a0c5177d01a0a7b720 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 15:27:33 -0500 Subject: [PATCH 37/42] r/aws_cloudwatch_query_definition: Tidy up. --- internal/service/logs/query_definition.go | 45 ++++++++++++-------- internal/service/logs/service_package_gen.go | 1 + 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/internal/service/logs/query_definition.go b/internal/service/logs/query_definition.go index d3fde0ec435..3967f5f8e68 100644 --- a/internal/service/logs/query_definition.go +++ b/internal/service/logs/query_definition.go @@ -12,7 +12,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -20,12 +20,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_cloudwatch_query_definition") +// @SDKResource("aws_cloudwatch_query_definition", name="Query Definition") func resourceQueryDefinition() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceQueryDefinitionPut, @@ -68,7 +69,6 @@ func resourceQueryDefinition() *schema.Resource { func resourceQueryDefinitionPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) name := d.Get(names.AttrName).(string) @@ -100,7 +100,6 @@ func resourceQueryDefinitionPut(ctx context.Context, d *schema.ResourceData, met func resourceQueryDefinitionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) result, err := findQueryDefinitionByTwoPartKey(ctx, conn, d.Get(names.AttrName).(string), d.Id()) @@ -125,7 +124,6 @@ func resourceQueryDefinitionRead(ctx context.Context, d *schema.ResourceData, me func resourceQueryDefinitionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) log.Printf("[INFO] Deleting CloudWatch Logs Query Definition: %s", d.Id()) @@ -133,7 +131,7 @@ func resourceQueryDefinitionDelete(ctx context.Context, d *schema.ResourceData, QueryDefinitionId: aws.String(d.Id()), }) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diags } @@ -165,12 +163,29 @@ func resourceQueryDefinitionImport(ctx context.Context, d *schema.ResourceData, return []*schema.ResourceData{d}, nil } -func findQueryDefinitionByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, name, queryDefinitionID string) (*types.QueryDefinition, error) { - input := &cloudwatchlogs.DescribeQueryDefinitionsInput{} +func findQueryDefinitionByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, name, queryDefinitionID string) (*awstypes.QueryDefinition, error) { + input := cloudwatchlogs.DescribeQueryDefinitionsInput{} if name != "" { input.QueryDefinitionNamePrefix = aws.String(name) } - var output *types.QueryDefinition + + return findQueryDefinition(ctx, conn, &input, func(v *awstypes.QueryDefinition) bool { + return aws.ToString(v.QueryDefinitionId) == queryDefinitionID + }) +} + +func findQueryDefinition(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeQueryDefinitionsInput, filter tfslices.Predicate[*awstypes.QueryDefinition]) (*awstypes.QueryDefinition, error) { + output, err := findQueryDefinitions(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findQueryDefinitions(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeQueryDefinitionsInput, filter tfslices.Predicate[*awstypes.QueryDefinition]) ([]awstypes.QueryDefinition, error) { + var output []awstypes.QueryDefinition err := describeQueryDefinitionsPages(ctx, conn, input, func(page *cloudwatchlogs.DescribeQueryDefinitionsOutput, lastPage bool) bool { if page == nil { @@ -178,10 +193,8 @@ func findQueryDefinitionByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.C } for _, v := range page.QueryDefinitions { - if aws.ToString(v.QueryDefinitionId) == queryDefinitionID { - output = &v - - return false + if filter(&v) { + output = append(output, v) } } @@ -192,9 +205,5 @@ func findQueryDefinitionByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.C return nil, err } - if output == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - return output, err + return output, nil } diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index 8125ed95517..e8910925e2d 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -107,6 +107,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka { Factory: resourceQueryDefinition, TypeName: "aws_cloudwatch_query_definition", + Name: "Query Definition", }, } } From 7fd411f3b03dad10a1359975a62bbf271b777644 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 15:38:54 -0500 Subject: [PATCH 38/42] r/aws_cloudwatch_log_resource_policy: Tidy up. --- .../service/logs/data_protection_policy.go | 2 +- internal/service/logs/destination_policy.go | 2 +- internal/service/logs/resource_policy.go | 80 ++++++++++--------- internal/service/logs/service_package_gen.go | 3 + internal/service/logs/validate.go | 13 --- 5 files changed, 49 insertions(+), 51 deletions(-) diff --git a/internal/service/logs/data_protection_policy.go b/internal/service/logs/data_protection_policy.go index 2ed777a6549..5b530e90b46 100644 --- a/internal/service/logs/data_protection_policy.go +++ b/internal/service/logs/data_protection_policy.go @@ -23,7 +23,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_cloudwatch_log_data_protection_policy") +// @SDKResource("aws_cloudwatch_log_data_protection_policy", name="Data Protection Policy") func resourceDataProtectionPolicy() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceDataProtectionPolicyPut, diff --git a/internal/service/logs/destination_policy.go b/internal/service/logs/destination_policy.go index 425bdf0bfd9..6adf0a6344d 100644 --- a/internal/service/logs/destination_policy.go +++ b/internal/service/logs/destination_policy.go @@ -20,7 +20,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/verify" ) -// @SDKResource("aws_cloudwatch_log_destination_policy") +// @SDKResource("aws_cloudwatch_log_destination_policy", name="Destination Policy") func resourceDestinationPolicy() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceDestinationPolicyPut, diff --git a/internal/service/logs/resource_policy.go b/internal/service/logs/resource_policy.go index 6d73c4f7ec6..6b66676b794 100644 --- a/internal/service/logs/resource_policy.go +++ b/internal/service/logs/resource_policy.go @@ -9,18 +9,20 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) -// @SDKResource("aws_cloudwatch_log_resource_policy") +// @SDKResource("aws_cloudwatch_log_resource_policy", name="Resource Policy") func resourceResourcePolicy() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceResourcePolicyPut, @@ -36,17 +38,7 @@ func resourceResourcePolicy() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "policy_document": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validResourcePolicyDocument, - DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, - DiffSuppressOnRefresh: true, - StateFunc: func(v interface{}) string { - json, _ := structure.NormalizeJsonString(v) - return json - }, - }, + "policy_document": sdkv2.IAMPolicyDocumentSchemaRequired(), "policy_name": { Type: schema.TypeString, Required: true, @@ -58,13 +50,11 @@ func resourceResourcePolicy() *schema.Resource { func resourceResourcePolicyPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) policy, err := structure.NormalizeJsonString(d.Get("policy_document").(string)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "policy (%s) is invalid JSON: %s", policy, err) + return sdkdiag.AppendFromErr(diags, err) } name := d.Get("policy_name").(string) @@ -73,20 +63,21 @@ func resourceResourcePolicyPut(ctx context.Context, d *schema.ResourceData, meta PolicyName: aws.String(name), } - output, err := conn.PutResourcePolicy(ctx, input) + _, err = conn.PutResourcePolicy(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating CloudWatch Logs Resource Policy (%s): %s", name, err) } - d.SetId(aws.ToString(output.ResourcePolicy.PolicyName)) + if d.IsNewResource() { + d.SetId(name) + } return append(diags, resourceResourcePolicyRead(ctx, d, meta)...) } func resourceResourcePolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) resourcePolicy, err := findResourcePolicyByName(ctx, conn, d.Id()) @@ -102,15 +93,13 @@ func resourceResourcePolicyRead(ctx context.Context, d *schema.ResourceData, met } policyToSet, err := verify.SecondJSONUnlessEquivalent(d.Get("policy_document").(string), aws.ToString(resourcePolicy.PolicyDocument)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "while setting policy (%s), encountered: %s", policyToSet, err) + return sdkdiag.AppendFromErr(diags, err) } policyToSet, err = structure.NormalizeJsonString(policyToSet) - if err != nil { - return sdkdiag.AppendErrorf(diags, "policy (%s) is invalid JSON: %s", policyToSet, err) + return sdkdiag.AppendFromErr(diags, err) } d.Set("policy_document", policyToSet) @@ -120,7 +109,6 @@ func resourceResourcePolicyRead(ctx context.Context, d *schema.ResourceData, met func resourceResourcePolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) log.Printf("[DEBUG] Deleting CloudWatch Logs Resource Policy: %s", d.Id()) @@ -128,7 +116,7 @@ func resourceResourcePolicyDelete(ctx context.Context, d *schema.ResourceData, m PolicyName: aws.String(d.Id()), }) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diags } @@ -139,9 +127,35 @@ func resourceResourcePolicyDelete(ctx context.Context, d *schema.ResourceData, m return diags } -func findResourcePolicyByName(ctx context.Context, conn *cloudwatchlogs.Client, name string) (*types.ResourcePolicy, error) { - input := &cloudwatchlogs.DescribeResourcePoliciesInput{} - var output *types.ResourcePolicy +func findResourcePolicyByName(ctx context.Context, conn *cloudwatchlogs.Client, name string) (*awstypes.ResourcePolicy, error) { + input := cloudwatchlogs.DescribeResourcePoliciesInput{} + output, err := findResourcePolicy(ctx, conn, &input, func(v *awstypes.ResourcePolicy) bool { + return aws.ToString(v.PolicyName) == name + }) + + if err != nil { + return nil, err + } + + if output.PolicyDocument == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, err +} + +func findResourcePolicy(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeResourcePoliciesInput, filter tfslices.Predicate[*awstypes.ResourcePolicy]) (*awstypes.ResourcePolicy, error) { + output, err := findResourcePolicies(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findResourcePolicies(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeResourcePoliciesInput, filter tfslices.Predicate[*awstypes.ResourcePolicy]) ([]awstypes.ResourcePolicy, error) { + var output []awstypes.ResourcePolicy err := describeResourcePoliciesPages(ctx, conn, input, func(page *cloudwatchlogs.DescribeResourcePoliciesOutput, lastPage bool) bool { if page == nil { @@ -149,10 +163,8 @@ func findResourcePolicyByName(ctx context.Context, conn *cloudwatchlogs.Client, } for _, v := range page.ResourcePolicies { - if aws.ToString(v.PolicyName) == name { - output = &v - - return false + if filter(&v) { + output = append(output, v) } } @@ -163,9 +175,5 @@ func findResourcePolicyByName(ctx context.Context, conn *cloudwatchlogs.Client, return nil, err } - if output == nil { - return nil, tfresource.NewEmptyResultError(input) - } - return output, nil } diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index e8910925e2d..f79b8413fe1 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -66,6 +66,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka { Factory: resourceDataProtectionPolicy, TypeName: "aws_cloudwatch_log_data_protection_policy", + Name: "Data Protection Policy", }, { Factory: resourceDestination, @@ -78,6 +79,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka { Factory: resourceDestinationPolicy, TypeName: "aws_cloudwatch_log_destination_policy", + Name: "Destination Policy", }, { Factory: resourceGroup, @@ -95,6 +97,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka { Factory: resourceResourcePolicy, TypeName: "aws_cloudwatch_log_resource_policy", + Name: "Resource Policy", }, { Factory: resourceStream, diff --git a/internal/service/logs/validate.go b/internal/service/logs/validate.go index 1cf142c0d3c..5ceb4a69868 100644 --- a/internal/service/logs/validate.go +++ b/internal/service/logs/validate.go @@ -7,21 +7,8 @@ import ( "fmt" "github.com/YakDriver/regexache" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" ) -func validResourcePolicyDocument(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutResourcePolicy.html - if len(value) > 5120 || (len(value) == 0) { - errors = append(errors, fmt.Errorf("CloudWatch log resource policy document must be between 1 and 5120 characters.")) - } - if _, err := structure.NormalizeJsonString(v); err != nil { - errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) - } - return -} - func validLogGroupName(v interface{}, k string) (ws []string, errors []error) { value := v.(string) From 9699b339956b4832a6fcb857c3d9898b6506b4b8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 15:46:31 -0500 Subject: [PATCH 39/42] r/aws_cloudwatch_log_stream: Tidy up. --- internal/service/logs/exports_test.go | 4 +- internal/service/logs/service_package_gen.go | 1 + internal/service/logs/stream.go | 57 ++++++++++---------- internal/service/logs/validate.go | 14 +++++ internal/service/logs/validate_test.go | 6 +-- 5 files changed, 49 insertions(+), 33 deletions(-) diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index d3e4bf05dbd..07763908608 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -22,11 +22,11 @@ var ( FindDataProtectionPolicyByLogGroupName = findDataProtectionPolicyByLogGroupName FindDestinationByName = findDestinationByName FindDestinationPolicyByName = findDestinationPolicyByName + FindIndexPolicyByLogGroupName = findIndexPolicyByLogGroupName FindLogAnomalyDetectorByARN = findLogAnomalyDetectorByARN FindLogGroupByName = findLogGroupByName FindLogStreamByTwoPartKey = findLogStreamByTwoPartKey // nosemgrep:ci.logs-in-var-name FindMetricFilterByTwoPartKey = findMetricFilterByTwoPartKey - FindIndexPolicyByLogGroupName = findIndexPolicyByLogGroupName FindQueryDefinitionByTwoPartKey = findQueryDefinitionByTwoPartKey FindResourcePolicyByName = findResourcePolicyByName FindSubscriptionFilterByTwoPartKey = findSubscriptionFilterByTwoPartKey @@ -36,5 +36,5 @@ var ( ValidLogGroupNamePrefix = validLogGroupNamePrefix ValidLogMetricFilterName = validLogMetricFilterName ValidLogMetricFilterTransformationName = validLogMetricFilterTransformationName - ValidStreamName = validStreamName + ValidLogStreamName = validLogStreamName ) diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index f79b8413fe1..7a0d8068964 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -102,6 +102,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka { Factory: resourceStream, TypeName: "aws_cloudwatch_log_stream", + Name: "Log Stream", }, { Factory: resourceSubscriptionFilter, diff --git a/internal/service/logs/stream.go b/internal/service/logs/stream.go index dfa90ffceea..ffad3ba3ff9 100644 --- a/internal/service/logs/stream.go +++ b/internal/service/logs/stream.go @@ -9,21 +9,21 @@ import ( "log" "strings" - "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_cloudwatch_log_stream") +// @SDKResource("aws_cloudwatch_log_stream", name="Log Stream") func resourceStream() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceStreamCreate, @@ -48,7 +48,7 @@ func resourceStream() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validStreamName, + ValidateFunc: validLogStreamName, }, }, } @@ -56,7 +56,6 @@ func resourceStream() *schema.Resource { func resourceStreamCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) name := d.Get(names.AttrName).(string) @@ -86,7 +85,6 @@ func resourceStreamCreate(ctx context.Context, d *schema.ResourceData, meta inte func resourceStreamRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) ls, err := findLogStreamByTwoPartKey(ctx, conn, d.Get(names.AttrLogGroupName).(string), d.Id()) @@ -109,7 +107,6 @@ func resourceStreamRead(ctx context.Context, d *schema.ResourceData, meta interf func resourceStreamDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) log.Printf("[INFO] Deleting CloudWatch Logs Log Stream: %s", d.Id()) @@ -118,7 +115,7 @@ func resourceStreamDelete(ctx context.Context, d *schema.ResourceData, meta inte LogStreamName: aws.String(d.Id()), }) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diags } @@ -144,17 +141,35 @@ func resourceStreamImport(d *schema.ResourceData, meta interface{}) ([]*schema.R return []*schema.ResourceData{d}, nil } -func findLogStreamByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName, name string) (*types.LogStream, error) { // nosemgrep:ci.logs-in-func-name - input := &cloudwatchlogs.DescribeLogStreamsInput{ +func findLogStreamByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName, name string) (*awstypes.LogStream, error) { // nosemgrep:ci.logs-in-func-name + input := cloudwatchlogs.DescribeLogStreamsInput{ LogGroupName: aws.String(logGroupName), LogStreamNamePrefix: aws.String(name), } + return findLogStream(ctx, conn, &input, func(v *awstypes.LogStream) bool { + return aws.ToString(v.LogStreamName) == name + }) +} + +func findLogStream(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeLogStreamsInput, filter tfslices.Predicate[*awstypes.LogStream]) (*awstypes.LogStream, error) { + output, err := findLogStreams(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findLogStreams(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeLogStreamsInput, filter tfslices.Predicate[*awstypes.LogStream]) ([]awstypes.LogStream, error) { + var output []awstypes.LogStream + pages := cloudwatchlogs.NewDescribeLogStreamsPaginator(conn, input) for pages.HasMorePages() { page, err := pages.NextPage(ctx) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, @@ -166,25 +181,11 @@ func findLogStreamByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, } for _, v := range page.LogStreams { - if aws.ToString(v.LogStreamName) == name { - return &v, nil + if filter(&v) { + output = append(output, v) } } } - return nil, tfresource.NewEmptyResultError(input) -} - -func validStreamName(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if regexache.MustCompile(`:`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "colons not allowed in %q:", k)) - } - if len(value) < 1 || len(value) > 512 { - errors = append(errors, fmt.Errorf( - "%q must be between 1 and 512 characters: %q", k, value)) - } - - return + return output, nil } diff --git a/internal/service/logs/validate.go b/internal/service/logs/validate.go index 5ceb4a69868..e2058588324 100644 --- a/internal/service/logs/validate.go +++ b/internal/service/logs/validate.go @@ -87,3 +87,17 @@ func validLogMetricFilterTransformationName(v interface{}, k string) (ws []strin return } + +func validLogStreamName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if regexache.MustCompile(`:`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "colons not allowed in %q:", k)) + } + if len(value) < 1 || len(value) > 512 { + errors = append(errors, fmt.Errorf( + "%q must be between 1 and 512 characters: %q", k, value)) + } + + return +} diff --git a/internal/service/logs/validate_test.go b/internal/service/logs/validate_test.go index 2195629818f..3c6dbc464d4 100644 --- a/internal/service/logs/validate_test.go +++ b/internal/service/logs/validate_test.go @@ -152,7 +152,7 @@ func TestValidLogMetricTransformationName(t *testing.T) { } } -func TestValidStreamName(t *testing.T) { +func TestValidLogStreamName(t *testing.T) { t.Parallel() validNames := []string{ @@ -162,7 +162,7 @@ func TestValidStreamName(t *testing.T) { "logstream/1234", } for _, v := range validNames { - _, errors := tflogs.ValidStreamName(v, names.AttrName) + _, errors := tflogs.ValidLogStreamName(v, names.AttrName) if len(errors) != 0 { t.Fatalf("%q should be a valid CloudWatch LogStream name: %q", v, errors) } @@ -174,7 +174,7 @@ func TestValidStreamName(t *testing.T) { "stringwith:colon", } for _, v := range invalidNames { - _, errors := tflogs.ValidStreamName(v, names.AttrName) + _, errors := tflogs.ValidLogStreamName(v, names.AttrName) if len(errors) == 0 { t.Fatalf("%q should be an invalid CloudWatch LogStream name", v) } From 2669b40b31d04b4e6112013b1dab83607656f78f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 16:11:03 -0500 Subject: [PATCH 40/42] r/aws_cloudwatch_log_subscription_filter: Tidy up. --- internal/service/logs/service_package_gen.go | 1 + internal/service/logs/subscription_filter.go | 67 ++++++++++++------- .../service/logs/subscription_filter_test.go | 18 ++--- 3 files changed, 55 insertions(+), 31 deletions(-) diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index 7a0d8068964..4834de875ec 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -107,6 +107,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka { Factory: resourceSubscriptionFilter, TypeName: "aws_cloudwatch_log_subscription_filter", + Name: "Subscription Filter", }, { Factory: resourceQueryDefinition, diff --git a/internal/service/logs/subscription_filter.go b/internal/service/logs/subscription_filter.go index 39ddddd045b..5c199e93e71 100644 --- a/internal/service/logs/subscription_filter.go +++ b/internal/service/logs/subscription_filter.go @@ -13,7 +13,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -23,12 +23,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_cloudwatch_log_subscription_filter") +// @SDKResource("aws_cloudwatch_log_subscription_filter", name="Subscription Filter") func resourceSubscriptionFilter() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceSubscriptionFilterPut, @@ -50,8 +51,8 @@ func resourceSubscriptionFilter() *schema.Resource { "distribution": { Type: schema.TypeString, Optional: true, - Default: types.DistributionByLogStream, - ValidateDiagFunc: enum.Validate[types.Distribution](), + Default: awstypes.DistributionByLogStream, + ValidateDiagFunc: enum.Validate[awstypes.Distribution](), }, "filter_pattern": { Type: schema.TypeString, @@ -81,7 +82,6 @@ func resourceSubscriptionFilter() *schema.Resource { func resourceSubscriptionFilterPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) logGroupName := d.Get(names.AttrLogGroupName).(string) @@ -94,27 +94,30 @@ func resourceSubscriptionFilterPut(ctx context.Context, d *schema.ResourceData, } if v, ok := d.GetOk("distribution"); ok { - input.Distribution = types.Distribution(v.(string)) + input.Distribution = awstypes.Distribution(v.(string)) } if v, ok := d.GetOk(names.AttrRoleARN); ok { input.RoleArn = aws.String(v.(string)) } - _, err := tfresource.RetryWhen(ctx, 5*time.Minute, + const ( + timeout = 5 * time.Minute + ) + _, err := tfresource.RetryWhen(ctx, timeout, func() (interface{}, error) { return conn.PutSubscriptionFilter(ctx, input) }, func(err error) (bool, error) { - if errs.IsAErrorMessageContains[*types.InvalidParameterException](err, "Could not deliver test message to specified") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "Could not deliver test message to specified") { return true, err } - if errs.IsAErrorMessageContains[*types.InvalidParameterException](err, "Could not execute the lambda function") { + if errs.IsAErrorMessageContains[*awstypes.InvalidParameterException](err, "Could not execute the lambda function") { return true, err } - if errs.IsAErrorMessageContains[*types.OperationAbortedException](err, "Please try again") { + if errs.IsAErrorMessageContains[*awstypes.OperationAbortedException](err, "Please try again") { return true, err } @@ -125,14 +128,15 @@ func resourceSubscriptionFilterPut(ctx context.Context, d *schema.ResourceData, return sdkdiag.AppendErrorf(diags, "putting CloudWatch Logs Subscription Filter (%s): %s", name, err) } - d.SetId(subscriptionFilterID(logGroupName)) + if d.IsNewResource() { + d.SetId(subscriptionFilterCreateResourceID(logGroupName)) + } return diags } func resourceSubscriptionFilterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) subscriptionFilter, err := findSubscriptionFilterByTwoPartKey(ctx, conn, d.Get(names.AttrLogGroupName).(string), d.Get(names.AttrName).(string)) @@ -159,7 +163,6 @@ func resourceSubscriptionFilterRead(ctx context.Context, d *schema.ResourceData, func resourceSubscriptionFilterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).LogsClient(ctx) log.Printf("[INFO] Deleting CloudWatch Logs Subscription Filter: %s", d.Id()) @@ -168,7 +171,7 @@ func resourceSubscriptionFilterDelete(ctx context.Context, d *schema.ResourceDat LogGroupName: aws.String(d.Get(names.AttrLogGroupName).(string)), }) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diags } @@ -190,30 +193,48 @@ func resourceSubscriptionFilterImport(d *schema.ResourceData, meta interface{}) d.Set(names.AttrLogGroupName, logGroupName) d.Set(names.AttrName, filterNamePrefix) - d.SetId(subscriptionFilterID(filterNamePrefix)) + d.SetId(subscriptionFilterCreateResourceID(filterNamePrefix)) return []*schema.ResourceData{d}, nil } -func subscriptionFilterID(log_group_name string) string { +func subscriptionFilterCreateResourceID(logGroupName string) string { var buf bytes.Buffer - buf.WriteString(fmt.Sprintf("%s-", log_group_name)) // only one filter allowed per log_group_name at the moment + buf.WriteString(fmt.Sprintf("%s-", logGroupName)) // only one filter allowed per log_group_name at the moment return fmt.Sprintf("cwlsf-%d", create.StringHashcode(buf.String())) } -func findSubscriptionFilterByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName, name string) (*types.SubscriptionFilter, error) { - input := &cloudwatchlogs.DescribeSubscriptionFiltersInput{ +func findSubscriptionFilterByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, logGroupName, name string) (*awstypes.SubscriptionFilter, error) { + input := cloudwatchlogs.DescribeSubscriptionFiltersInput{ FilterNamePrefix: aws.String(name), LogGroupName: aws.String(logGroupName), } + return findSubscriptionFilter(ctx, conn, &input, func(v *awstypes.SubscriptionFilter) bool { + return aws.ToString(v.FilterName) == name + }) +} + +func findSubscriptionFilter(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeSubscriptionFiltersInput, filter tfslices.Predicate[*awstypes.SubscriptionFilter]) (*awstypes.SubscriptionFilter, error) { + output, err := findSubscriptionFilters(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findSubscriptionFilters(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeSubscriptionFiltersInput, filter tfslices.Predicate[*awstypes.SubscriptionFilter]) ([]awstypes.SubscriptionFilter, error) { + var output []awstypes.SubscriptionFilter + pages := cloudwatchlogs.NewDescribeSubscriptionFiltersPaginator(conn, input) for pages.HasMorePages() { page, err := pages.NextPage(ctx) - if errs.IsA[*types.ResourceNotFoundException](err) { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, @@ -225,11 +246,11 @@ func findSubscriptionFilterByTwoPartKey(ctx context.Context, conn *cloudwatchlog } for _, v := range page.SubscriptionFilters { - if aws.ToString(v.FilterName) == name { - return &v, nil + if filter(&v) { + output = append(output, v) } } } - return nil, tfresource.NewEmptyResultError(input) + return output, nil } diff --git a/internal/service/logs/subscription_filter_test.go b/internal/service/logs/subscription_filter_test.go index dacbd4e6969..70b81d14c0f 100644 --- a/internal/service/logs/subscription_filter_test.go +++ b/internal/service/logs/subscription_filter_test.go @@ -45,10 +45,11 @@ func TestAccLogsSubscriptionFilter_basic(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccSubscriptionFilterImportStateIDFunc(resourceName), - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccSubscriptionFilterImportStateIDFunc(resourceName), + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{names.AttrRoleARN}, }, }, }) @@ -202,10 +203,11 @@ func TestAccLogsSubscriptionFilter_distribution(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccSubscriptionFilterImportStateIDFunc(resourceName), - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccSubscriptionFilterImportStateIDFunc(resourceName), + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{names.AttrRoleARN}, }, { Config: testAccSubscriptionFilterConfig_distribution(rName, "ByLogStream"), From e0d5b2d65d25ccb1c2cc0b95b1505645f7135f9c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 16:43:39 -0500 Subject: [PATCH 41/42] r/aws_cloudwatch_log_anomaly_detector: Tidy up. --- internal/service/logs/anomaly_detector.go | 250 ++++++++---------- .../service/logs/anomaly_detector_test.go | 53 ++-- internal/service/logs/exports_test.go | 2 +- internal/service/logs/index_policy.go | 6 +- internal/service/logs/service_package_gen.go | 10 +- 5 files changed, 141 insertions(+), 180 deletions(-) diff --git a/internal/service/logs/anomaly_detector.go b/internal/service/logs/anomaly_detector.go index 788672e044f..3ea03d937b3 100644 --- a/internal/service/logs/anomaly_detector.go +++ b/internal/service/logs/anomaly_detector.go @@ -5,7 +5,7 @@ package logs import ( "context" - "errors" + "fmt" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" @@ -20,11 +20,10 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" "github.com/hashicorp/terraform-provider-aws/internal/framework" - "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -37,33 +36,23 @@ import ( // @Testing(importStateIdAttribute="arn") // @Testing(importIgnore="enabled") // @Testing(existsType="github.com/aws/aws-sdk-go-v2/service/logs;cloudwatchlogs.GetLogAnomalyDetectorOutput") -func newResourceAnomalyDetector(_ context.Context) (resource.ResourceWithConfigure, error) { - r := &resourceAnomalyDetector{} +func newAnomalyDetectorResource(context.Context) (resource.ResourceWithConfigure, error) { + r := &anomalyDetectorResource{} return r, nil } -const ( - ResNameAnomalyDetector = "Anomaly Detector" -) - -type resourceAnomalyDetector struct { +type anomalyDetectorResource struct { framework.ResourceWithConfigure } -func (r *resourceAnomalyDetector) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "aws_cloudwatch_log_anomaly_detector" +func (*anomalyDetectorResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_cloudwatch_log_anomaly_detector" } -func (r *resourceAnomalyDetector) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ +func (r *anomalyDetectorResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - names.AttrARN: framework.ARNAttributeComputedOnly(), - "log_group_arn_list": schema.ListAttribute{ - CustomType: fwtypes.ListOfStringType, - ElementType: types.StringType, - Required: true, - }, "anomaly_visibility_time": schema.Int64Attribute{ Optional: true, Computed: true, @@ -74,9 +63,13 @@ func (r *resourceAnomalyDetector) Schema(ctx context.Context, req resource.Schem int64planmodifier.UseStateForUnknown(), }, }, + names.AttrARN: framework.ARNAttributeComputedOnly(), "detector_name": schema.StringAttribute{ Optional: true, }, + names.AttrEnabled: schema.BoolAttribute{ + Required: true, + }, "evaluation_frequency": schema.StringAttribute{ CustomType: fwtypes.StringEnumType[awstypes.EvaluationFrequency](), Optional: true, @@ -84,219 +77,206 @@ func (r *resourceAnomalyDetector) Schema(ctx context.Context, req resource.Schem "filter_pattern": schema.StringAttribute{ Optional: true, }, - names.AttrEnabled: schema.BoolAttribute{ - Required: true, - }, names.AttrKMSKeyID: schema.StringAttribute{ Optional: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), - stringplanmodifier.UseStateForUnknown(), }, }, + "log_group_arn_list": schema.ListAttribute{ + CustomType: fwtypes.ListOfARNType, + Required: true, + ElementType: types.StringType, + }, names.AttrTags: tftags.TagsAttribute(), names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), }, } } -func (r *resourceAnomalyDetector) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - conn := r.Meta().LogsClient(ctx) - - var plan resourceAnomalyDetectorData - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { +func (r *anomalyDetectorResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data anomalyDetectorResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - in := &cloudwatchlogs.CreateLogAnomalyDetectorInput{ - Tags: getTagsIn(ctx), - } - resp.Diagnostics.Append(flex.Expand(ctx, plan, in)...) + conn := r.Meta().LogsClient(ctx) - if resp.Diagnostics.HasError() { + input := &cloudwatchlogs.CreateLogAnomalyDetectorInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, data, input)...) + if response.Diagnostics.HasError() { return } - out, err := conn.CreateLogAnomalyDetector(ctx, in) + // Additional fields. + input.Tags = getTagsIn(ctx) + + outputCLAD, err := conn.CreateLogAnomalyDetector(ctx, input) + if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionCreating, ResNameAnomalyDetector, plan.ARN.String(), err), - err.Error(), - ) - return - } + response.Diagnostics.AddError("creating CloudWatch Logs Anomaly Detector", err.Error()) - if out == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionCreating, ResNameAnomalyDetector, plan.ARN.String(), nil), - errors.New("empty output").Error(), - ) return } - resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) - if resp.Diagnostics.HasError() { + // Set values for unknowns. + data.AnomalyDetectorARN = fwflex.StringToFramework(ctx, outputCLAD.AnomalyDetectorArn) + + outputGLAD, err := findLogAnomalyDetectorByARN(ctx, conn, data.AnomalyDetectorARN.ValueString()) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("reading CloudWatch Logs Anomaly Detector (%s)", data.AnomalyDetectorARN.ValueString()), err.Error()) + return } - plan.ARN = flex.StringToFramework(ctx, out.AnomalyDetectorArn) + data.AnomalyVisibilityTime = fwflex.Int64ToFramework(ctx, outputGLAD.AnomalyVisibilityTime) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *resourceAnomalyDetector) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - conn := r.Meta().LogsClient(ctx) - - var state resourceAnomalyDetectorData - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *anomalyDetectorResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data anomalyDetectorResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - out, err := findLogAnomalyDetectorByARN(ctx, conn, state.ARN.ValueString()) + conn := r.Meta().LogsClient(ctx) + + output, err := findLogAnomalyDetectorByARN(ctx, conn, data.AnomalyDetectorARN.ValueString()) + if tfresource.NotFound(err) { - resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) - resp.State.RemoveResource(ctx) + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) + return } + if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionSetting, ResNameAnomalyDetector, state.ARN.String(), err), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("reading CloudWatch Logs Anomaly Detector (%s)", data.AnomalyDetectorARN.ValueString()), err.Error()) + return } - resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...) - if resp.Diagnostics.HasError() { + response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...) + if response.Diagnostics.HasError() { return } - state.AnomalyVisibilityTime = flex.Int64ToFramework(ctx, out.AnomalyVisibilityTime) - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *resourceAnomalyDetector) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - conn := r.Meta().LogsClient(ctx) - - var plan, state resourceAnomalyDetectorData - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *anomalyDetectorResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var old, new anomalyDetectorResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &new)...) + if response.Diagnostics.HasError() { + return + } + response.Diagnostics.Append(request.State.Get(ctx, &old)...) + if response.Diagnostics.HasError() { return } - diff, d := flex.Calculate(ctx, plan, state) - resp.Diagnostics.Append(d...) - if resp.Diagnostics.HasError() { + conn := r.Meta().LogsClient(ctx) + + diff, d := fwflex.Calculate(ctx, new, old) + response.Diagnostics.Append(d...) + if response.Diagnostics.HasError() { return } if diff.HasChanges() { - in := &cloudwatchlogs.UpdateLogAnomalyDetectorInput{} + input := &cloudwatchlogs.UpdateLogAnomalyDetectorInput{} - resp.Diagnostics.Append(flex.Expand(ctx, plan, in)...) - if resp.Diagnostics.HasError() { + response.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) + if response.Diagnostics.HasError() { return } - in.AnomalyDetectorArn = plan.ARN.ValueStringPointer() - in.AnomalyVisibilityTime = plan.AnomalyVisibilityTime.ValueInt64Pointer() + _, err := conn.UpdateLogAnomalyDetector(ctx, input) - out, err := conn.UpdateLogAnomalyDetector(ctx, in) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionUpdating, ResNameAnomalyDetector, plan.ARN.String(), err), - err.Error(), - ) - return - } - if out == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionUpdating, ResNameAnomalyDetector, plan.ARN.String(), nil), - errors.New("empty output").Error(), - ) - return - } + response.Diagnostics.AddError(fmt.Sprintf("updating CloudWatch Logs Anomaly Detector (%s)", new.AnomalyDetectorARN.ValueString()), err.Error()) - resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) - if resp.Diagnostics.HasError() { return } } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + response.Diagnostics.Append(response.State.Set(ctx, &new)...) } -func (r *resourceAnomalyDetector) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - conn := r.Meta().LogsClient(ctx) - - var state resourceAnomalyDetectorData - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *anomalyDetectorResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var data anomalyDetectorResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - in := &cloudwatchlogs.DeleteLogAnomalyDetectorInput{ - AnomalyDetectorArn: state.ARN.ValueStringPointer(), + conn := r.Meta().LogsClient(ctx) + + _, err := conn.DeleteLogAnomalyDetector(ctx, &cloudwatchlogs.DeleteLogAnomalyDetectorInput{ + AnomalyDetectorArn: data.AnomalyDetectorARN.ValueStringPointer(), + }) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return } - _, err := conn.DeleteLogAnomalyDetector(ctx, in) if err != nil { - if errs.IsA[*awstypes.ResourceNotFoundException](err) { - return - } - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Logs, create.ErrActionDeleting, ResNameAnomalyDetector, state.ARN.String(), err), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("deleting CloudWatch Logs Anomaly Detector (%s)", data.AnomalyDetectorARN.ValueString()), err.Error()) + return } } -func (r *resourceAnomalyDetector) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root(names.AttrARN), req, resp) +func (r *anomalyDetectorResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root(names.AttrARN), request, response) } -func (r *resourceAnomalyDetector) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - r.SetTagsAll(ctx, req, resp) +func (r *anomalyDetectorResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) } func findLogAnomalyDetectorByARN(ctx context.Context, conn *cloudwatchlogs.Client, arn string) (*cloudwatchlogs.GetLogAnomalyDetectorOutput, error) { - in := &cloudwatchlogs.GetLogAnomalyDetectorInput{ + input := cloudwatchlogs.GetLogAnomalyDetectorInput{ AnomalyDetectorArn: aws.String(arn), } - out, err := conn.GetLogAnomalyDetector(ctx, in) - if err != nil { - if errs.IsA[*awstypes.ResourceNotFoundException](err) || errs.IsA[*awstypes.AccessDeniedException](err) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: in, - } + return findLogAnomalyDetector(ctx, conn, &input) +} + +func findLogAnomalyDetector(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.GetLogAnomalyDetectorInput) (*cloudwatchlogs.GetLogAnomalyDetectorOutput, error) { + output, err := conn.GetLogAnomalyDetector(ctx, input) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) || errs.IsA[*awstypes.AccessDeniedException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, } + } + if err != nil { return nil, err } - if out == nil { - return nil, tfresource.NewEmptyResultError(in) + if output == nil { + return nil, tfresource.NewEmptyResultError(input) } - return out, nil + return output, nil } -type resourceAnomalyDetectorData struct { - ARN types.String `tfsdk:"arn"` - LogGroupARNList fwtypes.ListValueOf[types.String] `tfsdk:"log_group_arn_list"` +type anomalyDetectorResourceModel struct { + AnomalyDetectorARN types.String `tfsdk:"arn"` AnomalyVisibilityTime types.Int64 `tfsdk:"anomaly_visibility_time"` DetectorName types.String `tfsdk:"detector_name"` Enabled types.Bool `tfsdk:"enabled"` EvaluationFrequency fwtypes.StringEnum[awstypes.EvaluationFrequency] `tfsdk:"evaluation_frequency"` FilterPattern types.String `tfsdk:"filter_pattern"` KMSKeyID types.String `tfsdk:"kms_key_id"` + LogGroupARNList fwtypes.ListOfARN `tfsdk:"log_group_arn_list"` Tags tftags.Map `tfsdk:"tags"` TagsAll tftags.Map `tfsdk:"tags_all"` } diff --git a/internal/service/logs/anomaly_detector_test.go b/internal/service/logs/anomaly_detector_test.go index 1ea32a48a18..8a694fce8d2 100644 --- a/internal/service/logs/anomaly_detector_test.go +++ b/internal/service/logs/anomaly_detector_test.go @@ -5,7 +5,6 @@ package logs_test import ( "context" - "errors" "fmt" "testing" @@ -15,7 +14,6 @@ import ( "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" tflogs "github.com/hashicorp/terraform-provider-aws/internal/service/logs" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -23,11 +21,7 @@ import ( func TestAccLogsAnomalyDetector_basic(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - - var loganomalydetector cloudwatchlogs.GetLogAnomalyDetectorOutput + var v cloudwatchlogs.GetLogAnomalyDetectorOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_cloudwatch_log_anomaly_detector.test" @@ -42,7 +36,7 @@ func TestAccLogsAnomalyDetector_basic(t *testing.T) { { Config: testAccLogAnomalyDetectorConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAnomalyDetectorExists(ctx, resourceName, &loganomalydetector), + testAccCheckAnomalyDetectorExists(ctx, resourceName, &v), resource.TestCheckResourceAttrSet(resourceName, "detector_name"), resource.TestCheckResourceAttr(resourceName, "evaluation_frequency", "TEN_MIN"), resource.TestCheckResourceAttr(resourceName, "anomaly_visibility_time", "7"), @@ -63,11 +57,7 @@ func TestAccLogsAnomalyDetector_basic(t *testing.T) { func TestAccLogsAnomalyDetector_update(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - - var loganomalydetector cloudwatchlogs.GetLogAnomalyDetectorOutput + var v cloudwatchlogs.GetLogAnomalyDetectorOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_cloudwatch_log_anomaly_detector.test" @@ -82,7 +72,7 @@ func TestAccLogsAnomalyDetector_update(t *testing.T) { { Config: testAccLogAnomalyDetectorConfig_update(rName, "TEN_MIN", acctest.CtFalse, 7), Check: resource.ComposeTestCheckFunc( - testAccCheckAnomalyDetectorExists(ctx, resourceName, &loganomalydetector), + testAccCheckAnomalyDetectorExists(ctx, resourceName, &v), resource.TestCheckResourceAttrSet(resourceName, "detector_name"), resource.TestCheckResourceAttr(resourceName, "evaluation_frequency", "TEN_MIN"), resource.TestCheckResourceAttr(resourceName, "anomaly_visibility_time", "7"), @@ -102,7 +92,7 @@ func TestAccLogsAnomalyDetector_update(t *testing.T) { { Config: testAccLogAnomalyDetectorConfig_update(rName, "FIVE_MIN", acctest.CtTrue, 8), Check: resource.ComposeTestCheckFunc( - testAccCheckAnomalyDetectorExists(ctx, resourceName, &loganomalydetector), + testAccCheckAnomalyDetectorExists(ctx, resourceName, &v), resource.TestCheckResourceAttrSet(resourceName, "detector_name"), resource.TestCheckResourceAttr(resourceName, "evaluation_frequency", "FIVE_MIN"), resource.TestCheckResourceAttr(resourceName, "anomaly_visibility_time", "8"), @@ -124,11 +114,7 @@ func TestAccLogsAnomalyDetector_update(t *testing.T) { func TestAccLogsAnomalyDetector_disappears(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - - var loganomalydetector cloudwatchlogs.GetLogAnomalyDetectorOutput + var v cloudwatchlogs.GetLogAnomalyDetectorOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_cloudwatch_log_anomaly_detector.test" @@ -143,7 +129,7 @@ func TestAccLogsAnomalyDetector_disappears(t *testing.T) { { Config: testAccLogAnomalyDetectorConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAnomalyDetectorExists(ctx, resourceName, &loganomalydetector), + testAccCheckAnomalyDetectorExists(ctx, resourceName, &v), acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tflogs.ResourceAnomalyDetector, resourceName), ), ExpectNonEmptyPlan: true, @@ -162,6 +148,7 @@ func testAccCheckAnomalyDetectorDestroy(ctx context.Context) resource.TestCheckF } _, err := tflogs.FindLogAnomalyDetectorByARN(ctx, conn, rs.Primary.Attributes[names.AttrARN]) + if tfresource.NotFound(err) { continue } @@ -170,43 +157,39 @@ func testAccCheckAnomalyDetectorDestroy(ctx context.Context) resource.TestCheckF return err } - return fmt.Errorf("CloudwatchLogs Anomaly Detector %s still exists", rs.Primary.Attributes[names.AttrARN]) + return fmt.Errorf("CloudWatch Logs Anomaly Detector still exists: %s", rs.Primary.Attributes[names.AttrARN]) } return nil } } -func testAccCheckAnomalyDetectorExists(ctx context.Context, name string, loganomalydetector *cloudwatchlogs.GetLogAnomalyDetectorOutput) resource.TestCheckFunc { +func testAccCheckAnomalyDetectorExists(ctx context.Context, n string, v *cloudwatchlogs.GetLogAnomalyDetectorOutput) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameAnomalyDetector, name, errors.New("not found")) - } - - if rs.Primary.ID == "" { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameAnomalyDetector, name, errors.New("not set")) + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).LogsClient(ctx) - resp, err := tflogs.FindLogAnomalyDetectorByARN(ctx, conn, rs.Primary.Attributes[names.AttrARN]) + output, err := tflogs.FindLogAnomalyDetectorByARN(ctx, conn, rs.Primary.Attributes[names.AttrARN]) if err != nil { - return create.Error(names.Logs, create.ErrActionCheckingExistence, tflogs.ResNameAnomalyDetector, rs.Primary.Attributes[names.AttrARN], err) + return err } - *loganomalydetector = *resp + *v = *output return nil } } -func testAccAnomalyDetectorImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { +func testAccAnomalyDetectorImportStateIDFunc(n string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return "", fmt.Errorf("Not found: %s", resourceName) + return "", fmt.Errorf("Not found: %s", n) } return rs.Primary.Attributes[names.AttrARN], nil diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index 07763908608..5c99baf0623 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -6,7 +6,7 @@ package logs // Exports for use in tests only. var ( ResourceAccountPolicy = resourceAccountPolicy - ResourceAnomalyDetector = newResourceAnomalyDetector + ResourceAnomalyDetector = newAnomalyDetectorResource ResourceDataProtectionPolicy = resourceDataProtectionPolicy ResourceDestination = resourceDestination ResourceDestinationPolicy = resourceDestinationPolicy diff --git a/internal/service/logs/index_policy.go b/internal/service/logs/index_policy.go index 01ad28a70cc..4f5e7c908ec 100644 --- a/internal/service/logs/index_policy.go +++ b/internal/service/logs/index_policy.go @@ -150,11 +150,9 @@ func (r *indexPolicyResource) Delete(ctx context.Context, request resource.Delet conn := r.Meta().LogsClient(ctx) - input := cloudwatchlogs.DeleteIndexPolicyInput{ + _, err := conn.DeleteIndexPolicy(ctx, &cloudwatchlogs.DeleteIndexPolicyInput{ LogGroupIdentifier: fwflex.StringFromFramework(ctx, data.LogGroupName), - } - - _, err := conn.DeleteIndexPolicy(ctx, &input) + }) if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diff --git a/internal/service/logs/service_package_gen.go b/internal/service/logs/service_package_gen.go index 4834de875ec..4d0e642de5b 100644 --- a/internal/service/logs/service_package_gen.go +++ b/internal/service/logs/service_package_gen.go @@ -21,16 +21,16 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { return []*types.ServicePackageFrameworkResource{ { - Factory: newIndexPolicyResource, - Name: "Index Policy", - }, - { - Factory: newResourceAnomalyDetector, + Factory: newAnomalyDetectorResource, Name: "Anomaly Detector", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, }, + { + Factory: newIndexPolicyResource, + Name: "Index Policy", + }, } } From dbed232ea6752f4fc946eb0ab99c22261e769bf6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Dec 2024 16:56:33 -0500 Subject: [PATCH 42/42] Fix semgrep 'ci.logs-in-var-name' and 'ci.logs-in-func-name'. --- internal/service/logs/exports_test.go | 2 +- internal/service/logs/stream.go | 4 ++-- internal/service/logs/validate.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/logs/exports_test.go b/internal/service/logs/exports_test.go index 5c99baf0623..a86bab8871d 100644 --- a/internal/service/logs/exports_test.go +++ b/internal/service/logs/exports_test.go @@ -36,5 +36,5 @@ var ( ValidLogGroupNamePrefix = validLogGroupNamePrefix ValidLogMetricFilterName = validLogMetricFilterName ValidLogMetricFilterTransformationName = validLogMetricFilterTransformationName - ValidLogStreamName = validLogStreamName + ValidLogStreamName = validLogStreamName // nosemgrep:ci.logs-in-var-name ) diff --git a/internal/service/logs/stream.go b/internal/service/logs/stream.go index ffad3ba3ff9..abdf0705e5e 100644 --- a/internal/service/logs/stream.go +++ b/internal/service/logs/stream.go @@ -152,7 +152,7 @@ func findLogStreamByTwoPartKey(ctx context.Context, conn *cloudwatchlogs.Client, }) } -func findLogStream(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeLogStreamsInput, filter tfslices.Predicate[*awstypes.LogStream]) (*awstypes.LogStream, error) { +func findLogStream(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeLogStreamsInput, filter tfslices.Predicate[*awstypes.LogStream]) (*awstypes.LogStream, error) { // nosemgrep:ci.logs-in-func-name output, err := findLogStreams(ctx, conn, input, filter) if err != nil { @@ -162,7 +162,7 @@ func findLogStream(ctx context.Context, conn *cloudwatchlogs.Client, input *clou return tfresource.AssertSingleValueResult(output) } -func findLogStreams(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeLogStreamsInput, filter tfslices.Predicate[*awstypes.LogStream]) ([]awstypes.LogStream, error) { +func findLogStreams(ctx context.Context, conn *cloudwatchlogs.Client, input *cloudwatchlogs.DescribeLogStreamsInput, filter tfslices.Predicate[*awstypes.LogStream]) ([]awstypes.LogStream, error) { // nosemgrep:ci.logs-in-func-name var output []awstypes.LogStream pages := cloudwatchlogs.NewDescribeLogStreamsPaginator(conn, input) diff --git a/internal/service/logs/validate.go b/internal/service/logs/validate.go index e2058588324..6dda12d1989 100644 --- a/internal/service/logs/validate.go +++ b/internal/service/logs/validate.go @@ -88,7 +88,7 @@ func validLogMetricFilterTransformationName(v interface{}, k string) (ws []strin return } -func validLogStreamName(v interface{}, k string) (ws []string, errors []error) { +func validLogStreamName(v interface{}, k string) (ws []string, errors []error) { // nosemgrep:ci.logs-in-func-name value := v.(string) if regexache.MustCompile(`:`).MatchString(value) { errors = append(errors, fmt.Errorf(