Skip to content

Commit

Permalink
Merge pull request #34310 from skyscrapr/f-aws_bedrock_custom_model
Browse files Browse the repository at this point in the history
F aws bedrock custom model
  • Loading branch information
ewbankkit committed Feb 2, 2024
2 parents 191165a + 2222c5c commit a316669
Show file tree
Hide file tree
Showing 28 changed files with 2,499 additions and 214 deletions.
11 changes: 11 additions & 0 deletions .changelog/34310.txt
@@ -0,0 +1,11 @@
```release-note:new-data-source
aws_bedrock_custom_model
```

```release-note:new-data-source
aws_bedrock_custom_models
```

```release-note:resource
aws_bedrock_custom_model
```
23 changes: 20 additions & 3 deletions internal/framework/base.go
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"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/types"
Expand Down Expand Up @@ -107,10 +108,26 @@ func (w *WithImportByID) ImportState(ctx context.Context, request resource.Impor
resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), request, response)
}

// WithNoOpUpdate is intended to be embedded in resources which have no need of an Update method.
type WithNoOpUpdate struct{}
// WithNoUpdate is intended to be embedded in resources which cannot be updated.
type WithNoUpdate struct{}

func (w *WithNoOpUpdate) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) {
func (w *WithNoUpdate) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) {
response.Diagnostics.Append(diag.NewErrorDiagnostic("not supported", "This resource's Update method should not have been called"))
}

// WithNoOpUpdate is intended to be embedded in resources which have no need of a custom Update method.
// For example, resources where only `tags` can be updated and that is handled via transparent tagging.
type WithNoOpUpdate[T any] struct{}

