Skip to content

Commit

Permalink
Add aws_opensearchserverless_lifecycle_policy resource
Browse files Browse the repository at this point in the history
  • Loading branch information
Joshua Luo committed Oct 27, 2023
1 parent 18e379b commit 7ce5fb1
Show file tree
Hide file tree
Showing 7 changed files with 688 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .changelog/34144.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_opensearchserverless_lifecycle_policy
```
22 changes: 12 additions & 10 deletions internal/service/opensearchserverless/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ package opensearchserverless

// Exports for use in tests only.
var (
ResourceAccessPolicy = newResourceAccessPolicy
ResourceCollection = newResourceCollection
ResourceSecurityConfig = newResourceSecurityConfig
ResourceSecurityPolicy = newResourceSecurityPolicy
ResourceVPCEndpoint = newResourceVPCEndpoint
ResourceAccessPolicy = newResourceAccessPolicy
ResourceCollection = newResourceCollection
ResourceLifecyclePolicy = newResourceLifecyclePolicy
ResourceSecurityConfig = newResourceSecurityConfig
ResourceSecurityPolicy = newResourceSecurityPolicy
ResourceVPCEndpoint = newResourceVPCEndpoint

FindAccessPolicyByNameAndType = findAccessPolicyByNameAndType
FindCollectionByID = findCollectionByID
FindSecurityConfigByID = findSecurityConfigByID
FindSecurityPolicyByNameAndType = findSecurityPolicyByNameAndType
FindVPCEndpointByID = findVPCEndpointByID
FindAccessPolicyByNameAndType = findAccessPolicyByNameAndType
FindCollectionByID = findCollectionByID
FindLifecyclePolicyByNameAndType = findLifecyclePolicyByNameAndType
FindSecurityConfigByID = findSecurityConfigByID
FindSecurityPolicyByNameAndType = findSecurityPolicyByNameAndType
FindVPCEndpointByID = findVPCEndpointByID
)
34 changes: 34 additions & 0 deletions internal/service/opensearchserverless/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,37 @@ func findVPCEndpointByID(ctx context.Context, conn *opensearchserverless.Client,

return &out.VpcEndpointDetails[0], nil
}

func findLifecyclePolicyByNameAndType(ctx context.Context, conn *opensearchserverless.Client, name, policyType string) (*types.LifecyclePolicyDetail, error) {
in := &opensearchserverless.BatchGetLifecyclePolicyInput{
Identifiers: []types.LifecyclePolicyIdentifier{
{
Name: aws.String(name),
Type: types.LifecyclePolicyType(policyType),
},
},
}

out, err := conn.BatchGetLifecyclePolicy(ctx, in)
if err != nil {
var nfe *types.ResourceNotFoundException
if errors.As(err, &nfe) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: in,
}
}

return nil, err
}

if out == nil || out.LifecyclePolicyDetails == nil || len(out.LifecyclePolicyDetails) == 0 {
return nil, tfresource.NewEmptyResultError(in)
}

if len(out.LifecyclePolicyDetails) > 1 {
return nil, tfresource.NewTooManyResultsError(len(out.LifecyclePolicyDetails), in)
}

return &out.LifecyclePolicyDetails[0], nil
}
296 changes: 296 additions & 0 deletions internal/service/opensearchserverless/lifecycle_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package opensearchserverless

import (
"context"
"errors"
"fmt"
"strings"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/opensearchserverless"
awstypes "github.com/aws/aws-sdk-go-v2/service/opensearchserverless/types"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/id"
"github.com/hashicorp/terraform-provider-aws/internal/create"
"github.com/hashicorp/terraform-provider-aws/internal/enum"
"github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag"
"github.com/hashicorp/terraform-provider-aws/internal/framework"
"github.com/hashicorp/terraform-provider-aws/internal/framework/flex"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/names"
)

// Function annotations are used for resource registration to the Provider. DO NOT EDIT.
// @FrameworkResource(name="Lifecycle Policy")
func newResourceLifecyclePolicy(_ context.Context) (resource.ResourceWithConfigure, error) {
return &resourceLifecyclePolicy{}, nil
}

const (
ResNameLifecyclePolicy = "Lifecycle Policy"
)

type resourceLifecyclePolicy struct {
framework.ResourceWithConfigure
framework.WithTimeouts
}

func (r *resourceLifecyclePolicy) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = "aws_opensearchserverless_lifecycle_policy"
}

func (r *resourceLifecyclePolicy) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"description": schema.StringAttribute{
Optional: true,
Validators: []validator.String{
stringvalidator.LengthBetween(1, 1000),
},
},
"id": framework.IDAttribute(),
"name": schema.StringAttribute{
Required: true,
Validators: []validator.String{
stringvalidator.LengthBetween(3, 32),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"policy": schema.StringAttribute{
Required: true,
Validators: []validator.String{
stringvalidator.LengthBetween(1, 20480),
},
},
"policy_version": schema.StringAttribute{
Computed: true,
},
"type": schema.StringAttribute{
Required: true,
Validators: []validator.String{
enum.FrameworkValidate[awstypes.LifecyclePolicyType](),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
},
}
}

func (r *resourceLifecyclePolicy) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
conn := r.Meta().OpenSearchServerlessClient(ctx)

var plan resourceLifecyclePolicyData
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}

in := &opensearchserverless.CreateLifecyclePolicyInput{
ClientToken: aws.String(id.UniqueId()),
Name: aws.String(plan.Name.ValueString()),
Policy: aws.String(plan.Policy.ValueString()),
Type: awstypes.LifecyclePolicyType(plan.Type.ValueString()),
}

if !plan.Description.IsNull() {
in.Description = aws.String(plan.Description.ValueString())
}

out, err := conn.CreateLifecyclePolicy(ctx, in)
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.OpenSearchServerless, create.ErrActionCreating, ResNameLifecyclePolicy, plan.Name.String(), err),
err.Error(),
)
return
}
if out == nil || out.LifecyclePolicyDetail == nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.OpenSearchServerless, create.ErrActionCreating, ResNameLifecyclePolicy, plan.Name.String(), nil),
errors.New("empty output").Error(),
)
return
}

state := plan
resp.Diagnostics.Append(state.refreshFromOutput(ctx, out.LifecyclePolicyDetail)...)

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *resourceLifecyclePolicy) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
conn := r.Meta().OpenSearchServerlessClient(ctx)

var state resourceLifecyclePolicyData
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

out, err := findLifecyclePolicyByNameAndType(ctx, conn, state.ID.ValueString(), state.Type.ValueString())

if tfresource.NotFound(err) {
resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err))
resp.State.RemoveResource(ctx)
return
}
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.OpenSearchServerless, create.ErrActionSetting, ResNameLifecyclePolicy, state.ID.String(), err),
err.Error(),
)
return
}

resp.Diagnostics.Append(state.refreshFromOutput(ctx, out)...)
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *resourceLifecyclePolicy) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
conn := r.Meta().OpenSearchServerlessClient(ctx)

var plan, state resourceLifecyclePolicyData
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

if !plan.Description.Equal(state.Description) || !plan.Policy.Equal(state.Policy) {
in := &opensearchserverless.UpdateLifecyclePolicyInput{
ClientToken: aws.String(id.UniqueId()),
Name: flex.StringFromFramework(ctx, plan.Name),
PolicyVersion: flex.StringFromFramework(ctx, state.PolicyVersion),
Type: awstypes.LifecyclePolicyType(plan.Type.ValueString()),
}

if !plan.Policy.Equal(state.Policy) {
in.Policy = aws.String(plan.Policy.ValueString())
}

if !plan.Description.Equal(state.Description) {
in.Description = aws.String(plan.Description.ValueString())
}

out, err := conn.UpdateLifecyclePolicy(ctx, in)
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.OpenSearchServerless, create.ErrActionUpdating, ResNameLifecyclePolicy, plan.ID.String(), err),
err.Error(),
)
return
}
if out == nil || out.LifecyclePolicyDetail == nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.OpenSearchServerless, create.ErrActionUpdating, ResNameLifecyclePolicy, plan.ID.String(), nil),
errors.New("empty output").Error(),
)
return
}

resp.Diagnostics.Append(state.refreshFromOutput(ctx, out.LifecyclePolicyDetail)...)
}

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *resourceLifecyclePolicy) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
conn := r.Meta().OpenSearchServerlessClient(ctx)

var state resourceLifecyclePolicyData
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

in := &opensearchserverless.DeleteLifecyclePolicyInput{
ClientToken: aws.String(id.UniqueId()),
Name: aws.String(state.Name.ValueString()),
Type: awstypes.LifecyclePolicyType(state.Type.ValueString()),
}

_, err := conn.DeleteLifecyclePolicy(ctx, in)

if err != nil {
var nfe *awstypes.ResourceNotFoundException
if errors.As(err, &nfe) {
return
}
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.OpenSearchServerless, create.ErrActionDeleting, ResNameLifecyclePolicy, state.ID.String(), err),
err.Error(),
)
return
}
}

func (r *resourceLifecyclePolicy) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
parts := strings.Split(req.ID, idSeparator)
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
err := fmt.Errorf("unexpected format for ID (%[1]s), expected lifecycle-policy-name%[2]slifecycle-policy-type", req.ID, idSeparator)
resp.Diagnostics.AddError(fmt.Sprintf("importing %s (%s)", ResNameLifecyclePolicy, req.ID), err.Error())
return
}

state := resourceLifecyclePolicyData{
ID: types.StringValue(parts[0]),
Name: types.StringValue(parts[0]),
Type: types.StringValue(parts[1]),
}

diags := resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}

// refreshFromOutput writes state data from an AWS response object
func (rd *resourceLifecyclePolicyData) refreshFromOutput(ctx context.Context, out *awstypes.LifecyclePolicyDetail) diag.Diagnostics {
var diags diag.Diagnostics

if out == nil {
return diags
}

rd.ID = flex.StringToFramework(ctx, out.Name)
rd.Description = flex.StringToFramework(ctx, out.Description)
rd.Name = flex.StringToFramework(ctx, out.Name)
rd.Type = flex.StringValueToFramework(ctx, out.Type)
rd.PolicyVersion = flex.StringToFramework(ctx, out.PolicyVersion)

policyBytes, err := out.Policy.MarshalSmithyDocument()
if err != nil {
diags.AddError(fmt.Sprintf("refreshing state for %s (%s)", ResNameLifecyclePolicy, rd.Name), err.Error())
return diags
}

p := string(policyBytes)

rd.Policy = flex.StringToFramework(ctx, &p)

return diags
}

type resourceLifecyclePolicyData struct {
Description types.String `tfsdk:"description"`
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Policy types.String `tfsdk:"policy"`
PolicyVersion types.String `tfsdk:"policy_version"`
Type types.String `tfsdk:"type"`
}
Loading

0 comments on commit 7ce5fb1

Please sign in to comment.