From 8f8fafbbba926fd8e768b23cf716dee9564feac4 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Thu, 8 Feb 2024 15:46:10 +0200 Subject: [PATCH 01/20] Network Monitor Monitor and Probe --- .github/labeler-issue-triage.yml | 2 + .github/labeler-pr-triage.yml | 3 + .../components/generated/services_all.kt | 1 + go.mod | 1 + go.sum | 2 + infrastructure/repository/labels-service.tf | 1 + internal/conns/awsclient_gen.go | 5 + internal/provider/service_packages_gen.go | 2 + .../service/networkmonitor/exports_tests.go | 10 + internal/service/networkmonitor/generate.go | 8 + internal/service/networkmonitor/monitor.go | 362 ++++++++++++ .../service/networkmonitor/monitor_test.go | 184 ++++++ internal/service/networkmonitor/probe.go | 539 ++++++++++++++++++ internal/service/networkmonitor/probe_test.go | 239 ++++++++ .../service_endpoints_gen_test.go | 496 ++++++++++++++++ .../networkmonitor/service_package_gen.go | 65 +++ internal/service/networkmonitor/tags_gen.go | 128 +++++ internal/sweep/service_packages_gen_test.go | 2 + names/consts_gen.go | 1 + names/data/names_data.csv | 1 + website/allowed-subcategories.txt | 1 + .../custom-service-endpoints.html.markdown | 1 + .../r/networkmonitor_monitor.html.markdown | 66 +++ .../docs/r/networkmonitor_probe.html.markdown | 84 +++ 24 files changed, 2204 insertions(+) create mode 100644 internal/service/networkmonitor/exports_tests.go create mode 100644 internal/service/networkmonitor/generate.go create mode 100644 internal/service/networkmonitor/monitor.go create mode 100644 internal/service/networkmonitor/monitor_test.go create mode 100644 internal/service/networkmonitor/probe.go create mode 100644 internal/service/networkmonitor/probe_test.go create mode 100644 internal/service/networkmonitor/service_endpoints_gen_test.go create mode 100644 internal/service/networkmonitor/service_package_gen.go create mode 100644 internal/service/networkmonitor/tags_gen.go create mode 100644 website/docs/r/networkmonitor_monitor.html.markdown create mode 100644 website/docs/r/networkmonitor_probe.html.markdown diff --git a/.github/labeler-issue-triage.yml b/.github/labeler-issue-triage.yml index b8581767a1f..a722ec06470 100644 --- a/.github/labeler-issue-triage.yml +++ b/.github/labeler-issue-triage.yml @@ -469,6 +469,8 @@ service/networkfirewall: - '((\*|-)\s*`?|(data|resource)\s+"?)aws_networkfirewall_' service/networkmanager: - '((\*|-)\s*`?|(data|resource)\s+"?)aws_networkmanager_' +service/networkmonitor: + - '((\*|-)\s*`?|(data|resource)\s+"?)aws_networkmonitor_' service/nimble: - '((\*|-)\s*`?|(data|resource)\s+"?)aws_nimble_' service/oam: diff --git a/.github/labeler-pr-triage.yml b/.github/labeler-pr-triage.yml index 5833a2193f7..013a73ef201 100644 --- a/.github/labeler-pr-triage.yml +++ b/.github/labeler-pr-triage.yml @@ -771,6 +771,9 @@ service/networkfirewall: service/networkmanager: - 'internal/service/networkmanager/**/*' - 'website/**/networkmanager_*' +service/networkmonitor: + - 'internal/service/networkmonitor/**/*' + - 'website/**/networkmonitor_*' service/nimble: - 'internal/service/nimble/**/*' - 'website/**/nimble_*' diff --git a/.teamcity/components/generated/services_all.kt b/.teamcity/components/generated/services_all.kt index 161781a9b01..dc0b8383d5e 100644 --- a/.teamcity/components/generated/services_all.kt +++ b/.teamcity/components/generated/services_all.kt @@ -145,6 +145,7 @@ val services = mapOf( "neptune" to ServiceSpec("Neptune"), "networkfirewall" to ServiceSpec("Network Firewall", vpcLock = true), "networkmanager" to ServiceSpec("Network Manager", vpcLock = true), + "networkmonitor" to ServiceSpec("CloudWatch Network Monitor"), "oam" to ServiceSpec("CloudWatch Observability Access Manager"), "opensearch" to ServiceSpec("OpenSearch", vpcLock = true), "opensearchserverless" to ServiceSpec("OpenSearch Serverless"), diff --git a/go.mod b/go.mod index 792fc46e01f..5e8baf6b8bb 100644 --- a/go.mod +++ b/go.mod @@ -83,6 +83,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/mediapackagev2 v1.7.7 github.com/aws/aws-sdk-go-v2/service/mediastore v1.18.6 github.com/aws/aws-sdk-go-v2/service/mq v1.20.7 + github.com/aws/aws-sdk-go-v2/service/networkmonitor v1.0.1 github.com/aws/aws-sdk-go-v2/service/oam v1.7.7 github.com/aws/aws-sdk-go-v2/service/opensearchserverless v1.9.6 github.com/aws/aws-sdk-go-v2/service/osis v1.6.6 diff --git a/go.sum b/go.sum index 27659437c8a..5abaa66735d 100644 --- a/go.sum +++ b/go.sum @@ -198,6 +198,8 @@ github.com/aws/aws-sdk-go-v2/service/mediastore v1.18.6 h1:8iy67SQ3gJDytDEOmkMXx github.com/aws/aws-sdk-go-v2/service/mediastore v1.18.6/go.mod h1:mjWnSGMBWSh9DOju44R6t8U3i9XlDxl9PlSnOPb2WTk= github.com/aws/aws-sdk-go-v2/service/mq v1.20.7 h1:d4hynfHB+JsY5zcek4ITxVnVEkkxa7ZZDJW6OOPwEeQ= github.com/aws/aws-sdk-go-v2/service/mq v1.20.7/go.mod h1:PHzqJZbmPmkFXCZiaOdEY9KoGFYWD2lVO2Rv8Om1hSg= +github.com/aws/aws-sdk-go-v2/service/networkmonitor v1.0.1 h1:wUbcZnEpBQ5oySe+Tu1Wn61PkyYtOXto0J8wglX05mw= +github.com/aws/aws-sdk-go-v2/service/networkmonitor v1.0.1/go.mod h1:5IyAR0j1NjMWDgcxRRceh+d0xsjenTf4c5wDZDVnxrM= github.com/aws/aws-sdk-go-v2/service/oam v1.7.7 h1:b9/KbZcdS1XmP6vjKj+62bVoRbY+9fcf3iDDBxS4yKU= github.com/aws/aws-sdk-go-v2/service/oam v1.7.7/go.mod h1:WItaxbv/9ciGpAVQJwYWBwpGF05ansTMUsM3rkiHumo= github.com/aws/aws-sdk-go-v2/service/opensearchserverless v1.9.6 h1:hOZ68emCJaOz2xKyph/xwp8EdIOA+FbE7ehRlf7TBaQ= diff --git a/infrastructure/repository/labels-service.tf b/infrastructure/repository/labels-service.tf index 237aab10086..6f4b5fc1b54 100644 --- a/infrastructure/repository/labels-service.tf +++ b/infrastructure/repository/labels-service.tf @@ -222,6 +222,7 @@ variable "service_labels" { "neptune", "networkfirewall", "networkmanager", + "networkmonitor", "nimble", "oam", "opensearch", diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index 93d3ae50131..636987654cd 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -77,6 +77,7 @@ import ( mediapackagev2_sdkv2 "github.com/aws/aws-sdk-go-v2/service/mediapackagev2" mediastore_sdkv2 "github.com/aws/aws-sdk-go-v2/service/mediastore" mq_sdkv2 "github.com/aws/aws-sdk-go-v2/service/mq" + networkmonitor_sdkv2 "github.com/aws/aws-sdk-go-v2/service/networkmonitor" oam_sdkv2 "github.com/aws/aws-sdk-go-v2/service/oam" opensearchserverless_sdkv2 "github.com/aws/aws-sdk-go-v2/service/opensearchserverless" osis_sdkv2 "github.com/aws/aws-sdk-go-v2/service/osis" @@ -872,6 +873,10 @@ func (c *AWSClient) NetworkManagerConn(ctx context.Context) *networkmanager_sdkv return errs.Must(conn[*networkmanager_sdkv1.NetworkManager](ctx, c, names.NetworkManager, make(map[string]any))) } +func (c *AWSClient) NetworkMonitorClient(ctx context.Context) *networkmonitor_sdkv2.Client { + return errs.Must(client[*networkmonitor_sdkv2.Client](ctx, c, names.NetworkMonitor, make(map[string]any))) +} + func (c *AWSClient) ObservabilityAccessManagerClient(ctx context.Context) *oam_sdkv2.Client { return errs.Must(client[*oam_sdkv2.Client](ctx, c, names.ObservabilityAccessManager, make(map[string]any))) } diff --git a/internal/provider/service_packages_gen.go b/internal/provider/service_packages_gen.go index 688f3aaf039..826bd1b1d33 100644 --- a/internal/provider/service_packages_gen.go +++ b/internal/provider/service_packages_gen.go @@ -154,6 +154,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/service/neptune" "github.com/hashicorp/terraform-provider-aws/internal/service/networkfirewall" "github.com/hashicorp/terraform-provider-aws/internal/service/networkmanager" + "github.com/hashicorp/terraform-provider-aws/internal/service/networkmonitor" "github.com/hashicorp/terraform-provider-aws/internal/service/oam" "github.com/hashicorp/terraform-provider-aws/internal/service/opensearch" "github.com/hashicorp/terraform-provider-aws/internal/service/opensearchserverless" @@ -383,6 +384,7 @@ func servicePackages(ctx context.Context) []conns.ServicePackage { neptune.ServicePackage(ctx), networkfirewall.ServicePackage(ctx), networkmanager.ServicePackage(ctx), + networkmonitor.ServicePackage(ctx), oam.ServicePackage(ctx), opensearch.ServicePackage(ctx), opensearchserverless.ServicePackage(ctx), diff --git a/internal/service/networkmonitor/exports_tests.go b/internal/service/networkmonitor/exports_tests.go new file mode 100644 index 00000000000..ba01ef3abc6 --- /dev/null +++ b/internal/service/networkmonitor/exports_tests.go @@ -0,0 +1,10 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package networkmonitor + +// Exports for use in tests only. +var ( + ResourceNetworkMonitorMonitor = newResourceNetworkMonitorMonitor + ResourceNetworkMonitorProbe = newResourceNetworkMonitorProbe +) diff --git a/internal/service/networkmonitor/generate.go b/internal/service/networkmonitor/generate.go new file mode 100644 index 00000000000..5c92af04d47 --- /dev/null +++ b/internal/service/networkmonitor/generate.go @@ -0,0 +1,8 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:generate go run ../../generate/servicepackage/main.go +//go:generate go run ../../generate/tags/main.go -AWSSDKVersion=2 -KVTValues -SkipTypesImp -ServiceTagsMap -ListTags -UpdateTags +// ONLY generate directives and package declaration! Do not add anything else to this file. + +package networkmonitor diff --git a/internal/service/networkmonitor/monitor.go b/internal/service/networkmonitor/monitor.go new file mode 100644 index 00000000000..52dbd892442 --- /dev/null +++ b/internal/service/networkmonitor/monitor.go @@ -0,0 +1,362 @@ +package networkmonitor + +import ( + "context" + "errors" + "regexp" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/networkmonitor" + awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +const ( + MonitorTimeout = time.Minute * 10 + ResNameNetworkMonitorMonitor = "CloudWatch Network Monitor Monitor" +) + +// @FrameworkResource(name="CloudWatch Network Monitor Monitor") +// @Tags(identifierAttribute="arn") +func newResourceNetworkMonitorMonitor(context.Context) (resource.ResourceWithConfigure, error) { + return &resourceNetworkMonitorMonitor{}, nil +} + +type resourceNetworkMonitorMonitor struct { + framework.ResourceWithConfigure +} + +func (r *resourceNetworkMonitorMonitor) Metadata(_ context.Context, request resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_networkmonitor_monitor" +} + +func (r *resourceNetworkMonitorMonitor) Schema(ctx context.Context, request resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "arn": framework.ARNAttributeComputedOnly(), + "aggregation_period": schema.Int64Attribute{ + Optional: true, + Validators: []validator.Int64{ + int64validator.OneOf(30, 60), + }, + }, + "created_at": schema.Int64Attribute{ + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + "modified_at": schema.Int64Attribute{ + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + "monitor_name": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("[a-zA-Z0-9_-]+"), "Must match [a-zA-Z0-9_-]+"), + stringvalidator.LengthBetween(1, 255), + }, + }, + names.AttrID: framework.IDAttribute(), + names.AttrTags: tftags.TagsAttribute(), + names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), + "state": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + } +} + +func (r *resourceNetworkMonitorMonitor) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().NetworkMonitorClient(ctx) + + var plan resourceNetworkMonitorMonitorModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + input := networkmonitor.CreateMonitorInput{ + MonitorName: plan.MonitorName.ValueStringPointer(), + AggregationPeriod: plan.AggregationPeriod.ValueInt64Pointer(), + Tags: getTagsIn(ctx), + } + + _, err := conn.CreateMonitor(ctx, &input) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameNetworkMonitorMonitor, plan.MonitorName.String(), nil), + err.Error(), + ) + return + } + + out, err := waitMonitorReady(ctx, conn, plan.MonitorName.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForCreation, ResNameNetworkMonitorMonitor, plan.MonitorName.ValueString(), nil), + err.Error(), + ) + } + + state := plan + + state.ID = flex.StringToFramework(ctx, out.MonitorName) + state.Arn = flex.StringToFramework(ctx, out.MonitorArn) + state.State = flex.StringToFramework(ctx, (*string)(&out.State)) + state.CreatedAt = flex.Int64ToFramework(ctx, (aws.Int64(out.CreatedAt.Unix()))) + state.ModifiedAt = flex.Int64ToFramework(ctx, (aws.Int64(out.ModifiedAt.Unix()))) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceNetworkMonitorMonitor) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().NetworkMonitorClient(ctx) + + var state resourceNetworkMonitorMonitorModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + output, err := FindMonitorByName(ctx, conn, state.ID.ValueString()) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + resp.State.RemoveResource(ctx) + return + } + + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameNetworkMonitorMonitor, state.ID.String(), err), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, output, &state)...) + + state.AggregationPeriod = flex.Int64ToFramework(ctx, output.AggregationPeriod) + state.MonitorName = flex.StringToFramework(ctx, output.MonitorName) + state.Arn = flex.StringToFramework(ctx, output.MonitorArn) + state.State = flex.StringToFramework(ctx, (*string)(&output.State)) + state.CreatedAt = flex.Int64ToFramework(ctx, (aws.Int64(output.CreatedAt.Unix()))) + state.ModifiedAt = flex.Int64ToFramework(ctx, (aws.Int64(output.ModifiedAt.Unix()))) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceNetworkMonitorMonitor) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + conn := r.Meta().NetworkMonitorClient(ctx) + + var plan, state resourceNetworkMonitorMonitorModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if !plan.AggregationPeriod.Equal(state.AggregationPeriod) { + input := networkmonitor.UpdateMonitorInput{ + MonitorName: plan.MonitorName.ValueStringPointer(), + AggregationPeriod: plan.AggregationPeriod.ValueInt64Pointer(), + } + + _, err := conn.UpdateMonitor(ctx, &input) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameNetworkMonitorMonitor, state.ID.String(), nil), + err.Error(), + ) + return + } + + _, err = waitMonitorReady(ctx, conn, plan.MonitorName.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameNetworkMonitorMonitor, plan.MonitorName.ValueString(), nil), + err.Error(), + ) + } + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForUpdate, ResNameNetworkMonitorMonitor, state.ID.String(), nil), + err.Error(), + ) + return + } + } + + state.AggregationPeriod = flex.Int64ToFramework(ctx, plan.AggregationPeriod.ValueInt64Pointer()) + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourceNetworkMonitorMonitor) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().NetworkMonitorClient(ctx) + + var state resourceNetworkMonitorMonitorModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + input := networkmonitor.DeleteMonitorInput{ + MonitorName: flex.StringFromFramework(ctx, state.MonitorName), + } + + _, err := conn.DeleteMonitor(ctx, &input) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameNetworkMonitorMonitor, state.ID.String(), nil), + err.Error(), + ) + return + } + + _, err = waitMonitorDeleted(ctx, conn, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForDeletion, ResNameNetworkMonitorMonitor, state.ID.String(), nil), + err.Error(), + ) + return + } + +} + +func statusMonitor(ctx context.Context, conn *networkmonitor.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := FindMonitorByName(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.State), nil + } +} + +func waitMonitorReady(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetMonitorOutput, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{ + string(awstypes.MonitorStatePending), + }, + Target: []string{ + string(awstypes.MonitorStateActive), + string(awstypes.MonitorStateInactive), + }, + Refresh: statusMonitor(ctx, conn, id), + Timeout: MonitorTimeout, + MinTimeout: 10 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if output, ok := outputRaw.(*networkmonitor.GetMonitorOutput); ok { + return output, err + } + + return nil, err +} + +func waitMonitorDeleted(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetMonitorOutput, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{ + string(awstypes.MonitorStateDeleting), + string(awstypes.MonitorStateActive), + string(awstypes.MonitorStateInactive), + }, + Target: []string{}, + Refresh: statusMonitor(ctx, conn, id), + Timeout: MonitorTimeout, + MinTimeout: 10 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if output, ok := outputRaw.(*networkmonitor.GetMonitorOutput); ok { + return output, err + } + + return nil, err +} + +func FindMonitorByName(ctx context.Context, conn *networkmonitor.Client, name string) (*networkmonitor.GetMonitorOutput, error) { + input := &networkmonitor.GetMonitorInput{ + MonitorName: &name, + } + + output, err := conn.GetMonitor(ctx, input) + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} + +func (r *resourceNetworkMonitorMonitor) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, req, resp) +} + +func (r *resourceNetworkMonitorMonitor) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +type resourceNetworkMonitorMonitorModel struct { + ID types.String `tfsdk:"id"` + Arn types.String `tfsdk:"arn"` + AggregationPeriod types.Int64 `tfsdk:"aggregation_period"` + CreatedAt types.Int64 `tfsdk:"created_at"` + ModifiedAt types.Int64 `tfsdk:"modified_at"` + MonitorName types.String `tfsdk:"monitor_name"` + State types.String `tfsdk:"state"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` +} diff --git a/internal/service/networkmonitor/monitor_test.go b/internal/service/networkmonitor/monitor_test.go new file mode 100644 index 00000000000..2668d70b07c --- /dev/null +++ b/internal/service/networkmonitor/monitor_test.go @@ -0,0 +1,184 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package networkmonitor_test + +import ( + "context" + "errors" + "fmt" + "testing" + + awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" + "github.com/aws/aws-sdk-go/service/networkmonitor" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + tfnetworkmonitor "github.com/hashicorp/terraform-provider-aws/internal/service/networkmonitor" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccNetworkMonitorMonitor_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmonitor_monitor.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmonitor.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckMonitorDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccMonitorConfig(rName, 30), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckMonitorExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "aggregation_period", "30"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"tags", "tags_all"}, + }, + }, + }) +} + +func TestAccNetworkMonitorMonitor_updates(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmonitor_monitor.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmonitor.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckMonitorDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccMonitorConfig(rName, 30), + Check: resource.ComposeTestCheckFunc( + testAccCheckMonitorExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "aggregation_period", "30"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + ), + }, + { + Config: testAccMonitorConfig_tags(rName, 60), + Check: resource.ComposeTestCheckFunc( + testAccCheckMonitorExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "aggregation_period", "60"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + ), + }, + }, + }) +} + +func TestAccNetworkMonitorMonitor_disappears(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmonitor_monitor.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, networkmonitor.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckMonitorDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccMonitorConfig(rName, 30), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckMonitorExists(ctx, resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfnetworkmonitor.ResourceNetworkMonitorMonitor, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckMonitorDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkMonitorClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_networkmonitor_monitor" { + continue + } + + _, err := tfnetworkmonitor.FindMonitorByName(ctx, conn, rs.Primary.ID) + if tfresource.NotFound(err) { + continue + } + + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return nil + } + + if err != nil { + return err + } + + return create.Error(names.NetworkMonitor, create.ErrActionCheckingDestroyed, tfnetworkmonitor.ResNameNetworkMonitorMonitor, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} + +func testAccCheckMonitorExists(ctx context.Context, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorMonitor, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorMonitor, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkMonitorClient(ctx) + + _, err := tfnetworkmonitor.FindMonitorByName(ctx, conn, rs.Primary.ID) + if err != nil { + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorMonitor, rs.Primary.ID, err) + } + + return nil + } +} + +func testAccMonitorConfig(rName string, aggregation int) string { + return fmt.Sprintf(` +resource "aws_networkmonitor_monitor" "test" { + aggregation_period = %[2]d + monitor_name = %[1]q + tags = { + tag1 = %[1]q + } +} +`, rName, aggregation) +} + +func testAccMonitorConfig_tags(rName string, aggregation int) string { + return fmt.Sprintf(` +resource "aws_networkmonitor_monitor" "test" { + aggregation_period = %[2]d + monitor_name = %[1]q + tags = { + tag1 = %[1]q + tag2 = %[1]q + } +} +`, rName, aggregation) +} diff --git a/internal/service/networkmonitor/probe.go b/internal/service/networkmonitor/probe.go new file mode 100644 index 00000000000..0a472043503 --- /dev/null +++ b/internal/service/networkmonitor/probe.go @@ -0,0 +1,539 @@ +package networkmonitor + +import ( + "context" + "errors" + "fmt" + "regexp" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/networkmonitor" + awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/names" +) + +const ( + ProbeTimeout = time.Minute * 10 + ResNameNetworkMonitorProbe = "CloudWatch Network Monitor Probe" +) + +// @FrameworkResource(name="CloudWatch Network Monitor Probe") +// @Tags(identifierAttribute="arn") +func newResourceNetworkMonitorProbe(context.Context) (resource.ResourceWithConfigure, error) { + return &resourceNetworkMonitorProbe{}, nil +} + +type resourceNetworkMonitorProbe struct { + framework.ResourceWithConfigure +} + +func (r *resourceNetworkMonitorProbe) Metadata(_ context.Context, request resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_networkmonitor_probe" +} + +func (r *resourceNetworkMonitorProbe) Schema(ctx context.Context, request resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + names.AttrID: framework.IDAttribute(), + "arn": framework.ARNAttributeComputedOnly(), + "monitor_name": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("[a-zA-Z0-9_-]+"), "Must match [a-zA-Z0-9_-]+"), + stringvalidator.LengthBetween(1, 255), + }, + }, + names.AttrTags: tftags.TagsAttribute(), + names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), + }, + Blocks: map[string]schema.Block{ + "probe": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "address_family": schema.StringAttribute{ + Computed: true, + }, + "created_at": schema.Int64Attribute{ + Computed: true, + }, + "destination": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 255), + }, + }, + "destination_port": schema.Int64Attribute{ + Optional: true, + Validators: []validator.Int64{ + int64validator.Between(0, 65536), + }, + }, + "modified_at": schema.Int64Attribute{ + Computed: true, + }, + "packet_size": schema.Int64Attribute{ + Optional: true, + Validators: []validator.Int64{ + int64validator.Between(56, 8500), + }, + }, + "probe_arn": schema.StringAttribute{ + Computed: true, + }, + "probe_id": schema.StringAttribute{ + Computed: true, + }, + "protocol": schema.StringAttribute{ + Required: true, + }, + "source_arn": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(20, 2048), + stringvalidator.RegexMatches(regexp.MustCompile("arn:.*"), "Must match pattern arn:*"), + }, + }, + "tags": schema.MapAttribute{ + ElementType: types.StringType, + Computed: true, + }, + "state": schema.StringAttribute{ + Computed: true, + }, + "vpc_id": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + } +} + +func (r *resourceNetworkMonitorProbe) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().NetworkMonitorClient(ctx) + + var state resourceNetworkMonitorProbeModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var probe probeConfigModel + resp.Diagnostics.Append( + state.Probe.As( + ctx, + &probe, + basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})...) + + probeConfig := expandProbeConfig(ctx, probe, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + input := networkmonitor.CreateProbeInput{ + MonitorName: state.MonitorName.ValueStringPointer(), + Probe: &probeConfig, + Tags: flex.ExpandFrameworkStringValueMap(ctx, state.Tags), + } + + out, err := conn.CreateProbe(ctx, &input) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameNetworkMonitorProbe, state.MonitorName.String(), nil), + err.Error(), + ) + return + } + + probeID := fmt.Sprintf("%s:%s", *out.ProbeId, *state.MonitorName.ValueStringPointer()) + + outWait, err := waitProbeReady(ctx, conn, probeID) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForCreation, ResNameNetworkMonitorProbe, probeID, nil), + err.Error(), + ) + } + + state.ID = flex.StringToFramework(ctx, &probeID) + state.MonitorName = flex.StringToFramework(ctx, state.MonitorName.ValueStringPointer()) + p, d := flattenProbeConfig(ctx, *outWait) + resp.Diagnostics.Append(d...) + state.Probe = p + state.Arn = flex.StringToFramework(ctx, out.ProbeArn) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceNetworkMonitorProbe) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().NetworkMonitorClient(ctx) + + var state resourceNetworkMonitorProbeModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + out, err := FindProbeByID(ctx, conn, state.ID.ValueString()) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + resp.State.RemoveResource(ctx) + return + } + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameNetworkMonitorProbe, state.ID.String(), err), + err.Error(), + ) + return + } + + state.ID = flex.StringToFramework(ctx, state.ID.ValueStringPointer()) + _, monitorName, err := probeParseID(state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameNetworkMonitorProbe, state.ID.String(), err), + err.Error(), + ) + return + } + + state.MonitorName = flex.StringToFramework(ctx, &monitorName) + state.Arn = flex.StringToFramework(ctx, out.ProbeArn) + p, d := flattenProbeConfig(ctx, *out) + resp.Diagnostics.Append(d...) + state.Probe = p + + setTagsOut(ctx, out.Tags) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceNetworkMonitorProbe) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + conn := r.Meta().NetworkMonitorClient(ctx) + + var plan, state resourceNetworkMonitorProbeModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var probePlan, probeState probeConfigModel + resp.Diagnostics.Append( + plan.Probe.As( + ctx, + &probePlan, + basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})...) + + resp.Diagnostics.Append( + state.Probe.As( + ctx, + &probeState, + basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})...) + + probeID, monitorName, err := probeParseID(state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameNetworkMonitorProbe, state.ID.String(), err), + err.Error(), + ) + return + } + + in := networkmonitor.UpdateProbeInput{ + MonitorName: &monitorName, + ProbeId: &probeID, + } + + if !probePlan.Destination.Equal(probeState.Destination) { + in.Destination = probePlan.Destination.ValueStringPointer() + } + if !probePlan.DestinationPort.Equal(probeState.DestinationPort) { + in.DestinationPort = aws.Int32(int32(probePlan.DestinationPort.ValueInt64())) + } + if !probePlan.PacketSize.Equal(probeState.PacketSize) { + in.PacketSize = aws.Int32(int32(probePlan.PacketSize.ValueInt64())) + } + if !probePlan.Protocol.Equal(probeState.Protocol) { + in.Protocol = awstypes.Protocol(*probePlan.Protocol.ValueStringPointer()) + } + + _, err = conn.UpdateProbe(ctx, &in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameNetworkMonitorProbe, state.ID.String(), nil), + err.Error(), + ) + return + } + + out, err := waitProbeReady(ctx, conn, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForUpdate, ResNameNetworkMonitorProbe, state.ID.String(), nil), + err.Error(), + ) + } + state = plan + p, d := flattenProbeConfig(ctx, *out) + resp.Diagnostics.Append(d...) + state.Probe = p + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceNetworkMonitorProbe) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().NetworkMonitorClient(ctx) + + var state resourceNetworkMonitorProbeModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + probeID, monitorName, err := probeParseID(state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameNetworkMonitorProbe, state.ID.String(), nil), + err.Error(), + ) + return + } + + input := networkmonitor.DeleteProbeInput{ + MonitorName: &monitorName, + ProbeId: &probeID, + } + _, err = conn.DeleteProbe(ctx, &input) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return + } + + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameNetworkMonitorProbe, state.ID.String(), nil), + err.Error(), + ) + return + } + + _, err = waitProbeDeleted(ctx, conn, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForDeletion, ResNameNetworkMonitorProbe, state.ID.String(), nil), + err.Error(), + ) + return + } +} + +func (r *resourceNetworkMonitorProbe) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, req, resp) +} + +func (r *resourceNetworkMonitorProbe) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func statusProbe(ctx context.Context, conn *networkmonitor.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := FindProbeByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.State), nil + } +} + +func waitProbeReady(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetProbeOutput, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{ + string(awstypes.ProbeStatePending), + }, + Target: []string{ + string(awstypes.ProbeStateActive), + string(awstypes.ProbeStateInactive), + }, + Refresh: statusProbe(ctx, conn, id), + Timeout: ProbeTimeout, + MinTimeout: 10 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if output, ok := outputRaw.(*networkmonitor.GetProbeOutput); ok { + return output, err + } + + return nil, err +} + +func waitProbeDeleted(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetProbeOutput, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{ + string(awstypes.ProbeStateActive), + string(awstypes.ProbeStateInactive), + string(awstypes.ProbeStateDeleting), + }, + Target: []string{}, + Refresh: statusProbe(ctx, conn, id), + Timeout: ProbeTimeout, + MinTimeout: 10 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if output, ok := outputRaw.(*networkmonitor.GetProbeOutput); ok { + return output, err + } + + return nil, err +} + +func FindProbeByID(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetProbeOutput, error) { + probeID, monitorName, err := probeParseID(id) + if err != nil { + return nil, err + } + + input := &networkmonitor.GetProbeInput{ + ProbeId: &probeID, + MonitorName: &monitorName, + } + + output, err := conn.GetProbe(ctx, input) + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} + +func probeParseID(id string) (string, string, error) { + parts := strings.SplitN(id, ":", 2) + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%s), expected probeID:monitorName", id) + } + + return parts[0], parts[1], nil +} + +var probeConfigTypes = map[string]attr.Type{ + "address_family": types.StringType, + "created_at": types.Int64Type, + "destination": types.StringType, + "destination_port": types.Int64Type, + "modified_at": types.Int64Type, + "packet_size": types.Int64Type, + "probe_arn": types.StringType, + "probe_id": types.StringType, + "protocol": types.StringType, + "source_arn": types.StringType, + "state": types.StringType, + "tags": types.MapType{ElemType: types.StringType}, + "vpc_id": types.StringType, +} + +type resourceNetworkMonitorProbeModel struct { + ID types.String `tfsdk:"id"` + Arn types.String `tfsdk:"arn"` + MonitorName types.String `tfsdk:"monitor_name"` + Probe types.Object `tfsdk:"probe"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` +} + +type probeConfigModel struct { + AddressFamily types.String `tfsdk:"address_family"` + CreatedAt types.Int64 `tfsdk:"created_at"` + Destination types.String `tfsdk:"destination"` + DestinationPort types.Int64 `tfsdk:"destination_port"` + ModifiedAt types.Int64 `tfsdk:"modified_at"` + PacketSize types.Int64 `tfsdk:"packet_size"` + ProbeArn types.String `tfsdk:"probe_arn"` + ProbeId types.String `tfsdk:"probe_id"` + Tags types.Map `tfsdk:"tags"` + Protocol types.String `tfsdk:"protocol"` + SourceArn types.String `tfsdk:"source_arn"` + State types.String `tfsdk:"state"` + VpcId types.String `tfsdk:"vpc_id"` +} + +func flattenProbeConfig(ctx context.Context, object networkmonitor.GetProbeOutput) (types.Object, diag.Diagnostics) { + + var diags diag.Diagnostics + + t := map[string]attr.Value{ + "address_family": flex.StringToFramework(ctx, (*string)(&object.AddressFamily)), + "created_at": flex.Int64ToFramework(ctx, aws.Int64(object.CreatedAt.Unix())), + "destination": flex.StringToFramework(ctx, object.Destination), + "destination_port": flex.Int64ToFramework(ctx, aws.Int64(int64(*object.DestinationPort))), + "modified_at": flex.Int64ToFramework(ctx, aws.Int64(object.ModifiedAt.Unix())), + "packet_size": flex.Int64ToFramework(ctx, aws.Int64(int64(*object.PacketSize))), + "probe_arn": flex.StringToFramework(ctx, object.ProbeArn), + "probe_id": flex.StringToFramework(ctx, object.ProbeId), + "protocol": flex.StringToFramework(ctx, (*string)(&object.Protocol)), + "source_arn": flex.StringToFramework(ctx, object.SourceArn), + "state": flex.StringToFramework(ctx, (*string)(&object.State)), + "tags": flex.FlattenFrameworkStringValueMap(ctx, object.Tags), + "vpc_id": flex.StringToFramework(ctx, object.VpcId), + } + objVal, d := types.ObjectValue(probeConfigTypes, t) + diags.Append(d...) + + return objVal, diags +} + +func expandProbeConfig(ctx context.Context, object probeConfigModel, diags diag.Diagnostics) awstypes.ProbeInput { + + return awstypes.ProbeInput{ + Destination: object.Destination.ValueStringPointer(), + DestinationPort: aws.Int32(int32(object.DestinationPort.ValueInt64())), + PacketSize: aws.Int32(int32(object.PacketSize.ValueInt64())), + Protocol: awstypes.Protocol(*aws.String(object.Protocol.ValueString())), + SourceArn: object.SourceArn.ValueStringPointer(), + Tags: flex.ExpandFrameworkStringValueMap(ctx, object.Tags), + } +} diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go new file mode 100644 index 00000000000..b906815c659 --- /dev/null +++ b/internal/service/networkmonitor/probe_test.go @@ -0,0 +1,239 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package networkmonitor_test + +import ( + "context" + "errors" + "fmt" + "testing" + + awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + tfnetworkmonitor "github.com/hashicorp/terraform-provider-aws/internal/service/networkmonitor" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccNetworkMonitorProbe_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmonitor_probe.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProbeDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProbeConfig_basic(rName, "10.0.0.1", 8080, 200), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProbeExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "probe.destination", "10.0.0.1"), + resource.TestCheckResourceAttr(resourceName, "probe.destination_port", "8080"), + resource.TestCheckResourceAttr(resourceName, "probe.packet_size", "200"), + resource.TestCheckResourceAttr(resourceName, "probe.protocol", "TCP"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.tag1", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNetworkMonitorProbe_updates(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmonitor_probe.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProbeDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProbeConfig_basic(rName, "10.0.0.1", 8080, 200), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProbeExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "probe.destination", "10.0.0.1"), + resource.TestCheckResourceAttr(resourceName, "probe.destination_port", "8080"), + resource.TestCheckResourceAttr(resourceName, "probe.packet_size", "200"), + resource.TestCheckResourceAttr(resourceName, "probe.protocol", "TCP"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.tag1", rName), + ), + }, + { + Config: testAccProbeConfig_2tags(rName, "10.0.0.2", 8081, 300), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProbeExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "probe.destination", "10.0.0.2"), + resource.TestCheckResourceAttr(resourceName, "probe.destination_port", "8081"), + resource.TestCheckResourceAttr(resourceName, "probe.packet_size", "300"), + resource.TestCheckResourceAttr(resourceName, "probe.protocol", "TCP"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.tag1", rName), + resource.TestCheckResourceAttr(resourceName, "tags.tag2", rName), + ), + }, + }, + }) +} + +func TestAccNetworkMonitorProbe_disappears(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmonitor_probe.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, "networkMonitor"), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProbeDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProbeConfig_basic(rName, "10.0.0.1", 8080, 200), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProbeExists(ctx, resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfnetworkmonitor.ResourceNetworkMonitorProbe, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckProbeDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkMonitorClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_networkmonitor_probe" { + continue + } + + _, err := tfnetworkmonitor.FindProbeByID(ctx, conn, rs.Primary.ID) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return nil + } + + if tfresource.NotFound(err) { + return nil + } + + return err + } + + return create.Error(names.NetworkMonitor, create.ErrActionCheckingDestroyed, tfnetworkmonitor.ResNameNetworkMonitorMonitor, rs.Primary.ID, errors.New("not destroyed")) + + } + + return nil + } +} + +func testAccCheckProbeExists(ctx context.Context, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorProbe, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorProbe, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkMonitorClient(ctx) + + _, err := tfnetworkmonitor.FindProbeByID(ctx, conn, rs.Primary.ID) + if err != nil { + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorProbe, rs.Primary.ID, err) + } + + return nil + } +} + +func testAccProbeConfig_base(rName string) string { + return fmt.Sprintf(` +data "aws_region" "current" {} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + vpc_id = aws_vpc.test.id + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 0) + + tags = { + Name = %[1]q + } +} + +resource "aws_networkmonitor_monitor" "test" { + aggregation_period = 30 + monitor_name = %[1]q + tags = { + tag1 = %[1]q + } +} +`, rName) +} + +func testAccProbeConfig_basic(rName, destination string, port, packetSize int) string { + return acctest.ConfigCompose(testAccProbeConfig_base(rName), fmt.Sprintf(` +resource "aws_networkmonitor_probe" "test" { + monitor_name = aws_networkmonitor_monitor.test.monitor_name + probe { + destination = %[2]q + destination_port = %[3]d + protocol = "TCP" + source_arn = aws_subnet.test.arn + packet_size = %[4]d + } + tags = { + tag1 = %[1]q + } +} +`, rName, destination, port, packetSize)) +} + +func testAccProbeConfig_2tags(rName, destination string, port, packetSize int) string { + return acctest.ConfigCompose(testAccProbeConfig_base(rName), fmt.Sprintf(` +resource "aws_networkmonitor_probe" "test" { + monitor_name = aws_networkmonitor_monitor.test.monitor_name + probe { + destination = %[2]q + destination_port = %[3]d + protocol = "TCP" + source_arn = aws_subnet.test.arn + packet_size = %[4]d + } + tags = { + tag1 = %[1]q + tag2 = %[1]q + } +} +`, rName, destination, port, packetSize)) +} diff --git a/internal/service/networkmonitor/service_endpoints_gen_test.go b/internal/service/networkmonitor/service_endpoints_gen_test.go new file mode 100644 index 00000000000..d4588becc7e --- /dev/null +++ b/internal/service/networkmonitor/service_endpoints_gen_test.go @@ -0,0 +1,496 @@ +// Code generated by internal/generate/serviceendpointtests/main.go; DO NOT EDIT. + +package networkmonitor_test + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + networkmonitor_sdkv2 "github.com/aws/aws-sdk-go-v2/service/networkmonitor" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/aws-sdk-go-base/v2/servicemocks" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + terraformsdk "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "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/provider" + "golang.org/x/exp/maps" +) + +type endpointTestCase struct { + with []setupFunc + expected caseExpectations +} + +type caseSetup struct { + config map[string]any + configFile configFile + environmentVariables map[string]string +} + +type configFile struct { + baseUrl string + serviceUrl string +} + +type caseExpectations struct { + diags diag.Diagnostics + endpoint string +} + +type setupFunc func(setup *caseSetup) + +type callFunc func(ctx context.Context, t *testing.T, meta *conns.AWSClient) string + +const ( + packageNameConfigEndpoint = "https://packagename-config.endpoint.test/" + awsServiceEnvvarEndpoint = "https://service-envvar.endpoint.test/" + baseEnvvarEndpoint = "https://base-envvar.endpoint.test/" + serviceConfigFileEndpoint = "https://service-configfile.endpoint.test/" + baseConfigFileEndpoint = "https://base-configfile.endpoint.test/" +) + +const ( + packageName = "networkmonitor" + awsEnvVar = "AWS_ENDPOINT_URL_NETWORKMONITOR" + baseEnvVar = "AWS_ENDPOINT_URL" + configParam = "networkmonitor" +) + +func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.Setenv + const region = "us-west-2" //lintignore:AWSAT003 + + testcases := map[string]endpointTestCase{ + "no config": { + with: []setupFunc{withNoConfig}, + expected: expectDefaultEndpoint(region), + }, + + // Package name endpoint on Config + + "package name endpoint config": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides aws service envvar": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withAwsEnvVar, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides base envvar": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withBaseEnvVar, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides service config file": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withServiceEndpointInConfigFile, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + "package name endpoint config overrides base config file": { + with: []setupFunc{ + withPackageNameEndpointInConfig, + withBaseEndpointInConfigFile, + }, + expected: expectPackageNameConfigEndpoint(), + }, + + // Service endpoint in AWS envvar + + "service aws envvar": { + with: []setupFunc{ + withAwsEnvVar, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + "service aws envvar overrides base envvar": { + with: []setupFunc{ + withAwsEnvVar, + withBaseEnvVar, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + "service aws envvar overrides service config file": { + with: []setupFunc{ + withAwsEnvVar, + withServiceEndpointInConfigFile, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + "service aws envvar overrides base config file": { + with: []setupFunc{ + withAwsEnvVar, + withBaseEndpointInConfigFile, + }, + expected: expectAwsEnvVarEndpoint(), + }, + + // Base endpoint in envvar + + "base endpoint envvar": { + with: []setupFunc{ + withBaseEnvVar, + }, + expected: expectBaseEnvVarEndpoint(), + }, + + "base endpoint envvar overrides service config file": { + with: []setupFunc{ + withBaseEnvVar, + withServiceEndpointInConfigFile, + }, + expected: expectBaseEnvVarEndpoint(), + }, + + "base endpoint envvar overrides base config file": { + with: []setupFunc{ + withBaseEnvVar, + withBaseEndpointInConfigFile, + }, + expected: expectBaseEnvVarEndpoint(), + }, + + // Service endpoint in config file + + "service config file": { + with: []setupFunc{ + withServiceEndpointInConfigFile, + }, + expected: expectServiceConfigFileEndpoint(), + }, + + "service config file overrides base config file": { + with: []setupFunc{ + withServiceEndpointInConfigFile, + withBaseEndpointInConfigFile, + }, + expected: expectServiceConfigFileEndpoint(), + }, + + // Base endpoint in config file + + "base endpoint config file": { + with: []setupFunc{ + withBaseEndpointInConfigFile, + }, + expected: expectBaseConfigFileEndpoint(), + }, + } + + for name, testcase := range testcases { //nolint:paralleltest // uses t.Setenv + testcase := testcase + + t.Run(name, func(t *testing.T) { + testEndpointCase(t, region, testcase, callService) + }) + } +} + +func defaultEndpoint(region string) string { + r := networkmonitor_sdkv2.NewDefaultEndpointResolverV2() + + ep, err := r.ResolveEndpoint(context.Background(), networkmonitor_sdkv2.EndpointParameters{ + Region: aws_sdkv2.String(region), + }) + if err != nil { + return err.Error() + } + + if ep.URI.Path == "" { + ep.URI.Path = "/" + } + + return ep.URI.String() +} + +func callService(ctx context.Context, t *testing.T, meta *conns.AWSClient) string { + t.Helper() + + var endpoint string + + client := meta.NetworkMonitorClient(ctx) + + _, err := client.ListMonitors(ctx, &networkmonitor_sdkv2.ListMonitorsInput{}, + func(opts *networkmonitor_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 +} + +func withNoConfig(_ *caseSetup) { + // no-op +} + +func withPackageNameEndpointInConfig(setup *caseSetup) { + if _, ok := setup.config["endpoints"]; !ok { + setup.config["endpoints"] = []any{ + map[string]any{}, + } + } + endpoints := setup.config["endpoints"].([]any)[0].(map[string]any) + endpoints[packageName] = packageNameConfigEndpoint +} + +func withAwsEnvVar(setup *caseSetup) { + setup.environmentVariables[awsEnvVar] = awsServiceEnvvarEndpoint +} + +func withBaseEnvVar(setup *caseSetup) { + setup.environmentVariables[baseEnvVar] = baseEnvvarEndpoint +} + +func withServiceEndpointInConfigFile(setup *caseSetup) { + setup.configFile.serviceUrl = serviceConfigFileEndpoint +} + +func withBaseEndpointInConfigFile(setup *caseSetup) { + setup.configFile.baseUrl = baseConfigFileEndpoint +} + +func expectDefaultEndpoint(region string) caseExpectations { + return caseExpectations{ + endpoint: defaultEndpoint(region), + } +} + +func expectPackageNameConfigEndpoint() caseExpectations { + return caseExpectations{ + endpoint: packageNameConfigEndpoint, + } +} + +func expectAwsEnvVarEndpoint() caseExpectations { + return caseExpectations{ + endpoint: awsServiceEnvvarEndpoint, + } +} + +func expectBaseEnvVarEndpoint() caseExpectations { + return caseExpectations{ + endpoint: baseEnvvarEndpoint, + } +} + +func expectServiceConfigFileEndpoint() caseExpectations { + return caseExpectations{ + endpoint: serviceConfigFileEndpoint, + } +} + +func expectBaseConfigFileEndpoint() caseExpectations { + return caseExpectations{ + endpoint: baseConfigFileEndpoint, + } +} + +func testEndpointCase(t *testing.T, region string, testcase endpointTestCase, callF callFunc) { + t.Helper() + + ctx := context.Background() + + setup := caseSetup{ + config: map[string]any{}, + environmentVariables: map[string]string{}, + } + + for _, f := range testcase.with { + f(&setup) + } + + config := map[string]any{ + "access_key": servicemocks.MockStaticAccessKey, + "secret_key": servicemocks.MockStaticSecretKey, + "region": region, + "skip_credentials_validation": true, + "skip_requesting_account_id": true, + } + + maps.Copy(config, setup.config) + + if setup.configFile.baseUrl != "" || setup.configFile.serviceUrl != "" { + config["profile"] = "default" + tempDir := t.TempDir() + writeSharedConfigFile(t, &config, tempDir, generateSharedConfigFile(setup.configFile)) + } + + for k, v := range setup.environmentVariables { + t.Setenv(k, v) + } + + p, err := provider.New(ctx) + if err != nil { + t.Fatal(err) + } + + expectedDiags := testcase.expected.diags + expectedDiags = append( + expectedDiags, + errs.NewWarningDiagnostic( + "AWS account ID not found for provider", + "See https://registry.terraform.io/providers/hashicorp/aws/latest/docs#skip_requesting_account_id for implications.", + ), + ) + + diags := p.Configure(ctx, terraformsdk.NewResourceConfigRaw(config)) + + if diff := cmp.Diff(diags, expectedDiags, cmp.Comparer(sdkdiag.Comparer)); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + + if diags.HasError() { + return + } + + meta := p.Meta().(*conns.AWSClient) + + endpoint := callF(ctx, t, meta) + + if endpoint != testcase.expected.endpoint { + t.Errorf("expected endpoint %q, got %q", testcase.expected.endpoint, endpoint) + } +} + +func addRetrieveEndpointURLMiddleware(t *testing.T, endpoint *string) func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Finalize.Add( + retrieveEndpointURLMiddleware(t, endpoint), + middleware.After, + ) + } +} + +func retrieveEndpointURLMiddleware(t *testing.T, endpoint *string) middleware.FinalizeMiddleware { + return middleware.FinalizeMiddlewareFunc( + "Test: Retrieve Endpoint", + func(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) { + t.Helper() + + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + t.Fatalf("Expected *github.com/aws/smithy-go/transport/http.Request, got %s", fullTypeName(in.Request)) + } + + url := request.URL + url.RawQuery = "" + url.Path = "/" + + *endpoint = url.String() + + return next.HandleFinalize(ctx, in) + }) +} + +var errCancelOperation = fmt.Errorf("Test: Cancelling request") + +func addCancelRequestMiddleware() func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Finalize.Add( + cancelRequestMiddleware(), + middleware.After, + ) + } +} + +// cancelRequestMiddleware creates a Smithy middleware that intercepts the request before sending and cancels it +func cancelRequestMiddleware() middleware.FinalizeMiddleware { + return middleware.FinalizeMiddlewareFunc( + "Test: Cancel Requests", + func(_ context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) { + return middleware.FinalizeOutput{}, middleware.Metadata{}, errCancelOperation + }) +} + +func fullTypeName(i interface{}) string { + return fullValueTypeName(reflect.ValueOf(i)) +} + +func fullValueTypeName(v reflect.Value) string { + if v.Kind() == reflect.Ptr { + return "*" + fullValueTypeName(reflect.Indirect(v)) + } + + requestType := v.Type() + return fmt.Sprintf("%s.%s", requestType.PkgPath(), requestType.Name()) +} + +func generateSharedConfigFile(config configFile) string { + var buf strings.Builder + + buf.WriteString(` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey +`) + if config.baseUrl != "" { + buf.WriteString(fmt.Sprintf("endpoint_url = %s\n", config.baseUrl)) + } + + if config.serviceUrl != "" { + buf.WriteString(fmt.Sprintf(` +services = endpoint-test + +[services endpoint-test] +%[1]s = + endpoint_url = %[2]s +`, configParam, serviceConfigFileEndpoint)) + } + + return buf.String() +} + +func writeSharedConfigFile(t *testing.T, config *map[string]any, tempDir, content string) string { + t.Helper() + + file, err := os.Create(filepath.Join(tempDir, "aws-sdk-go-base-shared-configuration-file")) + if err != nil { + t.Fatalf("creating shared configuration file: %s", err) + } + + _, err = file.WriteString(content) + if err != nil { + t.Fatalf(" writing shared configuration file: %s", err) + } + + if v, ok := (*config)["shared_config_files"]; !ok { + (*config)["shared_config_files"] = []any{file.Name()} + } else { + (*config)["shared_config_files"] = append(v.([]any), file.Name()) + } + + return file.Name() +} diff --git a/internal/service/networkmonitor/service_package_gen.go b/internal/service/networkmonitor/service_package_gen.go new file mode 100644 index 00000000000..6004dca67a6 --- /dev/null +++ b/internal/service/networkmonitor/service_package_gen.go @@ -0,0 +1,65 @@ +// Code generated by internal/generate/servicepackages/main.go; DO NOT EDIT. + +package networkmonitor + +import ( + "context" + + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + networkmonitor_sdkv2 "github.com/aws/aws-sdk-go-v2/service/networkmonitor" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/types" + "github.com/hashicorp/terraform-provider-aws/names" +) + +type servicePackage struct{} + +func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.ServicePackageFrameworkDataSource { + return []*types.ServicePackageFrameworkDataSource{} +} + +func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { + return []*types.ServicePackageFrameworkResource{ + { + Factory: newResourceNetworkMonitorMonitor, + Name: "CloudWatch Network Monitor Monitor", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "arn", + }, + }, + { + Factory: newResourceNetworkMonitorProbe, + Name: "CloudWatch Network Monitor Probe", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "arn", + }, + }, + } +} + +func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { + return []*types.ServicePackageSDKDataSource{} +} + +func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource { + return []*types.ServicePackageSDKResource{} +} + +func (p *servicePackage) ServicePackageName() string { + return names.NetworkMonitor +} + +// 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) (*networkmonitor_sdkv2.Client, error) { + cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) + + return networkmonitor_sdkv2.NewFromConfig(cfg, func(o *networkmonitor_sdkv2.Options) { + if endpoint := config["endpoint"].(string); endpoint != "" { + o.BaseEndpoint = aws_sdkv2.String(endpoint) + } + }), nil +} + +func ServicePackage(ctx context.Context) conns.ServicePackage { + return &servicePackage{} +} diff --git a/internal/service/networkmonitor/tags_gen.go b/internal/service/networkmonitor/tags_gen.go new file mode 100644 index 00000000000..40ab57a4ca8 --- /dev/null +++ b/internal/service/networkmonitor/tags_gen.go @@ -0,0 +1,128 @@ +// Code generated by internal/generate/tags/main.go; DO NOT EDIT. +package networkmonitor + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/networkmonitor" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/logging" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/types/option" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// listTags lists networkmonitor 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 *networkmonitor.Client, identifier string, optFns ...func(*networkmonitor.Options)) (tftags.KeyValueTags, error) { + input := &networkmonitor.ListTagsForResourceInput{ + ResourceArn: aws.String(identifier), + } + + output, err := conn.ListTagsForResource(ctx, input, optFns...) + + if err != nil { + return tftags.New(ctx, nil), err + } + + return KeyValueTags(ctx, output.Tags), nil +} + +// ListTags lists networkmonitor 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).NetworkMonitorClient(ctx), identifier) + + if err != nil { + return err + } + + if inContext, ok := tftags.FromContext(ctx); ok { + inContext.TagsOut = option.Some(tags) + } + + return nil +} + +// map[string]string handling + +// Tags returns networkmonitor service tags. +func Tags(tags tftags.KeyValueTags) map[string]string { + return tags.Map() +} + +// KeyValueTags creates tftags.KeyValueTags from networkmonitor service tags. +func KeyValueTags(ctx context.Context, tags map[string]string) tftags.KeyValueTags { + return tftags.New(ctx, tags) +} + +// getTagsIn returns networkmonitor service tags from Context. +// nil is returned if there are no input tags. +func getTagsIn(ctx context.Context) map[string]string { + if inContext, ok := tftags.FromContext(ctx); ok { + if tags := Tags(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { + return tags + } + } + + return nil +} + +// setTagsOut sets networkmonitor service tags in Context. +func setTagsOut(ctx context.Context, tags map[string]string) { + if inContext, ok := tftags.FromContext(ctx); ok { + inContext.TagsOut = option.Some(KeyValueTags(ctx, tags)) + } +} + +// updateTags updates networkmonitor 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 *networkmonitor.Client, identifier string, oldTagsMap, newTagsMap any, optFns ...func(*networkmonitor.Options)) error { + oldTags := tftags.New(ctx, oldTagsMap) + newTags := tftags.New(ctx, newTagsMap) + + ctx = tflog.SetField(ctx, logging.KeyResourceId, identifier) + + removedTags := oldTags.Removed(newTags) + removedTags = removedTags.IgnoreSystem(names.NetworkMonitor) + if len(removedTags) > 0 { + input := &networkmonitor.UntagResourceInput{ + ResourceArn: aws.String(identifier), + TagKeys: removedTags.Keys(), + } + + _, err := conn.UntagResource(ctx, input, optFns...) + + if err != nil { + return fmt.Errorf("untagging resource (%s): %w", identifier, err) + } + } + + updatedTags := oldTags.Updated(newTags) + updatedTags = updatedTags.IgnoreSystem(names.NetworkMonitor) + if len(updatedTags) > 0 { + input := &networkmonitor.TagResourceInput{ + ResourceArn: aws.String(identifier), + Tags: Tags(updatedTags), + } + + _, err := conn.TagResource(ctx, input, optFns...) + + if err != nil { + return fmt.Errorf("tagging resource (%s): %w", identifier, err) + } + } + + return nil +} + +// UpdateTags updates networkmonitor 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).NetworkMonitorClient(ctx), identifier, oldTags, newTags) +} diff --git a/internal/sweep/service_packages_gen_test.go b/internal/sweep/service_packages_gen_test.go index 6ac504d6ac6..5a7064098a6 100644 --- a/internal/sweep/service_packages_gen_test.go +++ b/internal/sweep/service_packages_gen_test.go @@ -154,6 +154,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/service/neptune" "github.com/hashicorp/terraform-provider-aws/internal/service/networkfirewall" "github.com/hashicorp/terraform-provider-aws/internal/service/networkmanager" + "github.com/hashicorp/terraform-provider-aws/internal/service/networkmonitor" "github.com/hashicorp/terraform-provider-aws/internal/service/oam" "github.com/hashicorp/terraform-provider-aws/internal/service/opensearch" "github.com/hashicorp/terraform-provider-aws/internal/service/opensearchserverless" @@ -383,6 +384,7 @@ func servicePackages(ctx context.Context) []conns.ServicePackage { neptune.ServicePackage(ctx), networkfirewall.ServicePackage(ctx), networkmanager.ServicePackage(ctx), + networkmonitor.ServicePackage(ctx), oam.ServicePackage(ctx), opensearch.ServicePackage(ctx), opensearchserverless.ServicePackage(ctx), diff --git a/names/consts_gen.go b/names/consts_gen.go index b69abddf038..c80d04c4028 100644 --- a/names/consts_gen.go +++ b/names/consts_gen.go @@ -149,6 +149,7 @@ const ( Neptune = "neptune" NetworkFirewall = "networkfirewall" NetworkManager = "networkmanager" + NetworkMonitor = "networkmonitor" ObservabilityAccessManager = "oam" OpenSearch = "opensearch" OpenSearchIngestion = "osis" diff --git a/names/data/names_data.csv b/names/data/names_data.csv index 58b072c2937..0a45f337407 100644 --- a/names/data/names_data.csv +++ b/names/data/names_data.csv @@ -387,3 +387,4 @@ xray,xray,xray,xray,,xray,,,XRay,XRay,,,2,,aws_xray_,,xray_,X-Ray,AWS,,,,,,,XRay verifiedpermissions,verifiedpermissions,verifiedpermissions,verifiedpermissions,,verifiedpermissions,,,VerifiedPermissions,VerifiedPermissions,,,2,,aws_verifiedpermissions_,,verifiedpermissions_,Verified Permissions,Amazon,,,,,,,VerifiedPermissions,ListPolicyStores,, codecatalyst,codecatalyst,codecatalyst,codecatalyst,,codecatalyst,,,CodeCatalyst,CodeCatalyst,,,2,,aws_codecatalyst_,,codecatalyst_,CodeCatalyst,Amazon,,,,,,,CodeCatalyst,ListAccessTokens,, mediapackagev2,mediapackagev2,mediapackagev2,mediapackagev2,,mediapackagev2,,,MediaPackageV2,MediaPackageV2,,,2,aws_media_packagev2_,aws_mediapackagev2_,,media_packagev2_,Elemental MediaPackage Version 2,AWS,,,,,,,MediaPackageV2,ListChannelGroups,, +networkmonitor,networkmonitor,networkmonitor,networkmonitor,,networkmonitor,,,NetworkMonitor,NetworkMonitor,,,2,,aws_networkmonitor_,,networkmonitor_,CloudWatch Network Monitor,Amazon,,,,,,,NetworkMonitor,ListMonitors,, \ No newline at end of file diff --git a/website/allowed-subcategories.txt b/website/allowed-subcategories.txt index 22d1635cd62..56c6316b9f1 100644 --- a/website/allowed-subcategories.txt +++ b/website/allowed-subcategories.txt @@ -41,6 +41,7 @@ CloudWatch Application Insights CloudWatch Evidently CloudWatch Internet Monitor CloudWatch Logs +CloudWatch Network Monitor CloudWatch Observability Access Manager CloudWatch RUM CloudWatch Synthetics diff --git a/website/docs/guides/custom-service-endpoints.html.markdown b/website/docs/guides/custom-service-endpoints.html.markdown index 0537780dbdf..a60a8c36c77 100644 --- a/website/docs/guides/custom-service-endpoints.html.markdown +++ b/website/docs/guides/custom-service-endpoints.html.markdown @@ -217,6 +217,7 @@ provider "aws" {
  • neptune
  • networkfirewall
  • networkmanager
  • +
  • networkmonitor
  • oam (or cloudwatchobservabilityaccessmanager)
  • opensearch (or opensearchservice)
  • opensearchserverless
  • diff --git a/website/docs/r/networkmonitor_monitor.html.markdown b/website/docs/r/networkmonitor_monitor.html.markdown new file mode 100644 index 00000000000..c3b411249ca --- /dev/null +++ b/website/docs/r/networkmonitor_monitor.html.markdown @@ -0,0 +1,66 @@ +--- +subcategory: "CloudWatch Network Monitor" +layout: "aws" +page_title: "AWS: aws_networkmonitor_monitor" +description: |- + Terraform resource for managing an Amazon Network Monitor Monitor. +--- + +# Resource: aws_networkmonitor_monitor + +Terraform resource for managing an AWS Network Monitor Monitor. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_networkmonitor_monitor" "example" { + subnet_arns = [aws_subnet.example.arn] + core_network_id = awscc_networkmanager_core_network.example.id + vpc_arn = aws_vpc.example.arn +} + +resource "aws_networkmonitor_monitor" "example" { + aggregation_period = 30 + monitor_name = "example +} +``` + +## Argument Reference + +The following arguments are required: + +- `monitor_name` - (Required) The name of the monitor. + +The following arguments are optional: + +- `aggregation_period` - (Optional) The time, in seconds, that metrics are aggregated and sent to Amazon CloudWatch. Valid values are either 30 or 60. +- `tags` - (Optional) Key-value tags for the monitor. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +- `arn` - The ARN of the attachment. +- `monitor_name` - The name of the monitor. +- `aggregation_period` - The time, in seconds, that metrics are aggregated and sent to Amazon CloudWatch. +- `state` - The state of the monitor. +- `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import `aws_networkmonitor_monitor` using the monitor name. For example: + +```terraform +import { + to = aws_networkmonitor_monitor.example + monitor_name = "example_monitor" +} +``` + +Using `terraform import`, import `aws_networkmonitor_monitor` using the monitor name. For example: + +```console +% terraform import aws_networkmonitor_monitor.example example_monitor +``` diff --git a/website/docs/r/networkmonitor_probe.html.markdown b/website/docs/r/networkmonitor_probe.html.markdown new file mode 100644 index 00000000000..54030d6a4e7 --- /dev/null +++ b/website/docs/r/networkmonitor_probe.html.markdown @@ -0,0 +1,84 @@ +--- +subcategory: "CloudWatch Network Monitor" +layout: "aws" +page_title: "AWS: aws_networkmonitor_probe" +description: |- + Terraform resource for managing an Amazon Network Monitor Probe. +--- + +# Resource: aws_networkmonitor_probe + +Terraform resource for managing an AWS Network Monitor Probe. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_networkmonitor_monitor" "example" { + aggregation_period = 30 + monitor_name = "example +} + +resource "aws_networkmonitor_probe" "test" { + monitor_name = aws_networkmonitor_monitor.example.monitor_name + probe { + destination = 127.0.0.1 + destination_port = 80 + protocol = "TCP" + source_arn = aws_subnet.example.arn + packet_size = 200 + } +} +``` + +## Argument Reference + +The following arguments are required: + +- `monitor_name` - (Required) The name of the monitor. +- `probe` - (Required) Describes the details of an individual probe for a monitor. + +The `probe` object supports the following: + +- `destination` - (Required) The destination IP address. This must be either IPV4 or IPV6. +- `destination_port` - (Optional) The port associated with the destination. This is required only if the protocol is TCP and must be a number between 1 and 65536 +- `protocol` - (Required) The protocol used for the network traffic between the source and destination. This must be either TCP or ICMP. +- `source_arn` - (Required) The ARN of the subnet. +- `packet_size` - (Optional) The size of the packets sent between the source and destination. This must be a number between 56 and 8500 + +The following arguments are optional: + +- `tags` - (Optional) Key-value tags for the monitor. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +- `arn` - The ARN of the attachment. +- `monitor_name` - The name of the monitor. +- `probe` - Describes the details of an individual probe for a monitor. +- `destination` - The destination IP address. +- `destination_port` - The port associated with the destination. +- `protocol` - The protocol used for the network traffic between the source and destination. +- `source_arn` - The ARN of the subnet. +- `packet_size` - The size of the packets sent between the source and destination. +- `state` - The state of the monitor. +- `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import `aws_networkmonitor_probe` using the monitor name. For example: + +```terraform +import { + to = aws_networkmonitor_monitor.example + monitor_name = "example_monitor" +} +``` + +Using `terraform import`, import `aws_networkmonitor_probe` using the monitor name and probe id. For example: + +```console +% terraform import aws_networkmonitor_probe.example example_monitor +``` From b3cc557e550216136ecc25d341091d6c28b2ed71 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 9 Feb 2024 10:49:07 +0200 Subject: [PATCH 02/20] Linting fix --- internal/service/networkmonitor/monitor.go | 21 ++++---------- internal/service/networkmonitor/probe.go | 28 ++++++------------- internal/service/networkmonitor/probe_test.go | 1 - 3 files changed, 15 insertions(+), 35 deletions(-) diff --git a/internal/service/networkmonitor/monitor.go b/internal/service/networkmonitor/monitor.go index 52dbd892442..858ec504270 100644 --- a/internal/service/networkmonitor/monitor.go +++ b/internal/service/networkmonitor/monitor.go @@ -3,9 +3,9 @@ package networkmonitor import ( "context" "errors" - "regexp" "time" + "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/networkmonitor" awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -73,7 +74,7 @@ func (r *resourceNetworkMonitorMonitor) Schema(ctx context.Context, request reso "monitor_name": schema.StringAttribute{ Required: true, Validators: []validator.String{ - stringvalidator.RegexMatches(regexp.MustCompile("[a-zA-Z0-9_-]+"), "Must match [a-zA-Z0-9_-]+"), + stringvalidator.RegexMatches(regexache.MustCompile("[a-zA-Z0-9_-]+"), "Must match [a-zA-Z0-9_-]+"), stringvalidator.LengthBetween(1, 255), }, }, @@ -257,7 +258,6 @@ func (r *resourceNetworkMonitorMonitor) Delete(ctx context.Context, req resource ) return } - } func statusMonitor(ctx context.Context, conn *networkmonitor.Client, id string) retry.StateRefreshFunc { @@ -283,13 +283,8 @@ func statusMonitor(ctx context.Context, conn *networkmonitor.Client, id string) func waitMonitorReady(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetMonitorOutput, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{ - string(awstypes.MonitorStatePending), - }, - Target: []string{ - string(awstypes.MonitorStateActive), - string(awstypes.MonitorStateInactive), - }, + Pending: enum.Slice(awstypes.MonitorStatePending), + Target: enum.Slice(awstypes.MonitorStateActive, awstypes.MonitorStateInactive), Refresh: statusMonitor(ctx, conn, id), Timeout: MonitorTimeout, MinTimeout: 10 * time.Second, @@ -305,11 +300,7 @@ func waitMonitorReady(ctx context.Context, conn *networkmonitor.Client, id strin func waitMonitorDeleted(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetMonitorOutput, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{ - string(awstypes.MonitorStateDeleting), - string(awstypes.MonitorStateActive), - string(awstypes.MonitorStateInactive), - }, + Pending: enum.Slice(awstypes.MonitorStateDeleting, awstypes.MonitorStateActive, awstypes.MonitorStateInactive), Target: []string{}, Refresh: statusMonitor(ctx, conn, id), Timeout: MonitorTimeout, diff --git a/internal/service/networkmonitor/probe.go b/internal/service/networkmonitor/probe.go index 0a472043503..6ebe4c00e03 100644 --- a/internal/service/networkmonitor/probe.go +++ b/internal/service/networkmonitor/probe.go @@ -4,10 +4,10 @@ import ( "context" "errors" "fmt" - "regexp" "strings" "time" + "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/networkmonitor" awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -63,7 +64,7 @@ func (r *resourceNetworkMonitorProbe) Schema(ctx context.Context, request resour stringplanmodifier.RequiresReplace(), }, Validators: []validator.String{ - stringvalidator.RegexMatches(regexp.MustCompile("[a-zA-Z0-9_-]+"), "Must match [a-zA-Z0-9_-]+"), + stringvalidator.RegexMatches(regexache.MustCompile("[a-zA-Z0-9_-]+"), "Must match [a-zA-Z0-9_-]+"), stringvalidator.LengthBetween(1, 255), }, }, @@ -113,7 +114,7 @@ func (r *resourceNetworkMonitorProbe) Schema(ctx context.Context, request resour Required: true, Validators: []validator.String{ stringvalidator.LengthBetween(20, 2048), - stringvalidator.RegexMatches(regexp.MustCompile("arn:.*"), "Must match pattern arn:*"), + stringvalidator.RegexMatches(regexache.MustCompile("arn:.*"), "Must match pattern arn:*"), }, }, "tags": schema.MapAttribute{ @@ -148,7 +149,7 @@ func (r *resourceNetworkMonitorProbe) Create(ctx context.Context, req resource.C &probe, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})...) - probeConfig := expandProbeConfig(ctx, probe, resp.Diagnostics) + probeConfig := expandProbeConfig(ctx, probe) if resp.Diagnostics.HasError() { return } @@ -386,13 +387,8 @@ func statusProbe(ctx context.Context, conn *networkmonitor.Client, id string) re func waitProbeReady(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetProbeOutput, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{ - string(awstypes.ProbeStatePending), - }, - Target: []string{ - string(awstypes.ProbeStateActive), - string(awstypes.ProbeStateInactive), - }, + Pending: enum.Slice(awstypes.ProbeStatePending), + Target: enum.Slice(awstypes.ProbeStateActive, awstypes.ProbeStateInactive), Refresh: statusProbe(ctx, conn, id), Timeout: ProbeTimeout, MinTimeout: 10 * time.Second, @@ -408,11 +404,7 @@ func waitProbeReady(ctx context.Context, conn *networkmonitor.Client, id string) func waitProbeDeleted(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetProbeOutput, error) { stateConf := &retry.StateChangeConf{ - Pending: []string{ - string(awstypes.ProbeStateActive), - string(awstypes.ProbeStateInactive), - string(awstypes.ProbeStateDeleting), - }, + Pending: enum.Slice(awstypes.ProbeStateActive, awstypes.ProbeStateInactive, awstypes.ProbeStateInactive), Target: []string{}, Refresh: statusProbe(ctx, conn, id), Timeout: ProbeTimeout, @@ -502,7 +494,6 @@ type probeConfigModel struct { } func flattenProbeConfig(ctx context.Context, object networkmonitor.GetProbeOutput) (types.Object, diag.Diagnostics) { - var diags diag.Diagnostics t := map[string]attr.Value{ @@ -526,8 +517,7 @@ func flattenProbeConfig(ctx context.Context, object networkmonitor.GetProbeOutpu return objVal, diags } -func expandProbeConfig(ctx context.Context, object probeConfigModel, diags diag.Diagnostics) awstypes.ProbeInput { - +func expandProbeConfig(ctx context.Context, object probeConfigModel) awstypes.ProbeInput { return awstypes.ProbeInput{ Destination: object.Destination.ValueStringPointer(), DestinationPort: aws.Int32(int32(object.DestinationPort.ValueInt64())), diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go index b906815c659..db2bd68c4d8 100644 --- a/internal/service/networkmonitor/probe_test.go +++ b/internal/service/networkmonitor/probe_test.go @@ -141,7 +141,6 @@ func testAccCheckProbeDestroy(ctx context.Context) resource.TestCheckFunc { } return create.Error(names.NetworkMonitor, create.ErrActionCheckingDestroyed, tfnetworkmonitor.ResNameNetworkMonitorMonitor, rs.Primary.ID, errors.New("not destroyed")) - } return nil From 655797237c7a5c27a1ec3c9710f784b951a6b316 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 9 Feb 2024 12:48:53 +0200 Subject: [PATCH 03/20] Linting fixes --- internal/service/networkmonitor/monitor.go | 1 - .../service/networkmonitor/monitor_test.go | 10 +-- internal/service/networkmonitor/probe.go | 13 ++-- internal/service/networkmonitor/probe_test.go | 70 +++++++++---------- 4 files changed, 46 insertions(+), 48 deletions(-) diff --git a/internal/service/networkmonitor/monitor.go b/internal/service/networkmonitor/monitor.go index 858ec504270..6f301be3551 100644 --- a/internal/service/networkmonitor/monitor.go +++ b/internal/service/networkmonitor/monitor.go @@ -20,7 +20,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/framework" diff --git a/internal/service/networkmonitor/monitor_test.go b/internal/service/networkmonitor/monitor_test.go index 2668d70b07c..1f49373580c 100644 --- a/internal/service/networkmonitor/monitor_test.go +++ b/internal/service/networkmonitor/monitor_test.go @@ -162,9 +162,9 @@ func testAccMonitorConfig(rName string, aggregation int) string { return fmt.Sprintf(` resource "aws_networkmonitor_monitor" "test" { aggregation_period = %[2]d - monitor_name = %[1]q + monitor_name = %[1]q tags = { - tag1 = %[1]q + tag1 = %[1]q } } `, rName, aggregation) @@ -174,10 +174,10 @@ func testAccMonitorConfig_tags(rName string, aggregation int) string { return fmt.Sprintf(` resource "aws_networkmonitor_monitor" "test" { aggregation_period = %[2]d - monitor_name = %[1]q + monitor_name = %[1]q tags = { - tag1 = %[1]q - tag2 = %[1]q + tag1 = %[1]q + tag2 = %[1]q } } `, rName, aggregation) diff --git a/internal/service/networkmonitor/probe.go b/internal/service/networkmonitor/probe.go index 6ebe4c00e03..cea308a6edf 100644 --- a/internal/service/networkmonitor/probe.go +++ b/internal/service/networkmonitor/probe.go @@ -13,13 +13,6 @@ import ( awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/enum" - "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" - tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" @@ -30,7 +23,13 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go index db2bd68c4d8..792a2a33999 100644 --- a/internal/service/networkmonitor/probe_test.go +++ b/internal/service/networkmonitor/probe_test.go @@ -174,27 +174,27 @@ func testAccProbeConfig_base(rName string) string { data "aws_region" "current" {} resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" + cidr_block = "10.0.0.0/16" - tags = { - Name = %[1]q - } + tags = { + Name = %[1]q + } } - + resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 0) + vpc_id = aws_vpc.test.id + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 0) - tags = { - Name = %[1]q - } + tags = { + Name = %[1]q + } } resource "aws_networkmonitor_monitor" "test" { aggregation_period = 30 - monitor_name = %[1]q + monitor_name = %[1]q tags = { - tag1 = %[1]q + tag1 = %[1]q } } `, rName) @@ -203,17 +203,17 @@ resource "aws_networkmonitor_monitor" "test" { func testAccProbeConfig_basic(rName, destination string, port, packetSize int) string { return acctest.ConfigCompose(testAccProbeConfig_base(rName), fmt.Sprintf(` resource "aws_networkmonitor_probe" "test" { - monitor_name = aws_networkmonitor_monitor.test.monitor_name - probe { - destination = %[2]q - destination_port = %[3]d - protocol = "TCP" - source_arn = aws_subnet.test.arn - packet_size = %[4]d - } - tags = { - tag1 = %[1]q - } + monitor_name = aws_networkmonitor_monitor.test.monitor_name + probe { + destination = %[2]q + destination_port = %[3]d + protocol = "TCP" + source_arn = aws_subnet.test.arn + packet_size = %[4]d + } + tags = { + tag1 = %[1]q + } } `, rName, destination, port, packetSize)) } @@ -221,18 +221,18 @@ resource "aws_networkmonitor_probe" "test" { func testAccProbeConfig_2tags(rName, destination string, port, packetSize int) string { return acctest.ConfigCompose(testAccProbeConfig_base(rName), fmt.Sprintf(` resource "aws_networkmonitor_probe" "test" { - monitor_name = aws_networkmonitor_monitor.test.monitor_name - probe { - destination = %[2]q - destination_port = %[3]d - protocol = "TCP" - source_arn = aws_subnet.test.arn - packet_size = %[4]d - } - tags = { - tag1 = %[1]q - tag2 = %[1]q - } + monitor_name = aws_networkmonitor_monitor.test.monitor_name + probe { + destination = %[2]q + destination_port = %[3]d + protocol = "TCP" + source_arn = aws_subnet.test.arn + packet_size = %[4]d + } + tags = { + tag1 = %[1]q + tag2 = %[1]q + } } `, rName, destination, port, packetSize)) } From 5981471cd7d6bac7d1af23763e9fdb08352ee9d8 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Fri, 9 Feb 2024 14:53:49 +0200 Subject: [PATCH 04/20] Fix import documentation --- website/docs/r/networkmonitor_monitor.html.markdown | 4 ++-- website/docs/r/networkmonitor_probe.html.markdown | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/website/docs/r/networkmonitor_monitor.html.markdown b/website/docs/r/networkmonitor_monitor.html.markdown index c3b411249ca..ff9eb446f16 100644 --- a/website/docs/r/networkmonitor_monitor.html.markdown +++ b/website/docs/r/networkmonitor_monitor.html.markdown @@ -55,12 +55,12 @@ In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashico ```terraform import { to = aws_networkmonitor_monitor.example - monitor_name = "example_monitor" + id = "monitor-7786087912324693644" } ``` Using `terraform import`, import `aws_networkmonitor_monitor` using the monitor name. For example: ```console -% terraform import aws_networkmonitor_monitor.example example_monitor +% terraform import aws_networkmonitor_monitor.example monitor-7786087912324693644 ``` diff --git a/website/docs/r/networkmonitor_probe.html.markdown b/website/docs/r/networkmonitor_probe.html.markdown index 54030d6a4e7..cfdfcaece32 100644 --- a/website/docs/r/networkmonitor_probe.html.markdown +++ b/website/docs/r/networkmonitor_probe.html.markdown @@ -68,17 +68,17 @@ This resource exports the following attributes in addition to the arguments abov ## Import -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import `aws_networkmonitor_probe` using the monitor name. For example: +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import `aws_networkmonitor_probe` using the monitor name and probe id. For example: ```terraform import { - to = aws_networkmonitor_monitor.example - monitor_name = "example_monitor" + to = aws_networkmonitor_probe.example + id = "probe-3qm8p693i4fi1h8lqylzkbp42e:monitor-7786087912324693644" } ``` Using `terraform import`, import `aws_networkmonitor_probe` using the monitor name and probe id. For example: ```console -% terraform import aws_networkmonitor_probe.example example_monitor +% terraform import aws_networkmonitor_probe.example probe-3qm8p693i4fi1h8lqylzkbp42e:monitor-7786087912324693644 ``` From 7252e03e07ae59ccc4645ac84c9aa3f88052acbc Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Sat, 10 Feb 2024 14:36:19 +0200 Subject: [PATCH 05/20] Add license information --- internal/service/networkmonitor/monitor.go | 3 +++ internal/service/networkmonitor/probe.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/internal/service/networkmonitor/monitor.go b/internal/service/networkmonitor/monitor.go index 6f301be3551..ba144787bf4 100644 --- a/internal/service/networkmonitor/monitor.go +++ b/internal/service/networkmonitor/monitor.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package networkmonitor import ( diff --git a/internal/service/networkmonitor/probe.go b/internal/service/networkmonitor/probe.go index cea308a6edf..def91396958 100644 --- a/internal/service/networkmonitor/probe.go +++ b/internal/service/networkmonitor/probe.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package networkmonitor import ( From 9b087f7542e19137a4ea31b535eddefe643c7423 Mon Sep 17 00:00:00 2001 From: Jose Juhala Date: Tue, 20 Feb 2024 11:15:36 +0200 Subject: [PATCH 06/20] Fix semgrep --- .../service/networkmonitor/exports_tests.go | 4 +-- internal/service/networkmonitor/monitor.go | 22 +++++++------- .../service/networkmonitor/monitor_test.go | 10 +++---- internal/service/networkmonitor/probe.go | 30 +++++++++---------- internal/service/networkmonitor/probe_test.go | 10 +++---- .../networkmonitor/service_package_gen.go | 4 +-- 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/internal/service/networkmonitor/exports_tests.go b/internal/service/networkmonitor/exports_tests.go index ba01ef3abc6..5d6435c06d7 100644 --- a/internal/service/networkmonitor/exports_tests.go +++ b/internal/service/networkmonitor/exports_tests.go @@ -5,6 +5,6 @@ package networkmonitor // Exports for use in tests only. var ( - ResourceNetworkMonitorMonitor = newResourceNetworkMonitorMonitor - ResourceNetworkMonitorProbe = newResourceNetworkMonitorProbe + ResourceMonitor = newResourceMonitor + ResourceProbe = newResourceProbe ) diff --git a/internal/service/networkmonitor/monitor.go b/internal/service/networkmonitor/monitor.go index ba144787bf4..8c68e638417 100644 --- a/internal/service/networkmonitor/monitor.go +++ b/internal/service/networkmonitor/monitor.go @@ -33,13 +33,13 @@ import ( ) const ( - MonitorTimeout = time.Minute * 10 - ResNameNetworkMonitorMonitor = "CloudWatch Network Monitor Monitor" + MonitorTimeout = time.Minute * 10 + ResNameMonitor = "CloudWatch Network Monitor Monitor" ) // @FrameworkResource(name="CloudWatch Network Monitor Monitor") // @Tags(identifierAttribute="arn") -func newResourceNetworkMonitorMonitor(context.Context) (resource.ResourceWithConfigure, error) { +func newResourceMonitor(context.Context) (resource.ResourceWithConfigure, error) { return &resourceNetworkMonitorMonitor{}, nil } @@ -111,7 +111,7 @@ func (r *resourceNetworkMonitorMonitor) Create(ctx context.Context, req resource _, err := conn.CreateMonitor(ctx, &input) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameNetworkMonitorMonitor, plan.MonitorName.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameMonitor, plan.MonitorName.String(), nil), err.Error(), ) return @@ -120,7 +120,7 @@ func (r *resourceNetworkMonitorMonitor) Create(ctx context.Context, req resource out, err := waitMonitorReady(ctx, conn, plan.MonitorName.ValueString()) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForCreation, ResNameNetworkMonitorMonitor, plan.MonitorName.ValueString(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForCreation, ResNameMonitor, plan.MonitorName.ValueString(), nil), err.Error(), ) } @@ -159,7 +159,7 @@ func (r *resourceNetworkMonitorMonitor) Read(ctx context.Context, req resource.R } resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameNetworkMonitorMonitor, state.ID.String(), err), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameMonitor, state.ID.String(), err), err.Error(), ) return @@ -199,7 +199,7 @@ func (r *resourceNetworkMonitorMonitor) Update(ctx context.Context, req resource _, err := conn.UpdateMonitor(ctx, &input) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameNetworkMonitorMonitor, state.ID.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameMonitor, state.ID.String(), nil), err.Error(), ) return @@ -208,13 +208,13 @@ func (r *resourceNetworkMonitorMonitor) Update(ctx context.Context, req resource _, err = waitMonitorReady(ctx, conn, plan.MonitorName.ValueString()) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameNetworkMonitorMonitor, plan.MonitorName.ValueString(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameMonitor, plan.MonitorName.ValueString(), nil), err.Error(), ) } if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForUpdate, ResNameNetworkMonitorMonitor, state.ID.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForUpdate, ResNameMonitor, state.ID.String(), nil), err.Error(), ) return @@ -246,7 +246,7 @@ func (r *resourceNetworkMonitorMonitor) Delete(ctx context.Context, req resource return } resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameNetworkMonitorMonitor, state.ID.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameMonitor, state.ID.String(), nil), err.Error(), ) return @@ -255,7 +255,7 @@ func (r *resourceNetworkMonitorMonitor) Delete(ctx context.Context, req resource _, err = waitMonitorDeleted(ctx, conn, state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForDeletion, ResNameNetworkMonitorMonitor, state.ID.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForDeletion, ResNameMonitor, state.ID.String(), nil), err.Error(), ) return diff --git a/internal/service/networkmonitor/monitor_test.go b/internal/service/networkmonitor/monitor_test.go index 1f49373580c..af4e71ca456 100644 --- a/internal/service/networkmonitor/monitor_test.go +++ b/internal/service/networkmonitor/monitor_test.go @@ -98,7 +98,7 @@ func TestAccNetworkMonitorMonitor_disappears(t *testing.T) { Config: testAccMonitorConfig(rName, 30), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckMonitorExists(ctx, resourceName), - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfnetworkmonitor.ResourceNetworkMonitorMonitor, resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfnetworkmonitor.ResourceMonitor, resourceName), ), ExpectNonEmptyPlan: true, }, @@ -129,7 +129,7 @@ func testAccCheckMonitorDestroy(ctx context.Context) resource.TestCheckFunc { return err } - return create.Error(names.NetworkMonitor, create.ErrActionCheckingDestroyed, tfnetworkmonitor.ResNameNetworkMonitorMonitor, rs.Primary.ID, errors.New("not destroyed")) + return create.Error(names.NetworkMonitor, create.ErrActionCheckingDestroyed, tfnetworkmonitor.ResNameMonitor, rs.Primary.ID, errors.New("not destroyed")) } return nil @@ -140,18 +140,18 @@ func testAccCheckMonitorExists(ctx context.Context, name string) resource.TestCh return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorMonitor, name, errors.New("not found")) + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameMonitor, name, errors.New("not found")) } if rs.Primary.ID == "" { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorMonitor, name, errors.New("not set")) + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameMonitor, name, errors.New("not set")) } conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkMonitorClient(ctx) _, err := tfnetworkmonitor.FindMonitorByName(ctx, conn, rs.Primary.ID) if err != nil { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorMonitor, rs.Primary.ID, err) + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameMonitor, rs.Primary.ID, err) } return nil diff --git a/internal/service/networkmonitor/probe.go b/internal/service/networkmonitor/probe.go index def91396958..6a9aacd3ad0 100644 --- a/internal/service/networkmonitor/probe.go +++ b/internal/service/networkmonitor/probe.go @@ -37,13 +37,13 @@ import ( ) const ( - ProbeTimeout = time.Minute * 10 - ResNameNetworkMonitorProbe = "CloudWatch Network Monitor Probe" + ProbeTimeout = time.Minute * 15 + ResNameProbe = "CloudWatch Network Monitor Probe" ) // @FrameworkResource(name="CloudWatch Network Monitor Probe") // @Tags(identifierAttribute="arn") -func newResourceNetworkMonitorProbe(context.Context) (resource.ResourceWithConfigure, error) { +func newResourceProbe(context.Context) (resource.ResourceWithConfigure, error) { return &resourceNetworkMonitorProbe{}, nil } @@ -165,7 +165,7 @@ func (r *resourceNetworkMonitorProbe) Create(ctx context.Context, req resource.C out, err := conn.CreateProbe(ctx, &input) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameNetworkMonitorProbe, state.MonitorName.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameProbe, state.MonitorName.String(), nil), err.Error(), ) return @@ -176,7 +176,7 @@ func (r *resourceNetworkMonitorProbe) Create(ctx context.Context, req resource.C outWait, err := waitProbeReady(ctx, conn, probeID) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForCreation, ResNameNetworkMonitorProbe, probeID, nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForCreation, ResNameProbe, probeID, nil), err.Error(), ) } @@ -212,7 +212,7 @@ func (r *resourceNetworkMonitorProbe) Read(ctx context.Context, req resource.Rea return } resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameNetworkMonitorProbe, state.ID.String(), err), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameProbe, state.ID.String(), err), err.Error(), ) return @@ -222,7 +222,7 @@ func (r *resourceNetworkMonitorProbe) Read(ctx context.Context, req resource.Rea _, monitorName, err := probeParseID(state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameNetworkMonitorProbe, state.ID.String(), err), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameProbe, state.ID.String(), err), err.Error(), ) return @@ -264,7 +264,7 @@ func (r *resourceNetworkMonitorProbe) Update(ctx context.Context, req resource.U probeID, monitorName, err := probeParseID(state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameNetworkMonitorProbe, state.ID.String(), err), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameProbe, state.ID.String(), err), err.Error(), ) return @@ -291,7 +291,7 @@ func (r *resourceNetworkMonitorProbe) Update(ctx context.Context, req resource.U _, err = conn.UpdateProbe(ctx, &in) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameNetworkMonitorProbe, state.ID.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameProbe, state.ID.String(), nil), err.Error(), ) return @@ -300,7 +300,7 @@ func (r *resourceNetworkMonitorProbe) Update(ctx context.Context, req resource.U out, err := waitProbeReady(ctx, conn, state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForUpdate, ResNameNetworkMonitorProbe, state.ID.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForUpdate, ResNameProbe, state.ID.String(), nil), err.Error(), ) } @@ -324,7 +324,7 @@ func (r *resourceNetworkMonitorProbe) Delete(ctx context.Context, req resource.D probeID, monitorName, err := probeParseID(state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameNetworkMonitorProbe, state.ID.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameProbe, state.ID.String(), nil), err.Error(), ) return @@ -342,7 +342,7 @@ func (r *resourceNetworkMonitorProbe) Delete(ctx context.Context, req resource.D } resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameNetworkMonitorProbe, state.ID.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameProbe, state.ID.String(), nil), err.Error(), ) return @@ -351,7 +351,7 @@ func (r *resourceNetworkMonitorProbe) Delete(ctx context.Context, req resource.D _, err = waitProbeDeleted(ctx, conn, state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForDeletion, ResNameNetworkMonitorProbe, state.ID.String(), nil), + create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForDeletion, ResNameProbe, state.ID.String(), nil), err.Error(), ) return @@ -406,7 +406,7 @@ func waitProbeReady(ctx context.Context, conn *networkmonitor.Client, id string) func waitProbeDeleted(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetProbeOutput, error) { stateConf := &retry.StateChangeConf{ - Pending: enum.Slice(awstypes.ProbeStateActive, awstypes.ProbeStateInactive, awstypes.ProbeStateInactive), + Pending: enum.Slice(awstypes.ProbeStateActive, awstypes.ProbeStateInactive, awstypes.ProbeStateDeleting), Target: []string{}, Refresh: statusProbe(ctx, conn, id), Timeout: ProbeTimeout, @@ -524,7 +524,7 @@ func expandProbeConfig(ctx context.Context, object probeConfigModel) awstypes.Pr Destination: object.Destination.ValueStringPointer(), DestinationPort: aws.Int32(int32(object.DestinationPort.ValueInt64())), PacketSize: aws.Int32(int32(object.PacketSize.ValueInt64())), - Protocol: awstypes.Protocol(*aws.String(object.Protocol.ValueString())), + Protocol: awstypes.Protocol(object.Protocol.ValueString()), SourceArn: object.SourceArn.ValueStringPointer(), Tags: flex.ExpandFrameworkStringValueMap(ctx, object.Tags), } diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go index 792a2a33999..faa226fb337 100644 --- a/internal/service/networkmonitor/probe_test.go +++ b/internal/service/networkmonitor/probe_test.go @@ -109,7 +109,7 @@ func TestAccNetworkMonitorProbe_disappears(t *testing.T) { Config: testAccProbeConfig_basic(rName, "10.0.0.1", 8080, 200), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckProbeExists(ctx, resourceName), - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfnetworkmonitor.ResourceNetworkMonitorProbe, resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfnetworkmonitor.ResourceProbe, resourceName), ), ExpectNonEmptyPlan: true, }, @@ -140,7 +140,7 @@ func testAccCheckProbeDestroy(ctx context.Context) resource.TestCheckFunc { return err } - return create.Error(names.NetworkMonitor, create.ErrActionCheckingDestroyed, tfnetworkmonitor.ResNameNetworkMonitorMonitor, rs.Primary.ID, errors.New("not destroyed")) + return create.Error(names.NetworkMonitor, create.ErrActionCheckingDestroyed, tfnetworkmonitor.ResNameMonitor, rs.Primary.ID, errors.New("not destroyed")) } return nil @@ -151,18 +151,18 @@ func testAccCheckProbeExists(ctx context.Context, name string) resource.TestChec return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorProbe, name, errors.New("not found")) + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameProbe, name, errors.New("not found")) } if rs.Primary.ID == "" { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorProbe, name, errors.New("not set")) + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameProbe, name, errors.New("not set")) } conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkMonitorClient(ctx) _, err := tfnetworkmonitor.FindProbeByID(ctx, conn, rs.Primary.ID) if err != nil { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameNetworkMonitorProbe, rs.Primary.ID, err) + return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameProbe, rs.Primary.ID, err) } return nil diff --git a/internal/service/networkmonitor/service_package_gen.go b/internal/service/networkmonitor/service_package_gen.go index 6004dca67a6..4693afeaf6b 100644 --- a/internal/service/networkmonitor/service_package_gen.go +++ b/internal/service/networkmonitor/service_package_gen.go @@ -21,14 +21,14 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { return []*types.ServicePackageFrameworkResource{ { - Factory: newResourceNetworkMonitorMonitor, + Factory: newResourceMonitor, Name: "CloudWatch Network Monitor Monitor", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: "arn", }, }, { - Factory: newResourceNetworkMonitorProbe, + Factory: newResourceProbe, Name: "CloudWatch Network Monitor Probe", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: "arn", From 8747306b653791270830bb608fae11e57dd85ee5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 22 Jun 2024 16:24:46 -0400 Subject: [PATCH 07/20] Run 'make fix-constants PKG=networkmonitor'. --- internal/service/networkmonitor/monitor.go | 8 ++-- .../service/networkmonitor/monitor_test.go | 10 ++--- internal/service/networkmonitor/probe.go | 40 +++++++++---------- internal/service/networkmonitor/probe_test.go | 12 +++--- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/internal/service/networkmonitor/monitor.go b/internal/service/networkmonitor/monitor.go index 8c68e638417..22247aef82e 100644 --- a/internal/service/networkmonitor/monitor.go +++ b/internal/service/networkmonitor/monitor.go @@ -54,14 +54,14 @@ func (r *resourceNetworkMonitorMonitor) Metadata(_ context.Context, request reso func (r *resourceNetworkMonitorMonitor) Schema(ctx context.Context, request resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "arn": framework.ARNAttributeComputedOnly(), + names.AttrARN: framework.ARNAttributeComputedOnly(), "aggregation_period": schema.Int64Attribute{ Optional: true, Validators: []validator.Int64{ int64validator.OneOf(30, 60), }, }, - "created_at": schema.Int64Attribute{ + names.AttrCreatedAt: schema.Int64Attribute{ Computed: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.UseStateForUnknown(), @@ -83,7 +83,7 @@ func (r *resourceNetworkMonitorMonitor) Schema(ctx context.Context, request reso names.AttrID: framework.IDAttribute(), names.AttrTags: tftags.TagsAttribute(), names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), - "state": schema.StringAttribute{ + names.AttrState: schema.StringAttribute{ Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), @@ -339,7 +339,7 @@ func (r *resourceNetworkMonitorMonitor) ModifyPlan(ctx context.Context, req reso } func (r *resourceNetworkMonitorMonitor) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp) } type resourceNetworkMonitorMonitorModel struct { diff --git a/internal/service/networkmonitor/monitor_test.go b/internal/service/networkmonitor/monitor_test.go index af4e71ca456..3aef50b05c5 100644 --- a/internal/service/networkmonitor/monitor_test.go +++ b/internal/service/networkmonitor/monitor_test.go @@ -37,8 +37,8 @@ func TestAccNetworkMonitorMonitor_basic(t *testing.T) { Config: testAccMonitorConfig(rName, 30), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckMonitorExists(ctx, resourceName), - resource.TestCheckResourceAttrSet(resourceName, "arn"), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "aggregation_period", "30"), ), }, @@ -46,7 +46,7 @@ func TestAccNetworkMonitorMonitor_basic(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"tags", "tags_all"}, + ImportStateVerifyIgnore: []string{names.AttrTags, names.AttrTagsAll}, }, }, }) @@ -68,7 +68,7 @@ func TestAccNetworkMonitorMonitor_updates(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckMonitorExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "aggregation_period", "30"), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), ), }, { @@ -76,7 +76,7 @@ func TestAccNetworkMonitorMonitor_updates(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckMonitorExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "aggregation_period", "60"), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), ), }, }, diff --git a/internal/service/networkmonitor/probe.go b/internal/service/networkmonitor/probe.go index 6a9aacd3ad0..3b62443b719 100644 --- a/internal/service/networkmonitor/probe.go +++ b/internal/service/networkmonitor/probe.go @@ -59,7 +59,7 @@ func (r *resourceNetworkMonitorProbe) Schema(ctx context.Context, request resour resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ names.AttrID: framework.IDAttribute(), - "arn": framework.ARNAttributeComputedOnly(), + names.AttrARN: framework.ARNAttributeComputedOnly(), "monitor_name": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -79,10 +79,10 @@ func (r *resourceNetworkMonitorProbe) Schema(ctx context.Context, request resour "address_family": schema.StringAttribute{ Computed: true, }, - "created_at": schema.Int64Attribute{ + names.AttrCreatedAt: schema.Int64Attribute{ Computed: true, }, - "destination": schema.StringAttribute{ + names.AttrDestination: schema.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.LengthBetween(1, 255), @@ -109,7 +109,7 @@ func (r *resourceNetworkMonitorProbe) Schema(ctx context.Context, request resour "probe_id": schema.StringAttribute{ Computed: true, }, - "protocol": schema.StringAttribute{ + names.AttrProtocol: schema.StringAttribute{ Required: true, }, "source_arn": schema.StringAttribute{ @@ -119,14 +119,14 @@ func (r *resourceNetworkMonitorProbe) Schema(ctx context.Context, request resour stringvalidator.RegexMatches(regexache.MustCompile("arn:.*"), "Must match pattern arn:*"), }, }, - "tags": schema.MapAttribute{ + names.AttrTags: schema.MapAttribute{ ElementType: types.StringType, Computed: true, }, - "state": schema.StringAttribute{ + names.AttrState: schema.StringAttribute{ Computed: true, }, - "vpc_id": schema.StringAttribute{ + names.AttrVPCID: schema.StringAttribute{ Computed: true, }, }, @@ -363,7 +363,7 @@ func (r *resourceNetworkMonitorProbe) ModifyPlan(ctx context.Context, req resour } func (r *resourceNetworkMonitorProbe) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp) } func statusProbe(ctx context.Context, conn *networkmonitor.Client, id string) retry.StateRefreshFunc { @@ -456,18 +456,18 @@ func probeParseID(id string) (string, string, error) { var probeConfigTypes = map[string]attr.Type{ "address_family": types.StringType, - "created_at": types.Int64Type, - "destination": types.StringType, + names.AttrCreatedAt: types.Int64Type, + names.AttrDestination: types.StringType, "destination_port": types.Int64Type, "modified_at": types.Int64Type, "packet_size": types.Int64Type, "probe_arn": types.StringType, "probe_id": types.StringType, - "protocol": types.StringType, + names.AttrProtocol: types.StringType, "source_arn": types.StringType, - "state": types.StringType, - "tags": types.MapType{ElemType: types.StringType}, - "vpc_id": types.StringType, + names.AttrState: types.StringType, + names.AttrTags: types.MapType{ElemType: types.StringType}, + names.AttrVPCID: types.StringType, } type resourceNetworkMonitorProbeModel struct { @@ -500,18 +500,18 @@ func flattenProbeConfig(ctx context.Context, object networkmonitor.GetProbeOutpu t := map[string]attr.Value{ "address_family": flex.StringToFramework(ctx, (*string)(&object.AddressFamily)), - "created_at": flex.Int64ToFramework(ctx, aws.Int64(object.CreatedAt.Unix())), - "destination": flex.StringToFramework(ctx, object.Destination), + names.AttrCreatedAt: flex.Int64ToFramework(ctx, aws.Int64(object.CreatedAt.Unix())), + names.AttrDestination: flex.StringToFramework(ctx, object.Destination), "destination_port": flex.Int64ToFramework(ctx, aws.Int64(int64(*object.DestinationPort))), "modified_at": flex.Int64ToFramework(ctx, aws.Int64(object.ModifiedAt.Unix())), "packet_size": flex.Int64ToFramework(ctx, aws.Int64(int64(*object.PacketSize))), "probe_arn": flex.StringToFramework(ctx, object.ProbeArn), "probe_id": flex.StringToFramework(ctx, object.ProbeId), - "protocol": flex.StringToFramework(ctx, (*string)(&object.Protocol)), + names.AttrProtocol: flex.StringToFramework(ctx, (*string)(&object.Protocol)), "source_arn": flex.StringToFramework(ctx, object.SourceArn), - "state": flex.StringToFramework(ctx, (*string)(&object.State)), - "tags": flex.FlattenFrameworkStringValueMap(ctx, object.Tags), - "vpc_id": flex.StringToFramework(ctx, object.VpcId), + names.AttrState: flex.StringToFramework(ctx, (*string)(&object.State)), + names.AttrTags: flex.FlattenFrameworkStringValueMap(ctx, object.Tags), + names.AttrVPCID: flex.StringToFramework(ctx, object.VpcId), } objVal, d := types.ObjectValue(probeConfigTypes, t) diags.Append(d...) diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go index faa226fb337..8d3629cee8a 100644 --- a/internal/service/networkmonitor/probe_test.go +++ b/internal/service/networkmonitor/probe_test.go @@ -35,12 +35,12 @@ func TestAccNetworkMonitorProbe_basic(t *testing.T) { Config: testAccProbeConfig_basic(rName, "10.0.0.1", 8080, 200), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckProbeExists(ctx, resourceName), - resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), resource.TestCheckResourceAttr(resourceName, "probe.destination", "10.0.0.1"), resource.TestCheckResourceAttr(resourceName, "probe.destination_port", "8080"), resource.TestCheckResourceAttr(resourceName, "probe.packet_size", "200"), resource.TestCheckResourceAttr(resourceName, "probe.protocol", "TCP"), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "tags.tag1", rName), ), }, @@ -67,12 +67,12 @@ func TestAccNetworkMonitorProbe_updates(t *testing.T) { Config: testAccProbeConfig_basic(rName, "10.0.0.1", 8080, 200), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckProbeExists(ctx, resourceName), - resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), resource.TestCheckResourceAttr(resourceName, "probe.destination", "10.0.0.1"), resource.TestCheckResourceAttr(resourceName, "probe.destination_port", "8080"), resource.TestCheckResourceAttr(resourceName, "probe.packet_size", "200"), resource.TestCheckResourceAttr(resourceName, "probe.protocol", "TCP"), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "tags.tag1", rName), ), }, @@ -80,12 +80,12 @@ func TestAccNetworkMonitorProbe_updates(t *testing.T) { Config: testAccProbeConfig_2tags(rName, "10.0.0.2", 8081, 300), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckProbeExists(ctx, resourceName), - resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), resource.TestCheckResourceAttr(resourceName, "probe.destination", "10.0.0.2"), resource.TestCheckResourceAttr(resourceName, "probe.destination_port", "8081"), resource.TestCheckResourceAttr(resourceName, "probe.packet_size", "300"), resource.TestCheckResourceAttr(resourceName, "probe.protocol", "TCP"), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), resource.TestCheckResourceAttr(resourceName, "tags.tag1", rName), resource.TestCheckResourceAttr(resourceName, "tags.tag2", rName), ), From be6470607609a181858c0d1bd236b34af33eb5dc Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 22 Jun 2024 17:07:53 -0400 Subject: [PATCH 08/20] r/aws_networkmonitor_monitor: Use AutoFlEx. --- .../{exports_tests.go => exports_test.go} | 4 +- internal/service/networkmonitor/monitor.go | 355 ++++++++---------- .../service/networkmonitor/monitor_test.go | 99 +++-- .../networkmonitor/service_package_gen.go | 4 +- .../r/networkmonitor_monitor.html.markdown | 5 +- 5 files changed, 229 insertions(+), 238 deletions(-) rename internal/service/networkmonitor/{exports_tests.go => exports_test.go} (68%) diff --git a/internal/service/networkmonitor/exports_tests.go b/internal/service/networkmonitor/exports_test.go similarity index 68% rename from internal/service/networkmonitor/exports_tests.go rename to internal/service/networkmonitor/exports_test.go index 5d6435c06d7..0f80064eba7 100644 --- a/internal/service/networkmonitor/exports_tests.go +++ b/internal/service/networkmonitor/exports_test.go @@ -5,6 +5,8 @@ package networkmonitor // Exports for use in tests only. var ( - ResourceMonitor = newResourceMonitor + ResourceMonitor = newMonitorResource ResourceProbe = newResourceProbe + + FindMonitorByName = findMonitorByName ) diff --git a/internal/service/networkmonitor/monitor.go b/internal/service/networkmonitor/monitor.go index 22247aef82e..367fac7ac4a 100644 --- a/internal/service/networkmonitor/monitor.go +++ b/internal/service/networkmonitor/monitor.go @@ -5,7 +5,7 @@ package networkmonitor import ( "context" - "errors" + "fmt" "time" "github.com/YakDriver/regexache" @@ -14,267 +14,252 @@ import ( awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "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/fwdiag" "github.com/hashicorp/terraform-provider-aws/internal/framework" - "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -const ( - MonitorTimeout = time.Minute * 10 - ResNameMonitor = "CloudWatch Network Monitor Monitor" -) - -// @FrameworkResource(name="CloudWatch Network Monitor Monitor") +// @FrameworkResource(name="Monitor") // @Tags(identifierAttribute="arn") -func newResourceMonitor(context.Context) (resource.ResourceWithConfigure, error) { - return &resourceNetworkMonitorMonitor{}, nil +func newMonitorResource(context.Context) (resource.ResourceWithConfigure, error) { + return &monitorResource{}, nil } -type resourceNetworkMonitorMonitor struct { +type monitorResource struct { framework.ResourceWithConfigure + framework.WithImportByID } -func (r *resourceNetworkMonitorMonitor) Metadata(_ context.Context, request resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "aws_networkmonitor_monitor" +func (*monitorResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_networkmonitor_monitor" } -func (r *resourceNetworkMonitorMonitor) Schema(ctx context.Context, request resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ +func (r *monitorResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - names.AttrARN: framework.ARNAttributeComputedOnly(), "aggregation_period": schema.Int64Attribute{ Optional: true, Validators: []validator.Int64{ int64validator.OneOf(30, 60), }, }, - names.AttrCreatedAt: schema.Int64Attribute{ - Computed: true, - PlanModifiers: []planmodifier.Int64{ - int64planmodifier.UseStateForUnknown(), - }, - }, - "modified_at": schema.Int64Attribute{ - Computed: true, - PlanModifiers: []planmodifier.Int64{ - int64planmodifier.UseStateForUnknown(), - }, - }, + names.AttrARN: framework.ARNAttributeComputedOnly(), + names.AttrID: framework.IDAttribute(), "monitor_name": schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, Validators: []validator.String{ stringvalidator.RegexMatches(regexache.MustCompile("[a-zA-Z0-9_-]+"), "Must match [a-zA-Z0-9_-]+"), stringvalidator.LengthBetween(1, 255), }, }, - names.AttrID: framework.IDAttribute(), names.AttrTags: tftags.TagsAttribute(), names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), - names.AttrState: schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, }, } } -func (r *resourceNetworkMonitorMonitor) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { +func (r *monitorResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data monitorResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + conn := r.Meta().NetworkMonitorClient(ctx) - var plan resourceNetworkMonitorMonitorModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { + name := data.MonitorName.ValueString() + input := &networkmonitor.CreateMonitorInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, data, input)...) + if response.Diagnostics.HasError() { return } - input := networkmonitor.CreateMonitorInput{ - MonitorName: plan.MonitorName.ValueStringPointer(), - AggregationPeriod: plan.AggregationPeriod.ValueInt64Pointer(), - Tags: getTagsIn(ctx), - } + input.ClientToken = aws.String(id.UniqueId()) + input.Tags = getTagsIn(ctx) + + output, err := conn.CreateMonitor(ctx, input) - _, err := conn.CreateMonitor(ctx, &input) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameMonitor, plan.MonitorName.String(), nil), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("creating CloudWatch Network Monitor Monitor (%s)", name), err.Error()) + return } - out, err := waitMonitorReady(ctx, conn, plan.MonitorName.ValueString()) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForCreation, ResNameMonitor, plan.MonitorName.ValueString(), nil), - err.Error(), - ) - } + // Set values for unknowns. + data.MonitorARN = fwflex.StringToFramework(ctx, output.MonitorArn) + data.setID() - state := plan + if _, err := waitMonitorReady(ctx, conn, data.ID.ValueString()); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for CloudWatch Network Monitor Monitor (%s) create", data.ID.ValueString()), err.Error()) - state.ID = flex.StringToFramework(ctx, out.MonitorName) - state.Arn = flex.StringToFramework(ctx, out.MonitorArn) - state.State = flex.StringToFramework(ctx, (*string)(&out.State)) - state.CreatedAt = flex.Int64ToFramework(ctx, (aws.Int64(out.CreatedAt.Unix()))) - state.ModifiedAt = flex.Int64ToFramework(ctx, (aws.Int64(out.ModifiedAt.Unix()))) + return + } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *resourceNetworkMonitorMonitor) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { +func (r *monitorResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data monitorResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + if err := data.InitFromID(); err != nil { + response.Diagnostics.AddError("parsing resource ID", err.Error()) + + return + } + conn := r.Meta().NetworkMonitorClient(ctx) - var state resourceNetworkMonitorMonitorModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { + output, err := findMonitorByName(ctx, conn, data.ID.ValueString()) + + if tfresource.NotFound(err) { + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) + return } - output, err := FindMonitorByName(ctx, conn, state.ID.ValueString()) if err != nil { - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - resp.State.RemoveResource(ctx) - return - } - - if tfresource.NotFound(err) { - resp.State.RemoveResource(ctx) - return - } + response.Diagnostics.AddError(fmt.Sprintf("reading CloudWatch Network Monitor Monitor (%s)", data.ID.ValueString()), err.Error()) - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameMonitor, state.ID.String(), err), - err.Error(), - ) return } - resp.Diagnostics.Append(flex.Flatten(ctx, output, &state)...) + // Set attributes for import. + response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...) + if response.Diagnostics.HasError() { + return + } - state.AggregationPeriod = flex.Int64ToFramework(ctx, output.AggregationPeriod) - state.MonitorName = flex.StringToFramework(ctx, output.MonitorName) - state.Arn = flex.StringToFramework(ctx, output.MonitorArn) - state.State = flex.StringToFramework(ctx, (*string)(&output.State)) - state.CreatedAt = flex.Int64ToFramework(ctx, (aws.Int64(output.CreatedAt.Unix()))) - state.ModifiedAt = flex.Int64ToFramework(ctx, (aws.Int64(output.ModifiedAt.Unix()))) + setTagsOut(ctx, output.Tags) - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *resourceNetworkMonitorMonitor) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - conn := r.Meta().NetworkMonitorClient(ctx) - - var plan, state resourceNetworkMonitorMonitorModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { +func (r *monitorResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var old, new monitorResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &new)...) + if response.Diagnostics.HasError() { return } - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { + response.Diagnostics.Append(request.State.Get(ctx, &old)...) + if response.Diagnostics.HasError() { return } - if !plan.AggregationPeriod.Equal(state.AggregationPeriod) { - input := networkmonitor.UpdateMonitorInput{ - MonitorName: plan.MonitorName.ValueStringPointer(), - AggregationPeriod: plan.AggregationPeriod.ValueInt64Pointer(), - } + conn := r.Meta().NetworkMonitorClient(ctx) - _, err := conn.UpdateMonitor(ctx, &input) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameMonitor, state.ID.String(), nil), - err.Error(), - ) + if !new.AggregationPeriod.Equal(old.AggregationPeriod) { + input := &networkmonitor.UpdateMonitorInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) + if response.Diagnostics.HasError() { return } - _, err = waitMonitorReady(ctx, conn, plan.MonitorName.ValueString()) + _, err := conn.UpdateMonitor(ctx, input) + if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameMonitor, plan.MonitorName.ValueString(), nil), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("updating CloudWatch Network Monitor Monitor (%s)", new.ID.ValueString()), err.Error()) + + return } - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForUpdate, ResNameMonitor, state.ID.String(), nil), - err.Error(), - ) + + if _, err := waitMonitorReady(ctx, conn, new.ID.ValueString()); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for CloudWatch Network Monitor Monitor (%s) update", new.ID.ValueString()), err.Error()) + return } } - state.AggregationPeriod = flex.Int64ToFramework(ctx, plan.AggregationPeriod.ValueInt64Pointer()) - - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + response.Diagnostics.Append(response.State.Set(ctx, &new)...) } -func (r *resourceNetworkMonitorMonitor) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *monitorResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var data monitorResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + conn := r.Meta().NetworkMonitorClient(ctx) - var state resourceNetworkMonitorMonitorModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { + _, err := conn.DeleteMonitor(ctx, &networkmonitor.DeleteMonitorInput{ + MonitorName: fwflex.StringFromFramework(ctx, data.ID), + }) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return } - input := networkmonitor.DeleteMonitorInput{ - MonitorName: flex.StringFromFramework(ctx, state.MonitorName), + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("deleting CloudWatch Network Monitor Monitor (%s)", data.ID.ValueString()), err.Error()) + + return } - _, err := conn.DeleteMonitor(ctx, &input) - if err != nil { - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return - } - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameMonitor, state.ID.String(), nil), - err.Error(), - ) + if _, err := waitMonitorDeleted(ctx, conn, data.ID.ValueString()); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for CloudWatch Network Monitor Monitor (%s) delete", data.ID.ValueString()), err.Error()) + return } +} + +func (r *monitorResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) +} + +func findMonitorByName(ctx context.Context, conn *networkmonitor.Client, name string) (*networkmonitor.GetMonitorOutput, error) { + input := &networkmonitor.GetMonitorInput{ + MonitorName: aws.String(name), + } + + output, err := conn.GetMonitor(ctx, input) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } - _, err = waitMonitorDeleted(ctx, conn, state.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForDeletion, ResNameMonitor, state.ID.String(), nil), - err.Error(), - ) - return + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) } + + return output, nil } -func statusMonitor(ctx context.Context, conn *networkmonitor.Client, id string) retry.StateRefreshFunc { +func statusMonitor(ctx context.Context, conn *networkmonitor.Client, name string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindMonitorByName(ctx, conn, id) + output, err := findMonitorByName(ctx, conn, name) if tfresource.NotFound(err) { return nil, "", nil } - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return nil, "", nil - } - if err != nil { return nil, "", err } @@ -283,16 +268,20 @@ func statusMonitor(ctx context.Context, conn *networkmonitor.Client, id string) } } -func waitMonitorReady(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetMonitorOutput, error) { +func waitMonitorReady(ctx context.Context, conn *networkmonitor.Client, name string) (*networkmonitor.GetMonitorOutput, error) { + const ( + timeout = time.Minute * 10 + ) stateConf := &retry.StateChangeConf{ Pending: enum.Slice(awstypes.MonitorStatePending), Target: enum.Slice(awstypes.MonitorStateActive, awstypes.MonitorStateInactive), - Refresh: statusMonitor(ctx, conn, id), - Timeout: MonitorTimeout, + Refresh: statusMonitor(ctx, conn, name), + Timeout: timeout, MinTimeout: 10 * time.Second, } outputRaw, err := stateConf.WaitForStateContext(ctx) + if output, ok := outputRaw.(*networkmonitor.GetMonitorOutput); ok { return output, err } @@ -300,16 +289,20 @@ func waitMonitorReady(ctx context.Context, conn *networkmonitor.Client, id strin return nil, err } -func waitMonitorDeleted(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetMonitorOutput, error) { +func waitMonitorDeleted(ctx context.Context, conn *networkmonitor.Client, name string) (*networkmonitor.GetMonitorOutput, error) { + const ( + timeout = time.Minute * 10 + ) stateConf := &retry.StateChangeConf{ Pending: enum.Slice(awstypes.MonitorStateDeleting, awstypes.MonitorStateActive, awstypes.MonitorStateInactive), Target: []string{}, - Refresh: statusMonitor(ctx, conn, id), - Timeout: MonitorTimeout, + Refresh: statusMonitor(ctx, conn, name), + Timeout: timeout, MinTimeout: 10 * time.Second, } outputRaw, err := stateConf.WaitForStateContext(ctx) + if output, ok := outputRaw.(*networkmonitor.GetMonitorOutput); ok { return output, err } @@ -317,39 +310,21 @@ func waitMonitorDeleted(ctx context.Context, conn *networkmonitor.Client, id str return nil, err } -func FindMonitorByName(ctx context.Context, conn *networkmonitor.Client, name string) (*networkmonitor.GetMonitorOutput, error) { - input := &networkmonitor.GetMonitorInput{ - MonitorName: &name, - } - - output, err := conn.GetMonitor(ctx, input) - if err != nil { - return nil, err - } - - if output == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - return output, nil +type monitorResourceModel struct { + AggregationPeriod types.Int64 `tfsdk:"aggregation_period"` + ID types.String `tfsdk:"id"` + MonitorARN types.String `tfsdk:"arn"` + MonitorName types.String `tfsdk:"monitor_name"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` } -func (r *resourceNetworkMonitorMonitor) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - r.SetTagsAll(ctx, req, resp) -} +func (model *monitorResourceModel) InitFromID() error { + model.MonitorName = model.ID -func (r *resourceNetworkMonitorMonitor) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp) + return nil } -type resourceNetworkMonitorMonitorModel struct { - ID types.String `tfsdk:"id"` - Arn types.String `tfsdk:"arn"` - AggregationPeriod types.Int64 `tfsdk:"aggregation_period"` - CreatedAt types.Int64 `tfsdk:"created_at"` - ModifiedAt types.Int64 `tfsdk:"modified_at"` - MonitorName types.String `tfsdk:"monitor_name"` - State types.String `tfsdk:"state"` - Tags types.Map `tfsdk:"tags"` - TagsAll types.Map `tfsdk:"tags_all"` +func (model *monitorResourceModel) setID() { + model.ID = model.MonitorName } diff --git a/internal/service/networkmonitor/monitor_test.go b/internal/service/networkmonitor/monitor_test.go index 3aef50b05c5..c00cbb23c63 100644 --- a/internal/service/networkmonitor/monitor_test.go +++ b/internal/service/networkmonitor/monitor_test.go @@ -5,18 +5,15 @@ package networkmonitor_test import ( "context" - "errors" "fmt" "testing" - awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" "github.com/aws/aws-sdk-go/service/networkmonitor" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/create" tfnetworkmonitor "github.com/hashicorp/terraform-provider-aws/internal/service/networkmonitor" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -34,25 +31,31 @@ func TestAccNetworkMonitorMonitor_basic(t *testing.T) { CheckDestroy: testAccCheckMonitorDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMonitorConfig(rName, 30), + Config: testAccMonitorConfig_basic(rName, 30), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckMonitorExists(ctx, resourceName), - resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "aggregation_period", "30"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{names.AttrTags, names.AttrTagsAll}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccMonitorConfig_basic(rName, 60), + Check: resource.ComposeTestCheckFunc( + testAccCheckMonitorExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "aggregation_period", "60"), + ), }, }, }) } -func TestAccNetworkMonitorMonitor_updates(t *testing.T) { +func TestAccNetworkMonitorMonitor_tags(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_networkmonitor_monitor.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -64,19 +67,33 @@ func TestAccNetworkMonitorMonitor_updates(t *testing.T) { CheckDestroy: testAccCheckMonitorDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMonitorConfig(rName, 30), + Config: testAccMonitorConfig_tags1(rName, acctest.CtKey1, acctest.CtValue1), Check: resource.ComposeTestCheckFunc( testAccCheckMonitorExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "aggregation_period", "30"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), ), }, { - Config: testAccMonitorConfig_tags(rName, 60), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccMonitorConfig_tags2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), Check: resource.ComposeTestCheckFunc( testAccCheckMonitorExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "aggregation_period", "60"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), + ), + }, + { + Config: testAccMonitorConfig_tags1(rName, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckMonitorExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), ), }, }, @@ -95,7 +112,7 @@ func TestAccNetworkMonitorMonitor_disappears(t *testing.T) { CheckDestroy: testAccCheckMonitorDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMonitorConfig(rName, 30), + Config: testAccMonitorConfig_basic(rName, 30), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckMonitorExists(ctx, resourceName), acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfnetworkmonitor.ResourceMonitor, resourceName), @@ -116,69 +133,69 @@ func testAccCheckMonitorDestroy(ctx context.Context) resource.TestCheckFunc { } _, err := tfnetworkmonitor.FindMonitorByName(ctx, conn, rs.Primary.ID) + if tfresource.NotFound(err) { continue } - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return nil - } - if err != nil { return err } - return create.Error(names.NetworkMonitor, create.ErrActionCheckingDestroyed, tfnetworkmonitor.ResNameMonitor, rs.Primary.ID, errors.New("not destroyed")) + return fmt.Errorf("CloudWatch Network Monitor Monitor %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckMonitorExists(ctx context.Context, name string) resource.TestCheckFunc { +func testAccCheckMonitorExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameMonitor, name, errors.New("not found")) - } - - if rs.Primary.ID == "" { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameMonitor, name, errors.New("not set")) + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkMonitorClient(ctx) _, err := tfnetworkmonitor.FindMonitorByName(ctx, conn, rs.Primary.ID) - if err != nil { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameMonitor, rs.Primary.ID, err) - } - return nil + return err } } -func testAccMonitorConfig(rName string, aggregation int) string { +func testAccMonitorConfig_basic(rName string, aggregation int) string { return fmt.Sprintf(` resource "aws_networkmonitor_monitor" "test" { aggregation_period = %[2]d monitor_name = %[1]q +} +`, rName, aggregation) +} + +func testAccMonitorConfig_tags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_networkmonitor_monitor" "test" { + aggregation_period = 30 + monitor_name = %[1]q + tags = { - tag1 = %[1]q + %[2]q = %[3]q } } -`, rName, aggregation) +`, rName, tagKey1, tagValue1) } -func testAccMonitorConfig_tags(rName string, aggregation int) string { +func testAccMonitorConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { return fmt.Sprintf(` resource "aws_networkmonitor_monitor" "test" { - aggregation_period = %[2]d + aggregation_period = 30 monitor_name = %[1]q + tags = { - tag1 = %[1]q - tag2 = %[1]q + %[2]q = %[3]q + %[4]q = %[5]q } } -`, rName, aggregation) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) } diff --git a/internal/service/networkmonitor/service_package_gen.go b/internal/service/networkmonitor/service_package_gen.go index b3bb1798417..e4008566741 100644 --- a/internal/service/networkmonitor/service_package_gen.go +++ b/internal/service/networkmonitor/service_package_gen.go @@ -21,8 +21,8 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { return []*types.ServicePackageFrameworkResource{ { - Factory: newResourceMonitor, - Name: "CloudWatch Network Monitor Monitor", + Factory: newMonitorResource, + Name: "Monitor", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, diff --git a/website/docs/r/networkmonitor_monitor.html.markdown b/website/docs/r/networkmonitor_monitor.html.markdown index ff9eb446f16..4aacd857c6f 100644 --- a/website/docs/r/networkmonitor_monitor.html.markdown +++ b/website/docs/r/networkmonitor_monitor.html.markdown @@ -42,10 +42,7 @@ The following arguments are optional: This resource exports the following attributes in addition to the arguments above: -- `arn` - The ARN of the attachment. -- `monitor_name` - The name of the monitor. -- `aggregation_period` - The time, in seconds, that metrics are aggregated and sent to Amazon CloudWatch. -- `state` - The state of the monitor. +- `arn` - The ARN of the monitor. - `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). ## Import From a88e2bfffdb661fe8be7a94ac25617115ff19849 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 23 Jun 2024 16:35:43 -0400 Subject: [PATCH 09/20] r/aws_networkmonitor_probe: Use AutoFlEx. --- .../service/networkmonitor/exports_test.go | 5 +- internal/service/networkmonitor/probe.go | 601 +++++++----------- internal/service/networkmonitor/probe_test.go | 230 ++++--- .../networkmonitor/service_package_gen.go | 4 +- .../docs/r/networkmonitor_probe.html.markdown | 35 +- 5 files changed, 396 insertions(+), 479 deletions(-) diff --git a/internal/service/networkmonitor/exports_test.go b/internal/service/networkmonitor/exports_test.go index 0f80064eba7..c7e8a69c051 100644 --- a/internal/service/networkmonitor/exports_test.go +++ b/internal/service/networkmonitor/exports_test.go @@ -6,7 +6,8 @@ package networkmonitor // Exports for use in tests only. var ( ResourceMonitor = newMonitorResource - ResourceProbe = newResourceProbe + ResourceProbe = newProbeResource - FindMonitorByName = findMonitorByName + FindMonitorByName = findMonitorByName + FindProbeByTwoPartKey = findProbeByTwoPartKey ) diff --git a/internal/service/networkmonitor/probe.go b/internal/service/networkmonitor/probe.go index 3b62443b719..02a2b817cff 100644 --- a/internal/service/networkmonitor/probe.go +++ b/internal/service/networkmonitor/probe.go @@ -5,9 +5,7 @@ package networkmonitor import ( "context" - "errors" "fmt" - "strings" "time" "github.com/YakDriver/regexache" @@ -16,50 +14,62 @@ import ( awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "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/fwdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/framework" - "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -const ( - ProbeTimeout = time.Minute * 15 - ResNameProbe = "CloudWatch Network Monitor Probe" -) - -// @FrameworkResource(name="CloudWatch Network Monitor Probe") +// @FrameworkResource(name="Probe") // @Tags(identifierAttribute="arn") -func newResourceProbe(context.Context) (resource.ResourceWithConfigure, error) { - return &resourceNetworkMonitorProbe{}, nil +func newProbeResource(context.Context) (resource.ResourceWithConfigure, error) { + return &probeResource{}, nil } -type resourceNetworkMonitorProbe struct { +type probeResource struct { framework.ResourceWithConfigure + framework.WithImportByID } -func (r *resourceNetworkMonitorProbe) Metadata(_ context.Context, request resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "aws_networkmonitor_probe" +func (*probeResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_networkmonitor_probe" } -func (r *resourceNetworkMonitorProbe) Schema(ctx context.Context, request resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ +func (r *probeResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ + "address_family": schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.AddressFamily](), + Computed: true, + }, + names.AttrARN: framework.ARNAttributeComputedOnly(), + names.AttrDestination: schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 255), + }, + }, + "destination_port": schema.Int64Attribute{ + Optional: true, + Validators: []validator.Int64{ + int64validator.Between(0, 65536), + }, + }, names.AttrID: framework.IDAttribute(), - names.AttrARN: framework.ARNAttributeComputedOnly(), "monitor_name": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -70,315 +80,257 @@ func (r *resourceNetworkMonitorProbe) Schema(ctx context.Context, request resour stringvalidator.LengthBetween(1, 255), }, }, + "packet_size": schema.Int64Attribute{ + Optional: true, + Validators: []validator.Int64{ + int64validator.Between(56, 8500), + }, + }, + "probe_id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + names.AttrProtocol: schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.Protocol](), + Required: true, + }, + "source_arn": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, names.AttrTags: tftags.TagsAttribute(), names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), - }, - Blocks: map[string]schema.Block{ - "probe": schema.SingleNestedBlock{ - Attributes: map[string]schema.Attribute{ - "address_family": schema.StringAttribute{ - Computed: true, - }, - names.AttrCreatedAt: schema.Int64Attribute{ - Computed: true, - }, - names.AttrDestination: schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.LengthBetween(1, 255), - }, - }, - "destination_port": schema.Int64Attribute{ - Optional: true, - Validators: []validator.Int64{ - int64validator.Between(0, 65536), - }, - }, - "modified_at": schema.Int64Attribute{ - Computed: true, - }, - "packet_size": schema.Int64Attribute{ - Optional: true, - Validators: []validator.Int64{ - int64validator.Between(56, 8500), - }, - }, - "probe_arn": schema.StringAttribute{ - Computed: true, - }, - "probe_id": schema.StringAttribute{ - Computed: true, - }, - names.AttrProtocol: schema.StringAttribute{ - Required: true, - }, - "source_arn": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.LengthBetween(20, 2048), - stringvalidator.RegexMatches(regexache.MustCompile("arn:.*"), "Must match pattern arn:*"), - }, - }, - names.AttrTags: schema.MapAttribute{ - ElementType: types.StringType, - Computed: true, - }, - names.AttrState: schema.StringAttribute{ - Computed: true, - }, - names.AttrVPCID: schema.StringAttribute{ - Computed: true, - }, + names.AttrVPCID: schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, }, }, } } -func (r *resourceNetworkMonitorProbe) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - conn := r.Meta().NetworkMonitorClient(ctx) - - var state resourceNetworkMonitorProbeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *probeResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data probeResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - var probe probeConfigModel - resp.Diagnostics.Append( - state.Probe.As( - ctx, - &probe, - basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})...) + conn := r.Meta().NetworkMonitorClient(ctx) - probeConfig := expandProbeConfig(ctx, probe) - if resp.Diagnostics.HasError() { + input := &networkmonitor.CreateProbeInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, data, input)...) + if response.Diagnostics.HasError() { return } - input := networkmonitor.CreateProbeInput{ - MonitorName: state.MonitorName.ValueStringPointer(), - Probe: &probeConfig, - Tags: flex.ExpandFrameworkStringValueMap(ctx, state.Tags), - } + input.ClientToken = aws.String(id.UniqueId()) + input.Tags = getTagsIn(ctx) + + outputCP, err := conn.CreateProbe(ctx, input) - out, err := conn.CreateProbe(ctx, &input) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionCreating, ResNameProbe, state.MonitorName.String(), nil), - err.Error(), - ) + response.Diagnostics.AddError("creating CloudWatch Network Monitor Probe (%s)", err.Error()) + return } - probeID := fmt.Sprintf("%s:%s", *out.ProbeId, *state.MonitorName.ValueStringPointer()) + // Set values for unknowns. + data.ProbeARN = fwflex.StringToFramework(ctx, outputCP.ProbeArn) + data.ProbeID = fwflex.StringToFramework(ctx, outputCP.ProbeId) + data.setID() + + outputGP, err := waitProbeReady(ctx, conn, data.MonitorName.ValueString(), data.ProbeID.ValueString()) - outWait, err := waitProbeReady(ctx, conn, probeID) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForCreation, ResNameProbe, probeID, nil), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("waiting for CloudWatch Network Monitor Probe (%s) create", data.ID.ValueString()), err.Error()) + + return } - state.ID = flex.StringToFramework(ctx, &probeID) - state.MonitorName = flex.StringToFramework(ctx, state.MonitorName.ValueStringPointer()) - p, d := flattenProbeConfig(ctx, *outWait) - resp.Diagnostics.Append(d...) - state.Probe = p - state.Arn = flex.StringToFramework(ctx, out.ProbeArn) + // Set values for unknowns. + data.AddressFamily = fwtypes.StringEnumValue(outputGP.AddressFamily) + data.VpcID = fwflex.StringToFramework(ctx, outputGP.VpcId) - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *resourceNetworkMonitorProbe) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { +func (r *probeResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data probeResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + if err := data.InitFromID(); err != nil { + response.Diagnostics.AddError("parsing resource ID", err.Error()) + + return + } + conn := r.Meta().NetworkMonitorClient(ctx) - var state resourceNetworkMonitorProbeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { + output, err := findProbeByTwoPartKey(ctx, conn, data.MonitorName.ValueString(), data.ProbeID.ValueString()) + + if tfresource.NotFound(err) { + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) + return } - out, err := FindProbeByID(ctx, conn, state.ID.ValueString()) if err != nil { - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - resp.State.RemoveResource(ctx) - return - } - if tfresource.NotFound(err) { - resp.State.RemoveResource(ctx) - return - } - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameProbe, state.ID.String(), err), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("reading CloudWatch Network Monitor Probe (%s)", data.ID.String()), err.Error()) + return } - state.ID = flex.StringToFramework(ctx, state.ID.ValueStringPointer()) - _, monitorName, err := probeParseID(state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionReading, ResNameProbe, state.ID.String(), err), - err.Error(), - ) + // Set attributes for import. + response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...) + if response.Diagnostics.HasError() { return } - state.MonitorName = flex.StringToFramework(ctx, &monitorName) - state.Arn = flex.StringToFramework(ctx, out.ProbeArn) - p, d := flattenProbeConfig(ctx, *out) - resp.Diagnostics.Append(d...) - state.Probe = p + setTagsOut(ctx, output.Tags) - setTagsOut(ctx, out.Tags) - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *resourceNetworkMonitorProbe) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - conn := r.Meta().NetworkMonitorClient(ctx) - - var plan, state resourceNetworkMonitorProbeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *probeResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var old, new probeResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &new)...) + if response.Diagnostics.HasError() { + return + } + response.Diagnostics.Append(request.State.Get(ctx, &old)...) + if response.Diagnostics.HasError() { return } - var probePlan, probeState probeConfigModel - resp.Diagnostics.Append( - plan.Probe.As( - ctx, - &probePlan, - basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})...) + conn := r.Meta().NetworkMonitorClient(ctx) - resp.Diagnostics.Append( - state.Probe.As( - ctx, - &probeState, - basetypes.ObjectAsOptions{UnhandledNullAsEmpty: false, UnhandledUnknownAsEmpty: false})...) + if !new.Destination.Equal(old.Destination) || + !new.DestinationPort.Equal(old.DestinationPort) || + !new.PacketSize.Equal(old.PacketSize) || + !new.Protocol.Equal(old.Protocol) { + input := &networkmonitor.UpdateProbeInput{ + MonitorName: fwflex.StringFromFramework(ctx, new.MonitorName), + ProbeId: fwflex.StringFromFramework(ctx, new.ProbeID), + } - probeID, monitorName, err := probeParseID(state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameProbe, state.ID.String(), err), - err.Error(), - ) - return - } + if !new.Destination.Equal(old.Destination) { + input.Destination = fwflex.StringFromFramework(ctx, new.Destination) + } + if !new.DestinationPort.Equal(old.DestinationPort) { + input.DestinationPort = fwflex.Int32FromFramework(ctx, new.DestinationPort) + } + if !new.PacketSize.Equal(old.PacketSize) { + input.PacketSize = fwflex.Int32FromFramework(ctx, new.PacketSize) + } + if !new.Protocol.Equal(old.Protocol) { + input.Protocol = new.Protocol.ValueEnum() + } - in := networkmonitor.UpdateProbeInput{ - MonitorName: &monitorName, - ProbeId: &probeID, - } + _, err := conn.UpdateProbe(ctx, input) - if !probePlan.Destination.Equal(probeState.Destination) { - in.Destination = probePlan.Destination.ValueStringPointer() - } - if !probePlan.DestinationPort.Equal(probeState.DestinationPort) { - in.DestinationPort = aws.Int32(int32(probePlan.DestinationPort.ValueInt64())) - } - if !probePlan.PacketSize.Equal(probeState.PacketSize) { - in.PacketSize = aws.Int32(int32(probePlan.PacketSize.ValueInt64())) - } - if !probePlan.Protocol.Equal(probeState.Protocol) { - in.Protocol = awstypes.Protocol(*probePlan.Protocol.ValueStringPointer()) - } + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("updating CloudWatch Network Monitor Probe (%s)", new.ID.String()), err.Error()) - _, err = conn.UpdateProbe(ctx, &in) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionUpdating, ResNameProbe, state.ID.String(), nil), - err.Error(), - ) - return - } + return + } - out, err := waitProbeReady(ctx, conn, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForUpdate, ResNameProbe, state.ID.String(), nil), - err.Error(), - ) + outputGP, err := waitProbeReady(ctx, conn, new.MonitorName.ValueString(), new.ProbeID.ValueString()) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for CloudWatch Network Monitor Probe (%s) update", new.ID.ValueString()), err.Error()) + + return + } + + // Set values for unknowns. + new.AddressFamily = fwtypes.StringEnumValue(outputGP.AddressFamily) + } else { + new.AddressFamily = old.AddressFamily } - state = plan - p, d := flattenProbeConfig(ctx, *out) - resp.Diagnostics.Append(d...) - state.Probe = p - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + response.Diagnostics.Append(response.State.Set(ctx, &new)...) } -func (r *resourceNetworkMonitorProbe) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - conn := r.Meta().NetworkMonitorClient(ctx) - - var state resourceNetworkMonitorProbeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *probeResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var data probeResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - probeID, monitorName, err := probeParseID(state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameProbe, state.ID.String(), nil), - err.Error(), - ) + conn := r.Meta().NetworkMonitorClient(ctx) + + _, err := conn.DeleteProbe(ctx, &networkmonitor.DeleteProbeInput{ + MonitorName: fwflex.StringFromFramework(ctx, data.MonitorName), + ProbeId: fwflex.StringFromFramework(ctx, data.ProbeID), + }) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return } - input := networkmonitor.DeleteProbeInput{ - MonitorName: &monitorName, - ProbeId: &probeID, - } - _, err = conn.DeleteProbe(ctx, &input) if err != nil { - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return - } + response.Diagnostics.AddError(fmt.Sprintf("deleting CloudWatch Network Monitor Probe (%s)", data.ID.ValueString()), err.Error()) - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionDeleting, ResNameProbe, state.ID.String(), nil), - err.Error(), - ) return } - _, err = waitProbeDeleted(ctx, conn, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.NetworkMonitor, create.ErrActionWaitingForDeletion, ResNameProbe, state.ID.String(), nil), - err.Error(), - ) + if _, err := waitProbeDeleted(ctx, conn, data.MonitorName.ValueString(), data.ProbeID.ValueString()); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for CloudWatch Network Monitor Probe (%s) delete", data.ID.ValueString()), err.Error()) + return } } -func (r *resourceNetworkMonitorProbe) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - r.SetTagsAll(ctx, req, resp) +func (r *probeResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) } -func (r *resourceNetworkMonitorProbe) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp) +func findProbeByTwoPartKey(ctx context.Context, conn *networkmonitor.Client, monitorName, probeID string) (*networkmonitor.GetProbeOutput, error) { + input := &networkmonitor.GetProbeInput{ + MonitorName: aws.String(monitorName), + ProbeId: aws.String(probeID), + } + + output, err := conn.GetProbe(ctx, input) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil } -func statusProbe(ctx context.Context, conn *networkmonitor.Client, id string) retry.StateRefreshFunc { +func statusProbe(ctx context.Context, conn *networkmonitor.Client, monitorName, probeID string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindProbeByID(ctx, conn, id) + output, err := findProbeByTwoPartKey(ctx, conn, monitorName, probeID) if tfresource.NotFound(err) { return nil, "", nil } - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return nil, "", nil - } - if err != nil { return nil, "", err } @@ -387,16 +339,20 @@ func statusProbe(ctx context.Context, conn *networkmonitor.Client, id string) re } } -func waitProbeReady(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetProbeOutput, error) { +func waitProbeReady(ctx context.Context, conn *networkmonitor.Client, monitorName, probeID string) (*networkmonitor.GetProbeOutput, error) { + const ( + timeout = time.Minute * 15 + ) stateConf := &retry.StateChangeConf{ Pending: enum.Slice(awstypes.ProbeStatePending), Target: enum.Slice(awstypes.ProbeStateActive, awstypes.ProbeStateInactive), - Refresh: statusProbe(ctx, conn, id), - Timeout: ProbeTimeout, + Refresh: statusProbe(ctx, conn, monitorName, probeID), + Timeout: timeout, MinTimeout: 10 * time.Second, } outputRaw, err := stateConf.WaitForStateContext(ctx) + if output, ok := outputRaw.(*networkmonitor.GetProbeOutput); ok { return output, err } @@ -404,16 +360,20 @@ func waitProbeReady(ctx context.Context, conn *networkmonitor.Client, id string) return nil, err } -func waitProbeDeleted(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetProbeOutput, error) { +func waitProbeDeleted(ctx context.Context, conn *networkmonitor.Client, monitorName, probeID string) (*networkmonitor.GetProbeOutput, error) { + const ( + timeout = time.Minute * 15 + ) stateConf := &retry.StateChangeConf{ Pending: enum.Slice(awstypes.ProbeStateActive, awstypes.ProbeStateInactive, awstypes.ProbeStateDeleting), Target: []string{}, - Refresh: statusProbe(ctx, conn, id), - Timeout: ProbeTimeout, + Refresh: statusProbe(ctx, conn, monitorName, probeID), + Timeout: timeout, MinTimeout: 10 * time.Second, } outputRaw, err := stateConf.WaitForStateContext(ctx) + if output, ok := outputRaw.(*networkmonitor.GetProbeOutput); ok { return output, err } @@ -421,111 +381,40 @@ func waitProbeDeleted(ctx context.Context, conn *networkmonitor.Client, id strin return nil, err } -func FindProbeByID(ctx context.Context, conn *networkmonitor.Client, id string) (*networkmonitor.GetProbeOutput, error) { - probeID, monitorName, err := probeParseID(id) - if err != nil { - return nil, err - } - - input := &networkmonitor.GetProbeInput{ - ProbeId: &probeID, - MonitorName: &monitorName, - } - - output, err := conn.GetProbe(ctx, input) - if err != nil { - return nil, err - } - - if output == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - return output, nil +type probeResourceModel struct { + AddressFamily fwtypes.StringEnum[awstypes.AddressFamily] `tfsdk:"address_family"` + Destination types.String `tfsdk:"destination"` + DestinationPort types.Int64 `tfsdk:"destination_port"` + ID types.String `tfsdk:"id"` + MonitorName types.String `tfsdk:"monitor_name"` + PacketSize types.Int64 `tfsdk:"packet_size"` + ProbeARN types.String `tfsdk:"arn"` + ProbeID types.String `tfsdk:"probe_id"` + Protocol fwtypes.StringEnum[awstypes.Protocol] `tfsdk:"protocol"` + SourceARN fwtypes.ARN `tfsdk:"source_arn"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` + VpcID types.String `tfsdk:"vpc_id"` } -func probeParseID(id string) (string, string, error) { - parts := strings.SplitN(id, ":", 2) - - if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - return "", "", fmt.Errorf("unexpected format of ID (%s), expected probeID:monitorName", id) - } - - return parts[0], parts[1], nil -} - -var probeConfigTypes = map[string]attr.Type{ - "address_family": types.StringType, - names.AttrCreatedAt: types.Int64Type, - names.AttrDestination: types.StringType, - "destination_port": types.Int64Type, - "modified_at": types.Int64Type, - "packet_size": types.Int64Type, - "probe_arn": types.StringType, - "probe_id": types.StringType, - names.AttrProtocol: types.StringType, - "source_arn": types.StringType, - names.AttrState: types.StringType, - names.AttrTags: types.MapType{ElemType: types.StringType}, - names.AttrVPCID: types.StringType, -} - -type resourceNetworkMonitorProbeModel struct { - ID types.String `tfsdk:"id"` - Arn types.String `tfsdk:"arn"` - MonitorName types.String `tfsdk:"monitor_name"` - Probe types.Object `tfsdk:"probe"` - Tags types.Map `tfsdk:"tags"` - TagsAll types.Map `tfsdk:"tags_all"` -} +const ( + probeResourceIDPartCount = 2 +) -type probeConfigModel struct { - AddressFamily types.String `tfsdk:"address_family"` - CreatedAt types.Int64 `tfsdk:"created_at"` - Destination types.String `tfsdk:"destination"` - DestinationPort types.Int64 `tfsdk:"destination_port"` - ModifiedAt types.Int64 `tfsdk:"modified_at"` - PacketSize types.Int64 `tfsdk:"packet_size"` - ProbeArn types.String `tfsdk:"probe_arn"` - ProbeId types.String `tfsdk:"probe_id"` - Tags types.Map `tfsdk:"tags"` - Protocol types.String `tfsdk:"protocol"` - SourceArn types.String `tfsdk:"source_arn"` - State types.String `tfsdk:"state"` - VpcId types.String `tfsdk:"vpc_id"` -} +func (m *probeResourceModel) InitFromID() error { + id := m.ID.ValueString() + parts, err := flex.ExpandResourceId(id, probeResourceIDPartCount, false) -func flattenProbeConfig(ctx context.Context, object networkmonitor.GetProbeOutput) (types.Object, diag.Diagnostics) { - var diags diag.Diagnostics - - t := map[string]attr.Value{ - "address_family": flex.StringToFramework(ctx, (*string)(&object.AddressFamily)), - names.AttrCreatedAt: flex.Int64ToFramework(ctx, aws.Int64(object.CreatedAt.Unix())), - names.AttrDestination: flex.StringToFramework(ctx, object.Destination), - "destination_port": flex.Int64ToFramework(ctx, aws.Int64(int64(*object.DestinationPort))), - "modified_at": flex.Int64ToFramework(ctx, aws.Int64(object.ModifiedAt.Unix())), - "packet_size": flex.Int64ToFramework(ctx, aws.Int64(int64(*object.PacketSize))), - "probe_arn": flex.StringToFramework(ctx, object.ProbeArn), - "probe_id": flex.StringToFramework(ctx, object.ProbeId), - names.AttrProtocol: flex.StringToFramework(ctx, (*string)(&object.Protocol)), - "source_arn": flex.StringToFramework(ctx, object.SourceArn), - names.AttrState: flex.StringToFramework(ctx, (*string)(&object.State)), - names.AttrTags: flex.FlattenFrameworkStringValueMap(ctx, object.Tags), - names.AttrVPCID: flex.StringToFramework(ctx, object.VpcId), + if err != nil { + return err } - objVal, d := types.ObjectValue(probeConfigTypes, t) - diags.Append(d...) - return objVal, diags + m.MonitorName = types.StringValue(parts[0]) + m.ProbeID = types.StringValue(parts[1]) + + return nil } -func expandProbeConfig(ctx context.Context, object probeConfigModel) awstypes.ProbeInput { - return awstypes.ProbeInput{ - Destination: object.Destination.ValueStringPointer(), - DestinationPort: aws.Int32(int32(object.DestinationPort.ValueInt64())), - PacketSize: aws.Int32(int32(object.PacketSize.ValueInt64())), - Protocol: awstypes.Protocol(object.Protocol.ValueString()), - SourceArn: object.SourceArn.ValueStringPointer(), - Tags: flex.ExpandFrameworkStringValueMap(ctx, object.Tags), - } +func (m *probeResourceModel) setID() { + m.ID = types.StringValue(errs.Must(flex.FlattenResourceId([]string{m.MonitorName.ValueString(), m.ProbeID.ValueString()}, probeResourceIDPartCount, false))) } diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go index 8d3629cee8a..2984f8f26a5 100644 --- a/internal/service/networkmonitor/probe_test.go +++ b/internal/service/networkmonitor/probe_test.go @@ -5,17 +5,14 @@ package networkmonitor_test import ( "context" - "errors" "fmt" "testing" - awstypes "github.com/aws/aws-sdk-go-v2/service/networkmonitor/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/create" tfnetworkmonitor "github.com/hashicorp/terraform-provider-aws/internal/service/networkmonitor" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -32,16 +29,18 @@ func TestAccNetworkMonitorProbe_basic(t *testing.T) { CheckDestroy: testAccCheckProbeDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProbeConfig_basic(rName, "10.0.0.1", 8080, 200), + Config: testAccProbeConfig_basic(rName, "10.0.0.1"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckProbeExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "address_family"), resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), - resource.TestCheckResourceAttr(resourceName, "probe.destination", "10.0.0.1"), - resource.TestCheckResourceAttr(resourceName, "probe.destination_port", "8080"), - resource.TestCheckResourceAttr(resourceName, "probe.packet_size", "200"), - resource.TestCheckResourceAttr(resourceName, "probe.protocol", "TCP"), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "tags.tag1", rName), + resource.TestCheckResourceAttr(resourceName, "destination", "10.0.0.1"), + resource.TestCheckNoResourceAttr(resourceName, "destination_port"), + resource.TestCheckNoResourceAttr(resourceName, "packet_size"), + resource.TestCheckResourceAttrSet(resourceName, "probe_id"), + resource.TestCheckResourceAttr(resourceName, "protocol", "TCP"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), + resource.TestCheckResourceAttrSet(resourceName, names.AttrVPCID), ), }, { @@ -53,65 +52,117 @@ func TestAccNetworkMonitorProbe_basic(t *testing.T) { }) } -func TestAccNetworkMonitorProbe_updates(t *testing.T) { +func TestAccNetworkMonitorProbe_disappears(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_networkmonitor_probe.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, "networkMonitor"), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckProbeDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProbeConfig_basic(rName, "10.0.0.1", 8080, 200), + Config: testAccProbeConfig_basic(rName, "10.0.0.1"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckProbeExists(ctx, resourceName), - resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), - resource.TestCheckResourceAttr(resourceName, "probe.destination", "10.0.0.1"), - resource.TestCheckResourceAttr(resourceName, "probe.destination_port", "8080"), - resource.TestCheckResourceAttr(resourceName, "probe.packet_size", "200"), - resource.TestCheckResourceAttr(resourceName, "probe.protocol", "TCP"), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfnetworkmonitor.ResourceProbe, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccNetworkMonitorProbe_tags(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_networkmonitor_probe.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProbeDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProbeConfig_tags1(rName, acctest.CtKey1, acctest.CtValue1), + Check: resource.ComposeTestCheckFunc( + testAccCheckProbeExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "tags.tag1", rName), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), ), }, { - Config: testAccProbeConfig_2tags(rName, "10.0.0.2", 8081, 300), - Check: resource.ComposeAggregateTestCheckFunc( + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccProbeConfig_tags2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( testAccCheckProbeExists(ctx, resourceName), - resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), - resource.TestCheckResourceAttr(resourceName, "probe.destination", "10.0.0.2"), - resource.TestCheckResourceAttr(resourceName, "probe.destination_port", "8081"), - resource.TestCheckResourceAttr(resourceName, "probe.packet_size", "300"), - resource.TestCheckResourceAttr(resourceName, "probe.protocol", "TCP"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), - resource.TestCheckResourceAttr(resourceName, "tags.tag1", rName), - resource.TestCheckResourceAttr(resourceName, "tags.tag2", rName), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), + ), + }, + { + Config: testAccProbeConfig_tags1(rName, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckProbeExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), ), }, }, }) } -func TestAccNetworkMonitorProbe_disappears(t *testing.T) { +func TestAccNetworkMonitorProbe_update(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_networkmonitor_probe.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, "networkMonitor"), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckProbeDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProbeConfig_basic(rName, "10.0.0.1", 8080, 200), - Check: resource.ComposeAggregateTestCheckFunc( + Config: testAccProbeConfig_full(rName, "10.0.0.1", 8080, 256), + Check: resource.ComposeTestCheckFunc( testAccCheckProbeExists(ctx, resourceName), - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfnetworkmonitor.ResourceProbe, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "address_family"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), + resource.TestCheckResourceAttr(resourceName, "destination", "10.0.0.1"), + resource.TestCheckResourceAttr(resourceName, "destination_port", "8080"), + resource.TestCheckResourceAttr(resourceName, "packet_size", "256"), + resource.TestCheckResourceAttrSet(resourceName, "probe_id"), + resource.TestCheckResourceAttr(resourceName, "protocol", "TCP"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), + resource.TestCheckResourceAttrSet(resourceName, names.AttrVPCID), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccProbeConfig_full(rName, "10.0.0.2", 8443, 512), + Check: resource.ComposeTestCheckFunc( + testAccCheckProbeExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "address_family"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), + resource.TestCheckResourceAttr(resourceName, "destination", "10.0.0.2"), + resource.TestCheckResourceAttr(resourceName, "destination_port", "8443"), + resource.TestCheckResourceAttr(resourceName, "packet_size", "512"), + resource.TestCheckResourceAttrSet(resourceName, "probe_id"), + resource.TestCheckResourceAttr(resourceName, "protocol", "TCP"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), + resource.TestCheckResourceAttrSet(resourceName, names.AttrVPCID), ), - ExpectNonEmptyPlan: true, }, }, }) @@ -126,113 +177,102 @@ func testAccCheckProbeDestroy(ctx context.Context) resource.TestCheckFunc { continue } - _, err := tfnetworkmonitor.FindProbeByID(ctx, conn, rs.Primary.ID) - if err != nil { - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return nil - } + _, err := tfnetworkmonitor.FindProbeByTwoPartKey(ctx, conn, rs.Primary.Attributes["monitor_name"], rs.Primary.Attributes["probe_id"]) - if tfresource.NotFound(err) { - return nil - } + if tfresource.NotFound(err) { + continue + } + if err != nil { return err } - return create.Error(names.NetworkMonitor, create.ErrActionCheckingDestroyed, tfnetworkmonitor.ResNameMonitor, rs.Primary.ID, errors.New("not destroyed")) + return fmt.Errorf("CloudWatch Network Monitor Probe %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckProbeExists(ctx context.Context, name string) resource.TestCheckFunc { +func testAccCheckProbeExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameProbe, name, errors.New("not found")) - } - - if rs.Primary.ID == "" { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameProbe, name, errors.New("not set")) + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).NetworkMonitorClient(ctx) - _, err := tfnetworkmonitor.FindProbeByID(ctx, conn, rs.Primary.ID) - if err != nil { - return create.Error(names.NetworkMonitor, create.ErrActionCheckingExistence, tfnetworkmonitor.ResNameProbe, rs.Primary.ID, err) - } + _, err := tfnetworkmonitor.FindProbeByTwoPartKey(ctx, conn, rs.Primary.Attributes["monitor_name"], rs.Primary.Attributes["probe_id"]) - return nil + return err } } func testAccProbeConfig_base(rName string) string { - return fmt.Sprintf(` -data "aws_region" "current" {} - -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` +resource "aws_networkmonitor_monitor" "test" { + aggregation_period = 30 + monitor_name = %[1]q tags = { Name = %[1]q } } +`, rName)) +} -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 0) - - tags = { - Name = %[1]q - } +func testAccProbeConfig_basic(rName, destination string) string { + return acctest.ConfigCompose(testAccProbeConfig_base(rName), fmt.Sprintf(` +resource "aws_networkmonitor_probe" "test" { + monitor_name = aws_networkmonitor_monitor.test.monitor_name + destination = %[2]q + protocol = "TCP" + source_arn = aws_subnet.test[0].arn +} +`, rName, destination)) } -resource "aws_networkmonitor_monitor" "test" { - aggregation_period = 30 - monitor_name = %[1]q - tags = { - tag1 = %[1]q - } +func testAccProbeConfig_full(rName, destination string, port, packetSize int) string { + return acctest.ConfigCompose(testAccProbeConfig_base(rName), fmt.Sprintf(` +resource "aws_networkmonitor_probe" "test" { + monitor_name = aws_networkmonitor_monitor.test.monitor_name + destination = %[2]q + destination_port = %[3]d + protocol = "TCP" + source_arn = aws_subnet.test[0].arn + packet_size = %[4]d } -`, rName) +`, rName, destination, port, packetSize)) } -func testAccProbeConfig_basic(rName, destination string, port, packetSize int) string { +func testAccProbeConfig_tags1(rName, tagKey1, tagValue1 string) string { return acctest.ConfigCompose(testAccProbeConfig_base(rName), fmt.Sprintf(` resource "aws_networkmonitor_probe" "test" { monitor_name = aws_networkmonitor_monitor.test.monitor_name - probe { - destination = %[2]q - destination_port = %[3]d - protocol = "TCP" - source_arn = aws_subnet.test.arn - packet_size = %[4]d - } + destination = "10.0.0.1" + protocol = "TCP" + source_arn = aws_subnet.test[0].arn + tags = { - tag1 = %[1]q + %[2]q = %[3]q } } -`, rName, destination, port, packetSize)) +`, rName, tagKey1, tagValue1)) } -func testAccProbeConfig_2tags(rName, destination string, port, packetSize int) string { +func testAccProbeConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { return acctest.ConfigCompose(testAccProbeConfig_base(rName), fmt.Sprintf(` resource "aws_networkmonitor_probe" "test" { monitor_name = aws_networkmonitor_monitor.test.monitor_name - probe { - destination = %[2]q - destination_port = %[3]d - protocol = "TCP" - source_arn = aws_subnet.test.arn - packet_size = %[4]d - } + destination = "10.0.0.1" + protocol = "TCP" + source_arn = aws_subnet.test[0].arn + tags = { - tag1 = %[1]q - tag2 = %[1]q + %[2]q = %[3]q + %[4]q = %[5]q } } -`, rName, destination, port, packetSize)) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } diff --git a/internal/service/networkmonitor/service_package_gen.go b/internal/service/networkmonitor/service_package_gen.go index e4008566741..de51b85ce97 100644 --- a/internal/service/networkmonitor/service_package_gen.go +++ b/internal/service/networkmonitor/service_package_gen.go @@ -28,8 +28,8 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic }, }, { - Factory: newResourceProbe, - Name: "CloudWatch Network Monitor Probe", + Factory: newProbeResource, + Name: "Probe", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, diff --git a/website/docs/r/networkmonitor_probe.html.markdown b/website/docs/r/networkmonitor_probe.html.markdown index cfdfcaece32..d63a93d5484 100644 --- a/website/docs/r/networkmonitor_probe.html.markdown +++ b/website/docs/r/networkmonitor_probe.html.markdown @@ -21,14 +21,12 @@ resource "aws_networkmonitor_monitor" "example" { } resource "aws_networkmonitor_probe" "test" { - monitor_name = aws_networkmonitor_monitor.example.monitor_name - probe { - destination = 127.0.0.1 - destination_port = 80 - protocol = "TCP" - source_arn = aws_subnet.example.arn - packet_size = 200 - } + monitor_name = aws_networkmonitor_monitor.example.monitor_name + destination = "127.0.0.1" + destination_port = 80 + protocol = "TCP" + source_arn = aws_subnet.example.arn + packet_size = 200 } ``` @@ -36,16 +34,12 @@ resource "aws_networkmonitor_probe" "test" { The following arguments are required: -- `monitor_name` - (Required) The name of the monitor. -- `probe` - (Required) Describes the details of an individual probe for a monitor. - -The `probe` object supports the following: - - `destination` - (Required) The destination IP address. This must be either IPV4 or IPV6. -- `destination_port` - (Optional) The port associated with the destination. This is required only if the protocol is TCP and must be a number between 1 and 65536 +- `destination_port` - (Optional) The port associated with the destination. This is required only if the protocol is TCP and must be a number between 1 and 65536. +- `monitor_name` - (Required) The name of the monitor. - `protocol` - (Required) The protocol used for the network traffic between the source and destination. This must be either TCP or ICMP. - `source_arn` - (Required) The ARN of the subnet. -- `packet_size` - (Optional) The size of the packets sent between the source and destination. This must be a number between 56 and 8500 +- `packet_size` - (Optional) The size of the packets sent between the source and destination. This must be a number between 56 and 8500. The following arguments are optional: @@ -56,14 +50,7 @@ The following arguments are optional: This resource exports the following attributes in addition to the arguments above: - `arn` - The ARN of the attachment. -- `monitor_name` - The name of the monitor. -- `probe` - Describes the details of an individual probe for a monitor. -- `destination` - The destination IP address. -- `destination_port` - The port associated with the destination. -- `protocol` - The protocol used for the network traffic between the source and destination. - `source_arn` - The ARN of the subnet. -- `packet_size` - The size of the packets sent between the source and destination. -- `state` - The state of the monitor. - `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). ## Import @@ -73,12 +60,12 @@ In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashico ```terraform import { to = aws_networkmonitor_probe.example - id = "probe-3qm8p693i4fi1h8lqylzkbp42e:monitor-7786087912324693644" + id = "monitor-7786087912324693644,probe-3qm8p693i4fi1h8lqylzkbp42e" } ``` Using `terraform import`, import `aws_networkmonitor_probe` using the monitor name and probe id. For example: ```console -% terraform import aws_networkmonitor_probe.example probe-3qm8p693i4fi1h8lqylzkbp42e:monitor-7786087912324693644 +% terraform import aws_networkmonitor_probe.example monitor-7786087912324693644,probe-3qm8p693i4fi1h8lqylzkbp42e ``` From bbf4f9bc5826e3efd42cfe8030f37069d9cd5836 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 23 Jun 2024 16:36:38 -0400 Subject: [PATCH 10/20] Add CHANGELOG entry. --- .changelog/35722.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/35722.txt diff --git a/.changelog/35722.txt b/.changelog/35722.txt new file mode 100644 index 00000000000..68bffb9b829 --- /dev/null +++ b/.changelog/35722.txt @@ -0,0 +1,7 @@ +```release-note:new-resource +aws_networkmonitor_monitor +``` + +```release-note:new-resource +aws_networkmonitor_probe +``` \ No newline at end of file From eecd94fd74c0138564c5e82e0f18fa0e89562002 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 23 Jun 2024 16:37:17 -0400 Subject: [PATCH 11/20] Run 'make fix-constants PKG=networkmonitor'. --- internal/service/networkmonitor/probe_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go index 2984f8f26a5..687f747c5f4 100644 --- a/internal/service/networkmonitor/probe_test.go +++ b/internal/service/networkmonitor/probe_test.go @@ -34,11 +34,11 @@ func TestAccNetworkMonitorProbe_basic(t *testing.T) { testAccCheckProbeExists(ctx, resourceName), resource.TestCheckResourceAttrSet(resourceName, "address_family"), resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), - resource.TestCheckResourceAttr(resourceName, "destination", "10.0.0.1"), + resource.TestCheckResourceAttr(resourceName, names.AttrDestination, "10.0.0.1"), resource.TestCheckNoResourceAttr(resourceName, "destination_port"), resource.TestCheckNoResourceAttr(resourceName, "packet_size"), resource.TestCheckResourceAttrSet(resourceName, "probe_id"), - resource.TestCheckResourceAttr(resourceName, "protocol", "TCP"), + resource.TestCheckResourceAttr(resourceName, names.AttrProtocol, "TCP"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttrSet(resourceName, names.AttrVPCID), ), @@ -135,11 +135,11 @@ func TestAccNetworkMonitorProbe_update(t *testing.T) { testAccCheckProbeExists(ctx, resourceName), resource.TestCheckResourceAttrSet(resourceName, "address_family"), resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), - resource.TestCheckResourceAttr(resourceName, "destination", "10.0.0.1"), + resource.TestCheckResourceAttr(resourceName, names.AttrDestination, "10.0.0.1"), resource.TestCheckResourceAttr(resourceName, "destination_port", "8080"), resource.TestCheckResourceAttr(resourceName, "packet_size", "256"), resource.TestCheckResourceAttrSet(resourceName, "probe_id"), - resource.TestCheckResourceAttr(resourceName, "protocol", "TCP"), + resource.TestCheckResourceAttr(resourceName, names.AttrProtocol, "TCP"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttrSet(resourceName, names.AttrVPCID), ), @@ -155,11 +155,11 @@ func TestAccNetworkMonitorProbe_update(t *testing.T) { testAccCheckProbeExists(ctx, resourceName), resource.TestCheckResourceAttrSet(resourceName, "address_family"), resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), - resource.TestCheckResourceAttr(resourceName, "destination", "10.0.0.2"), + resource.TestCheckResourceAttr(resourceName, names.AttrDestination, "10.0.0.2"), resource.TestCheckResourceAttr(resourceName, "destination_port", "8443"), resource.TestCheckResourceAttr(resourceName, "packet_size", "512"), resource.TestCheckResourceAttrSet(resourceName, "probe_id"), - resource.TestCheckResourceAttr(resourceName, "protocol", "TCP"), + resource.TestCheckResourceAttr(resourceName, names.AttrProtocol, "TCP"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttrSet(resourceName, names.AttrVPCID), ), From f0fdf95b67845c98192557de4f3ca79a91ac9a7f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 23 Jun 2024 16:39:46 -0400 Subject: [PATCH 12/20] Use 'names.NetworkMonitorServiceID'. --- internal/service/networkmonitor/monitor_test.go | 7 +++---- internal/service/networkmonitor/probe_test.go | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/service/networkmonitor/monitor_test.go b/internal/service/networkmonitor/monitor_test.go index c00cbb23c63..17be63198f2 100644 --- a/internal/service/networkmonitor/monitor_test.go +++ b/internal/service/networkmonitor/monitor_test.go @@ -8,7 +8,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/service/networkmonitor" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -26,7 +25,7 @@ func TestAccNetworkMonitorMonitor_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, networkmonitor.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.NetworkMonitorServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMonitorDestroy(ctx), Steps: []resource.TestStep{ @@ -62,7 +61,7 @@ func TestAccNetworkMonitorMonitor_tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, networkmonitor.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.NetworkMonitorServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMonitorDestroy(ctx), Steps: []resource.TestStep{ @@ -107,7 +106,7 @@ func TestAccNetworkMonitorMonitor_disappears(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, networkmonitor.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.NetworkMonitorServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMonitorDestroy(ctx), Steps: []resource.TestStep{ diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go index 687f747c5f4..fd05547f418 100644 --- a/internal/service/networkmonitor/probe_test.go +++ b/internal/service/networkmonitor/probe_test.go @@ -25,6 +25,7 @@ func TestAccNetworkMonitorProbe_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.NetworkMonitorServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckProbeDestroy(ctx), Steps: []resource.TestStep{ @@ -59,7 +60,7 @@ func TestAccNetworkMonitorProbe_disappears(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, "networkMonitor"), + ErrorCheck: acctest.ErrorCheck(t, names.NetworkMonitorServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckProbeDestroy(ctx), Steps: []resource.TestStep{ @@ -82,6 +83,7 @@ func TestAccNetworkMonitorProbe_tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.NetworkMonitorServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckProbeDestroy(ctx), Steps: []resource.TestStep{ @@ -126,6 +128,7 @@ func TestAccNetworkMonitorProbe_update(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.NetworkMonitorServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckProbeDestroy(ctx), Steps: []resource.TestStep{ From c8e338bb7678f5f574d6ca41b3957909d2e148f3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 23 Jun 2024 16:43:36 -0400 Subject: [PATCH 13/20] Fix golangci-lint 'unparam'. --- internal/service/networkmonitor/monitor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/networkmonitor/monitor.go b/internal/service/networkmonitor/monitor.go index 367fac7ac4a..c47a7836865 100644 --- a/internal/service/networkmonitor/monitor.go +++ b/internal/service/networkmonitor/monitor.go @@ -268,7 +268,7 @@ func statusMonitor(ctx context.Context, conn *networkmonitor.Client, name string } } -func waitMonitorReady(ctx context.Context, conn *networkmonitor.Client, name string) (*networkmonitor.GetMonitorOutput, error) { +func waitMonitorReady(ctx context.Context, conn *networkmonitor.Client, name string) (*networkmonitor.GetMonitorOutput, error) { //nolint:unparam const ( timeout = time.Minute * 10 ) From a07128bec1dec6f6fb50d88fb9316a89846719a0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 24 Jun 2024 07:47:34 -0400 Subject: [PATCH 14/20] r/aws_networkmonitor_probe: Fixes after some testing. --- internal/service/networkmonitor/probe.go | 21 +++++++++++++++---- internal/service/networkmonitor/probe_test.go | 12 ++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/internal/service/networkmonitor/probe.go b/internal/service/networkmonitor/probe.go index 02a2b817cff..e0f44ab1bc6 100644 --- a/internal/service/networkmonitor/probe.go +++ b/internal/service/networkmonitor/probe.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -82,6 +83,10 @@ func (r *probeResource) Schema(ctx context.Context, request resource.SchemaReque }, "packet_size": schema.Int64Attribute{ Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, Validators: []validator.Int64{ int64validator.Between(56, 8500), }, @@ -124,14 +129,18 @@ func (r *probeResource) Create(ctx context.Context, request resource.CreateReque conn := r.Meta().NetworkMonitorClient(ctx) - input := &networkmonitor.CreateProbeInput{} - response.Diagnostics.Append(fwflex.Expand(ctx, data, input)...) + probeInput := &awstypes.ProbeInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, data, probeInput)...) if response.Diagnostics.HasError() { return } - input.ClientToken = aws.String(id.UniqueId()) - input.Tags = getTagsIn(ctx) + input := &networkmonitor.CreateProbeInput{ + ClientToken: aws.String(id.UniqueId()), + MonitorName: fwflex.StringFromFramework(ctx, data.MonitorName), + Probe: probeInput, + Tags: getTagsIn(ctx), + } outputCP, err := conn.CreateProbe(ctx, input) @@ -156,6 +165,10 @@ func (r *probeResource) Create(ctx context.Context, request resource.CreateReque // Set values for unknowns. data.AddressFamily = fwtypes.StringEnumValue(outputGP.AddressFamily) + if data.PacketSize.IsUnknown() { + data.PacketSize = fwflex.Int32ToFramework(ctx, outputGP.PacketSize) + + } data.VpcID = fwflex.StringToFramework(ctx, outputGP.VpcId) response.Diagnostics.Append(response.State.Set(ctx, &data)...) diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go index fd05547f418..eb4a3adf030 100644 --- a/internal/service/networkmonitor/probe_test.go +++ b/internal/service/networkmonitor/probe_test.go @@ -37,9 +37,9 @@ func TestAccNetworkMonitorProbe_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), resource.TestCheckResourceAttr(resourceName, names.AttrDestination, "10.0.0.1"), resource.TestCheckNoResourceAttr(resourceName, "destination_port"), - resource.TestCheckNoResourceAttr(resourceName, "packet_size"), + resource.TestCheckResourceAttrSet(resourceName, "packet_size"), resource.TestCheckResourceAttrSet(resourceName, "probe_id"), - resource.TestCheckResourceAttr(resourceName, names.AttrProtocol, "TCP"), + resource.TestCheckResourceAttr(resourceName, names.AttrProtocol, "ICMP"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttrSet(resourceName, names.AttrVPCID), ), @@ -49,6 +49,8 @@ func TestAccNetworkMonitorProbe_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + // TODO Need to delete the AmazonCloudWatchNetworkMonitor_ security group, else + // "Error: deleting EC2 VPC (vpc-00620dca13a12fd02): operation error EC2: DeleteVpc, https response error StatusCode: 400, RequestID: 3102b862-e45e-4921-8aba-0f8a4bdfe29b, api error DependencyViolation: The vpc 'vpc-00620dca13a12fd02' has dependencies and cannot be deleted" }, }) } @@ -230,7 +232,7 @@ func testAccProbeConfig_basic(rName, destination string) string { resource "aws_networkmonitor_probe" "test" { monitor_name = aws_networkmonitor_monitor.test.monitor_name destination = %[2]q - protocol = "TCP" + protocol = "ICMP" source_arn = aws_subnet.test[0].arn } `, rName, destination)) @@ -254,7 +256,7 @@ func testAccProbeConfig_tags1(rName, tagKey1, tagValue1 string) string { resource "aws_networkmonitor_probe" "test" { monitor_name = aws_networkmonitor_monitor.test.monitor_name destination = "10.0.0.1" - protocol = "TCP" + protocol = "ICMP" source_arn = aws_subnet.test[0].arn tags = { @@ -269,7 +271,7 @@ func testAccProbeConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 stri resource "aws_networkmonitor_probe" "test" { monitor_name = aws_networkmonitor_monitor.test.monitor_name destination = "10.0.0.1" - protocol = "TCP" + protocol = "ICMP" source_arn = aws_subnet.test[0].arn tags = { From ddcc11bac8588497e5531efbbfa69317aeaf2ef9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 24 Jun 2024 08:08:03 -0400 Subject: [PATCH 15/20] ec2: Add 'FindSecurityGroupByDescriptionAndVPCID'. --- internal/service/ec2/exports.go | 3 +++ internal/service/ec2/find.go | 15 +++++++++++++-- internal/service/ec2/service_package_gen.go | 2 +- internal/service/ec2/vpc_security_group.go | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/internal/service/ec2/exports.go b/internal/service/ec2/exports.go index 78067a8dabe..681dc9a9918 100644 --- a/internal/service/ec2/exports.go +++ b/internal/service/ec2/exports.go @@ -12,13 +12,16 @@ var ( FindInstanceByID = findInstanceByID FindNetworkInterfacesByAttachmentInstanceOwnerIDAndDescription = findNetworkInterfacesByAttachmentInstanceOwnerIDAndDescription FindNetworkInterfacesV2 = findNetworkInterfaces + FindSecurityGroupByDescriptionAndVPCID = findSecurityGroupByDescriptionAndVPCID FindSecurityGroupByNameAndVPCID = findSecurityGroupByNameAndVPCID + FindSecurityGroupByNameAndVPCIDAndOwnerID = findSecurityGroupByNameAndVPCIDAndOwnerID FindVPCByIDV2 = findVPCByID FindVPCEndpointByID = findVPCEndpointByID NewCustomFilterListFrameworkV2 = newCustomFilterListFrameworkV2 NewFilter = newFilter NewFilterV2 = newFilterV2 ResourceAMI = resourceAMI + ResourceSecurityGroup = resourceSecurityGroup ResourceTransitGateway = resourceTransitGateway ResourceTransitGatewayConnectPeer = resourceTransitGatewayConnectPeer VPCEndpointCreationTimeout = vpcEndpointCreationTimeout diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index fd9d21fbb90..8be0294cc31 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -435,8 +435,19 @@ func FindSecurityGroupByID(ctx context.Context, conn *ec2.EC2, id string) (*ec2. return output, nil } -// FindSecurityGroupByNameAndVPCIDAndOwnerID looks up a security group by name, VPC ID and owner ID. Returns a retry.NotFoundError if not found. -func FindSecurityGroupByNameAndVPCIDAndOwnerID(ctx context.Context, conn *ec2.EC2, name, vpcID, ownerID string) (*ec2.SecurityGroup, error) { +func findSecurityGroupByDescriptionAndVPCID(ctx context.Context, conn *ec2.EC2, description, vpcID string) (*ec2.SecurityGroup, error) { + input := &ec2.DescribeSecurityGroupsInput{ + Filters: newAttributeFilterList( + map[string]string{ + "description": description, + "vpc-id": vpcID, + }, + ), + } + return FindSecurityGroup(ctx, conn, input) +} + +func findSecurityGroupByNameAndVPCIDAndOwnerID(ctx context.Context, conn *ec2.EC2, name, vpcID, ownerID string) (*ec2.SecurityGroup, error) { input := &ec2.DescribeSecurityGroupsInput{ Filters: newAttributeFilterList( map[string]string{ diff --git a/internal/service/ec2/service_package_gen.go b/internal/service/ec2/service_package_gen.go index 42565197ff7..9edd65786e7 100644 --- a/internal/service/ec2/service_package_gen.go +++ b/internal/service/ec2/service_package_gen.go @@ -1033,7 +1033,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka TypeName: "aws_route_table_association", }, { - Factory: ResourceSecurityGroup, + Factory: resourceSecurityGroup, TypeName: "aws_security_group", Name: "Security Group", Tags: &types.ServicePackageResourceTags{ diff --git a/internal/service/ec2/vpc_security_group.go b/internal/service/ec2/vpc_security_group.go index 0cf7358bf14..b47de6df567 100644 --- a/internal/service/ec2/vpc_security_group.go +++ b/internal/service/ec2/vpc_security_group.go @@ -37,7 +37,7 @@ import ( // @Tags(identifierAttribute="id") // @Testing(existsType="github.com/aws/aws-sdk-go/service/ec2;ec2.SecurityGroup") // @Testing(importIgnore="revoke_rules_on_delete") -func ResourceSecurityGroup() *schema.Resource { +func resourceSecurityGroup() *schema.Resource { //lintignore:R011 return &schema.Resource{ CreateWithoutTimeout: resourceSecurityGroupCreate, From c647f825e5c5fa4d499a5a31aeeda64389ad8e51 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 24 Jun 2024 08:38:08 -0400 Subject: [PATCH 16/20] r/aws_networkmonitor_probe: Clean up security group added by networkmonitor. --- internal/service/networkmonitor/probe_test.go | 69 ++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/internal/service/networkmonitor/probe_test.go b/internal/service/networkmonitor/probe_test.go index eb4a3adf030..1bbf0a6b253 100644 --- a/internal/service/networkmonitor/probe_test.go +++ b/internal/service/networkmonitor/probe_test.go @@ -8,11 +8,14 @@ import ( "fmt" "testing" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go/service/ec2" 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" + tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" tfnetworkmonitor "github.com/hashicorp/terraform-provider-aws/internal/service/networkmonitor" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -20,7 +23,9 @@ import ( func TestAccNetworkMonitorProbe_basic(t *testing.T) { ctx := acctest.Context(t) + var vpc ec2.Vpc resourceName := "aws_networkmonitor_probe.test" + vpcResourceName := "aws_vpc.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ @@ -49,15 +54,22 @@ func TestAccNetworkMonitorProbe_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - // TODO Need to delete the AmazonCloudWatchNetworkMonitor_ security group, else - // "Error: deleting EC2 VPC (vpc-00620dca13a12fd02): operation error EC2: DeleteVpc, https response error StatusCode: 400, RequestID: 3102b862-e45e-4921-8aba-0f8a4bdfe29b, api error DependencyViolation: The vpc 'vpc-00620dca13a12fd02' has dependencies and cannot be deleted" + { // nosemgrep:ci.test-config-funcs-correct-form + Config: acctest.ConfigVPCWithSubnets(rName, 1), + Check: resource.ComposeTestCheckFunc( + acctest.CheckVPCExists(ctx, vpcResourceName, &vpc), + testAccCheckProbeDeleteSecurityGroup(ctx, rName, &vpc), + ), + }, }, }) } func TestAccNetworkMonitorProbe_disappears(t *testing.T) { ctx := acctest.Context(t) + var vpc ec2.Vpc resourceName := "aws_networkmonitor_probe.test" + vpcResourceName := "aws_vpc.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ @@ -74,13 +86,22 @@ func TestAccNetworkMonitorProbe_disappears(t *testing.T) { ), ExpectNonEmptyPlan: true, }, + { // nosemgrep:ci.test-config-funcs-correct-form + Config: acctest.ConfigVPCWithSubnets(rName, 1), + Check: resource.ComposeTestCheckFunc( + acctest.CheckVPCExists(ctx, vpcResourceName, &vpc), + testAccCheckProbeDeleteSecurityGroup(ctx, rName, &vpc), + ), + }, }, }) } func TestAccNetworkMonitorProbe_tags(t *testing.T) { ctx := acctest.Context(t) + var vpc ec2.Vpc resourceName := "aws_networkmonitor_probe.test" + vpcResourceName := "aws_vpc.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ @@ -119,13 +140,22 @@ func TestAccNetworkMonitorProbe_tags(t *testing.T) { resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), ), }, + { // nosemgrep:ci.test-config-funcs-correct-form + Config: acctest.ConfigVPCWithSubnets(rName, 1), + Check: resource.ComposeTestCheckFunc( + acctest.CheckVPCExists(ctx, vpcResourceName, &vpc), + testAccCheckProbeDeleteSecurityGroup(ctx, rName, &vpc), + ), + }, }, }) } func TestAccNetworkMonitorProbe_update(t *testing.T) { ctx := acctest.Context(t) + var vpc ec2.Vpc resourceName := "aws_networkmonitor_probe.test" + vpcResourceName := "aws_vpc.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ @@ -169,6 +199,13 @@ func TestAccNetworkMonitorProbe_update(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, names.AttrVPCID), ), }, + { // nosemgrep:ci.test-config-funcs-correct-form + Config: acctest.ConfigVPCWithSubnets(rName, 1), + Check: resource.ComposeTestCheckFunc( + acctest.CheckVPCExists(ctx, vpcResourceName, &vpc), + testAccCheckProbeDeleteSecurityGroup(ctx, rName, &vpc), + ), + }, }, }) } @@ -214,6 +251,34 @@ func testAccCheckProbeExists(ctx context.Context, n string) resource.TestCheckFu } } +func testAccCheckProbeDeleteSecurityGroup(ctx context.Context, rName string, vpc *ec2.Vpc) resource.TestCheckFunc { + return func(s *terraform.State) error { + meta := acctest.Provider.Meta() + conn := meta.(*conns.AWSClient).EC2Conn(ctx) + + description := "Created By Amazon CloudWatch Network Monitor for " + rName + v, err := tfec2.FindSecurityGroupByDescriptionAndVPCID(ctx, conn, description, aws.ToString(vpc.VpcId)) + + if tfresource.NotFound(err) { + // Already gone. + return nil + } + + if err != nil { + return err + } + + r := tfec2.ResourceSecurityGroup() + d := r.Data(nil) + d.SetId(aws.ToString(v.GroupId)) + d.Set("revoke_rules_on_delete", true) + + err = acctest.DeleteResource(ctx, r, d, meta) + + return err + } +} + func testAccProbeConfig_base(rName string) string { return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` resource "aws_networkmonitor_monitor" "test" { From 622a29b0716f7b3b213bf0fe1c3d43747e88dd27 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 24 Jun 2024 08:41:51 -0400 Subject: [PATCH 17/20] Fix terrafmt errors. --- website/docs/r/networkmonitor_monitor.html.markdown | 8 +------- website/docs/r/networkmonitor_probe.html.markdown | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/website/docs/r/networkmonitor_monitor.html.markdown b/website/docs/r/networkmonitor_monitor.html.markdown index 4aacd857c6f..6f086c70de6 100644 --- a/website/docs/r/networkmonitor_monitor.html.markdown +++ b/website/docs/r/networkmonitor_monitor.html.markdown @@ -15,15 +15,9 @@ Terraform resource for managing an AWS Network Monitor Monitor. ### Basic Usage ```terraform -resource "aws_networkmonitor_monitor" "example" { - subnet_arns = [aws_subnet.example.arn] - core_network_id = awscc_networkmanager_core_network.example.id - vpc_arn = aws_vpc.example.arn -} - resource "aws_networkmonitor_monitor" "example" { aggregation_period = 30 - monitor_name = "example + monitor_name = "example } ``` diff --git a/website/docs/r/networkmonitor_probe.html.markdown b/website/docs/r/networkmonitor_probe.html.markdown index d63a93d5484..d9aa51614f9 100644 --- a/website/docs/r/networkmonitor_probe.html.markdown +++ b/website/docs/r/networkmonitor_probe.html.markdown @@ -17,10 +17,10 @@ Terraform resource for managing an AWS Network Monitor Probe. ```terraform resource "aws_networkmonitor_monitor" "example" { aggregation_period = 30 - monitor_name = "example + monitor_name = "example } -resource "aws_networkmonitor_probe" "test" { +resource "aws_networkmonitor_probe" "example" { monitor_name = aws_networkmonitor_monitor.example.monitor_name destination = "127.0.0.1" destination_port = 80 From b30ca1bb1c55823b76844675262c4411a8eb9667 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 24 Jun 2024 08:44:50 -0400 Subject: [PATCH 18/20] Really fix terrafmt errors. --- website/docs/r/networkmonitor_monitor.html.markdown | 2 +- website/docs/r/networkmonitor_probe.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/networkmonitor_monitor.html.markdown b/website/docs/r/networkmonitor_monitor.html.markdown index 6f086c70de6..4402cf33e35 100644 --- a/website/docs/r/networkmonitor_monitor.html.markdown +++ b/website/docs/r/networkmonitor_monitor.html.markdown @@ -17,7 +17,7 @@ Terraform resource for managing an AWS Network Monitor Monitor. ```terraform resource "aws_networkmonitor_monitor" "example" { aggregation_period = 30 - monitor_name = "example + monitor_name = "example" } ``` diff --git a/website/docs/r/networkmonitor_probe.html.markdown b/website/docs/r/networkmonitor_probe.html.markdown index d9aa51614f9..0220b85cba1 100644 --- a/website/docs/r/networkmonitor_probe.html.markdown +++ b/website/docs/r/networkmonitor_probe.html.markdown @@ -17,7 +17,7 @@ Terraform resource for managing an AWS Network Monitor Probe. ```terraform resource "aws_networkmonitor_monitor" "example" { aggregation_period = 30 - monitor_name = "example + monitor_name = "example" } resource "aws_networkmonitor_probe" "example" { From 76687c513912d13fd8a493ec89374925a0810bb0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 24 Jun 2024 09:49:18 -0400 Subject: [PATCH 19/20] Fix golangci-lint 'whitespace'. --- internal/service/networkmonitor/probe.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/service/networkmonitor/probe.go b/internal/service/networkmonitor/probe.go index e0f44ab1bc6..7d11f838386 100644 --- a/internal/service/networkmonitor/probe.go +++ b/internal/service/networkmonitor/probe.go @@ -167,7 +167,6 @@ func (r *probeResource) Create(ctx context.Context, request resource.CreateReque data.AddressFamily = fwtypes.StringEnumValue(outputGP.AddressFamily) if data.PacketSize.IsUnknown() { data.PacketSize = fwflex.Int32ToFramework(ctx, outputGP.PacketSize) - } data.VpcID = fwflex.StringToFramework(ctx, outputGP.VpcId) From 8ab3b0c255973539f422fe80a881510eeb66787e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 24 Jun 2024 09:50:11 -0400 Subject: [PATCH 20/20] Run 'make fix-constants PKG=ec2'. --- internal/service/ec2/find.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index 8be0294cc31..cd155b2c759 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -439,7 +439,7 @@ func findSecurityGroupByDescriptionAndVPCID(ctx context.Context, conn *ec2.EC2, input := &ec2.DescribeSecurityGroupsInput{ Filters: newAttributeFilterList( map[string]string{ - "description": description, + names.AttrDescription: description, "vpc-id": vpcID, }, ),