func (w *WithNoOpUpdate[T]) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) {
var t T

response.Diagnostics.Append(request.Plan.Get(ctx, &t)...)
if response.Diagnostics.HasError() {
return
}

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

// DataSourceWithConfigure is a structure to be embedded within a DataSource that implements the DataSourceWithConfigure interface.
Expand Down
29 changes: 17 additions & 12 deletions internal/framework/types/mapof.go
Expand Up @@ -16,22 +16,26 @@ import (
)

var (
_ basetypes.MapTypable = MapTypeOf[basetypes.StringValue]{}
_ basetypes.MapTypable = mapTypeOf[basetypes.StringValue]{}
_ basetypes.MapValuable = MapValueOf[basetypes.StringValue]{}
)

// MapTypeOf is the attribute type of a MapValueOf.
type MapTypeOf[T attr.Value] struct {
var (
// MapOfStringType is a custom type used for defining a Map of strings.
MapOfStringType = mapTypeOf[basetypes.StringValue]{basetypes.MapType{ElemType: basetypes.StringType{}}}
)

type mapTypeOf[T attr.Value] struct {
basetypes.MapType
}

func NewMapTypeOf[T attr.Value](ctx context.Context) MapTypeOf[T] {
func NewMapTypeOf[T attr.Value](ctx context.Context) mapTypeOf[T] {
var zero T
return MapTypeOf[T]{basetypes.MapType{ElemType: zero.Type(ctx)}}
return mapTypeOf[T]{basetypes.MapType{ElemType: zero.Type(ctx)}}
}

func (t MapTypeOf[T]) Equal(o attr.Type) bool {
other, ok := o.(MapTypeOf[T])
func (t mapTypeOf[T]) Equal(o attr.Type) bool {
other, ok := o.(mapTypeOf[T])

if !ok {
return false
Expand All @@ -40,18 +44,19 @@ func (t MapTypeOf[T]) Equal(o attr.Type) bool {
return t.MapType.Equal(other.MapType)
}

func (t MapTypeOf[T]) String() string {
func (t mapTypeOf[T]) String() string {
var zero T
return fmt.Sprintf("%T", zero)
}

func (t MapTypeOf[T]) ValueFromMap(ctx context.Context, in basetypes.MapValue) (basetypes.MapValuable, diag.Diagnostics) {
func (t mapTypeOf[T]) ValueFromMap(ctx context.Context, in basetypes.MapValue) (basetypes.MapValuable, diag.Diagnostics) {
var diags diag.Diagnostics
var zero T

if in.IsNull() {
return NewMapValueOfNull[T](ctx), diags
}

if in.IsUnknown() {
return NewMapValueOfUnknown[T](ctx), diags
}
Expand All @@ -73,7 +78,7 @@ func (t MapTypeOf[T]) ValueFromMap(ctx context.Context, in basetypes.MapValue) (
return value, diags
}

func (t MapTypeOf[T]) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
func (t mapTypeOf[T]) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
attrValue, err := t.MapType.ValueFromTerraform(ctx, in)

if err != nil {
Expand All @@ -93,11 +98,11 @@ func (t MapTypeOf[T]) ValueFromTerraform(ctx context.Context, in tftypes.Value)
return mapValuable, nil
}

func (t MapTypeOf[T]) ValueType(ctx context.Context) attr.Value {
func (t mapTypeOf[T]) ValueType(ctx context.Context) attr.Value {
return MapValueOf[T]{}
}

// MapValueOf represents a Terraform Plugin Framework Map value whose elements are of type MapTypeOf.
// MapValueOf represents a Terraform Plugin Framework Map value whose elements are of type mapTypeOf.
type MapValueOf[T attr.Value] struct {
basetypes.MapValue
}
Expand Down
1 change: 1 addition & 0 deletions internal/framework/types/string_enum.go
Expand Up @@ -177,6 +177,7 @@ func (v StringEnum[T]) ValueEnum() T {

// StringEnumValue is useful if you have a zero value StringEnum but need a
// way to get a non-zero value such as when flattening.
// It's called via reflection inside AutoFlEx.
func (v StringEnum[T]) StringEnumValue(value string) StringEnum[T] {
return StringEnum[T]{StringValue: basetypes.NewStringValue(value)}
}
48 changes: 48 additions & 0 deletions internal/framework/validators/s3_uri.go
@@ -0,0 +1,48 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package validators

import (
"context"

"github.com/YakDriver/regexache"
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
)

// s3URIValidator validates that a string Attribute's value is a valid S3 URI.
type s3URIValidator struct{}

func (validator s3URIValidator) Description(_ context.Context) string {
return "value must be a valid S3 URI"
}

func (validator s3URIValidator) MarkdownDescription(ctx context.Context) string {
return validator.Description(ctx)
}

func (validator s3URIValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) {
if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() {
return
}

if !regexache.MustCompile(`^s3://[a-z0-9][\.\-a-z0-9]{1,61}[a-z0-9](/.*)?$`).MatchString(request.ConfigValue.ValueString()) {
response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
request.Path,
validator.Description(ctx),
request.ConfigValue.ValueString(),
))
return
}
}

// S3URI returns a string validator which ensures that any configured
// attribute value:
//
// - Is a string, which represents a valid S3 URI (s3://bucket[/key]).
//
// Null (unconfigured) and unknown (known after apply) values are skipped.
func S3URI() validator.String {
return s3URIValidator{}
}
77 changes: 77 additions & 0 deletions internal/framework/validators/s3_uri_test.go
@@ -0,0 +1,77 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package validators_test

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators"
)

func TestS3URIValidator(t *testing.T) {
t.Parallel()

type testCase struct {
val types.String
expectedDiagnostics diag.Diagnostics
}
tests := map[string]testCase{
"unknown String": {
val: types.StringUnknown(),
},
"null String": {
val: types.StringNull(),
},
"invalid String": {
val: types.StringValue("test-value"),
expectedDiagnostics: diag.Diagnostics{
diag.NewAttributeErrorDiagnostic(
path.Root("test"),
"Invalid Attribute Value",
`Attribute test value must be a valid S3 URI, got: test-value`,
),
},
},
"valid S3 URI": {
val: types.StringValue("s3://bucket/path/to/key"),
},
"invalid characters": {
val: types.StringValue("s3://asbcdefg--#/key"),
expectedDiagnostics: diag.Diagnostics{
diag.NewAttributeErrorDiagnostic(
path.Root("test"),
"Invalid Attribute Value",
`Attribute test value must be a valid S3 URI, got: s3://asbcdefg--#/key`,
),
},
},
}

for name, test := range tests {
name, test := name, test
t.Run(name, func(t *testing.T) {
t.Parallel()

ctx := context.Background()

request := validator.StringRequest{
Path: path.Root("test"),
PathExpression: path.MatchRoot("test"),
ConfigValue: test.val,
}
response := validator.StringResponse{}
fwvalidators.S3URI().ValidateString(ctx, request, &response)

if diff := cmp.Diff(response.Diagnostics, test.expectedDiagnostics); diff != "" {
t.Errorf("unexpected diagnostics difference: %s", diff)
}
})
}
}
12 changes: 12 additions & 0 deletions internal/service/bedrock/consts.go
@@ -0,0 +1,12 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package bedrock

import (
"time"
)

const (
propagationTimeout = 2 * time.Minute
)

0 comments on commit a316669

Please sign in to comment.