From 79f6c2eb2de60e00e67ff231af196c08d837da48 Mon Sep 17 00:00:00 2001 From: Steph Date: Tue, 12 Aug 2025 13:07:43 +0200 Subject: [PATCH 1/2] add timeouts for list --- list/timeouts/schema.go | 112 +++++++++++++++ list/timeouts/schema_test.go | 247 +++++++++++++++++++++++++++++++++ list/timeouts/timeouts.go | 146 +++++++++++++++++++ list/timeouts/timeouts_test.go | 234 +++++++++++++++++++++++++++++++ 4 files changed, 739 insertions(+) create mode 100644 list/timeouts/schema.go create mode 100644 list/timeouts/schema_test.go create mode 100644 list/timeouts/timeouts.go create mode 100644 list/timeouts/timeouts_test.go diff --git a/list/timeouts/schema.go b/list/timeouts/schema.go new file mode 100644 index 0000000..2fd4a1b --- /dev/null +++ b/list/timeouts/schema.go @@ -0,0 +1,112 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package timeouts + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/list/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/internal/validators" +) + +const ( + attributeNameList = "list" +) + +// Opts is used as an argument to BlockWithOpts and AttributesWithOpts to indicate +// whether supplied descriptions should override default descriptions. +type Opts struct { + ListDescription string +} + +// BlockWithOpts returns a schema.Block containing attributes for `Open`, which is +// defined as types.StringType and optional. A validator is used to verify +// that the value assigned to `Open` can be parsed as time.Duration. The supplied +// Opts are used to override defaults. +func BlockWithOpts(ctx context.Context, opts Opts) schema.Block { + return schema.SingleNestedBlock{ + Attributes: attributesMap(opts), + CustomType: Type{ + ObjectType: types.ObjectType{ + AttrTypes: attrTypesMap(), + }, + }, + } +} + +// Block returns a schema.Block containing attributes for `Open`, which is +// defined as types.StringType and optional. A validator is used to verify +// that the value assigned to `Open` can be parsed as time.Duration. +func Block(ctx context.Context) schema.Block { + return schema.SingleNestedBlock{ + Attributes: attributesMap(Opts{}), + CustomType: Type{ + ObjectType: types.ObjectType{ + AttrTypes: attrTypesMap(), + }, + }, + } +} + +// AttributesWithOpts returns a schema.SingleNestedAttribute which contains an +// attribute for `Open`, which is defined as types.StringType and optional. +// A validator is used to verify that the value assigned to an attribute +// can be parsed as time.Duration. The supplied Opts are used to override defaults. +func AttributesWithOpts(ctx context.Context, opts Opts) schema.Attribute { + return schema.SingleNestedAttribute{ + Attributes: attributesMap(opts), + CustomType: Type{ + ObjectType: types.ObjectType{ + AttrTypes: attrTypesMap(), + }, + }, + Optional: true, + } +} + +// Attributes returns a schema.SingleNestedAttribute which contains an +// attribute for `Open`, which is defined as types.StringType and optional. +// A validator is used to verify that the value assigned to an attribute +// can be parsed as time.Duration. +func Attributes(ctx context.Context) schema.Attribute { + return schema.SingleNestedAttribute{ + Attributes: attributesMap(Opts{}), + CustomType: Type{ + ObjectType: types.ObjectType{ + AttrTypes: attrTypesMap(), + }, + }, + Optional: true, + } +} + +func attributesMap(opts Opts) map[string]schema.Attribute { + attribute := schema.StringAttribute{ + Optional: true, + Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` + + `consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` + + `"s" (seconds), "m" (minutes), "h" (hours).`, + Validators: []validator.String{ + validators.TimeDuration(), + }, + } + + if opts.ListDescription != "" { + attribute.Description = opts.ListDescription + } + + return map[string]schema.Attribute{ + attributeNameList: attribute, + } +} + +func attrTypesMap() map[string]attr.Type { + return map[string]attr.Type{ + attributeNameList: types.StringType, + } +} diff --git a/list/timeouts/schema_test.go b/list/timeouts/schema_test.go new file mode 100644 index 0000000..bed05a8 --- /dev/null +++ b/list/timeouts/schema_test.go @@ -0,0 +1,247 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package timeouts_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/list/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/internal/validators" + "github.com/hashicorp/terraform-plugin-framework-timeouts/list/timeouts" +) + +func TestBlockWithOpts(t *testing.T) { + t.Parallel() + + type testCase struct { + opts timeouts.Opts + expected schema.Block + } + tests := map[string]testCase{ + "empty-opts": { + opts: timeouts.Opts{}, + expected: schema.SingleNestedBlock{ + CustomType: timeouts.Type{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "list": types.StringType, + }, + }, + }, + Attributes: map[string]schema.Attribute{ + "list": schema.StringAttribute{ + Optional: true, + Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` + + `consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` + + `"s" (seconds), "m" (minutes), "h" (hours).`, + Validators: []validator.String{ + validators.TimeDuration(), + }, + }, + }, + }, + }, + "list-opts-description": { + opts: timeouts.Opts{ + ListDescription: "list description", + }, + expected: schema.SingleNestedBlock{ + CustomType: timeouts.Type{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "list": types.StringType, + }, + }, + }, + Attributes: map[string]schema.Attribute{ + "list": schema.StringAttribute{ + Optional: true, + Description: "list description", + Validators: []validator.String{ + validators.TimeDuration(), + }, + }, + }, + }, + }, + } + + for name, test := range tests { + + t.Run(name, func(t *testing.T) { + t.Parallel() + actual := timeouts.BlockWithOpts(context.Background(), test.opts) + + if diff := cmp.Diff(actual, test.expected); diff != "" { + t.Errorf("unexpected block difference: %s", diff) + } + }) + } +} + +func TestBlock(t *testing.T) { + t.Parallel() + + type testCase struct { + expected schema.Block + } + tests := map[string]testCase{ + "list": { + expected: schema.SingleNestedBlock{ + CustomType: timeouts.Type{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "list": types.StringType, + }, + }, + }, + Attributes: map[string]schema.Attribute{ + "list": schema.StringAttribute{ + Optional: true, + Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` + + `consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` + + `"s" (seconds), "m" (minutes), "h" (hours).`, + Validators: []validator.String{ + validators.TimeDuration(), + }, + }, + }, + }, + }, + } + + for name, test := range tests { + + t.Run(name, func(t *testing.T) { + t.Parallel() + actual := timeouts.Block(context.Background()) + + if diff := cmp.Diff(actual, test.expected); diff != "" { + t.Errorf("unexpected block difference: %s", diff) + } + }) + } +} + +func TestAttributesWithOpts(t *testing.T) { + t.Parallel() + + type testCase struct { + opts timeouts.Opts + expected schema.Attribute + } + tests := map[string]testCase{ + "empty-opts": { + opts: timeouts.Opts{}, + expected: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "list": schema.StringAttribute{ + Optional: true, + Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` + + `consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` + + `"s" (seconds), "m" (minutes), "h" (hours).`, + Validators: []validator.String{ + validators.TimeDuration(), + }, + }, + }, + CustomType: timeouts.Type{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "list": types.StringType, + }, + }, + }, + Optional: true, + }, + }, + "list-opts-description": { + opts: timeouts.Opts{ + ListDescription: "list description", + }, + expected: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "list": schema.StringAttribute{ + Optional: true, + Description: "list description", + Validators: []validator.String{ + validators.TimeDuration(), + }, + }, + }, + CustomType: timeouts.Type{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "list": types.StringType, + }, + }, + }, + Optional: true, + }, + }, + } + + for name, test := range tests { + + t.Run(name, func(t *testing.T) { + t.Parallel() + actual := timeouts.AttributesWithOpts(context.Background(), test.opts) + + if diff := cmp.Diff(actual, test.expected); diff != "" { + t.Errorf("unexpected block difference: %s", diff) + } + }) + } +} + +func TestAttributes(t *testing.T) { + t.Parallel() + + type testCase struct { + expected schema.Attribute + } + tests := map[string]testCase{ + "list": { + expected: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "list": schema.StringAttribute{ + Optional: true, + Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` + + `consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` + + `"s" (seconds), "m" (minutes), "h" (hours).`, + Validators: []validator.String{ + validators.TimeDuration(), + }, + }, + }, + CustomType: timeouts.Type{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "list": types.StringType, + }, + }, + }, + Optional: true, + }, + }, + } + + for name, test := range tests { + + t.Run(name, func(t *testing.T) { + t.Parallel() + actual := timeouts.Attributes(context.Background()) + + if diff := cmp.Diff(actual, test.expected); diff != "" { + t.Errorf("unexpected block difference: %s", diff) + } + }) + } +} diff --git a/list/timeouts/timeouts.go b/list/timeouts/timeouts.go new file mode 100644 index 0000000..8d14efd --- /dev/null +++ b/list/timeouts/timeouts.go @@ -0,0 +1,146 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package timeouts + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +var ( + _ basetypes.ObjectTypable = Type{} + _ basetypes.ObjectValuable = Value{} +) + +// Type is an attribute type that represents timeouts. +type Type struct { + basetypes.ObjectType +} + +// String returns a human-readable representation of the type. +func (t Type) String() string { + return "timeouts.Type" +} + +// ValueFromObject returns a Value given a basetypes.ObjectValue. +func (t Type) ValueFromObject(_ context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { + value := Value{ + Object: in, + } + + return value, nil +} + +// ValueFromTerraform returns a Value given a tftypes.Value. +// Value embeds the types.Object value returned from calling ValueFromTerraform on the +// types.ObjectType embedded in Type. +func (t Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + val, err := t.ObjectType.ValueFromTerraform(ctx, in) + if err != nil { + return nil, err + } + + obj, ok := val.(types.Object) + if !ok { + return nil, fmt.Errorf("%T cannot be used as types.Object", val) + } + + return Value{ + obj, + }, err +} + +// ValueType returns the associated Value type for debugging. +func (t Type) ValueType(context.Context) attr.Value { + // It does not need to be a fully valid implementation of the type. + return Value{} +} + +// Equal returns true if `candidate` is also a Type and has the same +// AttributeTypes. +func (t Type) Equal(candidate attr.Type) bool { + other, ok := candidate.(Type) + if !ok { + return false + } + + return t.ObjectType.Equal(other.ObjectType) +} + +// Value represents an object containing values to be used as time.Duration for timeouts. +type Value struct { + types.Object +} + +// Equal returns true if the Value is considered semantically equal +// (same type and same value) to the attr.Value passed as an argument. +func (t Value) Equal(c attr.Value) bool { + other, ok := c.(Value) + + if !ok { + return false + } + + return t.Object.Equal(other.Object) +} + +// ToObjectValue returns the underlying ObjectValue. +func (v Value) ToObjectValue(_ context.Context) (basetypes.ObjectValue, diag.Diagnostics) { + return v.Object, nil +} + +// Type returns a Type with the same attribute types as `t`. +func (t Value) Type(ctx context.Context) attr.Type { + return Type{ + types.ObjectType{ + AttrTypes: t.AttributeTypes(ctx), + }, + } +} + +// List attempts to retrieve the "list" attribute and parse it as time.Duration. +// If any diagnostics are generated they are returned along with the supplied default timeout. +func (t Value) List(ctx context.Context, defaultTimeout time.Duration) (time.Duration, diag.Diagnostics) { + return t.getTimeout(ctx, attributeNameList, defaultTimeout) +} + +func (t Value) getTimeout(ctx context.Context, timeoutName string, defaultTimeout time.Duration) (time.Duration, diag.Diagnostics) { + var diags diag.Diagnostics + + value, ok := t.Object.Attributes()[timeoutName] + if !ok { + tflog.Info(ctx, timeoutName+" timeout configuration not found, using provided default") + + return defaultTimeout, diags + } + + if value.IsNull() || value.IsUnknown() { + tflog.Info(ctx, timeoutName+" timeout configuration is null or unknown, using provided default") + + return defaultTimeout, diags + } + + // No type assertion check is required as the schema guarantees that the object attributes + // are types.String. + //nolint:forcetypeassert + timeout, err := time.ParseDuration(value.(types.String).ValueString()) + if err != nil { + diags.Append(diag.NewErrorDiagnostic( + "Timeout Cannot Be Parsed", + fmt.Sprintf("timeout for %q cannot be parsed, %s", timeoutName, err), + )) + + return defaultTimeout, diags + } + + return timeout, diags +} diff --git a/list/timeouts/timeouts_test.go b/list/timeouts/timeouts_test.go new file mode 100644 index 0000000..e6d157f --- /dev/null +++ b/list/timeouts/timeouts_test.go @@ -0,0 +1,234 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package timeouts_test + +import ( + "context" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/list/timeouts" +) + +func TestTimeoutsTypeValueFromTerraform(t *testing.T) { + t.Parallel() + + type testCase struct { + receiver timeouts.Type + input tftypes.Value + expected attr.Value + expectedErr string + } + tests := map[string]testCase{ + "basic-object": { + receiver: timeouts.Type{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "list": types.StringType, + }, + }, + }, + input: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "list": tftypes.String, + }, + }, map[string]tftypes.Value{ + "list": tftypes.NewValue(tftypes.String, "30m"), + }), + expected: timeouts.Value{ + Object: types.ObjectValueMust( + map[string]attr.Type{ + "list": types.StringType, + }, + map[string]attr.Value{ + "list": types.StringValue("30m"), + }, + ), + }, + }, + } + + for name, test := range tests { + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := test.receiver.ValueFromTerraform(context.Background(), test.input) + if err != nil { + if test.expectedErr == "" { + t.Errorf("Unexpected error: %s", err.Error()) + return + } + if err.Error() != test.expectedErr { + t.Errorf("Expected error to be %q, got %q", test.expectedErr, err.Error()) + return + } + } + + if diff := cmp.Diff(test.expected, got); diff != "" { + t.Errorf("unexpected result (-expected, +got): %s", diff) + } + }) + } +} + +func TestTimeoutsTypeEqual(t *testing.T) { + t.Parallel() + + type testCase struct { + receiver timeouts.Type + input attr.Type + expected bool + } + tests := map[string]testCase{ + "equal": { + receiver: timeouts.Type{ObjectType: types.ObjectType{AttrTypes: map[string]attr.Type{ + "a": types.StringType, + "b": types.NumberType, + "c": types.BoolType, + "d": types.ListType{ + ElemType: types.StringType, + }, + }}}, + input: timeouts.Type{ObjectType: types.ObjectType{AttrTypes: map[string]attr.Type{ + "a": types.StringType, + "b": types.NumberType, + "c": types.BoolType, + "d": types.ListType{ + ElemType: types.StringType, + }, + }}}, + expected: true, + }, + "missing-attr": { + receiver: timeouts.Type{ObjectType: types.ObjectType{AttrTypes: map[string]attr.Type{ + "a": types.StringType, + "b": types.NumberType, + "c": types.BoolType, + "d": types.ListType{ + ElemType: types.StringType, + }, + }}}, + input: timeouts.Type{ObjectType: types.ObjectType{AttrTypes: map[string]attr.Type{ + "a": types.StringType, + "b": types.NumberType, + "d": types.ListType{ + ElemType: types.StringType, + }, + }}}, + expected: false, + }, + } + for name, test := range tests { + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := test.receiver.Equal(test.input) + if test.expected != got { + t.Errorf("Expected %v, got %v", test.expected, got) + } + }) + } +} + +func TestTimeoutsValueOpen(t *testing.T) { + t.Parallel() + + type testCase struct { + timeoutsValue timeouts.Value + expectedTimeout time.Duration + expectedDiags diag.Diagnostics + } + tests := map[string]testCase{ + "list": { + timeoutsValue: timeouts.Value{ + Object: types.ObjectValueMust( + map[string]attr.Type{ + "list": types.StringType, + }, + map[string]attr.Value{ + "list": types.StringValue("10m"), + }, + ), + }, + expectedTimeout: 10 * time.Minute, + expectedDiags: nil, + }, + "list-not-set": { + timeoutsValue: timeouts.Value{ + Object: types.Object{}, + }, + expectedTimeout: 20 * time.Minute, + }, + "list-null": { + timeoutsValue: timeouts.Value{ + Object: types.ObjectValueMust( + map[string]attr.Type{ + "list": types.StringType, + }, + map[string]attr.Value{ + "list": types.StringNull(), + }, + ), + }, + expectedTimeout: 20 * time.Minute, + }, + "list-unknown": { + timeoutsValue: timeouts.Value{ + Object: types.ObjectValueMust( + map[string]attr.Type{ + "list": types.StringType, + }, + map[string]attr.Value{ + "list": types.StringUnknown(), + }, + ), + }, + expectedTimeout: 20 * time.Minute, + }, + "list-not-parseable-as-time-duration": { + timeoutsValue: timeouts.Value{ + Object: types.ObjectValueMust( + map[string]attr.Type{ + "list": types.StringType, + }, + map[string]attr.Value{ + "list": types.StringValue("10x"), + }, + ), + }, + expectedTimeout: 20 * time.Minute, + expectedDiags: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Timeout Cannot Be Parsed", + `timeout for "list" cannot be parsed, time: unknown unit "x" in duration "10x"`, + ), + }, + }, + } + + for name, test := range tests { + + t.Run(name, func(t *testing.T) { + t.Parallel() + + gotTimeout, gotErr := test.timeoutsValue.List(context.Background(), 20*time.Minute) + + if diff := cmp.Diff(gotTimeout, test.expectedTimeout); diff != "" { + t.Errorf("unexpected timeout difference: %s", diff) + } + + if diff := cmp.Diff(gotErr, test.expectedDiags); diff != "" { + t.Errorf("unexpected err difference: %s", diff) + } + }) + } +} From 7f5e5ad5c62fb7085a105a6bc053552655d26a75 Mon Sep 17 00:00:00 2001 From: Steph Date: Wed, 13 Aug 2025 09:40:07 +0200 Subject: [PATCH 2/2] bump go.mod --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index ab86ae3..861e4d9 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ toolchain go1.23.7 require ( github.com/google/go-cmp v0.7.0 - github.com/hashicorp/terraform-plugin-framework v1.15.1 - github.com/hashicorp/terraform-plugin-go v0.28.0 + github.com/hashicorp/terraform-plugin-framework v1.16.0-beta.1.0.20250812160436-e3cf75ae63ec + github.com/hashicorp/terraform-plugin-go v0.29.0-beta.1 github.com/hashicorp/terraform-plugin-log v0.9.0 ) @@ -19,5 +19,5 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - golang.org/x/sys v0.32.0 // indirect + golang.org/x/sys v0.33.0 // indirect ) diff --git a/go.sum b/go.sum index f9c02ef..ee87b21 100644 --- a/go.sum +++ b/go.sum @@ -7,10 +7,10 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/terraform-plugin-framework v1.15.1 h1:2mKDkwb8rlx/tvJTlIcpw0ykcmvdWv+4gY3SIgk8Pq8= -github.com/hashicorp/terraform-plugin-framework v1.15.1/go.mod h1:hxrNI/GY32KPISpWqlCoTLM9JZsGH3CyYlir09bD/fI= -github.com/hashicorp/terraform-plugin-go v0.28.0 h1:zJmu2UDwhVN0J+J20RE5huiF3XXlTYVIleaevHZgKPA= -github.com/hashicorp/terraform-plugin-go v0.28.0/go.mod h1:FDa2Bb3uumkTGSkTFpWSOwWJDwA7bf3vdP3ltLDTH6o= +github.com/hashicorp/terraform-plugin-framework v1.16.0-beta.1.0.20250812160436-e3cf75ae63ec h1:MoRO3dd5Q1zgRMTDdL7CaJHDdGXzO4IIiUYe14VBNRU= +github.com/hashicorp/terraform-plugin-framework v1.16.0-beta.1.0.20250812160436-e3cf75ae63ec/go.mod h1:XQfoA+oS1ETMuQrcM689/TWMx9leV59N7Xscm5t7b+g= +github.com/hashicorp/terraform-plugin-go v0.29.0-beta.1 h1:xeHlRQYev3iMXwX2W7+D1bSfLRBs9jojZXqE6hmNxMI= +github.com/hashicorp/terraform-plugin-go v0.29.0-beta.1/go.mod h1:5pww/UULn9C2tItq6o5sbScEkJxBUt9X9kI4DkeRsIw= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -37,8 +37,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=