From b251a1b44c1afc8ee5229422b2ace6066f679afb Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Mon, 21 Oct 2024 15:32:58 -0700 Subject: [PATCH 01/13] Initial commit plus schema --- .../authorize_vpc_endpoint_access.go | 653 ++++++++++++++++++ .../authorize_vpc_endpoint_access_test.go | 332 +++++++++ 2 files changed, 985 insertions(+) create mode 100644 internal/service/opensearch/authorize_vpc_endpoint_access.go create mode 100644 internal/service/opensearch/authorize_vpc_endpoint_access_test.go diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access.go b/internal/service/opensearch/authorize_vpc_endpoint_access.go new file mode 100644 index 00000000000..42c07d32c76 --- /dev/null +++ b/internal/service/opensearch/authorize_vpc_endpoint_access.go @@ -0,0 +1,653 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package opensearch + +import ( + "context" + "errors" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/opensearch" + awstypes "github.com/aws/aws-sdk-go-v2/service/opensearch/types" + "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/listplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/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" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource("aws_opensearch_authroize_vpc_endpoint_access", name="Authorize Vpc Endpoint Access") +func newResourceAuthorizeVpcEndpointAccess(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &resourceAuthorizeVpcEndpointAccess{} + + return r, nil +} + +const ( + ResNameAuthorizeVpcEndpointAccess = "Authorize Vpc Endpoint Access" +) + +type resourceAuthorizeVpcEndpointAccess struct { + framework.ResourceWithConfigure + framework.WithNoUpdate + framework.WithTimeouts +} + +func (r *resourceAuthorizeVpcEndpointAccess) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_opensearch_authorize_vpc_endpoint_access" +} + +func (r *resourceAuthorizeVpcEndpointAccess) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "account": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "domain_name": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + Blocks: map[string]schema.Block{ + "authorized_principal": schema.ListNestedBlock{ + PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), + }, + CustomType: fwtypes.NewListNestedObjectTypeOf[resourceAuthorizeVpcEndpointAccessData](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "principal": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "principal_type": schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.PrincipalType](), + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + }, + }, + }, + } +} + +func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().OpenSearchClient(ctx) + + var plan resourceAuthorizeVpcEndpointAccessData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + in := &opensearch.AuthorizeVpcEndpointAccessInput{ + Account: aws.String(plan.Account.ValueString()), + DomainName: aws.String(plan.DomainName.ValueString()), + } + resp.Diagnostics.Append(fwflex.Expand(ctx, &plan, in, fwflex.WithFieldNamePrefix("AuthorizeVpcEndpointAccess"))...) + if resp.Diagnostics.HasError() { + return + } + + out, err := conn.AuthorizeVpcEndpointAccess(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVpcEndpointAccess, plan.Name.String(), err), + err.Error(), + ) + return + } + if out == nil || out.AuthorizeVpcEndpointAccess == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVpcEndpointAccess, plan.Name.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + // TIP: -- 5. Using the output from the create function, set the minimum attributes + plan.ARN = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccess.Arn) + plan.ID = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccess.AuthorizeVpcEndpointAccessId) + + // TIP: -- 6. Use a waiter to wait for create to complete + createTimeout := r.CreateTimeout(ctx, plan.Timeouts) + _, err = waitAuthorizeVpcEndpointAccessCreated(ctx, conn, plan.ID.ValueString(), createTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionWaitingForCreation, ResNameAuthorizeVpcEndpointAccess, plan.Name.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 7. Save the request plan to response state + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *resourceAuthorizeVpcEndpointAccess) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // TIP: ==== RESOURCE READ ==== + // Generally, the Read function should do the following things. Make + // sure there is a good reason if you don't do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the state + // 3. Get the resource from AWS + // 4. Remove resource from state if it is not found + // 5. Set the arguments and attributes + // 6. Set the state + + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().OpenSearchClient(ctx) + + // TIP: -- 2. Fetch the state + var state resourceAuthorizeVpcEndpointAccessData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Get the resource from AWS using an API Get, List, or Describe- + // type function, or, better yet, using a finder. + out, err := findAuthorizeVpcEndpointAccessByID(ctx, conn, state.ID.ValueString()) + // TIP: -- 4. Remove resource from state if it is not found + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionSetting, ResNameAuthorizeVpcEndpointAccess, state.ID.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 5. Set the arguments and attributes + // + // For simple data types (i.e., schema.StringAttribute, schema.BoolAttribute, + // schema.Int64Attribute, and schema.Float64Attribue), simply setting the + // appropriate data struct field is sufficient. The flex package implements + // helpers for converting between Go and Plugin-Framework types seamlessly. No + // error or nil checking is necessary. + // + // However, there are some situations where more handling is needed such as + // complex data types (e.g., schema.ListAttribute, schema.SetAttribute). In + // these cases the flatten function may have a diagnostics return value, which + // should be appended to resp.Diagnostics. + state.ARN = flex.StringToFramework(ctx, out.Arn) + state.ID = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccessId) + state.Name = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccessName) + state.Type = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccessType) + + // TIP: Setting a complex type. + complexArgument, d := flattenComplexArgument(ctx, out.ComplexArgument) + resp.Diagnostics.Append(d...) + state.ComplexArgument = complexArgument + + // TIP: -- 6. Set the state + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceAuthorizeVpcEndpointAccess) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // TIP: ==== RESOURCE UPDATE ==== + // Not all resources have Update functions. There are a few reasons: + // a. The AWS API does not support changing a resource + // b. All arguments have RequiresReplace() plan modifiers + // c. The AWS API uses a create call to modify an existing resource + // + // In the cases of a. and b., the resource will not have an update method + // defined. In the case of c., Update and Create can be refactored to call + // the same underlying function. + // + // The rest of the time, there should be an Update function and it should + // do the following things. Make sure there is a good reason if you don't + // do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the plan and state + // 3. Populate a modify input structure and check for changes + // 4. Call the AWS modify/update function + // 5. Use a waiter to wait for update to complete + // 6. Save the request plan to response state + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().OpenSearchClient(ctx) + + // TIP: -- 2. Fetch the plan + var plan, state resourceAuthorizeVpcEndpointAccessData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Populate a modify input structure and check for changes + if !plan.Name.Equal(state.Name) || + !plan.Description.Equal(state.Description) || + !plan.ComplexArgument.Equal(state.ComplexArgument) || + !plan.Type.Equal(state.Type) { + + in := &opensearch.UpdateAuthorizeVpcEndpointAccessInput{ + // TIP: Mandatory or fields that will always be present can be set when + // you create the Input structure. (Replace these with real fields.) + AuthorizeVpcEndpointAccessId: aws.String(plan.ID.ValueString()), + AuthorizeVpcEndpointAccessName: aws.String(plan.Name.ValueString()), + AuthorizeVpcEndpointAccessType: aws.String(plan.Type.ValueString()), + } + + if !plan.Description.IsNull() { + // TIP: Optional fields should be set based on whether or not they are + // used. + in.Description = aws.String(plan.Description.ValueString()) + } + if !plan.ComplexArgument.IsNull() { + // TIP: Use an expander to assign a complex argument. The elements must be + // deserialized into the appropriate struct before being passed to the expander. + var tfList []complexArgumentData + resp.Diagnostics.Append(plan.ComplexArgument.ElementsAs(ctx, &tfList, false)...) + if resp.Diagnostics.HasError() { + return + } + + in.ComplexArgument = expandComplexArgument(tfList) + } + + // TIP: -- 4. Call the AWS modify/update function + out, err := conn.UpdateAuthorizeVpcEndpointAccess(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionUpdating, ResNameAuthorizeVpcEndpointAccess, plan.ID.String(), err), + err.Error(), + ) + return + } + if out == nil || out.AuthorizeVpcEndpointAccess == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionUpdating, ResNameAuthorizeVpcEndpointAccess, plan.ID.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + // TIP: Using the output from the update function, re-set any computed attributes + plan.ARN = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccess.Arn) + plan.ID = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccess.AuthorizeVpcEndpointAccessId) + } + + // TIP: -- 5. Use a waiter to wait for update to complete + updateTimeout := r.UpdateTimeout(ctx, plan.Timeouts) + _, err := waitAuthorizeVpcEndpointAccessUpdated(ctx, conn, plan.ID.ValueString(), updateTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionWaitingForUpdate, ResNameAuthorizeVpcEndpointAccess, plan.ID.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 6. Save the request plan to response state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourceAuthorizeVpcEndpointAccess) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // TIP: ==== RESOURCE DELETE ==== + // Most resources have Delete functions. There are rare situations + // where you might not need a delete: + // a. The AWS API does not provide a way to delete the resource + // b. The point of your resource is to perform an action (e.g., reboot a + // server) and deleting serves no purpose. + // + // The Delete function should do the following things. Make sure there + // is a good reason if you don't do one of these. + // + // 1. Get a client connection to the relevant service + // 2. Fetch the state + // 3. Populate a delete input structure + // 4. Call the AWS delete function + // 5. Use a waiter to wait for delete to complete + // TIP: -- 1. Get a client connection to the relevant service + conn := r.Meta().OpenSearchClient(ctx) + + // TIP: -- 2. Fetch the state + var state resourceAuthorizeVpcEndpointAccessData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // TIP: -- 3. Populate a delete input structure + in := &opensearch.DeleteAuthorizeVpcEndpointAccessInput{ + AuthorizeVpcEndpointAccessId: aws.String(state.ID.ValueString()), + } + + // TIP: -- 4. Call the AWS delete function + _, err := conn.DeleteAuthorizeVpcEndpointAccess(ctx, in) + // TIP: On rare occassions, the API returns a not found error after deleting a + // resource. If that happens, we don't want it to show up as an error. + if err != nil { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionDeleting, ResNameAuthorizeVpcEndpointAccess, state.ID.String(), err), + err.Error(), + ) + return + } + + // TIP: -- 5. Use a waiter to wait for delete to complete + deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) + _, err = waitAuthorizeVpcEndpointAccessDeleted(ctx, conn, state.ID.ValueString(), deleteTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionWaitingForDeletion, ResNameAuthorizeVpcEndpointAccess, state.ID.String(), err), + err.Error(), + ) + return + } +} + +// TIP: ==== TERRAFORM IMPORTING ==== +// If Read can get all the information it needs from the Identifier +// (i.e., path.Root("id")), you can use the PassthroughID importer. Otherwise, +// you'll need a custom import function. +// +// See more: +// https://developer.hashicorp.com/terraform/plugin/framework/resources/import +func (r *resourceAuthorizeVpcEndpointAccess) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +// TIP: ==== STATUS CONSTANTS ==== +// Create constants for states and statuses if the service does not +// already have suitable constants. We prefer that you use the constants +// provided in the service if available (e.g., awstypes.StatusInProgress). +const ( + statusChangePending = "Pending" + statusDeleting = "Deleting" + statusNormal = "Normal" + statusUpdated = "Updated" +) + +// TIP: ==== WAITERS ==== +// Some resources of some services have waiters provided by the AWS API. +// Unless they do not work properly, use them rather than defining new ones +// here. +// +// Sometimes we define the wait, status, and find functions in separate +// files, wait.go, status.go, and find.go. Follow the pattern set out in the +// service and define these where it makes the most sense. +// +// If these functions are used in the _test.go file, they will need to be +// exported (i.e., capitalized). +// +// You will need to adjust the parameters and names to fit the service. +func waitAuthorizeVpcEndpointAccessCreated(ctx context.Context, conn *opensearch.Client, id string, timeout time.Duration) (*awstypes.AuthorizeVpcEndpointAccess, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{}, + Target: []string{statusNormal}, + Refresh: statusAuthorizeVpcEndpointAccess(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*opensearch.AuthorizeVpcEndpointAccess); ok { + return out, err + } + + return nil, err +} + +// TIP: It is easier to determine whether a resource is updated for some +// resources than others. The best case is a status flag that tells you when +// the update has been fully realized. Other times, you can check to see if a +// key resource argument is updated to a new value or not. +func waitAuthorizeVpcEndpointAccessUpdated(ctx context.Context, conn *opensearch.Client, id string, timeout time.Duration) (*awstypes.AuthorizeVpcEndpointAccess, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{statusChangePending}, + Target: []string{statusUpdated}, + Refresh: statusAuthorizeVpcEndpointAccess(ctx, conn, id), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*opensearch.AuthorizeVpcEndpointAccess); ok { + return out, err + } + + return nil, err +} + +// TIP: A deleted waiter is almost like a backwards created waiter. There may +// be additional pending states, however. +func waitAuthorizeVpcEndpointAccessDeleted(ctx context.Context, conn *opensearch.Client, id string, timeout time.Duration) (*awstypes.AuthorizeVpcEndpointAccess, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{statusDeleting, statusNormal}, + Target: []string{}, + Refresh: statusAuthorizeVpcEndpointAccess(ctx, conn, id), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*opensearch.AuthorizeVpcEndpointAccess); ok { + return out, err + } + + return nil, err +} + +// TIP: ==== STATUS ==== +// The status function can return an actual status when that field is +// available from the API (e.g., out.Status). Otherwise, you can use custom +// statuses to communicate the states of the resource. +// +// Waiters consume the values returned by status functions. Design status so +// that it can be reused by a create, update, and delete waiter, if possible. +func statusAuthorizeVpcEndpointAccess(ctx context.Context, conn *opensearch.Client, id string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + out, err := findAuthorizeVpcEndpointAccessByID(ctx, conn, id) + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return out, aws.ToString(out.Status), nil + } +} + +// TIP: ==== FINDERS ==== +// The find function is not strictly necessary. You could do the API +// request from the status function. However, we have found that find often +// comes in handy in other places besides the status function. As a result, it +// is good practice to define it separately. +func findAuthorizeVpcEndpointAccessByID(ctx context.Context, conn *opensearch.Client, id string) (*awstypes.AuthorizeVpcEndpointAccess, error) { + in := &opensearch.GetAuthorizeVpcEndpointAccessInput{ + Id: aws.String(id), + } + + out, err := conn.GetAuthorizeVpcEndpointAccess(ctx, in) + if err != nil { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + return nil, err + } + + if out == nil || out.AuthorizeVpcEndpointAccess == nil { + return nil, tfresource.NewEmptyResultError(in) + } + + return out.AuthorizeVpcEndpointAccess, nil +} + +// TIP: ==== FLEX ==== +// Flatteners and expanders ("flex" functions) help handle complex data +// types. Flatteners take an API data type and return the equivalent Plugin-Framework +// type. In other words, flatteners translate from AWS -> Terraform. +// +// On the other hand, expanders take a Terraform data structure and return +// something that you can send to the AWS API. In other words, expanders +// translate from Terraform -> AWS. +// +// See more: +// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ +func flattenComplexArgument(ctx context.Context, apiObject *awstypes.ComplexArgument) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} + + if apiObject == nil { + return types.ListNull(elemType), diags + } + + obj := map[string]attr.Value{ + "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), + "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), + } + objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) + diags.Append(d...) + + listVal, d := types.ListValue(elemType, []attr.Value{objVal}) + diags.Append(d...) + + return listVal, diags +} + +// TIP: Often the AWS API will return a slice of structures in response to a +// request for information. Sometimes you will have set criteria (e.g., the ID) +// that means you'll get back a one-length slice. This plural function works +// brilliantly for that situation too. +func flattenComplexArguments(ctx context.Context, apiObjects []*awstypes.ComplexArgument) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} + + if len(apiObjects) == 0 { + return types.ListNull(elemType), diags + } + + elems := []attr.Value{} + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + obj := map[string]attr.Value{ + "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), + "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), + } + objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) + diags.Append(d...) + + elems = append(elems, objVal) + } + + listVal, d := types.ListValue(elemType, elems) + diags.Append(d...) + + return listVal, diags +} + +// TIP: Remember, as mentioned above, expanders take a Terraform data structure +// and return something that you can send to the AWS API. In other words, +// expanders translate from Terraform -> AWS. +// +// See more: +// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ +func expandComplexArgument(tfList []complexArgumentData) *awstypes.ComplexArgument { + if len(tfList) == 0 { + return nil + } + + tfObj := tfList[0] + apiObject := &awstypes.ComplexArgument{ + NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), + } + if !tfObj.NestedOptional.IsNull() { + apiObject.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) + } + + return apiObject +} + +// TIP: Even when you have a list with max length of 1, this plural function +// works brilliantly. However, if the AWS API takes a structure rather than a +// slice of structures, you will not need it. +func expandComplexArguments(tfList []complexArgumentData) []*opensearch.ComplexArgument { + // TIP: The AWS API can be picky about whether you send a nil or zero- + // length for an argument that should be cleared. For example, in some + // cases, if you send a nil value, the AWS API interprets that as "make no + // changes" when what you want to say is "remove everything." Sometimes + // using a zero-length list will cause an error. + // + // As a result, here are two options. Usually, option 1, nil, will work as + // expected, clearing the field. But, test going from something to nothing + // to make sure it works. If not, try the second option. + // TIP: Option 1: Returning nil for zero-length list + if len(tfList) == 0 { + return nil + } + var apiObject []*awstypes.ComplexArgument + // TIP: Option 2: Return zero-length list for zero-length list. If option 1 does + // not work, after testing going from something to nothing (if that is + // possible), uncomment out the next line and remove option 1. + // + // apiObject := make([]*opensearch.ComplexArgument, 0) + + for _, tfObj := range tfList { + item := &opensearch.ComplexArgument{ + NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), + } + if !tfObj.NestedOptional.IsNull() { + item.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) + } + + apiObject = append(apiObject, item) + } + + return apiObject +} + +type resourceAuthorizeVpcEndpointAccessData struct { + Account types.String `tfsdk:"account"` + DomainName types.String `tfsdk:"domain_name"` + AuthorizedPrincipal fwtypes.ListNestedObjectValueOf[authorizedPrincipalData] `tfsdk:"authorized_principal"` +} + +type authorizedPrincipalData struct { + Principal types.String `tfsdk:"principal"` + PrincipalType fwtypes.StringEnum[awstypes.PrincipalType] `tfsdk:"principal_type"` +} diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go new file mode 100644 index 00000000000..a535bf377ff --- /dev/null +++ b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go @@ -0,0 +1,332 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package opensearch_test +// **PLEASE DELETE THIS AND ALL TIP COMMENTS BEFORE SUBMITTING A PR FOR REVIEW!** +// +// TIP: ==== INTRODUCTION ==== +// Thank you for trying the skaff tool! +// +// You have opted to include these helpful comments. They all include "TIP:" +// to help you find and remove them when you're done with them. +// +// While some aspects of this file are customized to your input, the +// scaffold tool does *not* look at the AWS API and ensure it has correct +// function, structure, and variable names. It makes guesses based on +// commonalities. You will need to make significant adjustments. +// +// In other words, as generated, this is a rough outline of the work you will +// need to do. If something doesn't make sense for your situation, get rid of +// it. + +import ( + // TIP: ==== IMPORTS ==== + // This is a common set of imports but not customized to your code since + // your code hasn't been written yet. Make sure you, your IDE, or + // goimports -w fixes these imports. + // + // The provider linter wants your imports to be in two groups: first, + // standard library (i.e., "fmt" or "strings"), second, everything else. + // + // Also, AWS Go SDK v2 may handle nested structures differently than v1, + // using the services/opensearch/types package. If so, you'll + // need to import types and reference the nested types, e.g., as + // types.. + "context" + "errors" + "fmt" + "testing" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/opensearch" + "github.com/aws/aws-sdk-go-v2/service/opensearch/types" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/names" + + // TIP: You will often need to import the package that this test file lives + // in. Since it is in the "test" context, it must import the package to use + // any normal context constants, variables, or functions. + tfopensearch "github.com/hashicorp/terraform-provider-aws/internal/service/opensearch" +) + +// TIP: File Structure. The basic outline for all test files should be as +// follows. Improve this resource's maintainability by following this +// outline. +// +// 1. Package declaration (add "_test" since this is a test file) +// 2. Imports +// 3. Unit tests +// 4. Basic test +// 5. Disappears test +// 6. All the other tests +// 7. Helper functions (exists, destroy, check, etc.) +// 8. Functions that return Terraform configurations + +// TIP: ==== UNIT TESTS ==== +// This is an example of a unit test. Its name is not prefixed with +// "TestAcc" like an acceptance test. +// +// Unlike acceptance tests, unit tests do not access AWS and are focused on a +// function (or method). Because of this, they are quick and cheap to run. +// +// In designing a resource's implementation, isolate complex bits from AWS bits +// so that they can be tested through a unit test. We encourage more unit tests +// in the provider. +// +// Cut and dry functions using well-used patterns, like typical flatteners and +// expanders, don't need unit testing. However, if they are complex or +// intricate, they should be unit tested. +func TestAuthorizeVpcEndpointAccessExampleUnitTest(t *testing.T) { + t.Parallel() + + testCases := []struct { + TestName string + Input string + Expected string + Error bool + }{ + { + TestName: "empty", + Input: "", + Expected: "", + Error: true, + }, + { + TestName: "descriptive name", + Input: "some input", + Expected: "some output", + Error: false, + }, + { + TestName: "another descriptive name", + Input: "more input", + Expected: "more output", + Error: false, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.TestName, func(t *testing.T) { + t.Parallel() + got, err := tfopensearch.FunctionFromResource(testCase.Input) + + if err != nil && !testCase.Error { + t.Errorf("got error (%s), expected no error", err) + } + + if err == nil && testCase.Error { + t.Errorf("got (%s) and no error, expected error", got) + } + + if got != testCase.Expected { + t.Errorf("got %s, expected %s", got, testCase.Expected) + } + }) + } +} + +// TIP: ==== ACCEPTANCE TESTS ==== +// This is an example of a basic acceptance test. This should test as much of +// standard functionality of the resource as possible, and test importing, if +// applicable. We prefix its name with "TestAcc", the service, and the +// resource name. +// +// Acceptance test access AWS and cost money to run. +func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { + ctx := acctest.Context(t) + // TIP: This is a long-running test guard for tests that run longer than + // 300s (5 min) generally. + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var authorizevpcendpointaccess opensearch.DescribeAuthorizeVpcEndpointAccessResponse + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_opensearch_authorize_vpc_endpoint_access.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.OpenSearchEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.OpenSearchServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAuthorizeVpcEndpointAccessConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAuthorizeVpcEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ + "console_access": "false", + "groups.#": "0", + "username": "Test", + "password": "TestTest1234", + }), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "opensearch", regexache.MustCompile(`authorizevpcendpointaccess:+.`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + }, + }, + }) +} + +func TestAccOpenSearchAuthorizeVpcEndpointAccess_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var authorizevpcendpointaccess opensearch.DescribeAuthorizeVpcEndpointAccessResponse + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_opensearch_authorize_vpc_endpoint_access.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.OpenSearchEndpointID) + testAccPreCheck(t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.OpenSearchServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAuthorizeVpcEndpointAccessConfig_basic(rName, testAccAuthorizeVpcEndpointAccessVersionNewer), + Check: resource.ComposeTestCheckFunc( + testAccCheckAuthorizeVpcEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), + // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, + // but expects a new resource factory function as the third argument. To expose this + // private function to the testing package, you may need to add a line like the following + // to exports_test.go: + // + // var ResourceAuthorizeVpcEndpointAccess = newResourceAuthorizeVpcEndpointAccess + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfopensearch.ResourceAuthorizeVpcEndpointAccess, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_opensearch_authorize_vpc_endpoint_access" { + continue + } + + input := &opensearch.DescribeAuthorizeVpcEndpointAccessInput{ + AuthorizeVpcEndpointAccessId: aws.String(rs.Primary.ID), + } + _, err := conn.DescribeAuthorizeVpcEndpointAccess(ctx, &opensearch.DescribeAuthorizeVpcEndpointAccessInput{ + AuthorizeVpcEndpointAccessId: aws.String(rs.Primary.ID), + }) + if errs.IsA[*types.ResourceNotFoundException](err){ + return nil + } + if err != nil { + return create.Error(names.OpenSearch, create.ErrActionCheckingDestroyed, tfopensearch.ResNameAuthorizeVpcEndpointAccess, rs.Primary.ID, err) + } + + return create.Error(names.OpenSearch, create.ErrActionCheckingDestroyed, tfopensearch.ResNameAuthorizeVpcEndpointAccess, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} + +func testAccCheckAuthorizeVpcEndpointAccessExists(ctx context.Context, name string, authorizevpcendpointaccess *opensearch.DescribeAuthorizeVpcEndpointAccessResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) + resp, err := conn.DescribeAuthorizeVpcEndpointAccess(ctx, &opensearch.DescribeAuthorizeVpcEndpointAccessInput{ + AuthorizeVpcEndpointAccessId: aws.String(rs.Primary.ID), + }) + + if err != nil { + return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, rs.Primary.ID, err) + } + + *authorizevpcendpointaccess = *resp + + return nil + } +} + +func testAccPreCheck(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) + + input := &opensearch.ListAuthorizeVpcEndpointAccesssInput{} + _, err := conn.ListAuthorizeVpcEndpointAccesss(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccCheckAuthorizeVpcEndpointAccessNotRecreated(before, after *opensearch.DescribeAuthorizeVpcEndpointAccessResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.ToString(before.AuthorizeVpcEndpointAccessId), aws.ToString(after.AuthorizeVpcEndpointAccessId); before != after { + return create.Error(names.OpenSearch, create.ErrActionCheckingNotRecreated, tfopensearch.ResNameAuthorizeVpcEndpointAccess, aws.ToString(before.AuthorizeVpcEndpointAccessId), errors.New("recreated")) + } + + return nil + } +} + +func testAccAuthorizeVpcEndpointAccessConfig_basic(rName, version string) string { + return fmt.Sprintf(` +resource "aws_security_group" "test" { + name = %[1]q +} + +resource "aws_opensearch_authorize_vpc_endpoint_access" "test" { + authorize_vpc_endpoint_access_name = %[1]q + engine_type = "ActiveOpenSearch" + engine_version = %[2]q + host_instance_type = "opensearch.t2.micro" + security_groups = [aws_security_group.test.id] + authentication_strategy = "simple" + storage_type = "efs" + + logs { + general = true + } + + user { + username = "Test" + password = "TestTest1234" + } +} +`, rName, version) +} From ca7d35189da51e7e84a6c0ca22e10e8310470c01 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Tue, 22 Oct 2024 08:56:47 -0700 Subject: [PATCH 02/13] temp progress --- .../authorize_vpc_endpoint_access.go | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access.go b/internal/service/opensearch/authorize_vpc_endpoint_access.go index 42c07d32c76..7223c55d8e0 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access.go @@ -109,7 +109,7 @@ func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req res Account: aws.String(plan.Account.ValueString()), DomainName: aws.String(plan.DomainName.ValueString()), } - resp.Diagnostics.Append(fwflex.Expand(ctx, &plan, in, fwflex.WithFieldNamePrefix("AuthorizeVpcEndpointAccess"))...) + resp.Diagnostics.Append(flex.Expand(ctx, plan, in)...) if resp.Diagnostics.HasError() { return } @@ -117,31 +117,21 @@ func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req res out, err := conn.AuthorizeVpcEndpointAccess(ctx, in) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVpcEndpointAccess, plan.Name.String(), err), + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVpcEndpointAccess, plan.DomainName.String(), err), err.Error(), ) return } - if out == nil || out.AuthorizeVpcEndpointAccess == nil { + if out == nil || out.AuthorizedPrincipal == nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVpcEndpointAccess, plan.Name.String(), nil), + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVpcEndpointAccess, plan.DomainName.String(), nil), errors.New("empty output").Error(), ) return } - // TIP: -- 5. Using the output from the create function, set the minimum attributes - plan.ARN = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccess.Arn) - plan.ID = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccess.AuthorizeVpcEndpointAccessId) - - // TIP: -- 6. Use a waiter to wait for create to complete - createTimeout := r.CreateTimeout(ctx, plan.Timeouts) - _, err = waitAuthorizeVpcEndpointAccessCreated(ctx, conn, plan.ID.ValueString(), createTimeout) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionWaitingForCreation, ResNameAuthorizeVpcEndpointAccess, plan.Name.String(), err), - err.Error(), - ) + resp.Diagnostics.Append(fwflex.Flatten(ctx, out, &plan)...) + if resp.Diagnostics.HasError() { return } From 979751a5576e691ce19a88bc01d0ba482c5eea40 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Tue, 22 Oct 2024 17:21:45 -0700 Subject: [PATCH 03/13] Generate paginator --- internal/service/opensearch/generate.go | 1 + internal/service/opensearch/list_pages_gen.go | 27 +++++++++++++++++++ .../service/opensearch/service_package_gen.go | 7 ++++- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 internal/service/opensearch/list_pages_gen.go diff --git a/internal/service/opensearch/generate.go b/internal/service/opensearch/generate.go index a4f69c05c98..1c0fe75304c 100644 --- a/internal/service/opensearch/generate.go +++ b/internal/service/opensearch/generate.go @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MPL-2.0 //go:generate go run ../../generate/tags/main.go -ListTags -ListTagsOp=ListTags -ListTagsInIDElem=ARN -ListTagsOutTagsElem=TagList -ServiceTagsSlice -TagOp=AddTags -TagInIDElem=ARN -TagInTagsElem=TagList -UntagOp=RemoveTags -UpdateTags +//go:generate go run ../../generate/listpages/main.go -ListOps=ListVpcEndpointAccess //go:generate go run ../../generate/servicepackage/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. diff --git a/internal/service/opensearch/list_pages_gen.go b/internal/service/opensearch/list_pages_gen.go new file mode 100644 index 00000000000..0064b71dc35 --- /dev/null +++ b/internal/service/opensearch/list_pages_gen.go @@ -0,0 +1,27 @@ +// Code generated by "internal/generate/listpages/main.go -ListOps=ListVpcEndpointAccess"; DO NOT EDIT. + +package opensearch + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/opensearch" +) + +func listVPCEndpointAccessPages(ctx context.Context, conn *opensearch.Client, input *opensearch.ListVpcEndpointAccessInput, fn func(*opensearch.ListVpcEndpointAccessOutput, bool) bool) error { + for { + output, err := conn.ListVpcEndpointAccess(ctx, input) + if err != nil { + return err + } + + lastPage := aws.ToString(output.NextToken) == "" + if !fn(output, lastPage) || lastPage { + break + } + + input.NextToken = output.NextToken + } + return nil +} diff --git a/internal/service/opensearch/service_package_gen.go b/internal/service/opensearch/service_package_gen.go index 5397c228da6..f923518f7c0 100644 --- a/internal/service/opensearch/service_package_gen.go +++ b/internal/service/opensearch/service_package_gen.go @@ -19,7 +19,12 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv } func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { - return []*types.ServicePackageFrameworkResource{} + return []*types.ServicePackageFrameworkResource{ + { + Factory: newResourceAuthorizeVpcEndpointAccess, + Name: "Authorize Vpc Endpoint Access", + }, + } } func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { From f52448f81f2a97c5da5a4c24d2c3bba8ebde7b46 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Tue, 22 Oct 2024 17:21:59 -0700 Subject: [PATCH 04/13] CRD --- .../authorize_vpc_endpoint_access.go | 452 ++---------------- ...uthorize_vpc_endpoint_access.html.markdown | 69 +++ 2 files changed, 96 insertions(+), 425 deletions(-) create mode 100644 website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access.go b/internal/service/opensearch/authorize_vpc_endpoint_access.go index 7223c55d8e0..d6f03a63334 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access.go @@ -6,13 +6,10 @@ package opensearch import ( "context" "errors" - "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/opensearch" awstypes "github.com/aws/aws-sdk-go-v2/service/opensearch/types" - "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" @@ -20,7 +17,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/framework" @@ -135,194 +131,41 @@ func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req res return } - // TIP: -- 7. Save the request plan to response state resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) } func (r *resourceAuthorizeVpcEndpointAccess) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - // TIP: ==== RESOURCE READ ==== - // Generally, the Read function should do the following things. Make - // sure there is a good reason if you don't do one of these. - // - // 1. Get a client connection to the relevant service - // 2. Fetch the state - // 3. Get the resource from AWS - // 4. Remove resource from state if it is not found - // 5. Set the arguments and attributes - // 6. Set the state - - // TIP: -- 1. Get a client connection to the relevant service conn := r.Meta().OpenSearchClient(ctx) - // TIP: -- 2. Fetch the state var state resourceAuthorizeVpcEndpointAccessData resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - // TIP: -- 3. Get the resource from AWS using an API Get, List, or Describe- - // type function, or, better yet, using a finder. - out, err := findAuthorizeVpcEndpointAccessByID(ctx, conn, state.ID.ValueString()) - // TIP: -- 4. Remove resource from state if it is not found + out, err := findAuthorizeVpcEndpointAccessByName(ctx, conn, state.DomainName.ValueString()) if tfresource.NotFound(err) { resp.State.RemoveResource(ctx) return } if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionSetting, ResNameAuthorizeVpcEndpointAccess, state.ID.String(), err), + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionSetting, ResNameAuthorizeVpcEndpointAccess, state.DomainName.String(), err), err.Error(), ) return } - // TIP: -- 5. Set the arguments and attributes - // - // For simple data types (i.e., schema.StringAttribute, schema.BoolAttribute, - // schema.Int64Attribute, and schema.Float64Attribue), simply setting the - // appropriate data struct field is sufficient. The flex package implements - // helpers for converting between Go and Plugin-Framework types seamlessly. No - // error or nil checking is necessary. - // - // However, there are some situations where more handling is needed such as - // complex data types (e.g., schema.ListAttribute, schema.SetAttribute). In - // these cases the flatten function may have a diagnostics return value, which - // should be appended to resp.Diagnostics. - state.ARN = flex.StringToFramework(ctx, out.Arn) - state.ID = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccessId) - state.Name = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccessName) - state.Type = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccessType) - - // TIP: Setting a complex type. - complexArgument, d := flattenComplexArgument(ctx, out.ComplexArgument) - resp.Diagnostics.Append(d...) - state.ComplexArgument = complexArgument - - // TIP: -- 6. Set the state - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *resourceAuthorizeVpcEndpointAccess) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - // TIP: ==== RESOURCE UPDATE ==== - // Not all resources have Update functions. There are a few reasons: - // a. The AWS API does not support changing a resource - // b. All arguments have RequiresReplace() plan modifiers - // c. The AWS API uses a create call to modify an existing resource - // - // In the cases of a. and b., the resource will not have an update method - // defined. In the case of c., Update and Create can be refactored to call - // the same underlying function. - // - // The rest of the time, there should be an Update function and it should - // do the following things. Make sure there is a good reason if you don't - // do one of these. - // - // 1. Get a client connection to the relevant service - // 2. Fetch the plan and state - // 3. Populate a modify input structure and check for changes - // 4. Call the AWS modify/update function - // 5. Use a waiter to wait for update to complete - // 6. Save the request plan to response state - // TIP: -- 1. Get a client connection to the relevant service - conn := r.Meta().OpenSearchClient(ctx) - - // TIP: -- 2. Fetch the plan - var plan, state resourceAuthorizeVpcEndpointAccessData - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + resp.Diagnostics.Append(fwflex.Flatten(ctx, out, &state)...) if resp.Diagnostics.HasError() { return } - - // TIP: -- 3. Populate a modify input structure and check for changes - if !plan.Name.Equal(state.Name) || - !plan.Description.Equal(state.Description) || - !plan.ComplexArgument.Equal(state.ComplexArgument) || - !plan.Type.Equal(state.Type) { - - in := &opensearch.UpdateAuthorizeVpcEndpointAccessInput{ - // TIP: Mandatory or fields that will always be present can be set when - // you create the Input structure. (Replace these with real fields.) - AuthorizeVpcEndpointAccessId: aws.String(plan.ID.ValueString()), - AuthorizeVpcEndpointAccessName: aws.String(plan.Name.ValueString()), - AuthorizeVpcEndpointAccessType: aws.String(plan.Type.ValueString()), - } - - if !plan.Description.IsNull() { - // TIP: Optional fields should be set based on whether or not they are - // used. - in.Description = aws.String(plan.Description.ValueString()) - } - if !plan.ComplexArgument.IsNull() { - // TIP: Use an expander to assign a complex argument. The elements must be - // deserialized into the appropriate struct before being passed to the expander. - var tfList []complexArgumentData - resp.Diagnostics.Append(plan.ComplexArgument.ElementsAs(ctx, &tfList, false)...) - if resp.Diagnostics.HasError() { - return - } - - in.ComplexArgument = expandComplexArgument(tfList) - } - - // TIP: -- 4. Call the AWS modify/update function - out, err := conn.UpdateAuthorizeVpcEndpointAccess(ctx, in) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionUpdating, ResNameAuthorizeVpcEndpointAccess, plan.ID.String(), err), - err.Error(), - ) - return - } - if out == nil || out.AuthorizeVpcEndpointAccess == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionUpdating, ResNameAuthorizeVpcEndpointAccess, plan.ID.String(), nil), - errors.New("empty output").Error(), - ) - return - } - - // TIP: Using the output from the update function, re-set any computed attributes - plan.ARN = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccess.Arn) - plan.ID = flex.StringToFramework(ctx, out.AuthorizeVpcEndpointAccess.AuthorizeVpcEndpointAccessId) - } - - // TIP: -- 5. Use a waiter to wait for update to complete - updateTimeout := r.UpdateTimeout(ctx, plan.Timeouts) - _, err := waitAuthorizeVpcEndpointAccessUpdated(ctx, conn, plan.ID.ValueString(), updateTimeout) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionWaitingForUpdate, ResNameAuthorizeVpcEndpointAccess, plan.ID.String(), err), - err.Error(), - ) - return - } - - // TIP: -- 6. Save the request plan to response state - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } func (r *resourceAuthorizeVpcEndpointAccess) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // TIP: ==== RESOURCE DELETE ==== - // Most resources have Delete functions. There are rare situations - // where you might not need a delete: - // a. The AWS API does not provide a way to delete the resource - // b. The point of your resource is to perform an action (e.g., reboot a - // server) and deleting serves no purpose. - // - // The Delete function should do the following things. Make sure there - // is a good reason if you don't do one of these. - // - // 1. Get a client connection to the relevant service - // 2. Fetch the state - // 3. Populate a delete input structure - // 4. Call the AWS delete function - // 5. Use a waiter to wait for delete to complete - // TIP: -- 1. Get a client connection to the relevant service conn := r.Meta().OpenSearchClient(ctx) - // TIP: -- 2. Fetch the state var state resourceAuthorizeVpcEndpointAccessData resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { @@ -330,305 +173,64 @@ func (r *resourceAuthorizeVpcEndpointAccess) Delete(ctx context.Context, req res } // TIP: -- 3. Populate a delete input structure - in := &opensearch.DeleteAuthorizeVpcEndpointAccessInput{ - AuthorizeVpcEndpointAccessId: aws.String(state.ID.ValueString()), + in := &opensearch.RevokeVpcEndpointAccessInput{ + Account: aws.String(state.Account.ValueString()), + DomainName: aws.String(state.DomainName.ValueString()), } - // TIP: -- 4. Call the AWS delete function - _, err := conn.DeleteAuthorizeVpcEndpointAccess(ctx, in) - // TIP: On rare occassions, the API returns a not found error after deleting a - // resource. If that happens, we don't want it to show up as an error. + _, err := conn.RevokeVpcEndpointAccess(ctx, in) if err != nil { if errs.IsA[*awstypes.ResourceNotFoundException](err) { return } resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionDeleting, ResNameAuthorizeVpcEndpointAccess, state.ID.String(), err), - err.Error(), - ) - return - } - - // TIP: -- 5. Use a waiter to wait for delete to complete - deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) - _, err = waitAuthorizeVpcEndpointAccessDeleted(ctx, conn, state.ID.ValueString(), deleteTimeout) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionWaitingForDeletion, ResNameAuthorizeVpcEndpointAccess, state.ID.String(), err), + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionDeleting, ResNameAuthorizeVpcEndpointAccess, state.DomainName.String(), err), err.Error(), ) return } } -// TIP: ==== TERRAFORM IMPORTING ==== -// If Read can get all the information it needs from the Identifier -// (i.e., path.Root("id")), you can use the PassthroughID importer. Otherwise, -// you'll need a custom import function. -// -// See more: -// https://developer.hashicorp.com/terraform/plugin/framework/resources/import func (r *resourceAuthorizeVpcEndpointAccess) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } -// TIP: ==== STATUS CONSTANTS ==== -// Create constants for states and statuses if the service does not -// already have suitable constants. We prefer that you use the constants -// provided in the service if available (e.g., awstypes.StatusInProgress). -const ( - statusChangePending = "Pending" - statusDeleting = "Deleting" - statusNormal = "Normal" - statusUpdated = "Updated" -) - -// TIP: ==== WAITERS ==== -// Some resources of some services have waiters provided by the AWS API. -// Unless they do not work properly, use them rather than defining new ones -// here. -// -// Sometimes we define the wait, status, and find functions in separate -// files, wait.go, status.go, and find.go. Follow the pattern set out in the -// service and define these where it makes the most sense. -// -// If these functions are used in the _test.go file, they will need to be -// exported (i.e., capitalized). -// -// You will need to adjust the parameters and names to fit the service. -func waitAuthorizeVpcEndpointAccessCreated(ctx context.Context, conn *opensearch.Client, id string, timeout time.Duration) (*awstypes.AuthorizeVpcEndpointAccess, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{}, - Target: []string{statusNormal}, - Refresh: statusAuthorizeVpcEndpointAccess(ctx, conn, id), - Timeout: timeout, - NotFoundChecks: 20, - ContinuousTargetOccurence: 2, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*opensearch.AuthorizeVpcEndpointAccess); ok { - return out, err - } - - return nil, err -} - -// TIP: It is easier to determine whether a resource is updated for some -// resources than others. The best case is a status flag that tells you when -// the update has been fully realized. Other times, you can check to see if a -// key resource argument is updated to a new value or not. -func waitAuthorizeVpcEndpointAccessUpdated(ctx context.Context, conn *opensearch.Client, id string, timeout time.Duration) (*awstypes.AuthorizeVpcEndpointAccess, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{statusChangePending}, - Target: []string{statusUpdated}, - Refresh: statusAuthorizeVpcEndpointAccess(ctx, conn, id), - Timeout: timeout, - NotFoundChecks: 20, - ContinuousTargetOccurence: 2, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*opensearch.AuthorizeVpcEndpointAccess); ok { - return out, err - } - - return nil, err -} - -// TIP: A deleted waiter is almost like a backwards created waiter. There may -// be additional pending states, however. -func waitAuthorizeVpcEndpointAccessDeleted(ctx context.Context, conn *opensearch.Client, id string, timeout time.Duration) (*awstypes.AuthorizeVpcEndpointAccess, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{statusDeleting, statusNormal}, - Target: []string{}, - Refresh: statusAuthorizeVpcEndpointAccess(ctx, conn, id), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*opensearch.AuthorizeVpcEndpointAccess); ok { - return out, err +func findAuthorizeVpcEndpointAccessByName(ctx context.Context, conn *opensearch.Client, id string) (*awstypes.AuthorizedPrincipal, error) { + in := &opensearch.ListVpcEndpointAccessInput{ + DomainName: aws.String(id), } - return nil, err + return findAuthroizeVpcEndpointAccess(ctx, conn, in) } -// TIP: ==== STATUS ==== -// The status function can return an actual status when that field is -// available from the API (e.g., out.Status). Otherwise, you can use custom -// statuses to communicate the states of the resource. -// -// Waiters consume the values returned by status functions. Design status so -// that it can be reused by a create, update, and delete waiter, if possible. -func statusAuthorizeVpcEndpointAccess(ctx context.Context, conn *opensearch.Client, id string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - out, err := findAuthorizeVpcEndpointAccessByID(ctx, conn, id) - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return out, aws.ToString(out.Status), nil - } -} +func findAuthroizeVpcEndpointAccess(ctx context.Context, conn *opensearch.Client, input *opensearch.ListVpcEndpointAccessInput) (*awstypes.AuthorizedPrincipal, error) { + output, err := findAuthorizeVpcEndpointAccesses(ctx, conn, input) -// TIP: ==== FINDERS ==== -// The find function is not strictly necessary. You could do the API -// request from the status function. However, we have found that find often -// comes in handy in other places besides the status function. As a result, it -// is good practice to define it separately. -func findAuthorizeVpcEndpointAccessByID(ctx context.Context, conn *opensearch.Client, id string) (*awstypes.AuthorizeVpcEndpointAccess, error) { - in := &opensearch.GetAuthorizeVpcEndpointAccessInput{ - Id: aws.String(id), - } - - out, err := conn.GetAuthorizeVpcEndpointAccess(ctx, in) if err != nil { - if errs.IsA[*awstypes.ResourceNotFoundException](err) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: in, - } - } - return nil, err } - if out == nil || out.AuthorizeVpcEndpointAccess == nil { - return nil, tfresource.NewEmptyResultError(in) - } - - return out.AuthorizeVpcEndpointAccess, nil -} - -// TIP: ==== FLEX ==== -// Flatteners and expanders ("flex" functions) help handle complex data -// types. Flatteners take an API data type and return the equivalent Plugin-Framework -// type. In other words, flatteners translate from AWS -> Terraform. -// -// On the other hand, expanders take a Terraform data structure and return -// something that you can send to the AWS API. In other words, expanders -// translate from Terraform -> AWS. -// -// See more: -// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ -func flattenComplexArgument(ctx context.Context, apiObject *awstypes.ComplexArgument) (types.List, diag.Diagnostics) { - var diags diag.Diagnostics - elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} - - if apiObject == nil { - return types.ListNull(elemType), diags - } - - obj := map[string]attr.Value{ - "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), - "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), - } - objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) - diags.Append(d...) - - listVal, d := types.ListValue(elemType, []attr.Value{objVal}) - diags.Append(d...) - - return listVal, diags + return tfresource.AssertSingleValueResult(output) } -// TIP: Often the AWS API will return a slice of structures in response to a -// request for information. Sometimes you will have set criteria (e.g., the ID) -// that means you'll get back a one-length slice. This plural function works -// brilliantly for that situation too. -func flattenComplexArguments(ctx context.Context, apiObjects []*awstypes.ComplexArgument) (types.List, diag.Diagnostics) { - var diags diag.Diagnostics - elemType := types.ObjectType{AttrTypes: complexArgumentAttrTypes} - - if len(apiObjects) == 0 { - return types.ListNull(elemType), diags - } - - elems := []attr.Value{} - for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } +func findAuthorizeVpcEndpointAccesses(ctx context.Context, conn *opensearch.Client, input *opensearch.ListVpcEndpointAccessInput) ([]awstypes.AuthorizedPrincipal, error) { + var output []awstypes.AuthorizedPrincipal - obj := map[string]attr.Value{ - "nested_required": flex.StringValueToFramework(ctx, apiObject.NestedRequired), - "nested_optional": flex.StringValueToFramework(ctx, apiObject.NestedOptional), + err := listVPCEndpointAccessPages(ctx, conn, input, func(page *opensearch.ListVpcEndpointAccessOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - objVal, d := types.ObjectValue(complexArgumentAttrTypes, obj) - diags.Append(d...) - - elems = append(elems, objVal) - } - listVal, d := types.ListValue(elemType, elems) - diags.Append(d...) + output = append(output, page.AuthorizedPrincipalList...) - return listVal, diags -} - -// TIP: Remember, as mentioned above, expanders take a Terraform data structure -// and return something that you can send to the AWS API. In other words, -// expanders translate from Terraform -> AWS. -// -// See more: -// https://hashicorp.github.io/terraform-provider-aws/data-handling-and-conversion/ -func expandComplexArgument(tfList []complexArgumentData) *awstypes.ComplexArgument { - if len(tfList) == 0 { - return nil - } + return !lastPage + }) - tfObj := tfList[0] - apiObject := &awstypes.ComplexArgument{ - NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), - } - if !tfObj.NestedOptional.IsNull() { - apiObject.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) - } - - return apiObject -} - -// TIP: Even when you have a list with max length of 1, this plural function -// works brilliantly. However, if the AWS API takes a structure rather than a -// slice of structures, you will not need it. -func expandComplexArguments(tfList []complexArgumentData) []*opensearch.ComplexArgument { - // TIP: The AWS API can be picky about whether you send a nil or zero- - // length for an argument that should be cleared. For example, in some - // cases, if you send a nil value, the AWS API interprets that as "make no - // changes" when what you want to say is "remove everything." Sometimes - // using a zero-length list will cause an error. - // - // As a result, here are two options. Usually, option 1, nil, will work as - // expected, clearing the field. But, test going from something to nothing - // to make sure it works. If not, try the second option. - // TIP: Option 1: Returning nil for zero-length list - if len(tfList) == 0 { - return nil - } - var apiObject []*awstypes.ComplexArgument - // TIP: Option 2: Return zero-length list for zero-length list. If option 1 does - // not work, after testing going from something to nothing (if that is - // possible), uncomment out the next line and remove option 1. - // - // apiObject := make([]*opensearch.ComplexArgument, 0) - - for _, tfObj := range tfList { - item := &opensearch.ComplexArgument{ - NestedRequired: aws.String(tfObj.NestedRequired.ValueString()), - } - if !tfObj.NestedOptional.IsNull() { - item.NestedOptional = aws.String(tfObj.NestedOptional.ValueString()) - } - - apiObject = append(apiObject, item) + if err != nil { + return nil, err } - return apiObject + return output, nil } type resourceAuthorizeVpcEndpointAccessData struct { diff --git a/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown b/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown new file mode 100644 index 00000000000..40468ddd100 --- /dev/null +++ b/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown @@ -0,0 +1,69 @@ +--- +subcategory: "OpenSearch" +layout: "aws" +page_title: "AWS: aws_opensearch_authorize_vpc_endpoint_access" +description: |- + Terraform resource for managing an AWS OpenSearch Authorize Vpc Endpoint Access. +--- +` +# Resource: aws_opensearch_authorize_vpc_endpoint_access + +Terraform resource for managing an AWS OpenSearch Authorize Vpc Endpoint Access. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_opensearch_authorize_vpc_endpoint_access" "example" { +} +``` + +## Argument Reference + +The following arguments are required: + +* `example_arg` - (Required) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +The following arguments are optional: + +* `optional_arg` - (Optional) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Authorize Vpc Endpoint Access. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `example_attribute` - Concise description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `60m`) +* `update` - (Default `180m`) +* `delete` - (Default `90m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import OpenSearch Authorize Vpc Endpoint Access using the `example_id_arg`. For example: + +```terraform +import { + to = aws_opensearch_authorize_vpc_endpoint_access.example + id = "authorize_vpc_endpoint_access-id-12345678" +} +``` + +Using `terraform import`, import OpenSearch Authorize Vpc Endpoint Access using the `example_id_arg`. For example: + +```console +% terraform import aws_opensearch_authorize_vpc_endpoint_access.example authorize_vpc_endpoint_access-id-12345678 +``` From a51900de3d159e71990ae0832bb5e5a2b9a79efa Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Tue, 22 Oct 2024 23:51:08 -0700 Subject: [PATCH 05/13] CleanUp and add tests --- .../authorize_vpc_endpoint_access.go | 2 +- .../authorize_vpc_endpoint_access_test.go | 267 ++++-------------- internal/service/opensearch/exports_test.go | 9 +- 3 files changed, 63 insertions(+), 215 deletions(-) diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access.go b/internal/service/opensearch/authorize_vpc_endpoint_access.go index d6f03a63334..05c83ffb0e5 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access.go @@ -195,7 +195,7 @@ func (r *resourceAuthorizeVpcEndpointAccess) ImportState(ctx context.Context, re resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } -func findAuthorizeVpcEndpointAccessByName(ctx context.Context, conn *opensearch.Client, id string) (*awstypes.AuthorizedPrincipal, error) { +func findAuthorizeVpcEndpointAccessByName(ctx context.Context, conn *opensearch.Client, domainName string) (*awstypes.AuthorizedPrincipal, error) { in := &opensearch.ListVpcEndpointAccessInput{ DomainName: aws.String(id), } diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go index a535bf377ff..6c9bf805e14 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go @@ -2,186 +2,58 @@ // SPDX-License-Identifier: MPL-2.0 package opensearch_test -// **PLEASE DELETE THIS AND ALL TIP COMMENTS BEFORE SUBMITTING A PR FOR REVIEW!** -// -// TIP: ==== INTRODUCTION ==== -// Thank you for trying the skaff tool! -// -// You have opted to include these helpful comments. They all include "TIP:" -// to help you find and remove them when you're done with them. -// -// While some aspects of this file are customized to your input, the -// scaffold tool does *not* look at the AWS API and ensure it has correct -// function, structure, and variable names. It makes guesses based on -// commonalities. You will need to make significant adjustments. -// -// In other words, as generated, this is a rough outline of the work you will -// need to do. If something doesn't make sense for your situation, get rid of -// it. import ( - // TIP: ==== IMPORTS ==== - // This is a common set of imports but not customized to your code since - // your code hasn't been written yet. Make sure you, your IDE, or - // goimports -w fixes these imports. - // - // The provider linter wants your imports to be in two groups: first, - // standard library (i.e., "fmt" or "strings"), second, everything else. - // - // Also, AWS Go SDK v2 may handle nested structures differently than v1, - // using the services/opensearch/types package. If so, you'll - // need to import types and reference the nested types, e.g., as - // types.. "context" "errors" "fmt" "testing" - "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/opensearch" - "github.com/aws/aws-sdk-go-v2/service/opensearch/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/opensearch/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" - // TIP: You will often need to import the package that this test file lives - // in. Since it is in the "test" context, it must import the package to use - // any normal context constants, variables, or functions. tfopensearch "github.com/hashicorp/terraform-provider-aws/internal/service/opensearch" ) -// TIP: File Structure. The basic outline for all test files should be as -// follows. Improve this resource's maintainability by following this -// outline. -// -// 1. Package declaration (add "_test" since this is a test file) -// 2. Imports -// 3. Unit tests -// 4. Basic test -// 5. Disappears test -// 6. All the other tests -// 7. Helper functions (exists, destroy, check, etc.) -// 8. Functions that return Terraform configurations - -// TIP: ==== UNIT TESTS ==== -// This is an example of a unit test. Its name is not prefixed with -// "TestAcc" like an acceptance test. -// -// Unlike acceptance tests, unit tests do not access AWS and are focused on a -// function (or method). Because of this, they are quick and cheap to run. -// -// In designing a resource's implementation, isolate complex bits from AWS bits -// so that they can be tested through a unit test. We encourage more unit tests -// in the provider. -// -// Cut and dry functions using well-used patterns, like typical flatteners and -// expanders, don't need unit testing. However, if they are complex or -// intricate, they should be unit tested. -func TestAuthorizeVpcEndpointAccessExampleUnitTest(t *testing.T) { - t.Parallel() - - testCases := []struct { - TestName string - Input string - Expected string - Error bool - }{ - { - TestName: "empty", - Input: "", - Expected: "", - Error: true, - }, - { - TestName: "descriptive name", - Input: "some input", - Expected: "some output", - Error: false, - }, - { - TestName: "another descriptive name", - Input: "more input", - Expected: "more output", - Error: false, - }, - } - - for _, testCase := range testCases { - testCase := testCase - t.Run(testCase.TestName, func(t *testing.T) { - t.Parallel() - got, err := tfopensearch.FunctionFromResource(testCase.Input) - - if err != nil && !testCase.Error { - t.Errorf("got error (%s), expected no error", err) - } - - if err == nil && testCase.Error { - t.Errorf("got (%s) and no error, expected error", got) - } - - if got != testCase.Expected { - t.Errorf("got %s, expected %s", got, testCase.Expected) - } - }) - } -} - -// TIP: ==== ACCEPTANCE TESTS ==== -// This is an example of a basic acceptance test. This should test as much of -// standard functionality of the resource as possible, and test importing, if -// applicable. We prefix its name with "TestAcc", the service, and the -// resource name. -// -// Acceptance test access AWS and cost money to run. func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { ctx := acctest.Context(t) - // TIP: This is a long-running test guard for tests that run longer than - // 300s (5 min) generally. if testing.Short() { t.Skip("skipping long-running test in short mode") } - var authorizevpcendpointaccess opensearch.DescribeAuthorizeVpcEndpointAccessResponse + var authorizevpcendpointaccess awstypes.AuthorizedPrincipal rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_opensearch_authorize_vpc_endpoint_access.test" + domainName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.OpenSearchEndpointID) - testAccPreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.OpenSearchServiceID) }, ErrorCheck: acctest.ErrorCheck(t, names.OpenSearchServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccAuthorizeVpcEndpointAccessConfig_basic(rName), + Config: testAccAuthorizeVpcEndpointAccessConfig_basic(rName, domainName), Check: resource.ComposeTestCheckFunc( testAccCheckAuthorizeVpcEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), - resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), - resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ - "console_access": "false", - "groups.#": "0", - "username": "Test", - "password": "TestTest1234", - }), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "opensearch", regexache.MustCompile(`authorizevpcendpointaccess:+.`)), + resource.TestCheckResourceAttrSet(resourceName, "account"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrDomainName), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -189,35 +61,25 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { func TestAccOpenSearchAuthorizeVpcEndpointAccess_disappears(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - var authorizevpcendpointaccess opensearch.DescribeAuthorizeVpcEndpointAccessResponse + var authorizevpcendpointaccess awstypes.AuthorizedPrincipal rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_opensearch_authorize_vpc_endpoint_access.test" + domainName := testAccRandomDomainName() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.OpenSearchEndpointID) - testAccPreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, names.OpenSearchServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccAuthorizeVpcEndpointAccessConfig_basic(rName, testAccAuthorizeVpcEndpointAccessVersionNewer), + Config: testAccAuthorizeVpcEndpointAccessConfig_basic(rName, domainName), Check: resource.ComposeTestCheckFunc( testAccCheckAuthorizeVpcEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), - // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, - // but expects a new resource factory function as the third argument. To expose this - // private function to the testing package, you may need to add a line like the following - // to exports_test.go: - // - // var ResourceAuthorizeVpcEndpointAccess = newResourceAuthorizeVpcEndpointAccess - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfopensearch.ResourceAuthorizeVpcEndpointAccess, resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfopensearch.resourceAuthorizeVpcEndpointAccess, resourceName), ), ExpectNonEmptyPlan: true, }, @@ -234,41 +96,33 @@ func testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx context.Context) resource continue } - input := &opensearch.DescribeAuthorizeVpcEndpointAccessInput{ - AuthorizeVpcEndpointAccessId: aws.String(rs.Primary.ID), - } - _, err := conn.DescribeAuthorizeVpcEndpointAccess(ctx, &opensearch.DescribeAuthorizeVpcEndpointAccessInput{ - AuthorizeVpcEndpointAccessId: aws.String(rs.Primary.ID), - }) - if errs.IsA[*types.ResourceNotFoundException](err){ - return nil + _, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes["domain_name"]) + + if tfresource.NotFound(err) { + continue } + if err != nil { - return create.Error(names.OpenSearch, create.ErrActionCheckingDestroyed, tfopensearch.ResNameAuthorizeVpcEndpointAccess, rs.Primary.ID, err) + return err } - return create.Error(names.OpenSearch, create.ErrActionCheckingDestroyed, tfopensearch.ResNameAuthorizeVpcEndpointAccess, rs.Primary.ID, errors.New("not destroyed")) + return fmt.Errorf("Elastic Beanstalk Application Version %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckAuthorizeVpcEndpointAccessExists(ctx context.Context, name string, authorizevpcendpointaccess *opensearch.DescribeAuthorizeVpcEndpointAccessResponse) resource.TestCheckFunc { +func testAccCheckAuthorizeVpcEndpointAccessExists(ctx context.Context, name string, authorizevpcendpointaccess *awstypes.AuthorizedPrincipal) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, name, errors.New("not found")) } - if rs.Primary.ID == "" { - return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, name, errors.New("not set")) - } - conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) - resp, err := conn.DescribeAuthorizeVpcEndpointAccess(ctx, &opensearch.DescribeAuthorizeVpcEndpointAccessInput{ - AuthorizeVpcEndpointAccessId: aws.String(rs.Primary.ID), - }) + + resp, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes["authorized_principal"]) if err != nil { return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, rs.Primary.ID, err) @@ -280,53 +134,46 @@ func testAccCheckAuthorizeVpcEndpointAccessExists(ctx context.Context, name stri } } -func testAccPreCheck(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) +// ``func testAccPreCheck(ctx context.Context, t *testing.T) { +// conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) - input := &opensearch.ListAuthorizeVpcEndpointAccesssInput{} - _, err := conn.ListAuthorizeVpcEndpointAccesss(ctx, input) +// input := &opensearch.ListAuthorizeVpcEndpointAccesssInput{} +// _, err := conn.ListAuthorizeVpcEndpointAccesss(ctx, input) - if acctest.PreCheckSkipError(err) { - t.Skipf("skipping acceptance testing: %s", err) - } - if err != nil { - t.Fatalf("unexpected PreCheck error: %s", err) - } -} +// if acctest.PreCheckSkipError(err) { +// t.Skipf("skipping acceptance testing: %s", err) +// } +// if err != nil { +// t.Fatalf("unexpected PreCheck error: %s", err) +// } +// } -func testAccCheckAuthorizeVpcEndpointAccessNotRecreated(before, after *opensearch.DescribeAuthorizeVpcEndpointAccessResponse) resource.TestCheckFunc { - return func(s *terraform.State) error { - if before, after := aws.ToString(before.AuthorizeVpcEndpointAccessId), aws.ToString(after.AuthorizeVpcEndpointAccessId); before != after { - return create.Error(names.OpenSearch, create.ErrActionCheckingNotRecreated, tfopensearch.ResNameAuthorizeVpcEndpointAccess, aws.ToString(before.AuthorizeVpcEndpointAccessId), errors.New("recreated")) - } +// func testAccCheckAuthorizeVpcEndpointAccessNotRecreated(before, after *opensearch.DescribeAuthorizeVpcEndpointAccessResponse) resource.TestCheckFunc { +// return func(s *terraform.State) error { +// if before, after := aws.ToString(before.AuthorizeVpcEndpointAccessId), aws.ToString(after.AuthorizeVpcEndpointAccessId); before != after { +// return create.Error(names.OpenSearch, create.ErrActionCheckingNotRecreated, tfopensearch.ResNameAuthorizeVpcEndpointAccess, aws.ToString(before.AuthorizeVpcEndpointAccessId), errors.New("recreated")) +// } - return nil - } -} +// return nil +// } +// } + +func testAccAuthorizeVpcEndpointAccessConfig_basic(rName, domainName string) string { + return acctest.ConfigCompose(testAccVPCEndpointConfig_base(rName, domainName), ` +data "aws_caller_identity" "current" {} -func testAccAuthorizeVpcEndpointAccessConfig_basic(rName, version string) string { - return fmt.Sprintf(` -resource "aws_security_group" "test" { - name = %[1]q +resource "aws_opensearch_vpc_endpoint" "test" { + domain_arn = aws_opensearch_domain.test.arn + + vpc_options { + subnet_ids = aws_subnet.client[*].id + } } resource "aws_opensearch_authorize_vpc_endpoint_access" "test" { - authorize_vpc_endpoint_access_name = %[1]q - engine_type = "ActiveOpenSearch" - engine_version = %[2]q - host_instance_type = "opensearch.t2.micro" - security_groups = [aws_security_group.test.id] - authentication_strategy = "simple" - storage_type = "efs" - - logs { - general = true - } + domain_name = aws_opensearch_domain.name + account = data.aws_caller_identity.current.account_id - user { - username = "Test" - password = "TestTest1234" - } } -`, rName, version) +`) } diff --git a/internal/service/opensearch/exports_test.go b/internal/service/opensearch/exports_test.go index 7de73962449..bfad6ec2bb9 100644 --- a/internal/service/opensearch/exports_test.go +++ b/internal/service/opensearch/exports_test.go @@ -12,10 +12,11 @@ var ( ResourcePackageAssociation = resourcePackageAssociation ResourceVPCEndpoint = resourceVPCEndpoint - FindDomainByName = findDomainByName - FindPackageByID = findPackageByID - FindPackageAssociationByTwoPartKey = findPackageAssociationByTwoPartKey - FindVPCEndpointByID = findVPCEndpointByID + FindDomainByName = findDomainByName + FindPackageByID = findPackageByID + FindPackageAssociationByTwoPartKey = findPackageAssociationByTwoPartKey + FindVPCEndpointByID = findVPCEndpointByID + FindAuthorizeVpcEndpointAccessByName = findAuthorizeVpcEndpointAccessByName EBSVolumeTypePermitsIopsInput = ebsVolumeTypePermitsIopsInput EBSVolumeTypePermitsThroughputInput = ebsVolumeTypePermitsThroughputInput From cab2955fafff760eb4adca5e4fc24e7c7a65fe7d Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Wed, 23 Oct 2024 14:52:16 -0700 Subject: [PATCH 06/13] Add docs --- ...uthorize_vpc_endpoint_access.html.markdown | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown b/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown index 40468ddd100..b9d8d8c92f5 100644 --- a/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown +++ b/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown @@ -5,14 +5,7 @@ page_title: "AWS: aws_opensearch_authorize_vpc_endpoint_access" description: |- Terraform resource for managing an AWS OpenSearch Authorize Vpc Endpoint Access. --- -` + # Resource: aws_opensearch_authorize_vpc_endpoint_access Terraform resource for managing an AWS OpenSearch Authorize Vpc Endpoint Access. @@ -22,7 +15,11 @@ Terraform resource for managing an AWS OpenSearch Authorize Vpc Endpoint Access. ### Basic Usage ```terraform -resource "aws_opensearch_authorize_vpc_endpoint_access" "example" { +data "aws_caller_identity" "current" {} + +resource "aws_opensearch_authorize_vpc_endpoint_access" "test" { + domain_name = aws_opensearch_domain.test.domain_name + account = data.aws_caller_identity.current.account_id } ``` @@ -30,26 +27,19 @@ resource "aws_opensearch_authorize_vpc_endpoint_access" "example" { The following arguments are required: -* `example_arg` - (Required) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. - -The following arguments are optional: - -* `optional_arg` - (Optional) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `account` - (Required) AWS account ID to grant access to. +* `domain_name` - (Required) Name of OpenSearch Service domain to provide access to. ## Attribute Reference This resource exports the following attributes in addition to the arguments above: -* `arn` - ARN of the Authorize Vpc Endpoint Access. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. -* `example_attribute` - Concise description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. - -## Timeouts +* `authorized_principal` - Information about the Amazon Web Services account or service that was provided access to the domain. See [authorized principal](#authorized_principal) attribute for further details. -[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): +### authorized_principal -* `create` - (Default `60m`) -* `update` - (Default `180m`) -* `delete` - (Default `90m`) +* `principal` - IAM principal that is allowed to access to the domain. +* `principal_type` - Type of principal. ## Import From efe9e600792fda61751571f643a64094394ed9eb Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 24 Oct 2024 20:15:28 -0700 Subject: [PATCH 07/13] Clean up --- .../authorize_vpc_endpoint_access.go | 53 ++++++------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access.go b/internal/service/opensearch/authorize_vpc_endpoint_access.go index 05c83ffb0e5..e5b94244b39 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access.go @@ -27,7 +27,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @FrameworkResource("aws_opensearch_authroize_vpc_endpoint_access", name="Authorize Vpc Endpoint Access") +// @FrameworkResource("aws_opensearch_authorize_vpc_endpoint_access", name="Authorize VPC Endpoint Access") func newResourceAuthorizeVpcEndpointAccess(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourceAuthorizeVpcEndpointAccess{} @@ -40,8 +40,8 @@ const ( type resourceAuthorizeVpcEndpointAccess struct { framework.ResourceWithConfigure + framework.WithImportByID framework.WithNoUpdate - framework.WithTimeouts } func (r *resourceAuthorizeVpcEndpointAccess) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -52,41 +52,19 @@ func (r *resourceAuthorizeVpcEndpointAccess) Schema(ctx context.Context, req res resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "account": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ + Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, }, "domain_name": schema.StringAttribute{ Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, }, - }, - Blocks: map[string]schema.Block{ - "authorized_principal": schema.ListNestedBlock{ + "authorized_principal": schema.ListAttribute{ + CustomType: fwtypes.NewListNestedObjectTypeOf[authorizedPrincipalData](ctx), + Computed: true, PlanModifiers: []planmodifier.List{ listplanmodifier.UseStateForUnknown(), }, - CustomType: fwtypes.NewListNestedObjectTypeOf[resourceAuthorizeVpcEndpointAccessData](ctx), - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "principal": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - "principal_type": schema.StringAttribute{ - CustomType: fwtypes.StringEnumType[awstypes.PrincipalType](), - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - }, - }, }, }, } @@ -102,9 +80,10 @@ func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req res } in := &opensearch.AuthorizeVpcEndpointAccessInput{ - Account: aws.String(plan.Account.ValueString()), - DomainName: aws.String(plan.DomainName.ValueString()), + Account: plan.Account.ValueStringPointer(), + DomainName: plan.DomainName.ValueStringPointer(), } + resp.Diagnostics.Append(flex.Expand(ctx, plan, in)...) if resp.Diagnostics.HasError() { return @@ -118,6 +97,7 @@ func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req res ) return } + if out == nil || out.AuthorizedPrincipal == nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVpcEndpointAccess, plan.DomainName.String(), nil), @@ -172,7 +152,6 @@ func (r *resourceAuthorizeVpcEndpointAccess) Delete(ctx context.Context, req res return } - // TIP: -- 3. Populate a delete input structure in := &opensearch.RevokeVpcEndpointAccessInput{ Account: aws.String(state.Account.ValueString()), DomainName: aws.String(state.DomainName.ValueString()), @@ -192,18 +171,18 @@ func (r *resourceAuthorizeVpcEndpointAccess) Delete(ctx context.Context, req res } func (r *resourceAuthorizeVpcEndpointAccess) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + resource.ImportStatePassthroughID(ctx, path.Root("domain_name"), req, resp) } func findAuthorizeVpcEndpointAccessByName(ctx context.Context, conn *opensearch.Client, domainName string) (*awstypes.AuthorizedPrincipal, error) { in := &opensearch.ListVpcEndpointAccessInput{ - DomainName: aws.String(id), + DomainName: aws.String(domainName), } - return findAuthroizeVpcEndpointAccess(ctx, conn, in) + return findAuthorizeVpcEndpointAccess(ctx, conn, in) } -func findAuthroizeVpcEndpointAccess(ctx context.Context, conn *opensearch.Client, input *opensearch.ListVpcEndpointAccessInput) (*awstypes.AuthorizedPrincipal, error) { +func findAuthorizeVpcEndpointAccess(ctx context.Context, conn *opensearch.Client, input *opensearch.ListVpcEndpointAccessInput) (*awstypes.AuthorizedPrincipal, error) { output, err := findAuthorizeVpcEndpointAccesses(ctx, conn, input) if err != nil { @@ -240,6 +219,6 @@ type resourceAuthorizeVpcEndpointAccessData struct { } type authorizedPrincipalData struct { - Principal types.String `tfsdk:"principal"` - PrincipalType fwtypes.StringEnum[awstypes.PrincipalType] `tfsdk:"principal_type"` + Principal types.String `tfsdk:"principal"` + PrincipalType types.String `tfsdk:"principal_type"` } From 8a2081997d51fea0855d8ad8ac5c74004270c050 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 24 Oct 2024 20:15:53 -0700 Subject: [PATCH 08/13] Work on tests --- .../authorize_vpc_endpoint_access_test.go | 33 ++++++++++++++----- internal/service/opensearch/exports_test.go | 13 ++++---- .../service/opensearch/service_package_gen.go | 2 +- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go index 6c9bf805e14..c7b2f1a4688 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go @@ -36,7 +36,6 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.OpenSearchServiceID) }, ErrorCheck: acctest.ErrorCheck(t, names.OpenSearchServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -51,9 +50,12 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateId: domainName, + ImportStateIdFunc: testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName), + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: "domain_name", }, }, }) @@ -79,7 +81,7 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_disappears(t *testing.T) { Config: testAccAuthorizeVpcEndpointAccessConfig_basic(rName, domainName), Check: resource.ComposeTestCheckFunc( testAccCheckAuthorizeVpcEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfopensearch.resourceAuthorizeVpcEndpointAccess, resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfopensearch.ResourceAuthorizeVpcEndpointAccess, resourceName), ), ExpectNonEmptyPlan: true, }, @@ -120,10 +122,13 @@ func testAccCheckAuthorizeVpcEndpointAccessExists(ctx context.Context, name stri return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, name, errors.New("not found")) } - conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) + if rs.Primary.ID == "" { + return create.Error(names.Route53Profiles, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, name, errors.New("not set")) + } - resp, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes["authorized_principal"]) + conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) + resp, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes["domain_name"]) if err != nil { return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, rs.Primary.ID, err) } @@ -134,6 +139,17 @@ func testAccCheckAuthorizeVpcEndpointAccessExists(ctx context.Context, name stri } } +func testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not found: %s", resourceName) + } + + return rs.Primary.Attributes["domain_name"], nil + } +} + // ``func testAccPreCheck(ctx context.Context, t *testing.T) { // conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) @@ -171,9 +187,8 @@ resource "aws_opensearch_vpc_endpoint" "test" { } resource "aws_opensearch_authorize_vpc_endpoint_access" "test" { - domain_name = aws_opensearch_domain.name + domain_name = aws_opensearch_domain.test.domain_name account = data.aws_caller_identity.current.account_id - } `) } diff --git a/internal/service/opensearch/exports_test.go b/internal/service/opensearch/exports_test.go index bfad6ec2bb9..ba78e0e2027 100644 --- a/internal/service/opensearch/exports_test.go +++ b/internal/service/opensearch/exports_test.go @@ -5,12 +5,13 @@ package opensearch // Exports for use in tests only. var ( - ResourceDomainSAMLOptions = resourceDomainSAMLOptions - ResourceInboundConnectionAccepter = resourceInboundConnectionAccepter - ResourceOutboundConnection = resourceOutboundConnection - ResourcePackage = resourcePackage - ResourcePackageAssociation = resourcePackageAssociation - ResourceVPCEndpoint = resourceVPCEndpoint + ResourceDomainSAMLOptions = resourceDomainSAMLOptions + ResourceInboundConnectionAccepter = resourceInboundConnectionAccepter + ResourceOutboundConnection = resourceOutboundConnection + ResourcePackage = resourcePackage + ResourcePackageAssociation = resourcePackageAssociation + ResourceVPCEndpoint = resourceVPCEndpoint + ResourceAuthorizeVpcEndpointAccess = newResourceAuthorizeVpcEndpointAccess FindDomainByName = findDomainByName FindPackageByID = findPackageByID diff --git a/internal/service/opensearch/service_package_gen.go b/internal/service/opensearch/service_package_gen.go index f923518f7c0..921e45579c3 100644 --- a/internal/service/opensearch/service_package_gen.go +++ b/internal/service/opensearch/service_package_gen.go @@ -22,7 +22,7 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic return []*types.ServicePackageFrameworkResource{ { Factory: newResourceAuthorizeVpcEndpointAccess, - Name: "Authorize Vpc Endpoint Access", + Name: "Authorize VPC Endpoint Access", }, } } From b414abfcc99192753ab0053557c6882b41e13d7d Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 24 Oct 2024 22:12:32 -0700 Subject: [PATCH 09/13] File clean up --- .../authorize_vpc_endpoint_access_test.go | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go index c7b2f1a4688..a6c04780ee0 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go @@ -54,7 +54,6 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { ImportState: true, ImportStateId: domainName, ImportStateIdFunc: testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName), - ImportStateVerify: true, ImportStateVerifyIdentifierAttribute: "domain_name", }, }, @@ -150,30 +149,6 @@ func testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName string) res } } -// ``func testAccPreCheck(ctx context.Context, t *testing.T) { -// conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) - -// input := &opensearch.ListAuthorizeVpcEndpointAccesssInput{} -// _, err := conn.ListAuthorizeVpcEndpointAccesss(ctx, input) - -// if acctest.PreCheckSkipError(err) { -// t.Skipf("skipping acceptance testing: %s", err) -// } -// if err != nil { -// t.Fatalf("unexpected PreCheck error: %s", err) -// } -// } - -// func testAccCheckAuthorizeVpcEndpointAccessNotRecreated(before, after *opensearch.DescribeAuthorizeVpcEndpointAccessResponse) resource.TestCheckFunc { -// return func(s *terraform.State) error { -// if before, after := aws.ToString(before.AuthorizeVpcEndpointAccessId), aws.ToString(after.AuthorizeVpcEndpointAccessId); before != after { -// return create.Error(names.OpenSearch, create.ErrActionCheckingNotRecreated, tfopensearch.ResNameAuthorizeVpcEndpointAccess, aws.ToString(before.AuthorizeVpcEndpointAccessId), errors.New("recreated")) -// } - -// return nil -// } -// } - func testAccAuthorizeVpcEndpointAccessConfig_basic(rName, domainName string) string { return acctest.ConfigCompose(testAccVPCEndpointConfig_base(rName, domainName), ` data "aws_caller_identity" "current" {} @@ -188,7 +163,7 @@ resource "aws_opensearch_vpc_endpoint" "test" { resource "aws_opensearch_authorize_vpc_endpoint_access" "test" { domain_name = aws_opensearch_domain.test.domain_name - account = data.aws_caller_identity.current.account_id + account = data.aws_caller_identity.current.account_id } `) } From 2531d34425be7ac2a766fc02c61c766392cfb97c Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 24 Oct 2024 22:59:39 -0700 Subject: [PATCH 10/13] Check fixes --- .../service/opensearch/authorize_vpc_endpoint_access_test.go | 5 ++--- .../r/opensearch_authorize_vpc_endpoint_access.html.markdown | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go index a6c04780ee0..de8be486855 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go @@ -16,10 +16,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" + tfopensearch "github.com/hashicorp/terraform-provider-aws/internal/service/opensearch" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" - - tfopensearch "github.com/hashicorp/terraform-provider-aws/internal/service/opensearch" ) func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { @@ -53,8 +52,8 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateId: domainName, - ImportStateIdFunc: testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName), ImportStateVerifyIdentifierAttribute: "domain_name", + ImportStateIdFunc: testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName), }, }, }) diff --git a/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown b/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown index b9d8d8c92f5..ab2c0630ca3 100644 --- a/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown +++ b/website/docs/r/opensearch_authorize_vpc_endpoint_access.html.markdown @@ -19,7 +19,7 @@ data "aws_caller_identity" "current" {} resource "aws_opensearch_authorize_vpc_endpoint_access" "test" { domain_name = aws_opensearch_domain.test.domain_name - account = data.aws_caller_identity.current.account_id + account = data.aws_caller_identity.current.account_id } ``` From 4ba79fb65fefaaa954f51605c75d1f8c120dc314 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 24 Oct 2024 23:03:27 -0700 Subject: [PATCH 11/13] semgrep fixes --- .../service/opensearch/authorize_vpc_endpoint_access.go | 4 ++-- .../opensearch/authorize_vpc_endpoint_access_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access.go b/internal/service/opensearch/authorize_vpc_endpoint_access.go index e5b94244b39..c2936b2dcdd 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access.go @@ -56,7 +56,7 @@ func (r *resourceAuthorizeVpcEndpointAccess) Schema(ctx context.Context, req res stringplanmodifier.UseStateForUnknown(), }, }, - "domain_name": schema.StringAttribute{ + names.AttrDomainName: schema.StringAttribute{ Required: true, }, "authorized_principal": schema.ListAttribute{ @@ -171,7 +171,7 @@ func (r *resourceAuthorizeVpcEndpointAccess) Delete(ctx context.Context, req res } func (r *resourceAuthorizeVpcEndpointAccess) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("domain_name"), req, resp) + resource.ImportStatePassthroughID(ctx, path.Root(names.AttrDomainName), req, resp) } func findAuthorizeVpcEndpointAccessByName(ctx context.Context, conn *opensearch.Client, domainName string) (*awstypes.AuthorizedPrincipal, error) { diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go index de8be486855..5fab6277bab 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go @@ -52,7 +52,7 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateId: domainName, - ImportStateVerifyIdentifierAttribute: "domain_name", + ImportStateVerifyIdentifierAttribute: names.AttrDomainName, ImportStateIdFunc: testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName), }, }, @@ -96,7 +96,7 @@ func testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx context.Context) resource continue } - _, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes["domain_name"]) + _, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes[names.AttrDomainName]) if tfresource.NotFound(err) { continue @@ -126,7 +126,7 @@ func testAccCheckAuthorizeVpcEndpointAccessExists(ctx context.Context, name stri conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) - resp, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes["domain_name"]) + resp, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes[names.AttrDomainName]) if err != nil { return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, rs.Primary.ID, err) } @@ -144,7 +144,7 @@ func testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName string) res return "", fmt.Errorf("Not found: %s", resourceName) } - return rs.Primary.Attributes["domain_name"], nil + return rs.Primary.Attributes[names.AttrDomainName], nil } } From 2ca67052d7ef0f8c1a2ba5633e1317878d959ae4 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 24 Oct 2024 23:26:00 -0700 Subject: [PATCH 12/13] More semgrep fixes; linter fixes --- .../authorize_vpc_endpoint_access.go | 57 +++++++++---------- .../authorize_vpc_endpoint_access_test.go | 32 +++++------ internal/service/opensearch/exports_test.go | 4 +- 3 files changed, 46 insertions(+), 47 deletions(-) diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access.go b/internal/service/opensearch/authorize_vpc_endpoint_access.go index c2936b2dcdd..29feba46784 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access.go @@ -21,34 +21,33 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" - fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) // @FrameworkResource("aws_opensearch_authorize_vpc_endpoint_access", name="Authorize VPC Endpoint Access") -func newResourceAuthorizeVpcEndpointAccess(_ context.Context) (resource.ResourceWithConfigure, error) { - r := &resourceAuthorizeVpcEndpointAccess{} +func newResourceAuthorizeVPCEndpointAccess(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &resourceAuthorizeVPCEndpointAccess{} return r, nil } const ( - ResNameAuthorizeVpcEndpointAccess = "Authorize Vpc Endpoint Access" + ResNameAuthorizeVPCEndpointAccess = "Authorize Vpc Endpoint Access" ) -type resourceAuthorizeVpcEndpointAccess struct { +type resourceAuthorizeVPCEndpointAccess struct { framework.ResourceWithConfigure framework.WithImportByID framework.WithNoUpdate } -func (r *resourceAuthorizeVpcEndpointAccess) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { +func (r *resourceAuthorizeVPCEndpointAccess) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = "aws_opensearch_authorize_vpc_endpoint_access" } -func (r *resourceAuthorizeVpcEndpointAccess) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *resourceAuthorizeVPCEndpointAccess) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "account": schema.StringAttribute{ @@ -70,10 +69,10 @@ func (r *resourceAuthorizeVpcEndpointAccess) Schema(ctx context.Context, req res } } -func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { +func (r *resourceAuthorizeVPCEndpointAccess) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { conn := r.Meta().OpenSearchClient(ctx) - var plan resourceAuthorizeVpcEndpointAccessData + var plan resourceAuthorizeVPCEndpointAccessData resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return @@ -92,7 +91,7 @@ func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req res out, err := conn.AuthorizeVpcEndpointAccess(ctx, in) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVpcEndpointAccess, plan.DomainName.String(), err), + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVPCEndpointAccess, plan.DomainName.String(), err), err.Error(), ) return @@ -100,13 +99,13 @@ func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req res if out == nil || out.AuthorizedPrincipal == nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVpcEndpointAccess, plan.DomainName.String(), nil), + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionCreating, ResNameAuthorizeVPCEndpointAccess, plan.DomainName.String(), nil), errors.New("empty output").Error(), ) return } - resp.Diagnostics.Append(fwflex.Flatten(ctx, out, &plan)...) + resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...) if resp.Diagnostics.HasError() { return } @@ -114,47 +113,47 @@ func (r *resourceAuthorizeVpcEndpointAccess) Create(ctx context.Context, req res resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) } -func (r *resourceAuthorizeVpcEndpointAccess) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { +func (r *resourceAuthorizeVPCEndpointAccess) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { conn := r.Meta().OpenSearchClient(ctx) - var state resourceAuthorizeVpcEndpointAccessData + var state resourceAuthorizeVPCEndpointAccessData resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - out, err := findAuthorizeVpcEndpointAccessByName(ctx, conn, state.DomainName.ValueString()) + out, err := findAuthorizeVPCEndpointAccessByName(ctx, conn, state.DomainName.ValueString()) if tfresource.NotFound(err) { resp.State.RemoveResource(ctx) return } if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionSetting, ResNameAuthorizeVpcEndpointAccess, state.DomainName.String(), err), + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionSetting, ResNameAuthorizeVPCEndpointAccess, state.DomainName.String(), err), err.Error(), ) return } - resp.Diagnostics.Append(fwflex.Flatten(ctx, out, &state)...) + resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...) if resp.Diagnostics.HasError() { return } resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *resourceAuthorizeVpcEndpointAccess) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *resourceAuthorizeVPCEndpointAccess) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { conn := r.Meta().OpenSearchClient(ctx) - var state resourceAuthorizeVpcEndpointAccessData + var state resourceAuthorizeVPCEndpointAccessData resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } in := &opensearch.RevokeVpcEndpointAccessInput{ - Account: aws.String(state.Account.ValueString()), - DomainName: aws.String(state.DomainName.ValueString()), + Account: state.Account.ValueStringPointer(), + DomainName: state.DomainName.ValueStringPointer(), } _, err := conn.RevokeVpcEndpointAccess(ctx, in) @@ -163,27 +162,27 @@ func (r *resourceAuthorizeVpcEndpointAccess) Delete(ctx context.Context, req res return } resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.OpenSearch, create.ErrActionDeleting, ResNameAuthorizeVpcEndpointAccess, state.DomainName.String(), err), + create.ProblemStandardMessage(names.OpenSearch, create.ErrActionDeleting, ResNameAuthorizeVPCEndpointAccess, state.DomainName.String(), err), err.Error(), ) return } } -func (r *resourceAuthorizeVpcEndpointAccess) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { +func (r *resourceAuthorizeVPCEndpointAccess) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { resource.ImportStatePassthroughID(ctx, path.Root(names.AttrDomainName), req, resp) } -func findAuthorizeVpcEndpointAccessByName(ctx context.Context, conn *opensearch.Client, domainName string) (*awstypes.AuthorizedPrincipal, error) { +func findAuthorizeVPCEndpointAccessByName(ctx context.Context, conn *opensearch.Client, domainName string) (*awstypes.AuthorizedPrincipal, error) { in := &opensearch.ListVpcEndpointAccessInput{ DomainName: aws.String(domainName), } - return findAuthorizeVpcEndpointAccess(ctx, conn, in) + return findAuthorizeVPCEndpointAccess(ctx, conn, in) } -func findAuthorizeVpcEndpointAccess(ctx context.Context, conn *opensearch.Client, input *opensearch.ListVpcEndpointAccessInput) (*awstypes.AuthorizedPrincipal, error) { - output, err := findAuthorizeVpcEndpointAccesses(ctx, conn, input) +func findAuthorizeVPCEndpointAccess(ctx context.Context, conn *opensearch.Client, input *opensearch.ListVpcEndpointAccessInput) (*awstypes.AuthorizedPrincipal, error) { + output, err := findAuthorizeVPCEndpointAccesses(ctx, conn, input) if err != nil { return nil, err @@ -192,7 +191,7 @@ func findAuthorizeVpcEndpointAccess(ctx context.Context, conn *opensearch.Client return tfresource.AssertSingleValueResult(output) } -func findAuthorizeVpcEndpointAccesses(ctx context.Context, conn *opensearch.Client, input *opensearch.ListVpcEndpointAccessInput) ([]awstypes.AuthorizedPrincipal, error) { +func findAuthorizeVPCEndpointAccesses(ctx context.Context, conn *opensearch.Client, input *opensearch.ListVpcEndpointAccessInput) ([]awstypes.AuthorizedPrincipal, error) { var output []awstypes.AuthorizedPrincipal err := listVPCEndpointAccessPages(ctx, conn, input, func(page *opensearch.ListVpcEndpointAccessOutput, lastPage bool) bool { @@ -212,7 +211,7 @@ func findAuthorizeVpcEndpointAccesses(ctx context.Context, conn *opensearch.Clie return output, nil } -type resourceAuthorizeVpcEndpointAccessData struct { +type resourceAuthorizeVPCEndpointAccessData struct { Account types.String `tfsdk:"account"` DomainName types.String `tfsdk:"domain_name"` AuthorizedPrincipal fwtypes.ListNestedObjectValueOf[authorizedPrincipalData] `tfsdk:"authorized_principal"` diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go index 5fab6277bab..74a1a014042 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go @@ -21,7 +21,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { +func TestAccOpenSearchAuthorizeVPCEndpointAccess_basic(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -38,12 +38,12 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, names.OpenSearchServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx), + CheckDestroy: testAccCheckAuthorizeVPCEndpointAccessDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccAuthorizeVpcEndpointAccessConfig_basic(rName, domainName), + Config: testAccAuthorizeVPCEndpointAccessConfig_basic(rName, domainName), Check: resource.ComposeTestCheckFunc( - testAccCheckAuthorizeVpcEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), + testAccCheckAuthorizeVPCEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), resource.TestCheckResourceAttrSet(resourceName, "account"), resource.TestCheckResourceAttrSet(resourceName, names.AttrDomainName), ), @@ -53,13 +53,13 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_basic(t *testing.T) { ImportState: true, ImportStateId: domainName, ImportStateVerifyIdentifierAttribute: names.AttrDomainName, - ImportStateIdFunc: testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName), + ImportStateIdFunc: testAccAuthorizeVPCEndpointAccessImportStateIDFunc(resourceName), }, }, }) } -func TestAccOpenSearchAuthorizeVpcEndpointAccess_disappears(t *testing.T) { +func TestAccOpenSearchAuthorizeVPCEndpointAccess_disappears(t *testing.T) { ctx := acctest.Context(t) var authorizevpcendpointaccess awstypes.AuthorizedPrincipal @@ -73,12 +73,12 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_disappears(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, names.OpenSearchServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx), + CheckDestroy: testAccCheckAuthorizeVPCEndpointAccessDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccAuthorizeVpcEndpointAccessConfig_basic(rName, domainName), + Config: testAccAuthorizeVPCEndpointAccessConfig_basic(rName, domainName), Check: resource.ComposeTestCheckFunc( - testAccCheckAuthorizeVpcEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), + testAccCheckAuthorizeVPCEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfopensearch.ResourceAuthorizeVpcEndpointAccess, resourceName), ), ExpectNonEmptyPlan: true, @@ -87,7 +87,7 @@ func TestAccOpenSearchAuthorizeVpcEndpointAccess_disappears(t *testing.T) { }) } -func testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx context.Context) resource.TestCheckFunc { +func testAccCheckAuthorizeVPCEndpointAccessDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) @@ -113,22 +113,22 @@ func testAccCheckAuthorizeVpcEndpointAccessDestroy(ctx context.Context) resource } } -func testAccCheckAuthorizeVpcEndpointAccessExists(ctx context.Context, name string, authorizevpcendpointaccess *awstypes.AuthorizedPrincipal) resource.TestCheckFunc { +func testAccCheckAuthorizeVPCEndpointAccessExists(ctx context.Context, name string, authorizevpcendpointaccess *awstypes.AuthorizedPrincipal) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { - return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, name, errors.New("not found")) + return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVPCEndpointAccess, name, errors.New("not found")) } if rs.Primary.ID == "" { - return create.Error(names.Route53Profiles, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, name, errors.New("not set")) + return create.Error(names.Route53Profiles, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVPCEndpointAccess, name, errors.New("not set")) } conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) resp, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes[names.AttrDomainName]) if err != nil { - return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVpcEndpointAccess, rs.Primary.ID, err) + return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVPCEndpointAccess, rs.Primary.ID, err) } *authorizevpcendpointaccess = *resp @@ -137,7 +137,7 @@ func testAccCheckAuthorizeVpcEndpointAccessExists(ctx context.Context, name stri } } -func testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { +func testAccAuthorizeVPCEndpointAccessImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -148,7 +148,7 @@ func testAccAuthorizeVpcEndpointAccessImportStateIDFunc(resourceName string) res } } -func testAccAuthorizeVpcEndpointAccessConfig_basic(rName, domainName string) string { +func testAccAuthorizeVPCEndpointAccessConfig_basic(rName, domainName string) string { return acctest.ConfigCompose(testAccVPCEndpointConfig_base(rName, domainName), ` data "aws_caller_identity" "current" {} diff --git a/internal/service/opensearch/exports_test.go b/internal/service/opensearch/exports_test.go index ba78e0e2027..eae7324ea04 100644 --- a/internal/service/opensearch/exports_test.go +++ b/internal/service/opensearch/exports_test.go @@ -11,13 +11,13 @@ var ( ResourcePackage = resourcePackage ResourcePackageAssociation = resourcePackageAssociation ResourceVPCEndpoint = resourceVPCEndpoint - ResourceAuthorizeVpcEndpointAccess = newResourceAuthorizeVpcEndpointAccess + ResourceAuthorizeVpcEndpointAccess = newResourceAuthorizeVPCEndpointAccess FindDomainByName = findDomainByName FindPackageByID = findPackageByID FindPackageAssociationByTwoPartKey = findPackageAssociationByTwoPartKey FindVPCEndpointByID = findVPCEndpointByID - FindAuthorizeVpcEndpointAccessByName = findAuthorizeVpcEndpointAccessByName + FindAuthorizeVpcEndpointAccessByName = findAuthorizeVPCEndpointAccessByName EBSVolumeTypePermitsIopsInput = ebsVolumeTypePermitsIopsInput EBSVolumeTypePermitsThroughputInput = ebsVolumeTypePermitsThroughputInput From 232e7fc4484c10a772780c25599b2185c94f1a51 Mon Sep 17 00:00:00 2001 From: Sharon Nam Date: Thu, 24 Oct 2024 23:31:47 -0700 Subject: [PATCH 13/13] Capitalization fixes --- .../opensearch/authorize_vpc_endpoint_access_test.go | 6 +++--- internal/service/opensearch/exports_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go index 74a1a014042..327ec6b3ef6 100644 --- a/internal/service/opensearch/authorize_vpc_endpoint_access_test.go +++ b/internal/service/opensearch/authorize_vpc_endpoint_access_test.go @@ -79,7 +79,7 @@ func TestAccOpenSearchAuthorizeVPCEndpointAccess_disappears(t *testing.T) { Config: testAccAuthorizeVPCEndpointAccessConfig_basic(rName, domainName), Check: resource.ComposeTestCheckFunc( testAccCheckAuthorizeVPCEndpointAccessExists(ctx, resourceName, &authorizevpcendpointaccess), - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfopensearch.ResourceAuthorizeVpcEndpointAccess, resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfopensearch.ResourceAuthorizeVPCEndpointAccess, resourceName), ), ExpectNonEmptyPlan: true, }, @@ -96,7 +96,7 @@ func testAccCheckAuthorizeVPCEndpointAccessDestroy(ctx context.Context) resource continue } - _, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes[names.AttrDomainName]) + _, err := tfopensearch.FindAuthorizeVPCEndpointAccessByName(ctx, conn, rs.Primary.Attributes[names.AttrDomainName]) if tfresource.NotFound(err) { continue @@ -126,7 +126,7 @@ func testAccCheckAuthorizeVPCEndpointAccessExists(ctx context.Context, name stri conn := acctest.Provider.Meta().(*conns.AWSClient).OpenSearchClient(ctx) - resp, err := tfopensearch.FindAuthorizeVpcEndpointAccessByName(ctx, conn, rs.Primary.Attributes[names.AttrDomainName]) + resp, err := tfopensearch.FindAuthorizeVPCEndpointAccessByName(ctx, conn, rs.Primary.Attributes[names.AttrDomainName]) if err != nil { return create.Error(names.OpenSearch, create.ErrActionCheckingExistence, tfopensearch.ResNameAuthorizeVPCEndpointAccess, rs.Primary.ID, err) } diff --git a/internal/service/opensearch/exports_test.go b/internal/service/opensearch/exports_test.go index eae7324ea04..1bd2801b179 100644 --- a/internal/service/opensearch/exports_test.go +++ b/internal/service/opensearch/exports_test.go @@ -11,13 +11,13 @@ var ( ResourcePackage = resourcePackage ResourcePackageAssociation = resourcePackageAssociation ResourceVPCEndpoint = resourceVPCEndpoint - ResourceAuthorizeVpcEndpointAccess = newResourceAuthorizeVPCEndpointAccess + ResourceAuthorizeVPCEndpointAccess = newResourceAuthorizeVPCEndpointAccess FindDomainByName = findDomainByName FindPackageByID = findPackageByID FindPackageAssociationByTwoPartKey = findPackageAssociationByTwoPartKey FindVPCEndpointByID = findVPCEndpointByID - FindAuthorizeVpcEndpointAccessByName = findAuthorizeVPCEndpointAccessByName + FindAuthorizeVPCEndpointAccessByName = findAuthorizeVPCEndpointAccessByName EBSVolumeTypePermitsIopsInput = ebsVolumeTypePermitsIopsInput EBSVolumeTypePermitsThroughputInput = ebsVolumeTypePermitsThroughputInput