diff --git a/.changelog/35569.txt b/.changelog/35569.txt new file mode 100644 index 00000000000..32e817bcd05 --- /dev/null +++ b/.changelog/35569.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_cloudwatch_metric_stream: Add plan-time validation of `output_format` +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 4ba0a5a178d..5d596a85592 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/cleanrooms v1.8.6 github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.15.7 github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.36.0 + github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.32.2 github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.31.0 github.com/aws/aws-sdk-go-v2/service/codeartifact v1.23.6 github.com/aws/aws-sdk-go-v2/service/codebuild v1.28.0 diff --git a/go.sum b/go.sum index 216811f34de..2761cfaf023 100644 --- a/go.sum +++ b/go.sum @@ -74,6 +74,8 @@ github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.15.7 h1:8sBfx7QkDZ6dgfUNXWH github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.15.7/go.mod h1:P1EMD13hrBE2KUw030w482Eyk2NmOFIvGqmgNi4XRDc= github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.36.0 h1:tRzTDe5E/dgGwJRR1cltjV9NPG9J5L7HK01+p2B4gCM= github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.36.0/go.mod h1:ZyywmYcQbdJcIh8YMwqkw18mkA6nuQ+Uj1ouT2rXTYQ= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.32.2 h1:vQfCIHSDouEvbE4EuDrlCGKcrtABEqF3cMt61nGEV4g= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.32.2/go.mod h1:3ToKMEhVj+Q+HzZ8Hqin6LdAKtsi3zVXVNUPpQMd+Xk= github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.31.0 h1:Rk+Ft0Mu/eiNt2iJ2oS8Gf1h5m6q5crwS8cmlTylnvM= github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.31.0/go.mod h1:jZNaJEtn9TLi3pfxycLz79HVkKxP8ZdYm92iaNFgBsA= github.com/aws/aws-sdk-go-v2/service/codeartifact v1.23.6 h1:QuI+Nh9lQ9EgCMhLzIDEp95cLnNd1vFHyODD0he0oQs= diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index 390c5c4bca2..59e8d118df0 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -21,6 +21,7 @@ import ( cleanrooms_sdkv2 "github.com/aws/aws-sdk-go-v2/service/cleanrooms" cloudcontrol_sdkv2 "github.com/aws/aws-sdk-go-v2/service/cloudcontrol" cloudtrail_sdkv2 "github.com/aws/aws-sdk-go-v2/service/cloudtrail" + cloudwatch_sdkv2 "github.com/aws/aws-sdk-go-v2/service/cloudwatch" cloudwatchlogs_sdkv2 "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" codeartifact_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codeartifact" codebuild_sdkv2 "github.com/aws/aws-sdk-go-v2/service/codebuild" @@ -140,7 +141,6 @@ import ( cloudfront_sdkv1 "github.com/aws/aws-sdk-go/service/cloudfront" cloudhsmv2_sdkv1 "github.com/aws/aws-sdk-go/service/cloudhsmv2" cloudsearch_sdkv1 "github.com/aws/aws-sdk-go/service/cloudsearch" - cloudwatch_sdkv1 "github.com/aws/aws-sdk-go/service/cloudwatch" cloudwatchrum_sdkv1 "github.com/aws/aws-sdk-go/service/cloudwatchrum" cognitoidentity_sdkv1 "github.com/aws/aws-sdk-go/service/cognitoidentity" cognitoidentityprovider_sdkv1 "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" @@ -401,8 +401,8 @@ func (c *AWSClient) CloudTrailClient(ctx context.Context) *cloudtrail_sdkv2.Clie return errs.Must(client[*cloudtrail_sdkv2.Client](ctx, c, names.CloudTrail, make(map[string]any))) } -func (c *AWSClient) CloudWatchConn(ctx context.Context) *cloudwatch_sdkv1.CloudWatch { - return errs.Must(conn[*cloudwatch_sdkv1.CloudWatch](ctx, c, names.CloudWatch, make(map[string]any))) +func (c *AWSClient) CloudWatchClient(ctx context.Context) *cloudwatch_sdkv2.Client { + return errs.Must(client[*cloudwatch_sdkv2.Client](ctx, c, names.CloudWatch, make(map[string]any))) } func (c *AWSClient) CodeArtifactClient(ctx context.Context) *codeartifact_sdkv2.Client { diff --git a/internal/service/cloudwatch/composite_alarm.go b/internal/service/cloudwatch/composite_alarm.go index 7c7059d7571..4c7096388bd 100644 --- a/internal/service/cloudwatch/composite_alarm.go +++ b/internal/service/cloudwatch/composite_alarm.go @@ -7,17 +7,17 @@ import ( "context" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch/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/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/flex" + 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" @@ -26,7 +26,7 @@ import ( // @SDKResource("aws_cloudwatch_composite_alarm", name="Composite Alarm") // @Tags(identifierAttribute="arn") -func ResourceCompositeAlarm() *schema.Resource { +func resourceCompositeAlarm() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceCompositeAlarmCreate, ReadWithoutTimeout: resourceCompositeAlarmRead, @@ -68,7 +68,6 @@ func ResourceCompositeAlarm() *schema.Resource { "alarm_actions": { Type: schema.TypeSet, Optional: true, - Set: schema.HashString, MaxItems: 5, Elem: &schema.Schema{ Type: schema.TypeString, @@ -98,7 +97,6 @@ func ResourceCompositeAlarm() *schema.Resource { "insufficient_data_actions": { Type: schema.TypeSet, Optional: true, - Set: schema.HashString, MaxItems: 5, Elem: &schema.Schema{ Type: schema.TypeString, @@ -108,7 +106,6 @@ func ResourceCompositeAlarm() *schema.Resource { "ok_actions": { Type: schema.TypeSet, Optional: true, - Set: schema.HashString, MaxItems: 5, Elem: &schema.Schema{ Type: schema.TypeString, @@ -125,19 +122,18 @@ func ResourceCompositeAlarm() *schema.Resource { func resourceCompositeAlarmCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) name := d.Get("alarm_name").(string) input := expandPutCompositeAlarmInput(ctx, d) - _, err := conn.PutCompositeAlarmWithContext(ctx, input) + _, err := conn.PutCompositeAlarm(ctx, input) // Some partitions (e.g. ISO) may not support tag-on-create. - if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(meta.(*conns.AWSClient).Partition, err) { input.Tags = nil - _, err = conn.PutCompositeAlarmWithContext(ctx, input) + _, err = conn.PutCompositeAlarm(ctx, input) } if err != nil { @@ -148,16 +144,16 @@ func resourceCompositeAlarmCreate(ctx context.Context, d *schema.ResourceData, m // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { - alarm, err := FindCompositeAlarmByName(ctx, conn, d.Id()) + alarm, err := findCompositeAlarmByName(ctx, conn, d.Id()) if err != nil { return sdkdiag.AppendErrorf(diags, "reading CloudWatch Composite Alarm (%s): %s", d.Id(), err) } - err = createTags(ctx, conn, aws.StringValue(alarm.AlarmArn), tags) + err = createTags(ctx, conn, aws.ToString(alarm.AlarmArn), tags) // If default tags only, continue. Otherwise, error. - if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(meta.(*conns.AWSClient).Partition, err) { return append(diags, resourceCompositeAlarmRead(ctx, d, meta)...) } @@ -171,10 +167,9 @@ func resourceCompositeAlarmCreate(ctx context.Context, d *schema.ResourceData, m func resourceCompositeAlarmRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) - - alarm, err := FindCompositeAlarmByName(ctx, conn, d.Id()) + alarm, err := findCompositeAlarmByName(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] CloudWatch Composite Alarm %s not found, removing from state", d.Id()) @@ -194,26 +189,25 @@ func resourceCompositeAlarmRead(ctx context.Context, d *schema.ResourceData, met } else { d.Set("actions_suppressor", nil) } - d.Set("alarm_actions", aws.StringValueSlice(alarm.AlarmActions)) + d.Set("alarm_actions", alarm.AlarmActions) d.Set("alarm_description", alarm.AlarmDescription) d.Set("alarm_name", alarm.AlarmName) d.Set("alarm_rule", alarm.AlarmRule) d.Set("arn", alarm.AlarmArn) - d.Set("insufficient_data_actions", aws.StringValueSlice(alarm.InsufficientDataActions)) - d.Set("ok_actions", aws.StringValueSlice(alarm.OKActions)) + d.Set("insufficient_data_actions", alarm.InsufficientDataActions) + d.Set("ok_actions", alarm.OKActions) return diags } func resourceCompositeAlarmUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) if d.HasChangesExcept("tags", "tags_all") { input := expandPutCompositeAlarmInput(ctx, d) - _, err := conn.PutCompositeAlarmWithContext(ctx, input) + _, err := conn.PutCompositeAlarm(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating CloudWatch Composite Alarm (%s): %s", d.Id(), err) @@ -225,15 +219,14 @@ func resourceCompositeAlarmUpdate(ctx context.Context, d *schema.ResourceData, m func resourceCompositeAlarmDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) log.Printf("[INFO] Deleting CloudWatch Composite Alarm: %s", d.Id()) - _, err := conn.DeleteAlarmsWithContext(ctx, &cloudwatch.DeleteAlarmsInput{ - AlarmNames: aws.StringSlice([]string{d.Id()}), + _, err := conn.DeleteAlarms(ctx, &cloudwatch.DeleteAlarmsInput{ + AlarmNames: tfslices.Of(d.Id()), }) - if tfawserr.ErrCodeEquals(err, cloudwatch.ErrCodeResourceNotFound) { + if errs.IsA[*types.ResourceNotFoundException](err) { return diags } @@ -244,20 +237,13 @@ func resourceCompositeAlarmDelete(ctx context.Context, d *schema.ResourceData, m return diags } -func FindCompositeAlarmByName(ctx context.Context, conn *cloudwatch.CloudWatch, name string) (*cloudwatch.CompositeAlarm, error) { +func findCompositeAlarmByName(ctx context.Context, conn *cloudwatch.Client, name string) (*types.CompositeAlarm, error) { input := &cloudwatch.DescribeAlarmsInput{ - AlarmNames: aws.StringSlice([]string{name}), - AlarmTypes: aws.StringSlice([]string{cloudwatch.AlarmTypeCompositeAlarm}), + AlarmNames: tfslices.Of(name), + AlarmTypes: tfslices.Of(types.AlarmTypeCompositeAlarm), } - output, err := conn.DescribeAlarmsWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, cloudwatch.ErrCodeResourceNotFound) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } + output, err := conn.DescribeAlarms(ctx, input) if err != nil { return nil, err @@ -267,7 +253,7 @@ func FindCompositeAlarmByName(ctx context.Context, conn *cloudwatch.CloudWatch, return nil, tfresource.NewEmptyResultError(input) } - return tfresource.AssertSinglePtrResult(output.CompositeAlarms) + return tfresource.AssertSingleValueResult(output.CompositeAlarms) } func expandPutCompositeAlarmInput(ctx context.Context, d *schema.ResourceData) *cloudwatch.PutCompositeAlarmInput { @@ -276,12 +262,12 @@ func expandPutCompositeAlarmInput(ctx context.Context, d *schema.ResourceData) * Tags: getTagsIn(ctx), } - if v, ok := d.GetOk("alarm_actions"); ok { - apiObject.AlarmActions = flex.ExpandStringSet(v.(*schema.Set)) + if v, ok := d.GetOk("alarm_actions"); ok && v.(*schema.Set).Len() > 0 { + apiObject.AlarmActions = flex.ExpandStringValueSet(v.(*schema.Set)) } - if v, ok := d.GetOk("actions_suppressor"); ok && v != nil && len(v.([]interface{})) > 0 { - alarm := expandActionsSuppressor(v.([]interface{})) + if v, ok := d.GetOk("actions_suppressor"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + alarm := expandActionsSuppressor(v.([]interface{})[0].(map[string]interface{})) apiObject.ActionsSuppressor = alarm.ActionsSuppressor apiObject.ActionsSuppressorExtensionPeriod = alarm.ActionsSuppressorExtensionPeriod apiObject.ActionsSuppressorWaitPeriod = alarm.ActionsSuppressorWaitPeriod @@ -299,43 +285,49 @@ func expandPutCompositeAlarmInput(ctx context.Context, d *schema.ResourceData) * apiObject.AlarmRule = aws.String(v.(string)) } - if v, ok := d.GetOk("insufficient_data_actions"); ok { - apiObject.InsufficientDataActions = flex.ExpandStringSet(v.(*schema.Set)) + if v, ok := d.GetOk("insufficient_data_actions"); ok && v.(*schema.Set).Len() > 0 { + apiObject.InsufficientDataActions = flex.ExpandStringValueSet(v.(*schema.Set)) } - if v, ok := d.GetOk("ok_actions"); ok { - apiObject.OKActions = flex.ExpandStringSet(v.(*schema.Set)) + if v, ok := d.GetOk("ok_actions"); ok && v.(*schema.Set).Len() > 0 { + apiObject.OKActions = flex.ExpandStringValueSet(v.(*schema.Set)) } return apiObject } -func flattenActionsSuppressor(alarm *cloudwatch.CompositeAlarm) map[string]interface{} { - actionsSuppressor := map[string]interface{}{ - "alarm": aws.StringValue(alarm.ActionsSuppressor), - "extension_period": aws.Int64Value(alarm.ActionsSuppressorExtensionPeriod), - "wait_period": aws.Int64Value(alarm.ActionsSuppressorWaitPeriod), +func flattenActionsSuppressor(apiObject *types.CompositeAlarm) map[string]interface{} { + if apiObject == nil || apiObject.ActionsSuppressor == nil { + return nil + } + + tfMap := map[string]interface{}{ + "alarm": aws.ToString(apiObject.ActionsSuppressor), + "extension_period": aws.ToInt32(apiObject.ActionsSuppressorExtensionPeriod), + "wait_period": aws.ToInt32(apiObject.ActionsSuppressorWaitPeriod), } - return actionsSuppressor + + return tfMap } -func expandActionsSuppressor(v []interface{}) *cloudwatch.CompositeAlarm { - if v[0] == nil { +func expandActionsSuppressor(tfMap map[string]interface{}) *types.CompositeAlarm { + if tfMap == nil { return nil } - alarmResource := v[0].(map[string]interface{}) - alarm := cloudwatch.CompositeAlarm{} + apiObject := &types.CompositeAlarm{} - if v, ok := alarmResource["alarm"]; ok && v.(string) != "" { - alarm.ActionsSuppressor = aws.String(v.(string)) + if v, ok := tfMap["alarm"]; ok && v.(string) != "" { + apiObject.ActionsSuppressor = aws.String(v.(string)) } - if v, ok := alarmResource["extension_period"]; ok { - alarm.ActionsSuppressorExtensionPeriod = aws.Int64(int64(v.(int))) + + if v, ok := tfMap["extension_period"]; ok { + apiObject.ActionsSuppressorExtensionPeriod = aws.Int32(int32(v.(int))) } - if v, ok := alarmResource["wait_period"]; ok { - alarm.ActionsSuppressorWaitPeriod = aws.Int64(int64(v.(int))) + + if v, ok := tfMap["wait_period"]; ok { + apiObject.ActionsSuppressorWaitPeriod = aws.Int32(int32(v.(int))) } - return &alarm + return apiObject } diff --git a/internal/service/cloudwatch/composite_alarm_test.go b/internal/service/cloudwatch/composite_alarm_test.go index 39638c29f1c..0adf652a0b8 100644 --- a/internal/service/cloudwatch/composite_alarm_test.go +++ b/internal/service/cloudwatch/composite_alarm_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/cloudwatch" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -17,6 +16,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" tfcloudwatch "github.com/hashicorp/terraform-provider-aws/internal/service/cloudwatch" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccCloudWatchCompositeAlarm_basic(t *testing.T) { @@ -26,7 +26,7 @@ func TestAccCloudWatchCompositeAlarm_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -62,7 +62,7 @@ func TestAccCloudWatchCompositeAlarm_disappears(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -85,7 +85,7 @@ func TestAccCloudWatchCompositeAlarm_tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -130,7 +130,7 @@ func TestAccCloudWatchCompositeAlarm_actionsEnabled(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -164,7 +164,7 @@ func TestAccCloudWatchCompositeAlarm_alarmActions(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -205,7 +205,7 @@ func TestAccCloudWatchCompositeAlarm_description(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -239,7 +239,7 @@ func TestAccCloudWatchCompositeAlarm_updateAlarmRule(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -272,7 +272,7 @@ func TestAccCloudWatchCompositeAlarm_insufficientDataActions(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -313,7 +313,7 @@ func TestAccCloudWatchCompositeAlarm_okActions(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -359,7 +359,7 @@ func TestAccCloudWatchCompositeAlarm_allActions(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -397,7 +397,7 @@ func TestAccCloudWatchCompositeAlarm_actionsSuppressor(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckCompositeAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -422,7 +422,7 @@ func TestAccCloudWatchCompositeAlarm_actionsSuppressor(t *testing.T) { func testAccCheckCompositeAlarmDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_cloudwatch_composite_alarm" { @@ -453,11 +453,7 @@ func testAccCheckCompositeAlarmExists(ctx context.Context, n string) resource.Te return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No CloudWatch Composite Alarm ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchClient(ctx) _, err := tfcloudwatch.FindCompositeAlarmByName(ctx, conn, rs.Primary.ID) diff --git a/internal/service/cloudwatch/consts.go b/internal/service/cloudwatch/consts.go index a2ffad3a878..fe66fb623ba 100644 --- a/internal/service/cloudwatch/consts.go +++ b/internal/service/cloudwatch/consts.go @@ -3,10 +3,6 @@ package cloudwatch -const ( - ResNameDashboard = "Dashboard" -) - const ( lowSampleCountPercentilesEvaluate = "evaluate" lowSampleCountPercentilesmissingDataIgnore = "ignore" diff --git a/internal/service/cloudwatch/dashboard.go b/internal/service/cloudwatch/dashboard.go index d408802fb77..f20745d6f1a 100644 --- a/internal/service/cloudwatch/dashboard.go +++ b/internal/service/cloudwatch/dashboard.go @@ -7,22 +7,23 @@ import ( "context" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "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/create" "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_dashboard") -func ResourceDashboard() *schema.Resource { +// @SDKResource("aws_cloudwatch_dashboard", name="Dashboard") +func resourceDashboard() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceDashboardPut, ReadWithoutTimeout: resourceDashboardRead, @@ -43,14 +44,15 @@ func ResourceDashboard() *schema.Resource { Computed: true, }, "dashboard_body": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsJSON, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsJSON, + DiffSuppressFunc: verify.SuppressEquivalentJSONDiffs, + DiffSuppressOnRefresh: true, StateFunc: func(v interface{}) string { json, _ := structure.NormalizeJsonString(v) return json }, - DiffSuppressFunc: verify.SuppressEquivalentJSONDiffs, }, "dashboard_name": { Type: schema.TypeString, @@ -62,75 +64,89 @@ func ResourceDashboard() *schema.Resource { } } -func resourceDashboardRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceDashboardPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - dashboardName := d.Get("dashboard_name").(string) - log.Printf("[DEBUG] Reading CloudWatch Dashboard: %s", dashboardName) - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) - params := cloudwatch.GetDashboardInput{ - DashboardName: aws.String(d.Id()), + name := d.Get("dashboard_name").(string) + input := &cloudwatch.PutDashboardInput{ + DashboardBody: aws.String(d.Get("dashboard_body").(string)), + DashboardName: aws.String(name), + } + + _, err := conn.PutDashboard(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "putting CloudWatch Dashboard (%s): %s", name, err) } - resp, err := conn.GetDashboardWithContext(ctx, ¶ms) - if !d.IsNewResource() && IsDashboardNotFoundErr(err) { - create.LogNotFoundRemoveState(names.CloudWatch, create.ErrActionReading, ResNameDashboard, d.Id()) + if d.IsNewResource() { + d.SetId(name) + } + + return append(diags, resourceDashboardRead(ctx, d, meta)...) +} + +func resourceDashboardRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) + + output, err := findDashboardByName(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] CloudWatch Dashboard (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return create.AppendDiagError(diags, names.CloudWatch, create.ErrActionReading, ResNameDashboard, d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading CloudWatch Dashboard (%s): %s", d.Id(), err) } - d.Set("dashboard_arn", resp.DashboardArn) - d.Set("dashboard_name", resp.DashboardName) - d.Set("dashboard_body", resp.DashboardBody) + d.Set("dashboard_arn", output.DashboardArn) + d.Set("dashboard_body", output.DashboardBody) + d.Set("dashboard_name", output.DashboardName) + return diags } -func resourceDashboardPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceDashboardDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) - params := cloudwatch.PutDashboardInput{ - DashboardBody: aws.String(d.Get("dashboard_body").(string)), - DashboardName: aws.String(d.Get("dashboard_name").(string)), - } + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) - log.Printf("[DEBUG] Putting CloudWatch Dashboard: %#v", params) + log.Printf("[DEBUG] Deleting CloudWatch Dashboard: %s", d.Id()) + _, err := conn.DeleteDashboards(ctx, &cloudwatch.DeleteDashboardsInput{ + DashboardNames: tfslices.Of(d.Id()), + }) - _, err := conn.PutDashboardWithContext(ctx, ¶ms) if err != nil { - return sdkdiag.AppendErrorf(diags, "Putting dashboard failed: %s", err) + return sdkdiag.AppendErrorf(diags, "deleting CloudWatch Dashboard (%s): %s", d.Id(), err) } - d.SetId(d.Get("dashboard_name").(string)) - log.Println("[INFO] CloudWatch Dashboard put finished") - return append(diags, resourceDashboardRead(ctx, d, meta)...) + return diags } -func resourceDashboardDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - log.Printf("[INFO] Deleting CloudWatch Dashboard %s", d.Id()) - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) - params := cloudwatch.DeleteDashboardsInput{ - DashboardNames: []*string{aws.String(d.Id())}, +func findDashboardByName(ctx context.Context, conn *cloudwatch.Client, name string) (*cloudwatch.GetDashboardOutput, error) { + input := &cloudwatch.GetDashboardInput{ + DashboardName: aws.String(name), } - if _, err := conn.DeleteDashboardsWithContext(ctx, ¶ms); err != nil { - if IsDashboardNotFoundErr(err) { - return diags + output, err := conn.GetDashboard(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFound) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, } - return sdkdiag.AppendErrorf(diags, "deleting CloudWatch Dashboard: %s", err) } - log.Printf("[INFO] CloudWatch Dashboard %s deleted", d.Id()) - return diags -} + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } -func IsDashboardNotFoundErr(err error) bool { - return tfawserr.ErrMessageContains( - err, - "ResourceNotFound", - "does not exist") + return output, nil } diff --git a/internal/service/cloudwatch/dashboard_test.go b/internal/service/cloudwatch/dashboard_test.go index f450a278362..a9ff18c2511 100644 --- a/internal/service/cloudwatch/dashboard_test.go +++ b/internal/service/cloudwatch/dashboard_test.go @@ -5,39 +5,37 @@ package cloudwatch_test import ( "context" - "encoding/json" "fmt" - "reflect" - "strings" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" 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" tfcloudwatch "github.com/hashicorp/terraform-provider-aws/internal/service/cloudwatch" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccCloudWatchDashboard_basic(t *testing.T) { ctx := acctest.Context(t) var dashboard cloudwatch.GetDashboardOutput resourceName := "aws_cloudwatch_dashboard.test" - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckDashboardDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccDashboardConfig_basic(rInt), + Config: testAccDashboardConfig_basic(rName, basicWidget), Check: resource.ComposeTestCheckFunc( testAccCheckDashboardExists(ctx, resourceName, &dashboard), - resource.TestCheckResourceAttr(resourceName, "dashboard_name", testAccDashboardName(rInt)), + resource.TestCheckResourceAttrSet(resourceName, "dashboard_arn"), ), }, { @@ -45,98 +43,57 @@ func TestAccCloudWatchDashboard_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - }, - }) -} - -func TestAccCloudWatchDashboard_update(t *testing.T) { - ctx := acctest.Context(t) - var dashboard cloudwatch.GetDashboardOutput - resourceName := "aws_cloudwatch_dashboard.test" - rInt := sdkacctest.RandInt() - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckDashboardDestroy(ctx), - Steps: []resource.TestStep{ { - Config: testAccDashboardConfig_basic(rInt), + Config: testAccDashboardConfig_basic(rName, updatedWidget), Check: resource.ComposeTestCheckFunc( testAccCheckDashboardExists(ctx, resourceName, &dashboard), - testAccCheckDashboardBodyIsExpected(ctx, resourceName, basicWidget), - resource.TestCheckResourceAttr(resourceName, "dashboard_name", testAccDashboardName(rInt)), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccDashboardConfig_updateBody(rInt), - Check: resource.ComposeTestCheckFunc( - testAccCheckDashboardExists(ctx, resourceName, &dashboard), - testAccCheckDashboardBodyIsExpected(ctx, resourceName, updatedWidget), - resource.TestCheckResourceAttr(resourceName, "dashboard_name", testAccDashboardName(rInt)), + resource.TestCheckResourceAttrSet(resourceName, "dashboard_arn"), ), }, }, }) } -func TestAccCloudWatchDashboard_updateName(t *testing.T) { +func TestAccCloudWatchDashboard_disappears(t *testing.T) { ctx := acctest.Context(t) var dashboard cloudwatch.GetDashboardOutput resourceName := "aws_cloudwatch_dashboard.test" - rInt := sdkacctest.RandInt() - rInt2 := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckDashboardDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccDashboardConfig_basic(rInt), - Check: resource.ComposeTestCheckFunc( - testAccCheckDashboardExists(ctx, resourceName, &dashboard), - testAccCheckDashboardBodyIsExpected(ctx, resourceName, basicWidget), - resource.TestCheckResourceAttr(resourceName, "dashboard_name", testAccDashboardName(rInt)), - ), - }, - { - Config: testAccDashboardConfig_basic(rInt2), + Config: testAccDashboardConfig_basic(rName, basicWidget), Check: resource.ComposeTestCheckFunc( testAccCheckDashboardExists(ctx, resourceName, &dashboard), - testAccCheckDashboardBodyIsExpected(ctx, resourceName, basicWidget), - resource.TestCheckResourceAttr(resourceName, "dashboard_name", testAccDashboardName(rInt2)), - testAccCheckDashboardDestroyPrevious(ctx, testAccDashboardName(rInt)), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfcloudwatch.ResourceDashboard(), resourceName), ), + ExpectNonEmptyPlan: true, }, }, }) } -func testAccCheckDashboardExists(ctx context.Context, n string, dashboard *cloudwatch.GetDashboardOutput) resource.TestCheckFunc { +func testAccCheckDashboardExists(ctx context.Context, n string, v *cloudwatch.GetDashboardOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } - conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchConn(ctx) - params := cloudwatch.GetDashboardInput{ - DashboardName: aws.String(rs.Primary.ID), - } + conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchClient(ctx) + + output, err := tfcloudwatch.FindDashboardByName(ctx, conn, rs.Primary.ID) - resp, err := conn.GetDashboardWithContext(ctx, ¶ms) if err != nil { return err } - *dashboard = *resp + *v = *output return nil } @@ -144,46 +101,24 @@ func testAccCheckDashboardExists(ctx context.Context, n string, dashboard *cloud func testAccCheckDashboardDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_cloudwatch_dashboard" { continue } - params := cloudwatch.GetDashboardInput{ - DashboardName: aws.String(rs.Primary.ID), - } + _, err := tfcloudwatch.FindDashboardByName(ctx, conn, rs.Primary.ID) - _, err := conn.GetDashboardWithContext(ctx, ¶ms) - if err == nil { - return fmt.Errorf("Dashboard still exists: %s", rs.Primary.ID) + if tfresource.NotFound(err) { + continue } - if !tfcloudwatch.IsDashboardNotFoundErr(err) { + + if err != nil { return err } - } - - return nil - } -} -func testAccCheckDashboardDestroyPrevious(ctx context.Context, dashboardName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchConn(ctx) - - params := cloudwatch.GetDashboardInput{ - DashboardName: aws.String(dashboardName), - } - - _, err := conn.GetDashboardWithContext(ctx, ¶ms) - - if err == nil { - return fmt.Errorf("Dashboard still exists: %s", dashboardName) - } - - if !tfcloudwatch.IsDashboardNotFoundErr(err) { - return err + return fmt.Errorf("CloudWatch Dashboard %s still exists", rs.Primary.ID) } return nil @@ -222,69 +157,14 @@ const ( }` ) -func testAccDashboardName(rInt int) string { - return fmt.Sprintf("terraform-test-dashboard-%d", rInt) -} - -func testAccDashboardConfig_basic(rInt int) string { +func testAccDashboardConfig_basic(rName, body string) string { return fmt.Sprintf(` resource "aws_cloudwatch_dashboard" "test" { - dashboard_name = "terraform-test-dashboard-%d" + dashboard_name = %[1]q dashboard_body = < 0 { - return fmt.Errorf("No metric_query may have both `expression` and a `metric` specified") + if v := diff.Get("metric_query"); v != nil { + for _, v := range v.(*schema.Set).List() { + tfMap := v.(map[string]interface{}) + if v, ok := tfMap["expression"]; ok && v.(string) != "" { + if v := tfMap["metric"]; v != nil { + if len(v.([]interface{})) > 0 { + return errors.New("No metric_query may have both `expression` and a `metric` specified") + } + } + } } } - } - } - } - return nil + return nil + }, + ), + } } func resourceMetricAlarmCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) - - err := validMetricAlarm(d) - if err != nil { - return sdkdiag.AppendErrorf(diags, "creating CloudWatch Metric Alarm (%s): %s", d.Get("alarm_name").(string), err) - } + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) name := d.Get("alarm_name").(string) input := expandPutMetricAlarmInput(ctx, d) - _, err = conn.PutMetricAlarmWithContext(ctx, input) + _, err := conn.PutMetricAlarm(ctx, input) // Some partitions (e.g. ISO) may not support tag-on-create. - if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(meta.(*conns.AWSClient).Partition, err) { input.Tags = nil - _, err = conn.PutMetricAlarmWithContext(ctx, input) + _, err = conn.PutMetricAlarm(ctx, input) } if err != nil { @@ -343,16 +343,16 @@ func resourceMetricAlarmCreate(ctx context.Context, d *schema.ResourceData, meta // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { - alarm, err := FindMetricAlarmByName(ctx, conn, d.Id()) + alarm, err := findMetricAlarmByName(ctx, conn, d.Id()) if err != nil { return sdkdiag.AppendErrorf(diags, "reading CloudWatch Metric Alarm (%s): %s", d.Id(), err) } - err = createTags(ctx, conn, aws.StringValue(alarm.AlarmArn), tags) + err = createTags(ctx, conn, aws.ToString(alarm.AlarmArn), tags) // If default tags only, continue. Otherwise, error. - if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(meta.(*conns.AWSClient).Partition, err) { return append(diags, resourceMetricAlarmRead(ctx, d, meta)...) } @@ -366,9 +366,9 @@ func resourceMetricAlarmCreate(ctx context.Context, d *schema.ResourceData, meta func resourceMetricAlarmRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) - alarm, err := FindMetricAlarmByName(ctx, conn, d.Id()) + alarm, err := findMetricAlarmByName(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] CloudWatch Metric Alarm %s not found, removing from state", d.Id()) @@ -381,7 +381,7 @@ func resourceMetricAlarmRead(ctx context.Context, d *schema.ResourceData, meta i } d.Set("actions_enabled", alarm.ActionsEnabled) - d.Set("alarm_actions", aws.StringValueSlice(alarm.AlarmActions)) + d.Set("alarm_actions", alarm.AlarmActions) d.Set("alarm_description", alarm.AlarmDescription) d.Set("alarm_name", alarm.AlarmName) d.Set("arn", alarm.AlarmArn) @@ -393,7 +393,7 @@ func resourceMetricAlarmRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("evaluate_low_sample_count_percentiles", alarm.EvaluateLowSampleCountPercentile) d.Set("evaluation_periods", alarm.EvaluationPeriods) d.Set("extended_statistic", alarm.ExtendedStatistic) - d.Set("insufficient_data_actions", aws.StringValueSlice(alarm.InsufficientDataActions)) + d.Set("insufficient_data_actions", alarm.InsufficientDataActions) d.Set("metric_name", alarm.MetricName) if len(alarm.Metrics) > 0 { if err := d.Set("metric_query", flattenMetricAlarmMetrics(alarm.Metrics)); err != nil { @@ -401,7 +401,7 @@ func resourceMetricAlarmRead(ctx context.Context, d *schema.ResourceData, meta i } } d.Set("namespace", alarm.Namespace) - d.Set("ok_actions", aws.StringValueSlice(alarm.OKActions)) + d.Set("ok_actions", alarm.OKActions) d.Set("period", alarm.Period) d.Set("statistic", alarm.Statistic) d.Set("threshold", alarm.Threshold) @@ -418,12 +418,12 @@ func resourceMetricAlarmRead(ctx context.Context, d *schema.ResourceData, meta i func resourceMetricAlarmUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) if d.HasChangesExcept("tags", "tags_all") { input := expandPutMetricAlarmInput(ctx, d) - _, err := conn.PutMetricAlarmWithContext(ctx, input) + _, err := conn.PutMetricAlarm(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating CloudWatch Metric Alarm (%s): %s", d.Id(), err) @@ -435,14 +435,14 @@ func resourceMetricAlarmUpdate(ctx context.Context, d *schema.ResourceData, meta func resourceMetricAlarmDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) log.Printf("[INFO] Deleting CloudWatch Metric Alarm: %s", d.Id()) - _, err := conn.DeleteAlarmsWithContext(ctx, &cloudwatch.DeleteAlarmsInput{ - AlarmNames: aws.StringSlice([]string{d.Id()}), + _, err := conn.DeleteAlarms(ctx, &cloudwatch.DeleteAlarmsInput{ + AlarmNames: tfslices.Of(d.Id()), }) - if tfawserr.ErrCodeEquals(err, cloudwatch.ErrCodeResourceNotFoundException) { + if errs.IsA[*types.ResourceNotFoundException](err) { return diags } @@ -453,41 +453,30 @@ func resourceMetricAlarmDelete(ctx context.Context, d *schema.ResourceData, meta return diags } -func FindMetricAlarmByName(ctx context.Context, conn *cloudwatch.CloudWatch, name string) (*cloudwatch.MetricAlarm, error) { +func findMetricAlarmByName(ctx context.Context, conn *cloudwatch.Client, name string) (*types.MetricAlarm, error) { input := &cloudwatch.DescribeAlarmsInput{ - AlarmNames: aws.StringSlice([]string{name}), - AlarmTypes: aws.StringSlice([]string{cloudwatch.AlarmTypeMetricAlarm}), + AlarmNames: tfslices.Of(name), + AlarmTypes: tfslices.Of(types.AlarmTypeMetricAlarm), } - output, err := conn.DescribeAlarmsWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, cloudwatch.ErrCodeResourceNotFound) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } + output, err := conn.DescribeAlarms(ctx, input) if err != nil { return nil, err } - if output == nil || len(output.MetricAlarms) == 0 || output.MetricAlarms[0] == nil { + if output == nil { return nil, tfresource.NewEmptyResultError(input) } - if count := len(output.MetricAlarms); count > 1 { - return nil, tfresource.NewTooManyResultsError(count, input) - } - - return output.MetricAlarms[0], nil + return tfresource.AssertSingleValueResult(output.MetricAlarms) } func expandPutMetricAlarmInput(ctx context.Context, d *schema.ResourceData) *cloudwatch.PutMetricAlarmInput { apiObject := &cloudwatch.PutMetricAlarmInput{ AlarmName: aws.String(d.Get("alarm_name").(string)), - ComparisonOperator: aws.String(d.Get("comparison_operator").(string)), - EvaluationPeriods: aws.Int64(int64(d.Get("evaluation_periods").(int))), + ComparisonOperator: types.ComparisonOperator(d.Get("comparison_operator").(string)), + EvaluationPeriods: aws.Int32(int32(d.Get("evaluation_periods").(int))), Tags: getTagsIn(ctx), TreatMissingData: aws.String(d.Get("treat_missing_data").(string)), } @@ -496,8 +485,8 @@ func expandPutMetricAlarmInput(ctx context.Context, d *schema.ResourceData) *clo apiObject.ActionsEnabled = aws.Bool(v.(bool)) } - if v, ok := d.GetOk("alarm_actions"); ok { - apiObject.AlarmActions = flex.ExpandStringSet(v.(*schema.Set)) + if v, ok := d.GetOk("alarm_actions"); ok && v.(*schema.Set).Len() > 0 { + apiObject.AlarmActions = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("alarm_description"); ok { @@ -505,10 +494,10 @@ func expandPutMetricAlarmInput(ctx context.Context, d *schema.ResourceData) *clo } if v, ok := d.GetOk("datapoints_to_alarm"); ok { - apiObject.DatapointsToAlarm = aws.Int64(int64(v.(int))) + apiObject.DatapointsToAlarm = aws.Int32(int32(v.(int))) } - if v, ok := d.GetOk("dimensions"); ok { + if v, ok := d.GetOk("dimensions"); ok && len(v.(map[string]interface{})) > 0 { apiObject.Dimensions = expandMetricAlarmDimensions(v.(map[string]interface{})) } @@ -520,32 +509,32 @@ func expandPutMetricAlarmInput(ctx context.Context, d *schema.ResourceData) *clo apiObject.ExtendedStatistic = aws.String(v.(string)) } - if v, ok := d.GetOk("insufficient_data_actions"); ok { - apiObject.InsufficientDataActions = flex.ExpandStringSet(v.(*schema.Set)) + if v, ok := d.GetOk("insufficient_data_actions"); ok && v.(*schema.Set).Len() > 0 { + apiObject.InsufficientDataActions = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("metric_name"); ok { apiObject.MetricName = aws.String(v.(string)) } - if v := d.Get("metric_query"); v != nil { - apiObject.Metrics = expandMetricAlarmMetrics(v.(*schema.Set)) + if v, ok := d.GetOk("metric_query"); ok && v.(*schema.Set).Len() > 0 { + apiObject.Metrics = expandMetricAlarmMetrics(v.(*schema.Set).List()) } if v, ok := d.GetOk("namespace"); ok { apiObject.Namespace = aws.String(v.(string)) } - if v, ok := d.GetOk("ok_actions"); ok { - apiObject.OKActions = flex.ExpandStringSet(v.(*schema.Set)) + if v, ok := d.GetOk("ok_actions"); ok && v.(*schema.Set).Len() > 0 { + apiObject.OKActions = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("period"); ok { - apiObject.Period = aws.Int64(int64(v.(int))) + apiObject.Period = aws.Int32(int32(v.(int))) } if v, ok := d.GetOk("statistic"); ok { - apiObject.Statistic = aws.String(v.(string)) + apiObject.Statistic = types.Statistic(v.(string)) } if v, ok := d.GetOk("threshold_metric_id"); ok { @@ -555,124 +544,168 @@ func expandPutMetricAlarmInput(ctx context.Context, d *schema.ResourceData) *clo } if v, ok := d.GetOk("unit"); ok { - apiObject.Unit = aws.String(v.(string)) + apiObject.Unit = types.StandardUnit(v.(string)) } return apiObject } -func flattenMetricAlarmDimensions(dims []*cloudwatch.Dimension) map[string]interface{} { - flatDims := make(map[string]interface{}) - for _, d := range dims { - flatDims[aws.StringValue(d.Name)] = aws.StringValue(d.Value) +func flattenMetricAlarmDimensions(apiObjects []types.Dimension) map[string]interface{} { + tfMap := map[string]interface{}{} + + for _, apiObject := range apiObjects { + tfMap[aws.ToString(apiObject.Name)] = aws.ToString(apiObject.Value) } - return flatDims + + return tfMap } -func flattenMetricAlarmMetrics(metrics []*cloudwatch.MetricDataQuery) []map[string]interface{} { - metricQueries := make([]map[string]interface{}, 0) - for _, mq := range metrics { - metricQuery := map[string]interface{}{ - "account_id": aws.StringValue(mq.AccountId), - "expression": aws.StringValue(mq.Expression), - "id": aws.StringValue(mq.Id), - "label": aws.StringValue(mq.Label), - "return_data": aws.BoolValue(mq.ReturnData), +func flattenMetricAlarmMetrics(apiObjects []types.MetricDataQuery) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + tfMap := map[string]interface{}{ + "account_id": aws.ToString(apiObject.AccountId), + "expression": aws.ToString(apiObject.Expression), + "id": aws.ToString(apiObject.Id), + "label": aws.ToString(apiObject.Label), + "return_data": aws.ToBool(apiObject.ReturnData), } - if mq.MetricStat != nil { - metric := flattenMetricAlarmMetricsMetricStat(mq.MetricStat) - metricQuery["metric"] = []interface{}{metric} + + if v := apiObject.MetricStat; v != nil { + tfMap["metric"] = []interface{}{flattenMetricAlarmMetricsMetricStat(v)} } - if mq.Period != nil { - metricQuery["period"] = aws.Int64Value(mq.Period) + + if apiObject.Period != nil { + tfMap["period"] = aws.ToInt32(apiObject.Period) } - metricQueries = append(metricQueries, metricQuery) + + tfList = append(tfList, tfMap) } - return metricQueries + return tfList } -func flattenMetricAlarmMetricsMetricStat(ms *cloudwatch.MetricStat) map[string]interface{} { - msm := ms.Metric - metric := map[string]interface{}{ - "metric_name": aws.StringValue(msm.MetricName), - "namespace": aws.StringValue(msm.Namespace), - "period": int(aws.Int64Value(ms.Period)), - "stat": aws.StringValue(ms.Stat), - "unit": aws.StringValue(ms.Unit), - "dimensions": flattenMetricAlarmDimensions(msm.Dimensions), +func flattenMetricAlarmMetricsMetricStat(apiObject *types.MetricStat) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{ + "period": aws.ToInt32(apiObject.Period), + "stat": aws.ToString(apiObject.Stat), + "unit": apiObject.Unit, } - return metric + if v := apiObject.Metric; v != nil { + tfMap["dimensions"] = flattenMetricAlarmDimensions(v.Dimensions) + tfMap["metric_name"] = aws.ToString(v.MetricName) + tfMap["namespace"] = aws.ToString(v.Namespace) + } + + return tfMap } -func expandMetricAlarmMetrics(v *schema.Set) []*cloudwatch.MetricDataQuery { - var metrics []*cloudwatch.MetricDataQuery +func expandMetricAlarmMetrics(tfList []interface{}) []types.MetricDataQuery { + var apiObjects []types.MetricDataQuery + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } - for _, v := range v.List() { - metricQueryResource := v.(map[string]interface{}) - id := metricQueryResource["id"].(string) + id := tfMap["id"].(string) if id == "" { continue } - metricQuery := cloudwatch.MetricDataQuery{ + + apiObject := types.MetricDataQuery{ Id: aws.String(id), } - if v, ok := metricQueryResource["expression"]; ok && v.(string) != "" { - metricQuery.Expression = aws.String(v.(string)) + + if v, ok := tfMap["account_id"]; ok && v.(string) != "" { + apiObject.AccountId = aws.String(v.(string)) } - if v, ok := metricQueryResource["label"]; ok && v.(string) != "" { - metricQuery.Label = aws.String(v.(string)) + + if v, ok := tfMap["expression"]; ok && v.(string) != "" { + apiObject.Expression = aws.String(v.(string)) } - if v, ok := metricQueryResource["return_data"]; ok { - metricQuery.ReturnData = aws.Bool(v.(bool)) + + if v, ok := tfMap["label"]; ok && v.(string) != "" { + apiObject.Label = aws.String(v.(string)) } - if v := metricQueryResource["metric"]; v != nil && len(v.([]interface{})) > 0 { - metricQuery.MetricStat = expandMetricAlarmMetricsMetric(v.([]interface{})) + + if v, ok := tfMap["return_data"]; ok { + apiObject.ReturnData = aws.Bool(v.(bool)) } - if v, ok := metricQueryResource["period"]; ok && v.(int) != 0 { - metricQuery.Period = aws.Int64(int64(v.(int))) + + if v, ok := tfMap["metric"].([]interface{}); ok && len(v) > 0 && v[0] != nil { + apiObject.MetricStat = expandMetricAlarmMetricsMetric(v[0].(map[string]interface{})) } - if v, ok := metricQueryResource["account_id"]; ok && v.(string) != "" { - metricQuery.AccountId = aws.String(v.(string)) + + if v, ok := tfMap["period"]; ok && v.(int) != 0 { + apiObject.Period = aws.Int32(int32(v.(int))) } - metrics = append(metrics, &metricQuery) + + apiObjects = append(apiObjects, apiObject) } - return metrics + + if len(apiObjects) == 0 { + return nil + } + + return apiObjects } -func expandMetricAlarmMetricsMetric(v []interface{}) *cloudwatch.MetricStat { - metricResource := v[0].(map[string]interface{}) - metric := cloudwatch.Metric{ - MetricName: aws.String(metricResource["metric_name"].(string)), +func expandMetricAlarmMetricsMetric(tfMap map[string]interface{}) *types.MetricStat { + if tfMap == nil { + return nil } - metricStat := cloudwatch.MetricStat{ - Metric: &metric, - Stat: aws.String(metricResource["stat"].(string)), + + apiObject := &types.MetricStat{ + Metric: &types.Metric{ + MetricName: aws.String(tfMap["metric_name"].(string)), + }, + Stat: aws.String(tfMap["stat"].(string)), } - if v, ok := metricResource["namespace"]; ok && v.(string) != "" { - metric.Namespace = aws.String(v.(string)) + + if v, ok := tfMap["dimensions"].(map[string]interface{}); ok && len(v) > 0 { + apiObject.Metric.Dimensions = expandMetricAlarmDimensions(v) } - if v, ok := metricResource["period"]; ok { - metricStat.Period = aws.Int64(int64(v.(int))) + + if v, ok := tfMap["namespace"]; ok && v.(string) != "" { + apiObject.Metric.Namespace = aws.String(v.(string)) } - if v, ok := metricResource["unit"]; ok && v.(string) != "" { - metricStat.Unit = aws.String(v.(string)) + + if v, ok := tfMap["period"]; ok { + apiObject.Period = aws.Int32(int32(v.(int))) } - if v, ok := metricResource["dimensions"]; ok { - metric.Dimensions = expandMetricAlarmDimensions(v.(map[string]interface{})) + + if v, ok := tfMap["unit"]; ok && v.(string) != "" { + apiObject.Unit = types.StandardUnit(v.(string)) } - return &metricStat + return apiObject } -func expandMetricAlarmDimensions(dims map[string]interface{}) []*cloudwatch.Dimension { - var dimensions []*cloudwatch.Dimension - for k, v := range dims { - dimensions = append(dimensions, &cloudwatch.Dimension{ +func expandMetricAlarmDimensions(tfMap map[string]interface{}) []types.Dimension { + if len(tfMap) == 0 { + return nil + } + + var apiObjects []types.Dimension + + for k, v := range tfMap { + apiObjects = append(apiObjects, types.Dimension{ Name: aws.String(k), Value: aws.String(v.(string)), }) } - return dimensions + + return apiObjects } diff --git a/internal/service/cloudwatch/metric_alarm_test.go b/internal/service/cloudwatch/metric_alarm_test.go index af3f38cf15e..5235345a313 100755 --- a/internal/service/cloudwatch/metric_alarm_test.go +++ b/internal/service/cloudwatch/metric_alarm_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch/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" @@ -17,17 +17,18 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" tfcloudwatch "github.com/hashicorp/terraform-provider-aws/internal/service/cloudwatch" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccCloudWatchMetricAlarm_basic(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -63,13 +64,13 @@ func TestAccCloudWatchMetricAlarm_basic(t *testing.T) { func TestAccCloudWatchMetricAlarm_AlarmActions_ec2Automate(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -112,13 +113,13 @@ func TestAccCloudWatchMetricAlarm_AlarmActions_ec2Automate(t *testing.T) { func TestAccCloudWatchMetricAlarm_AlarmActions_snsTopic(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -140,13 +141,13 @@ func TestAccCloudWatchMetricAlarm_AlarmActions_snsTopic(t *testing.T) { func TestAccCloudWatchMetricAlarm_AlarmActions_swfAction(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -168,13 +169,13 @@ func TestAccCloudWatchMetricAlarm_AlarmActions_swfAction(t *testing.T) { func TestAccCloudWatchMetricAlarm_dataPointsToAlarm(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -191,13 +192,13 @@ func TestAccCloudWatchMetricAlarm_dataPointsToAlarm(t *testing.T) { func TestAccCloudWatchMetricAlarm_treatMissingData(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -233,13 +234,13 @@ func TestAccCloudWatchMetricAlarm_treatMissingData(t *testing.T) { func TestAccCloudWatchMetricAlarm_evaluateLowSampleCountPercentiles(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -263,13 +264,13 @@ func TestAccCloudWatchMetricAlarm_evaluateLowSampleCountPercentiles(t *testing.T func TestAccCloudWatchMetricAlarm_extendedStatistic(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -416,13 +417,13 @@ func TestAccCloudWatchMetricAlarm_extendedStatistic(t *testing.T) { func TestAccCloudWatchMetricAlarm_metricQuery(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -445,9 +446,10 @@ func TestAccCloudWatchMetricAlarm_metricQuery(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metric_query"}, }, { Config: testAccMetricAlarmConfig_metricQueryExpressionReference(rName), @@ -476,9 +478,10 @@ func TestAccCloudWatchMetricAlarm_metricQuery(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metric_query"}, }, { Config: testAccMetricAlarmConfig_metricQueryCrossAccount(rName), @@ -491,9 +494,10 @@ func TestAccCloudWatchMetricAlarm_metricQuery(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metric_query"}, }, { Config: testAccMetricAlarmConfig_metricQueryExpressionReferenceUpdated(rName), @@ -517,9 +521,10 @@ func TestAccCloudWatchMetricAlarm_metricQuery(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metric_query"}, }, { Config: testAccMetricAlarmConfig_metricQueryExpressionReference(rName), @@ -529,9 +534,10 @@ func TestAccCloudWatchMetricAlarm_metricQuery(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metric_query"}, }, { Config: testAccMetricAlarmConfig_anomalyDetectionExpression(rName), @@ -548,9 +554,10 @@ func TestAccCloudWatchMetricAlarm_metricQuery(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metric_query"}, }, }, }) @@ -561,7 +568,7 @@ func TestAccCloudWatchMetricAlarm_missingStatistic(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -575,13 +582,13 @@ func TestAccCloudWatchMetricAlarm_missingStatistic(t *testing.T) { func TestAccCloudWatchMetricAlarm_tags(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -621,13 +628,13 @@ func TestAccCloudWatchMetricAlarm_tags(t *testing.T) { func TestAccCloudWatchMetricAlarm_disappears(t *testing.T) { ctx := acctest.Context(t) - var alarm cloudwatch.MetricAlarm + var alarm types.MetricAlarm resourceName := "aws_cloudwatch_metric_alarm.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricAlarmDestroy(ctx), Steps: []resource.TestStep{ @@ -643,18 +650,14 @@ func TestAccCloudWatchMetricAlarm_disappears(t *testing.T) { }) } -func testAccCheckMetricAlarmExists(ctx context.Context, n string, v *cloudwatch.MetricAlarm) resource.TestCheckFunc { +func testAccCheckMetricAlarmExists(ctx context.Context, n string, v *types.MetricAlarm) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No CloudWatch Metric Alarm ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchClient(ctx) output, err := tfcloudwatch.FindMetricAlarmByName(ctx, conn, rs.Primary.ID) @@ -670,7 +673,7 @@ func testAccCheckMetricAlarmExists(ctx context.Context, n string, v *cloudwatch. func testAccCheckMetricAlarmDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_cloudwatch_metric_alarm" { diff --git a/internal/service/cloudwatch/metric_stream.go b/internal/service/cloudwatch/metric_stream.go index c26b2f142ee..0808694f173 100644 --- a/internal/service/cloudwatch/metric_stream.go +++ b/internal/service/cloudwatch/metric_stream.go @@ -9,15 +9,16 @@ import ( "time" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch/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/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" + "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/flex" @@ -27,9 +28,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_cloudwatch_metric_stream", name="Metric Alarm") +// @SDKResource("aws_cloudwatch_metric_stream", name="Metric Stream") // @Tags(identifierAttribute="arn") -func ResourceMetricStream() *schema.Resource { +func resourceMetricStream() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceMetricStreamCreate, ReadWithoutTimeout: resourceMetricStreamRead, @@ -131,9 +132,9 @@ func ResourceMetricStream() *schema.Resource { ValidateFunc: validateMetricStreamName, }, "output_format": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 255), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: enum.Validate[types.MetricStreamOutputFormat](), }, "role_arn": { Type: schema.TypeString, @@ -201,38 +202,37 @@ func ResourceMetricStream() *schema.Resource { func resourceMetricStreamCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) name := create.Name(d.Get("name").(string), d.Get("name_prefix").(string)) input := &cloudwatch.PutMetricStreamInput{ FirehoseArn: aws.String(d.Get("firehose_arn").(string)), IncludeLinkedAccountsMetrics: aws.Bool(d.Get("include_linked_accounts_metrics").(bool)), Name: aws.String(name), - OutputFormat: aws.String(d.Get("output_format").(string)), + OutputFormat: types.MetricStreamOutputFormat(d.Get("output_format").(string)), RoleArn: aws.String(d.Get("role_arn").(string)), Tags: getTagsIn(ctx), } if v, ok := d.GetOk("exclude_filter"); ok && v.(*schema.Set).Len() > 0 { - input.ExcludeFilters = expandMetricStreamFilters(v.(*schema.Set)) + input.ExcludeFilters = expandMetricStreamFilters(v.(*schema.Set).List()) } if v, ok := d.GetOk("include_filter"); ok && v.(*schema.Set).Len() > 0 { - input.IncludeFilters = expandMetricStreamFilters(v.(*schema.Set)) + input.IncludeFilters = expandMetricStreamFilters(v.(*schema.Set).List()) } if v, ok := d.GetOk("statistics_configuration"); ok && v.(*schema.Set).Len() > 0 { - input.StatisticsConfigurations = expandMetricStreamStatisticsConfigurations(v.(*schema.Set)) + input.StatisticsConfigurations = expandMetricStreamStatisticsConfigurations(v.(*schema.Set).List()) } - output, err := conn.PutMetricStreamWithContext(ctx, input) + output, err := conn.PutMetricStream(ctx, input) // Some partitions (e.g. ISO) may not support tag-on-create. - if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(meta.(*conns.AWSClient).Partition, err) { input.Tags = nil - output, err = conn.PutMetricStreamWithContext(ctx, input) + output, err = conn.PutMetricStream(ctx, input) } if err != nil { @@ -247,10 +247,10 @@ func resourceMetricStreamCreate(ctx context.Context, d *schema.ResourceData, met // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { - err := createTags(ctx, conn, aws.StringValue(output.Arn), tags) + err := createTags(ctx, conn, aws.ToString(output.Arn), tags) // If default tags only, continue. Otherwise, error. - if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(meta.(*conns.AWSClient).Partition, err) { return append(diags, resourceMetricStreamRead(ctx, d, meta)...) } @@ -264,10 +264,9 @@ func resourceMetricStreamCreate(ctx context.Context, d *schema.ResourceData, met func resourceMetricStreamRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) - - output, err := FindMetricStreamByName(ctx, conn, d.Id()) + output, err := findMetricStreamByName(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] CloudWatch Metric Stream (%s) not found, removing from state", d.Id()) @@ -281,27 +280,24 @@ func resourceMetricStreamRead(ctx context.Context, d *schema.ResourceData, meta d.Set("arn", output.Arn) d.Set("creation_date", output.CreationDate.Format(time.RFC3339)) + if output.ExcludeFilters != nil { + if err := d.Set("exclude_filter", flattenMetricStreamFilters(output.ExcludeFilters)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting exclude_filter: %s", err) + } + } d.Set("firehose_arn", output.FirehoseArn) + if output.IncludeFilters != nil { + if err := d.Set("include_filter", flattenMetricStreamFilters(output.IncludeFilters)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting include_filter: %s", err) + } + } d.Set("include_linked_accounts_metrics", output.IncludeLinkedAccountsMetrics) d.Set("last_update_date", output.CreationDate.Format(time.RFC3339)) d.Set("name", output.Name) - d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(output.Name))) + d.Set("name_prefix", create.NamePrefixFromName(aws.ToString(output.Name))) d.Set("output_format", output.OutputFormat) d.Set("role_arn", output.RoleArn) d.Set("state", output.State) - - if output.IncludeFilters != nil { - if err := d.Set("include_filter", flattenMetricStreamFilters(output.IncludeFilters)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting include_filter: %s", err) - } - } - - if output.ExcludeFilters != nil { - if err := d.Set("exclude_filter", flattenMetricStreamFilters(output.ExcludeFilters)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting exclude_filter: %s", err) - } - } - if output.StatisticsConfigurations != nil { if err := d.Set("statistics_configuration", flattenMetricStreamStatisticsConfigurations(output.StatisticsConfigurations)); err != nil { return sdkdiag.AppendErrorf(diags, "setting statistics_configuration: %s", err) @@ -313,31 +309,30 @@ func resourceMetricStreamRead(ctx context.Context, d *schema.ResourceData, meta func resourceMetricStreamUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) if d.HasChangesExcept("tags", "tags_all") { input := &cloudwatch.PutMetricStreamInput{ FirehoseArn: aws.String(d.Get("firehose_arn").(string)), IncludeLinkedAccountsMetrics: aws.Bool(d.Get("include_linked_accounts_metrics").(bool)), Name: aws.String(d.Id()), - OutputFormat: aws.String(d.Get("output_format").(string)), + OutputFormat: types.MetricStreamOutputFormat(d.Get("output_format").(string)), RoleArn: aws.String(d.Get("role_arn").(string)), } if v, ok := d.GetOk("exclude_filter"); ok && v.(*schema.Set).Len() > 0 { - input.ExcludeFilters = expandMetricStreamFilters(v.(*schema.Set)) + input.ExcludeFilters = expandMetricStreamFilters(v.(*schema.Set).List()) } if v, ok := d.GetOk("include_filter"); ok && v.(*schema.Set).Len() > 0 { - input.IncludeFilters = expandMetricStreamFilters(v.(*schema.Set)) + input.IncludeFilters = expandMetricStreamFilters(v.(*schema.Set).List()) } if v, ok := d.GetOk("statistics_configuration"); ok && v.(*schema.Set).Len() > 0 { - input.StatisticsConfigurations = expandMetricStreamStatisticsConfigurations(v.(*schema.Set)) + input.StatisticsConfigurations = expandMetricStreamStatisticsConfigurations(v.(*schema.Set).List()) } - _, err := conn.PutMetricStreamWithContext(ctx, input) + _, err := conn.PutMetricStream(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating CloudWatch Metric Stream (%s): %s", d.Id(), err) @@ -353,11 +348,10 @@ func resourceMetricStreamUpdate(ctx context.Context, d *schema.ResourceData, met func resourceMetricStreamDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - - conn := meta.(*conns.AWSClient).CloudWatchConn(ctx) + conn := meta.(*conns.AWSClient).CloudWatchClient(ctx) log.Printf("[INFO] Deleting CloudWatch Metric Stream: %s", d.Id()) - _, err := conn.DeleteMetricStreamWithContext(ctx, &cloudwatch.DeleteMetricStreamInput{ + _, err := conn.DeleteMetricStream(ctx, &cloudwatch.DeleteMetricStreamInput{ Name: aws.String(d.Id()), }) @@ -372,14 +366,14 @@ func resourceMetricStreamDelete(ctx context.Context, d *schema.ResourceData, met return diags } -func FindMetricStreamByName(ctx context.Context, conn *cloudwatch.CloudWatch, name string) (*cloudwatch.GetMetricStreamOutput, error) { +func findMetricStreamByName(ctx context.Context, conn *cloudwatch.Client, name string) (*cloudwatch.GetMetricStreamOutput, error) { input := &cloudwatch.GetMetricStreamInput{ Name: aws.String(name), } - output, err := conn.GetMetricStreamWithContext(ctx, input) + output, err := conn.GetMetricStream(ctx, input) - if tfawserr.ErrCodeEquals(err, cloudwatch.ErrCodeResourceNotFoundException) { + if errs.IsA[*types.ResourceNotFoundException](err) { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, @@ -397,9 +391,9 @@ func FindMetricStreamByName(ctx context.Context, conn *cloudwatch.CloudWatch, na return output, nil } -func statusMetricStream(ctx context.Context, conn *cloudwatch.CloudWatch, name string) retry.StateRefreshFunc { +func statusMetricStream(ctx context.Context, conn *cloudwatch.Client, name string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindMetricStreamByName(ctx, conn, name) + output, err := findMetricStreamByName(ctx, conn, name) if tfresource.NotFound(err) { return nil, "", nil @@ -409,7 +403,7 @@ func statusMetricStream(ctx context.Context, conn *cloudwatch.CloudWatch, name s return nil, "", err } - return output, aws.StringValue(output.State), nil + return output, aws.ToString(output.State), nil } } @@ -418,7 +412,7 @@ const ( metricStreamStateStopped = "stopped" ) -func waitMetricStreamDeleted(ctx context.Context, conn *cloudwatch.CloudWatch, name string, timeout time.Duration) (*cloudwatch.GetMetricStreamOutput, error) { +func waitMetricStreamDeleted(ctx context.Context, conn *cloudwatch.Client, name string, timeout time.Duration) (*cloudwatch.GetMetricStreamOutput, error) { stateConf := &retry.StateChangeConf{ Pending: []string{metricStreamStateRunning, metricStreamStateStopped}, Target: []string{}, @@ -435,7 +429,7 @@ func waitMetricStreamDeleted(ctx context.Context, conn *cloudwatch.CloudWatch, n return nil, err } -func waitMetricStreamRunning(ctx context.Context, conn *cloudwatch.CloudWatch, name string, timeout time.Duration) (*cloudwatch.GetMetricStreamOutput, error) { //nolint:unparam +func waitMetricStreamRunning(ctx context.Context, conn *cloudwatch.Client, name string, timeout time.Duration) (*cloudwatch.GetMetricStreamOutput, error) { //nolint:unparam stateConf := &retry.StateChangeConf{ Pending: []string{metricStreamStateStopped}, Target: []string{metricStreamStateRunning}, @@ -459,120 +453,161 @@ func validateMetricStreamName(v interface{}, k string) (ws []string, errors []er )(v, k) } -func expandMetricStreamFilters(s *schema.Set) []*cloudwatch.MetricStreamFilter { - var filters []*cloudwatch.MetricStreamFilter +func expandMetricStreamFilters(tfList []interface{}) []types.MetricStreamFilter { + var apiObjects []types.MetricStreamFilter + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } - for _, filterRaw := range s.List() { - filter := &cloudwatch.MetricStreamFilter{} - mFilter := filterRaw.(map[string]interface{}) + apiObject := types.MetricStreamFilter{} - if v, ok := mFilter["metric_names"].(*schema.Set); ok && v.Len() > 0 { - filter.MetricNames = flex.ExpandStringSet(v) + if v, ok := tfMap["metric_names"].(*schema.Set); ok && v.Len() > 0 { + apiObject.MetricNames = flex.ExpandStringValueSet(v) } - if v, ok := mFilter["namespace"].(string); ok && v != "" { - filter.Namespace = aws.String(v) + + if v, ok := tfMap["namespace"].(string); ok && v != "" { + apiObject.Namespace = aws.String(v) } - filters = append(filters, filter) + + apiObjects = append(apiObjects, apiObject) } - return filters -} + if len(apiObjects) == 0 { + return nil + } -func flattenMetricStreamFilters(s []*cloudwatch.MetricStreamFilter) []map[string]interface{} { - filters := make([]map[string]interface{}, 0) + return apiObjects +} - for _, bd := range s { - if bd.Namespace != nil { - stage := make(map[string]interface{}) - stage["metric_names"] = aws.StringValueSlice(bd.MetricNames) - stage["namespace"] = aws.StringValue(bd.Namespace) - filters = append(filters, stage) - } +func flattenMetricStreamFilters(apiObjects []types.MetricStreamFilter) []interface{} { + if len(apiObjects) == 0 { + return nil } - if len(filters) > 0 { - return filters + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject.Namespace != nil { + tfMap := map[string]interface{}{ + "metric_names": apiObject.MetricNames, + } + + if v := apiObject.Namespace; v != nil { + tfMap["namespace"] = aws.ToString(v) + } + + tfList = append(tfList, tfMap) + } } - return nil + return tfList } -func expandMetricStreamStatisticsConfigurations(s *schema.Set) []*cloudwatch.MetricStreamStatisticsConfiguration { - var configurations []*cloudwatch.MetricStreamStatisticsConfiguration +func expandMetricStreamStatisticsConfigurations(tfList []interface{}) []types.MetricStreamStatisticsConfiguration { + var apiObjects []types.MetricStreamStatisticsConfiguration - for _, configurationRaw := range s.List() { - configuration := &cloudwatch.MetricStreamStatisticsConfiguration{} - mConfiguration := configurationRaw.(map[string]interface{}) + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } - if v, ok := mConfiguration["additional_statistics"].(*schema.Set); ok && v.Len() > 0 { - configuration.AdditionalStatistics = flex.ExpandStringSet(v) + apiObject := types.MetricStreamStatisticsConfiguration{} + + if v, ok := tfMap["additional_statistics"].(*schema.Set); ok && v.Len() > 0 { + apiObject.AdditionalStatistics = flex.ExpandStringValueSet(v) } - if v, ok := mConfiguration["include_metric"].(*schema.Set); ok && v.Len() > 0 { - configuration.IncludeMetrics = expandMetricStreamStatisticsConfigurationsIncludeMetrics(v) + if v, ok := tfMap["include_metric"].(*schema.Set); ok && v.Len() > 0 { + apiObject.IncludeMetrics = expandMetricStreamStatisticsConfigurationsIncludeMetrics(v.List()) } - configurations = append(configurations, configuration) + apiObjects = append(apiObjects, apiObject) } - if len(configurations) > 0 { - return configurations + if len(apiObjects) == 0 { + return nil } - return nil + return apiObjects } -func expandMetricStreamStatisticsConfigurationsIncludeMetrics(metrics *schema.Set) []*cloudwatch.MetricStreamStatisticsMetric { - var includeMetrics []*cloudwatch.MetricStreamStatisticsMetric +func expandMetricStreamStatisticsConfigurationsIncludeMetrics(tfList []interface{}) []types.MetricStreamStatisticsMetric { + var apiObjects []types.MetricStreamStatisticsMetric + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + if !ok { + continue + } - for _, metricRaw := range metrics.List() { - metric := &cloudwatch.MetricStreamStatisticsMetric{} - mMetric := metricRaw.(map[string]interface{}) + apiObject := types.MetricStreamStatisticsMetric{} - if v, ok := mMetric["metric_name"].(string); ok && v != "" { - metric.MetricName = aws.String(v) + if v, ok := tfMap["metric_name"].(string); ok && v != "" { + apiObject.MetricName = aws.String(v) } - if v, ok := mMetric["namespace"].(string); ok && v != "" { - metric.Namespace = aws.String(v) + if v, ok := tfMap["namespace"].(string); ok && v != "" { + apiObject.Namespace = aws.String(v) } - includeMetrics = append(includeMetrics, metric) + apiObjects = append(apiObjects, apiObject) } - if len(includeMetrics) > 0 { - return includeMetrics + if len(apiObjects) == 0 { + return nil } - return nil + return apiObjects } -func flattenMetricStreamStatisticsConfigurations(configurations []*cloudwatch.MetricStreamStatisticsConfiguration) []map[string]interface{} { - flatConfigurations := make([]map[string]interface{}, len(configurations)) +func flattenMetricStreamStatisticsConfigurations(apiObjects []types.MetricStreamStatisticsConfiguration) []interface{} { + if len(apiObjects) == 0 { + return nil + } - for i, configuration := range configurations { - flatConfiguration := map[string]interface{}{ - "additional_statistics": flex.FlattenStringSet(configuration.AdditionalStatistics), - "include_metric": flattenMetricStreamStatisticsConfigurationsIncludeMetrics(configuration.IncludeMetrics), + var tfList []interface{} + + for _, apiObject := range apiObjects { + tfMap := map[string]interface{}{} + + if v := apiObject.AdditionalStatistics; v != nil { + tfMap["additional_statistics"] = flex.FlattenStringValueSet(v) } - flatConfigurations[i] = flatConfiguration + if v := apiObject.IncludeMetrics; v != nil { + tfMap["include_metric"] = flattenMetricStreamStatisticsConfigurationsIncludeMetrics(v) + } + + tfList = append(tfList, tfMap) } - return flatConfigurations + return tfList } -func flattenMetricStreamStatisticsConfigurationsIncludeMetrics(metrics []*cloudwatch.MetricStreamStatisticsMetric) []map[string]interface{} { - flatMetrics := make([]map[string]interface{}, len(metrics)) +func flattenMetricStreamStatisticsConfigurationsIncludeMetrics(apiObjects []types.MetricStreamStatisticsMetric) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + tfMap := map[string]interface{}{} + + if v := apiObject.MetricName; v != nil { + tfMap["metric_name"] = aws.ToString(v) + } - for i, metric := range metrics { - flatMetric := map[string]interface{}{ - "metric_name": aws.StringValue(metric.MetricName), - "namespace": aws.StringValue(metric.Namespace), + if v := apiObject.Namespace; v != nil { + tfMap["namespace"] = aws.ToString(v) } - flatMetrics[i] = flatMetric + tfList = append(tfList, tfMap) } - return flatMetrics + return tfList } diff --git a/internal/service/cloudwatch/metric_stream_test.go b/internal/service/cloudwatch/metric_stream_test.go index b3138420c72..1b8ce6dd895 100644 --- a/internal/service/cloudwatch/metric_stream_test.go +++ b/internal/service/cloudwatch/metric_stream_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -18,10 +17,11 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" tfcloudwatch "github.com/hashicorp/terraform-provider-aws/internal/service/cloudwatch" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" ) func init() { - acctest.RegisterServiceErrorCheckFunc(cloudwatch.EndpointsID, testAccErrorCheckSkip) + acctest.RegisterServiceErrorCheckFunc(names.CloudWatchEndpointID, testAccErrorCheckSkip) } func testAccErrorCheckSkip(t *testing.T) resource.ErrorCheckFunc { @@ -37,7 +37,7 @@ func TestAccCloudWatchMetricStream_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -76,7 +76,7 @@ func TestAccCloudWatchMetricStream_disappears(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -99,7 +99,7 @@ func TestAccCloudWatchMetricStream_nameGenerated(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -126,8 +126,8 @@ func TestAccCloudWatchMetricStream_namePrefix(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, cloudwatch.EndpointsID) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, names.CloudWatchEndpointID) }, + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -155,7 +155,7 @@ func TestAccCloudWatchMetricStream_includeFilters(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -185,7 +185,7 @@ func TestAccCloudWatchMetricStream_includeFiltersWithMetricNames(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -217,7 +217,7 @@ func TestAccCloudWatchMetricStream_excludeFilters(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -246,7 +246,7 @@ func TestAccCloudWatchMetricStream_excludeFiltersWithMetricNames(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -278,7 +278,7 @@ func TestAccCloudWatchMetricStream_update(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -346,7 +346,7 @@ func TestAccCloudWatchMetricStream_tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -391,7 +391,7 @@ func TestAccCloudWatchMetricStream_additional_statistics(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -463,7 +463,7 @@ func TestAccCloudWatchMetricStream_includeLinkedAccountsMetrics(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, cloudwatch.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.CloudWatchEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMetricStreamDestroy(ctx), Steps: []resource.TestStep{ @@ -491,11 +491,8 @@ func testAccCheckMetricStreamExists(ctx context.Context, n string) resource.Test if !ok { return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { - return fmt.Errorf("No CloudWatch Metric Stream ID is set") - } - conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchClient(ctx) _, err := tfcloudwatch.FindMetricStreamByName(ctx, conn, rs.Primary.ID) @@ -505,7 +502,7 @@ func testAccCheckMetricStreamExists(ctx context.Context, n string) resource.Test func testAccCheckMetricStreamDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).CloudWatchClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_cloudwatch_metric_stream" { diff --git a/internal/service/cloudwatch/service_endpoints_gen_test.go b/internal/service/cloudwatch/service_endpoints_gen_test.go index 3cde08095e0..526850e2419 100644 --- a/internal/service/cloudwatch/service_endpoints_gen_test.go +++ b/internal/service/cloudwatch/service_endpoints_gen_test.go @@ -4,16 +4,16 @@ package cloudwatch_test import ( "context" + "errors" "fmt" - "net/url" "os" "path/filepath" "reflect" "strings" "testing" - "github.com/aws/aws-sdk-go/aws/endpoints" - cloudwatch_sdkv1 "github.com/aws/aws-sdk-go/service/cloudwatch" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + cloudwatch_sdkv2 "github.com/aws/aws-sdk-go-v2/service/cloudwatch" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" "github.com/google/go-cmp/cmp" @@ -212,32 +212,42 @@ func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.S } func defaultEndpoint(region string) string { - r := endpoints.DefaultResolver() + r := cloudwatch_sdkv2.NewDefaultEndpointResolverV2() - ep, err := r.EndpointFor(cloudwatch_sdkv1.EndpointsID, region) + ep, err := r.ResolveEndpoint(context.Background(), cloudwatch_sdkv2.EndpointParameters{ + Region: aws_sdkv2.String(region), + }) if err != nil { return err.Error() } - url, _ := url.Parse(ep.URL) - - if url.Path == "" { - url.Path = "/" + if ep.URI.Path == "" { + ep.URI.Path = "/" } - return url.String() + return ep.URI.String() } func callService(ctx context.Context, t *testing.T, meta *conns.AWSClient) string { t.Helper() - client := meta.CloudWatchConn(ctx) - - req, _ := client.ListDashboardsRequest(&cloudwatch_sdkv1.ListDashboardsInput{}) + var endpoint string - req.HTTPRequest.URL.Path = "/" + client := meta.CloudWatchClient(ctx) - endpoint := req.HTTPRequest.URL.String() + _, err := client.ListDashboards(ctx, &cloudwatch_sdkv2.ListDashboardsInput{}, + func(opts *cloudwatch_sdkv2.Options) { + opts.APIOptions = append(opts.APIOptions, + addRetrieveEndpointURLMiddleware(t, &endpoint), + addCancelRequestMiddleware(), + ) + }, + ) + if err == nil { + t.Fatal("Expected an error, got none") + } else if !errors.Is(err, errCancelOperation) { + t.Fatalf("Unexpected error: %s", err) + } return endpoint } diff --git a/internal/service/cloudwatch/service_package_gen.go b/internal/service/cloudwatch/service_package_gen.go index 608f2fb673a..e8b4d71ef5e 100644 --- a/internal/service/cloudwatch/service_package_gen.go +++ b/internal/service/cloudwatch/service_package_gen.go @@ -5,9 +5,8 @@ package cloudwatch import ( "context" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - session_sdkv1 "github.com/aws/aws-sdk-go/aws/session" - cloudwatch_sdkv1 "github.com/aws/aws-sdk-go/service/cloudwatch" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + cloudwatch_sdkv2 "github.com/aws/aws-sdk-go-v2/service/cloudwatch" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/types" "github.com/hashicorp/terraform-provider-aws/names" @@ -30,7 +29,7 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource { return []*types.ServicePackageSDKResource{ { - Factory: ResourceCompositeAlarm, + Factory: resourceCompositeAlarm, TypeName: "aws_cloudwatch_composite_alarm", Name: "Composite Alarm", Tags: &types.ServicePackageResourceTags{ @@ -38,11 +37,12 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceDashboard, + Factory: resourceDashboard, TypeName: "aws_cloudwatch_dashboard", + Name: "Dashboard", }, { - Factory: ResourceMetricAlarm, + Factory: resourceMetricAlarm, TypeName: "aws_cloudwatch_metric_alarm", Name: "Metric Alarm", Tags: &types.ServicePackageResourceTags{ @@ -50,9 +50,9 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceMetricStream, + Factory: resourceMetricStream, TypeName: "aws_cloudwatch_metric_stream", - Name: "Metric Alarm", + Name: "Metric Stream", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: "arn", }, @@ -64,11 +64,15 @@ func (p *servicePackage) ServicePackageName() string { return names.CloudWatch } -// NewConn returns a new AWS SDK for Go v1 client for this service package's AWS API. -func (p *servicePackage) NewConn(ctx context.Context, config map[string]any) (*cloudwatch_sdkv1.CloudWatch, error) { - sess := config["session"].(*session_sdkv1.Session) +// NewClient returns a new AWS SDK for Go v2 client for this service package's AWS API. +func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*cloudwatch_sdkv2.Client, error) { + cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) - return cloudwatch_sdkv1.New(sess.Copy(&aws_sdkv1.Config{Endpoint: aws_sdkv1.String(config["endpoint"].(string))})), nil + return cloudwatch_sdkv2.NewFromConfig(cfg, func(o *cloudwatch_sdkv2.Options) { + if endpoint := config["endpoint"].(string); endpoint != "" { + o.BaseEndpoint = aws_sdkv2.String(endpoint) + } + }), nil } func ServicePackage(ctx context.Context) conns.ServicePackage { diff --git a/internal/service/cloudwatch/sweep.go b/internal/service/cloudwatch/sweep.go index c04a006e80f..ab42846a54c 100644 --- a/internal/service/cloudwatch/sweep.go +++ b/internal/service/cloudwatch/sweep.go @@ -7,11 +7,13 @@ import ( "fmt" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/sweep" - "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv1" + "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv2" ) func RegisterSweepers() { @@ -19,6 +21,21 @@ func RegisterSweepers() { Name: "aws_cloudwatch_composite_alarm", F: sweepCompositeAlarms, }) + + resource.AddTestSweepers("aws_cloudwatch_dashboard", &resource.Sweeper{ + Name: "aws_cloudwatch_metric_stream", + F: sweepDashboards, + }) + + resource.AddTestSweepers("aws_cloudwatch_metric_alarm", &resource.Sweeper{ + Name: "aws_cloudwatch_metric_alarm", + F: sweepMetricAlarms, + }) + + resource.AddTestSweepers("aws_cloudwatch_metric_stream", &resource.Sweeper{ + Name: "aws_cloudwatch_metric_stream", + F: sweepMetricStreams, + }) } func sweepCompositeAlarms(region string) error { @@ -27,41 +44,163 @@ func sweepCompositeAlarms(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.CloudWatchConn(ctx) + conn := client.CloudWatchClient(ctx) input := &cloudwatch.DescribeAlarmsInput{ - AlarmTypes: aws.StringSlice([]string{cloudwatch.AlarmTypeCompositeAlarm}), + AlarmTypes: tfslices.Of(types.AlarmTypeCompositeAlarm), } sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeAlarmsPagesWithContext(ctx, input, func(page *cloudwatch.DescribeAlarmsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + pages := cloudwatch.NewDescribeAlarmsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] SkippingCloudWatch Composite Alarm sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing CloudWatch Composite Alarms (%s): %w", region, err) } for _, v := range page.CompositeAlarms { - r := ResourceCompositeAlarm() + r := resourceCompositeAlarm() d := r.Data(nil) - d.SetId(aws.StringValue(v.AlarmName)) + d.SetId(aws.ToString(v.AlarmName)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + } - return !lastPage - }) + err = sweep.SweepOrchestrator(ctx, sweepResources) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] SkippingCloudWatch Composite Alarm sweep for %s: %s", region, err) - return nil + if err != nil { + return fmt.Errorf("error sweeping CloudWatch Composite Alarms (%s): %w", region, err) } + return nil +} + +func sweepDashboards(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) if err != nil { - return fmt.Errorf("error listing CloudWatch Composite Alarms (%s): %w", region, err) + return fmt.Errorf("error getting client: %w", err) + } + conn := client.CloudWatchClient(ctx) + input := &cloudwatch.ListDashboardsInput{} + sweepResources := make([]sweep.Sweepable, 0) + + pages := cloudwatch.NewListDashboardsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] SkippingCloudWatch Dashboard sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing CloudWatch Dashboards (%s): %w", region, err) + } + + for _, v := range page.DashboardEntries { + r := resourceDashboard() + d := r.Data(nil) + d.SetId(aws.ToString(v.DashboardName)) + + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } } err = sweep.SweepOrchestrator(ctx, sweepResources) if err != nil { - return fmt.Errorf("error sweeping CloudWatch Composite Alarms (%s): %w", region, err) + return fmt.Errorf("error sweeping CloudWatch Dashboards (%s): %w", region, err) + } + + return nil +} + +func sweepMetricAlarms(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("error getting client: %w", err) + } + conn := client.CloudWatchClient(ctx) + input := &cloudwatch.DescribeAlarmsInput{ + AlarmTypes: tfslices.Of(types.AlarmTypeMetricAlarm), + } + sweepResources := make([]sweep.Sweepable, 0) + + pages := cloudwatch.NewDescribeAlarmsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] SkippingCloudWatch Metric Alarm sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing CloudWatch Metric Alarms (%s): %w", region, err) + } + + for _, v := range page.MetricAlarms { + r := resourceMetricAlarm() + d := r.Data(nil) + d.SetId(aws.ToString(v.AlarmName)) + + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } + } + + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping CloudWatch Metric Alarms (%s): %w", region, err) + } + + return nil +} + +func sweepMetricStreams(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("error getting client: %w", err) + } + conn := client.CloudWatchClient(ctx) + input := &cloudwatch.ListMetricStreamsInput{} + sweepResources := make([]sweep.Sweepable, 0) + + pages := cloudwatch.NewListMetricStreamsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] SkippingCloudWatch Metric Stream sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing CloudWatch Metric Streams (%s): %w", region, err) + } + + for _, v := range page.Entries { + r := resourceMetricStream() + d := r.Data(nil) + d.SetId(aws.ToString(v.Name)) + + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } + } + + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping CloudWatch Metric Streams (%s): %w", region, err) } return nil diff --git a/internal/service/cloudwatch/tags_gen.go b/internal/service/cloudwatch/tags_gen.go index b99205cb66a..99a3e883a67 100644 --- a/internal/service/cloudwatch/tags_gen.go +++ b/internal/service/cloudwatch/tags_gen.go @@ -5,9 +5,9 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatch" - "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + awstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/logging" @@ -19,12 +19,12 @@ import ( // listTags lists cloudwatch service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func listTags(ctx context.Context, conn cloudwatchiface.CloudWatchAPI, identifier string) (tftags.KeyValueTags, error) { +func listTags(ctx context.Context, conn *cloudwatch.Client, identifier string, optFns ...func(*cloudwatch.Options)) (tftags.KeyValueTags, error) { input := &cloudwatch.ListTagsForResourceInput{ ResourceARN: aws.String(identifier), } - output, err := conn.ListTagsForResourceWithContext(ctx, input) + output, err := conn.ListTagsForResource(ctx, input, optFns...) if err != nil { return tftags.New(ctx, nil), err @@ -36,7 +36,7 @@ func listTags(ctx context.Context, conn cloudwatchiface.CloudWatchAPI, identifie // ListTags lists cloudwatch service tags and set them in Context. // It is called from outside this package. func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier string) error { - tags, err := listTags(ctx, meta.(*conns.AWSClient).CloudWatchConn(ctx), identifier) + tags, err := listTags(ctx, meta.(*conns.AWSClient).CloudWatchClient(ctx), identifier) if err != nil { return err @@ -52,11 +52,11 @@ func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier stri // []*SERVICE.Tag handling // Tags returns cloudwatch service tags. -func Tags(tags tftags.KeyValueTags) []*cloudwatch.Tag { - result := make([]*cloudwatch.Tag, 0, len(tags)) +func Tags(tags tftags.KeyValueTags) []awstypes.Tag { + result := make([]awstypes.Tag, 0, len(tags)) for k, v := range tags.Map() { - tag := &cloudwatch.Tag{ + tag := awstypes.Tag{ Key: aws.String(k), Value: aws.String(v), } @@ -68,11 +68,11 @@ func Tags(tags tftags.KeyValueTags) []*cloudwatch.Tag { } // KeyValueTags creates tftags.KeyValueTags from cloudwatch service tags. -func KeyValueTags(ctx context.Context, tags []*cloudwatch.Tag) tftags.KeyValueTags { +func KeyValueTags(ctx context.Context, tags []awstypes.Tag) tftags.KeyValueTags { m := make(map[string]*string, len(tags)) for _, tag := range tags { - m[aws.StringValue(tag.Key)] = tag.Value + m[aws.ToString(tag.Key)] = tag.Value } return tftags.New(ctx, m) @@ -80,7 +80,7 @@ func KeyValueTags(ctx context.Context, tags []*cloudwatch.Tag) tftags.KeyValueTa // getTagsIn returns cloudwatch service tags from Context. // nil is returned if there are no input tags. -func getTagsIn(ctx context.Context) []*cloudwatch.Tag { +func getTagsIn(ctx context.Context) []awstypes.Tag { if inContext, ok := tftags.FromContext(ctx); ok { if tags := Tags(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { return tags @@ -91,14 +91,14 @@ func getTagsIn(ctx context.Context) []*cloudwatch.Tag { } // setTagsOut sets cloudwatch service tags in Context. -func setTagsOut(ctx context.Context, tags []*cloudwatch.Tag) { +func setTagsOut(ctx context.Context, tags []awstypes.Tag) { if inContext, ok := tftags.FromContext(ctx); ok { inContext.TagsOut = option.Some(KeyValueTags(ctx, tags)) } } // createTags creates cloudwatch service tags for new resources. -func createTags(ctx context.Context, conn cloudwatchiface.CloudWatchAPI, identifier string, tags []*cloudwatch.Tag) error { +func createTags(ctx context.Context, conn *cloudwatch.Client, identifier string, tags []awstypes.Tag) error { if len(tags) == 0 { return nil } @@ -109,7 +109,7 @@ func createTags(ctx context.Context, conn cloudwatchiface.CloudWatchAPI, identif // updateTags updates cloudwatch service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func updateTags(ctx context.Context, conn cloudwatchiface.CloudWatchAPI, identifier string, oldTagsMap, newTagsMap any) error { +func updateTags(ctx context.Context, conn *cloudwatch.Client, identifier string, oldTagsMap, newTagsMap any, optFns ...func(*cloudwatch.Options)) error { oldTags := tftags.New(ctx, oldTagsMap) newTags := tftags.New(ctx, newTagsMap) @@ -120,10 +120,10 @@ func updateTags(ctx context.Context, conn cloudwatchiface.CloudWatchAPI, identif if len(removedTags) > 0 { input := &cloudwatch.UntagResourceInput{ ResourceARN: aws.String(identifier), - TagKeys: aws.StringSlice(removedTags.Keys()), + TagKeys: removedTags.Keys(), } - _, err := conn.UntagResourceWithContext(ctx, input) + _, err := conn.UntagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("untagging resource (%s): %w", identifier, err) @@ -138,7 +138,7 @@ func updateTags(ctx context.Context, conn cloudwatchiface.CloudWatchAPI, identif Tags: Tags(updatedTags), } - _, err := conn.TagResourceWithContext(ctx, input) + _, err := conn.TagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("tagging resource (%s): %w", identifier, err) @@ -151,5 +151,5 @@ func updateTags(ctx context.Context, conn cloudwatchiface.CloudWatchAPI, identif // UpdateTags updates cloudwatch service tags. // It is called from outside this package. func (p *servicePackage) UpdateTags(ctx context.Context, meta any, identifier string, oldTags, newTags any) error { - return updateTags(ctx, meta.(*conns.AWSClient).CloudWatchConn(ctx), identifier, oldTags, newTags) + return updateTags(ctx, meta.(*conns.AWSClient).CloudWatchClient(ctx), identifier, oldTags, newTags) } diff --git a/names/data/names_data.csv b/names/data/names_data.csv index 41a7176edc7..8ae4f01ef13 100644 --- a/names/data/names_data.csv +++ b/names/data/names_data.csv @@ -67,7 +67,7 @@ cloudsearch,cloudsearch,cloudsearch,cloudsearch,,cloudsearch,,,CloudSearch,Cloud cloudsearchdomain,cloudsearchdomain,cloudsearchdomain,cloudsearchdomain,,cloudsearchdomain,,,CloudSearchDomain,CloudSearchDomain,,1,,,aws_cloudsearchdomain_,,cloudsearchdomain_,CloudSearch Domain,Amazon,,x,,,,,CloudSearch Domain,,, ,,,,,,,,,,,,,,,,,CloudShell,AWS,x,,,,,,,,,No SDK support cloudtrail,cloudtrail,cloudtrail,cloudtrail,,cloudtrail,,,CloudTrail,CloudTrail,,,2,aws_cloudtrail,aws_cloudtrail_,,cloudtrail,CloudTrail,AWS,,,,,,,CloudTrail,ListChannels,, -cloudwatch,cloudwatch,cloudwatch,cloudwatch,,cloudwatch,,,CloudWatch,CloudWatch,,1,,aws_cloudwatch_(?!(event_|log_|query_)),aws_cloudwatch_,,cloudwatch_dashboard;cloudwatch_metric_;cloudwatch_composite_,CloudWatch,Amazon,,,,,,,CloudWatch,ListDashboards,, +cloudwatch,cloudwatch,cloudwatch,cloudwatch,,cloudwatch,,,CloudWatch,CloudWatch,,,2,aws_cloudwatch_(?!(event_|log_|query_)),aws_cloudwatch_,,cloudwatch_dashboard;cloudwatch_metric_;cloudwatch_composite_,CloudWatch,Amazon,,,,,,,CloudWatch,ListDashboards,, application-insights,applicationinsights,applicationinsights,applicationinsights,,applicationinsights,,,ApplicationInsights,ApplicationInsights,,1,,,aws_applicationinsights_,,applicationinsights_,CloudWatch Application Insights,Amazon,,,,,,,Application Insights,CreateApplication,, evidently,evidently,cloudwatchevidently,evidently,,evidently,,cloudwatchevidently,Evidently,CloudWatchEvidently,,,2,,aws_evidently_,,evidently_,CloudWatch Evidently,Amazon,,,,,,,Evidently,ListProjects,, internetmonitor,internetmonitor,internetmonitor,internetmonitor,,internetmonitor,,,InternetMonitor,InternetMonitor,,,2,,aws_internetmonitor_,,internetmonitor_,CloudWatch Internet Monitor,Amazon,,,,,,,InternetMonitor,ListMonitors,, diff --git a/names/names.go b/names/names.go index 3917afa24a3..2de57b6b305 100644 --- a/names/names.go +++ b/names/names.go @@ -39,6 +39,7 @@ const ( ChimeSDKMediaPipelinesEndpointID = "media-pipelines-chime" CleanRoomsEndpointID = "cleanrooms" CloudTrailEndpointID = "cloudtrail" + CloudWatchEndpointID = "monitoring" CloudWatchLogsEndpointID = "logs" CodeArtifactEndpointID = "codeartifact" CodeBuildEndpointID = "codebuild"