Skip to content

Commit

Permalink
Merge pull request #40594 from jcoelho93/f-logs-field-index
Browse files Browse the repository at this point in the history
feat: add cloudwatch log group index policy
  • Loading branch information
ewbankkit authored Dec 23, 2024
2 parents af1f67a + dbed232 commit a1c56f2
Show file tree
Hide file tree
Showing 32 changed files with 1,190 additions and 556 deletions.
3 changes: 3 additions & 0 deletions .changelog/40594.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_cloudwatch_log_index_policy
```
22 changes: 22 additions & 0 deletions internal/sdkv2/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down Expand Up @@ -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,
}
}
}
28 changes: 28 additions & 0 deletions internal/sdkv2/suppress.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,39 @@ 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
// for strings that are equal under case-insensitivity.
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 == "{}")
}
80 changes: 50 additions & 30 deletions internal/service/logs/account_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -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,
Expand All @@ -58,13 +49,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,
Expand All @@ -88,8 +79,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 {
Expand All @@ -99,7 +90,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))
Expand All @@ -111,7 +102,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())
Expand Down Expand Up @@ -149,10 +140,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
}

Expand All @@ -178,15 +169,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)
}

if errs.IsA[*types.ResourceNotFoundException](err) {
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
}

output = append(output, page.AccountPolicies...)

return !lastPage
})

if errs.IsA[*awstypes.ResourceNotFoundException](err) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
Expand All @@ -197,9 +221,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
}
32 changes: 16 additions & 16 deletions internal/service/logs/account_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,21 +185,6 @@ func testAccCheckAccountPolicyExists(ctx context.Context, n string, v *types.Acc
}
}

func testAccAccountPolicyImportStateIDFunc(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)
}

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)
Expand All @@ -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 := `{
Expand Down
Loading

0 comments on commit a1c56f2

Please sign in to comment.