Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add new resource aws_bedrock_guardrail_version #39478

Merged
merged 11 commits into from
Sep 30, 2024
3 changes: 3 additions & 0 deletions .changelog/39478.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``release-note:new-resource
aws_bedrock_guardrail_version
```
3 changes: 2 additions & 1 deletion internal/service/bedrock/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ package bedrock
var (
ResourceCustomModel = newCustomModelResource
ResourceGuardrail = newResourceGuardrail
ResourceGuardrailVersion = newGuardrailVersionResource
ResourceModelInvocationLoggingConfiguration = newModelInvocationLoggingConfigurationResource

FindCustomModelByID = findCustomModelByID
FindGuardrailByID = findGuardrailByID
FindGuardrailByTwoPartKey = findGuardrailByTwoPartKey
FindModelCustomizationJobByID = findModelCustomizationJobByID
FindModelInvocationLoggingConfiguration = findModelInvocationLoggingConfiguration
FindProvisionedModelThroughputByID = findProvisionedModelThroughputByID
Expand Down
37 changes: 19 additions & 18 deletions internal/service/bedrock/guardrail.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@
return
}

output, err := findGuardrailByID(ctx, conn, plan.GuardrailID.ValueString(), plan.Version.ValueString())
output, err := findGuardrailByTwoPartKey(ctx, conn, plan.GuardrailID.ValueString(), plan.Version.ValueString())
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.Bedrock, create.ErrActionSetting, ResNameGuardrail, plan.GuardrailID.String(), err),
Expand All @@ -410,7 +410,7 @@
if resp.Diagnostics.HasError() {
return
}
out, err := findGuardrailByID(ctx, conn, state.GuardrailID.ValueString(), state.Version.ValueString())
out, err := findGuardrailByTwoPartKey(ctx, conn, state.GuardrailID.ValueString(), state.Version.ValueString())

if tfresource.NotFound(err) {
resp.State.RemoveResource(ctx)
Expand Down Expand Up @@ -487,7 +487,7 @@
return
}

output, err := findGuardrailByID(ctx, conn, plan.GuardrailID.ValueString(), plan.Version.ValueString())
output, err := findGuardrailByTwoPartKey(ctx, conn, plan.GuardrailID.ValueString(), plan.Version.ValueString())
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.Bedrock, create.ErrActionSetting, ResNameGuardrail, plan.GuardrailID.String(), err),
Expand Down Expand Up @@ -552,7 +552,7 @@
r.SetTagsAll(ctx, req, resp)
}

func waitGuardrailCreated(ctx context.Context, conn *bedrock.Client, id string, version string, timeout time.Duration) (*bedrock.GetGuardrailOutput, error) {

Check failure on line 555 in internal/service/bedrock/guardrail.go

View workflow job for this annotation

GitHub Actions / 3 of 3

waitGuardrailCreated - result 0 (*github.com/aws/aws-sdk-go-v2/service/bedrock.GetGuardrailOutput) is never used (unparam)
stateConf := &retry.StateChangeConf{
Pending: enum.Slice(awstypes.GuardrailStatusCreating),
Target: enum.Slice(awstypes.GuardrailStatusReady),
Expand Down Expand Up @@ -588,7 +588,7 @@
return nil, err
}

func waitGuardrailDeleted(ctx context.Context, conn *bedrock.Client, id string, version string, timeout time.Duration) (*bedrock.GetGuardrailOutput, error) {
func waitGuardrailDeleted(ctx context.Context, conn *bedrock.Client, id string, version string, timeout time.Duration) (*bedrock.GetGuardrailOutput, error) { //nolint:unparam
stateConf := &retry.StateChangeConf{
Pending: enum.Slice(awstypes.GuardrailStatusDeleting, awstypes.GuardrailStatusReady),
Target: []string{},
Expand All @@ -604,9 +604,9 @@
return nil, err
}

func statusGuardrail(ctx context.Context, conn *bedrock.Client, id string, version string) retry.StateRefreshFunc {
func statusGuardrail(ctx context.Context, conn *bedrock.Client, id, version string) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
out, err := findGuardrailByID(ctx, conn, id, version)
out, err := findGuardrailByTwoPartKey(ctx, conn, id, version)
if tfresource.NotFound(err) {
return nil, "", nil
}
Expand All @@ -619,29 +619,30 @@
}
}

func findGuardrailByID(ctx context.Context, conn *bedrock.Client, id string, version string) (*bedrock.GetGuardrailOutput, error) {
in := &bedrock.GetGuardrailInput{
func findGuardrailByTwoPartKey(ctx context.Context, conn *bedrock.Client, id, version string) (*bedrock.GetGuardrailOutput, error) {
input := &bedrock.GetGuardrailInput{
GuardrailIdentifier: aws.String(id),
GuardrailVersion: aws.String(version),
}

out, err := conn.GetGuardrail(ctx, in)
if err != nil {
if errs.IsA[*awstypes.ResourceNotFoundException](err) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: in,
}
output, err := conn.GetGuardrail(ctx, input)

if errs.IsA[*awstypes.ResourceNotFoundException](err) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if out == nil {
return nil, tfresource.NewEmptyResultError(in)
if output == nil {
return nil, tfresource.NewEmptyResultError(input)
}

return out, nil
return output, nil
}

type resourceGuardrailData struct {
Expand Down
4 changes: 2 additions & 2 deletions internal/service/bedrock/guardrail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ func testAccCheckGuardrailDestroy(ctx context.Context) resource.TestCheckFunc {
id := rs.Primary.Attributes["guardrail_id"]
version := rs.Primary.Attributes[names.AttrVersion]

_, err := tfbedrock.FindGuardrailByID(ctx, conn, id, version)
_, err := tfbedrock.FindGuardrailByTwoPartKey(ctx, conn, id, version)
if errs.IsA[*types.ResourceNotFoundException](err) {
return nil
}
Expand Down Expand Up @@ -292,7 +292,7 @@ func testAccCheckGuardrailExists(ctx context.Context, name string, guardrail *be

conn := acctest.Provider.Meta().(*conns.AWSClient).BedrockClient(ctx)

out, err := tfbedrock.FindGuardrailByID(ctx, conn, id, version)
out, err := tfbedrock.FindGuardrailByTwoPartKey(ctx, conn, id, version)
if err != nil {
return create.Error(names.Bedrock, create.ErrActionCheckingExistence, tfbedrock.ResNameGuardrail, rs.Primary.ID, err)
}
Expand Down
210 changes: 210 additions & 0 deletions internal/service/bedrock/guardrail_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package bedrock

import (
"context"
"fmt"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/bedrock"
awstypes "github.com/aws/aws-sdk-go-v2/service/bedrock/types"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/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-provider-aws/internal/errs"
"github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
"github.com/hashicorp/terraform-provider-aws/internal/framework"
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_bedrock_guardrail_version", name="Guardrail Version")
func newGuardrailVersionResource(_ context.Context) (resource.ResourceWithConfigure, error) {
r := &guardrailVersionResource{}

r.SetDefaultCreateTimeout(5 * time.Minute)
r.SetDefaultDeleteTimeout(5 * time.Minute)

return r, nil
}

type guardrailVersionResource struct {
framework.ResourceWithConfigure
framework.WithNoOpUpdate[guardrailVersionResourceModel]
framework.WithTimeouts
}

func (*guardrailVersionResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) {
response.TypeName = "aws_bedrock_guardrail_version"
}

func (r *guardrailVersionResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) {
response.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
names.AttrDescription: schema.StringAttribute{
Optional: true,
Validators: []validator.String{
stringvalidator.LengthBetween(0, 200),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplaceIfConfigured(),
},
},
"guardrail_arn": schema.StringAttribute{
CustomType: fwtypes.ARNType,
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
names.AttrSkipDestroy: schema.BoolAttribute{
Optional: true,
},
names.AttrVersion: schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
Blocks: map[string]schema.Block{
names.AttrTimeouts: timeouts.Block(ctx, timeouts.Opts{
Create: true,
Delete: true,
}),
},
}
}

func (r *guardrailVersionResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
var data guardrailVersionResourceModel
response.Diagnostics.Append(request.Plan.Get(ctx, &data)...)
if response.Diagnostics.HasError() {
return
}

conn := r.Meta().BedrockClient(ctx)

guardrailARN := data.GuardrailARN.ValueString()
input := &bedrock.CreateGuardrailVersionInput{
Description: fwflex.StringFromFramework(ctx, data.Description),
GuardrailIdentifier: aws.String(guardrailARN),
}

output, err := conn.CreateGuardrailVersion(ctx, input)

if err != nil {
response.Diagnostics.AddError(fmt.Sprintf("creating Bedrock Guardrail Version (%s)", guardrailARN), err.Error())

return
}

data.Version = fwflex.StringToFramework(ctx, output.Version)

if _, err := waitGuardrailCreated(ctx, conn, data.GuardrailARN.ValueString(), data.Version.ValueString(), r.CreateTimeout(ctx, data.Timeouts)); err != nil {
response.Diagnostics.AddError(fmt.Sprintf("waiting for Bedrock Guardrail (%s) Version (%s) create", data.GuardrailARN.ValueString(), data.Version.ValueString()), err.Error())

return
}

response.Diagnostics.Append(response.State.Set(ctx, data)...)
}

func (r *guardrailVersionResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
var data guardrailVersionResourceModel
response.Diagnostics.Append(request.State.Get(ctx, &data)...)
if response.Diagnostics.HasError() {
return
}

conn := r.Meta().BedrockClient(ctx)

output, err := findGuardrailByTwoPartKey(ctx, conn, data.GuardrailARN.ValueString(), data.Version.ValueString())

if tfresource.NotFound(err) {
response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err))
response.State.RemoveResource(ctx)
return
}

if err != nil {
response.Diagnostics.AddError(fmt.Sprintf("reading Bedrock Guardrail (%s) Version (%s)", data.GuardrailARN.ValueString(), data.Version.ValueString()), err.Error())

return
}

data.Description = fwflex.StringToFramework(ctx, output.Description)
data.GuardrailARN = fwtypes.ARNValue(aws.ToString(output.GuardrailArn))
data.Version = fwflex.StringToFramework(ctx, output.Version)

response.Diagnostics.Append(response.State.Set(ctx, &data)...)
}

func (r *guardrailVersionResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) {
var data guardrailVersionResourceModel
response.Diagnostics.Append(request.State.Get(ctx, &data)...)
if response.Diagnostics.HasError() {
return
}

conn := r.Meta().BedrockClient(ctx)

if data.SkipDestroy.ValueBool() {
return
}

_, err := conn.DeleteGuardrail(ctx, &bedrock.DeleteGuardrailInput{
GuardrailIdentifier: aws.String(data.GuardrailARN.ValueString()),
GuardrailVersion: aws.String(data.Version.ValueString()),
})

if errs.IsA[*awstypes.ResourceNotFoundException](err) {
return
}

if err != nil {
response.Diagnostics.AddError(fmt.Sprintf("deleting Bedrock Guardrail (%s) Version (%s)", data.GuardrailARN.ValueString(), data.Version.ValueString()), err.Error())

return
}

if _, err := waitGuardrailDeleted(ctx, conn, data.GuardrailARN.ValueString(), data.Version.ValueString(), r.DeleteTimeout(ctx, data.Timeouts)); err != nil {
response.Diagnostics.AddError(fmt.Sprintf("waiting for Bedrock Guardrail (%s) Version (%s) delete", data.GuardrailARN.ValueString(), data.Version.ValueString()), err.Error())

return
}
}

func (r *guardrailVersionResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) {
parts, err := flex.ExpandResourceId(request.ID, guardrailIDParts, false)
if err != nil {
response.Diagnostics.AddError(
"Unexpected Import Identifier",
fmt.Sprintf("Expected import identifier with format: guardrail_identifier,version. Got: %q", request.ID),
)
return
}

response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root("guardrail_arn"), parts[0])...)
response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root(names.AttrVersion), parts[1])...)
}

type guardrailVersionResourceModel struct {
Description types.String `tfsdk:"description"`
GuardrailARN fwtypes.ARN `tfsdk:"guardrail_arn"`
SkipDestroy types.Bool `tfsdk:"skip_destroy"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
Version types.String `tfsdk:"version"`
}
Loading
Loading