diff --git a/internal/providers/pluginfw/converters/converters_test.go b/internal/providers/pluginfw/converters/converters_test.go index c8f4bf1720..0f616782fd 100644 --- a/internal/providers/pluginfw/converters/converters_test.go +++ b/internal/providers/pluginfw/converters/converters_test.go @@ -15,28 +15,28 @@ import ( ) type DummyTfSdk struct { - Enabled types.Bool `tfsdk:"enabled" tf:"optional"` - Workers types.Int64 `tfsdk:"workers" tf:""` - Floats types.Float64 `tfsdk:"floats" tf:""` - Description types.String `tfsdk:"description" tf:""` - Tasks types.String `tfsdk:"task" tf:"optional"` - NoPointerNested types.List `tfsdk:"no_pointer_nested" tf:"optional"` - NestedList types.List `tfsdk:"nested_list" tf:"optional"` - NestedPointerList types.List `tfsdk:"nested_pointer_list" tf:"optional"` - Map types.Map `tfsdk:"map" tf:"optional"` - NestedMap types.Map `tfsdk:"nested_map" tf:"optional"` - Repeated types.List `tfsdk:"repeated" tf:"optional"` - Attributes types.Map `tfsdk:"attributes" tf:"optional"` - EnumField types.String `tfsdk:"enum_field" tf:"optional"` - AdditionalField types.String `tfsdk:"additional_field" tf:"optional"` - DistinctField types.String `tfsdk:"distinct_field" tf:"optional"` - SliceStructPtr types.List `tfsdk:"slice_struct_ptr" tf:"optional"` + Enabled types.Bool `tfsdk:"enabled"` + Workers types.Int64 `tfsdk:"workers"` + Floats types.Float64 `tfsdk:"floats"` + Description types.String `tfsdk:"description"` + Tasks types.String `tfsdk:"task"` + NoPointerNested types.List `tfsdk:"no_pointer_nested"` + NestedList types.List `tfsdk:"nested_list"` + NestedPointerList types.List `tfsdk:"nested_pointer_list"` + Map types.Map `tfsdk:"map"` + NestedMap types.Map `tfsdk:"nested_map"` + Repeated types.List `tfsdk:"repeated"` + Attributes types.Map `tfsdk:"attributes"` + EnumField types.String `tfsdk:"enum_field"` + AdditionalField types.String `tfsdk:"additional_field"` + DistinctField types.String `tfsdk:"distinct_field"` + SliceStructPtr types.List `tfsdk:"slice_struct_ptr"` Irrelevant types.String `tfsdk:"-"` - Object types.Object `tfsdk:"object" tf:"optional"` - ObjectPtr types.Object `tfsdk:"object_ptr" tf:"optional"` - Type_ types.String `tfsdk:"type" tf:""` // Test Type_ renaming - EmptyStructList types.List `tfsdk:"empty_struct_list" tf:"optional"` - EmptyStructObject types.Object `tfsdk:"empty_struct_object" tf:"optional"` + Object types.Object `tfsdk:"object"` + ObjectPtr types.Object `tfsdk:"object_ptr"` + Type_ types.String `tfsdk:"type"` // Test Type_ renaming + EmptyStructList types.List `tfsdk:"empty_struct_list"` + EmptyStructObject types.Object `tfsdk:"empty_struct_object"` } func (DummyTfSdk) GetComplexFieldTypes(ctx context.Context) map[string]reflect.Type { @@ -84,8 +84,8 @@ func (f *TestEnum) Type() string { } type DummyNestedTfSdk struct { - Name types.String `tfsdk:"name" tf:"optional"` - Enabled types.Bool `tfsdk:"enabled" tf:"optional"` + Name types.String `tfsdk:"name"` + Enabled types.Bool `tfsdk:"enabled"` } type DummyNestedTfSdkEmpty struct{} diff --git a/internal/providers/pluginfw/products/app/data_apps.go b/internal/providers/pluginfw/products/app/data_apps.go index e5cbc25921..19ebeeb6ed 100644 --- a/internal/providers/pluginfw/products/app/data_apps.go +++ b/internal/providers/pluginfw/products/app/data_apps.go @@ -25,7 +25,13 @@ type dataSourceApps struct { } type dataApps struct { - Apps types.List `tfsdk:"app" tf:"computed"` + Apps types.List `tfsdk:"app"` +} + +func (dataApps) ApplySchemaCustomizations(attrs map[string]tfschema.AttributeBuilder) map[string]tfschema.AttributeBuilder { + attrs["app"] = attrs["app"].SetComputed() + + return attrs } func (dataApps) GetComplexFieldTypes(context.Context) map[string]reflect.Type { diff --git a/internal/providers/pluginfw/products/cluster/data_cluster.go b/internal/providers/pluginfw/products/cluster/data_cluster.go index f85063794b..e43740f3b3 100644 --- a/internal/providers/pluginfw/products/cluster/data_cluster.go +++ b/internal/providers/pluginfw/products/cluster/data_cluster.go @@ -34,9 +34,17 @@ type ClusterDataSource struct { } type ClusterInfo struct { - ClusterId types.String `tfsdk:"cluster_id" tf:"optional,computed"` - Name types.String `tfsdk:"cluster_name" tf:"optional,computed"` - ClusterInfo types.List `tfsdk:"cluster_info" tf:"optional,computed"` + ClusterId types.String `tfsdk:"cluster_id"` + Name types.String `tfsdk:"cluster_name"` + ClusterInfo types.List `tfsdk:"cluster_info"` +} + +func (ClusterInfo) ApplySchemaCustomizations(attrs map[string]tfschema.AttributeBuilder) map[string]tfschema.AttributeBuilder { + attrs["cluster_id"] = attrs["cluster_id"].SetOptional().SetComputed() + attrs["cluster_name"] = attrs["cluster_name"].SetOptional().SetComputed() + attrs["cluster_info"] = attrs["cluster_info"].SetOptional().SetComputed() + + return attrs } func (ClusterInfo) GetComplexFieldTypes(context.Context) map[string]reflect.Type { diff --git a/internal/providers/pluginfw/products/notificationdestinations/data_notification_destinations.go b/internal/providers/pluginfw/products/notificationdestinations/data_notification_destinations.go index 5e6688bb79..1d5f5d6b7b 100755 --- a/internal/providers/pluginfw/products/notificationdestinations/data_notification_destinations.go +++ b/internal/providers/pluginfw/products/notificationdestinations/data_notification_destinations.go @@ -34,9 +34,17 @@ type NotificationDestinationsDataSource struct { } type NotificationDestinationsInfo struct { - DisplayNameContains types.String `tfsdk:"display_name_contains" tf:"optional"` - Type types.String `tfsdk:"type" tf:"optional"` - NotificationDestinations types.List `tfsdk:"notification_destinations" tf:"computed"` + DisplayNameContains types.String `tfsdk:"display_name_contains"` + Type types.String `tfsdk:"type"` + NotificationDestinations types.List `tfsdk:"notification_destinations"` +} + +func (NotificationDestinationsInfo) ApplySchemaCustomizations(attrs map[string]tfschema.AttributeBuilder) map[string]tfschema.AttributeBuilder { + attrs["display_name_contains"] = attrs["display_name_contains"].SetOptional() + attrs["type"] = attrs["type"].SetOptional() + attrs["notification_destinations"] = attrs["notification_destinations"].SetComputed() + + return attrs } func (NotificationDestinationsInfo) GetComplexFieldTypes(context.Context) map[string]reflect.Type { diff --git a/internal/providers/pluginfw/products/serving/data_serving_endpoints.go b/internal/providers/pluginfw/products/serving/data_serving_endpoints.go index d695bc93e1..53a95a070d 100644 --- a/internal/providers/pluginfw/products/serving/data_serving_endpoints.go +++ b/internal/providers/pluginfw/products/serving/data_serving_endpoints.go @@ -27,7 +27,13 @@ type ServingEndpointsDataSource struct { } type ServingEndpointsData struct { - Endpoints types.List `tfsdk:"endpoints" tf:"optional,computed"` + Endpoints types.List `tfsdk:"endpoints"` +} + +func (ServingEndpointsData) ApplySchemaCustomizations(attrs map[string]tfschema.AttributeBuilder) map[string]tfschema.AttributeBuilder { + attrs["endpoints"] = attrs["endpoints"].SetOptional().SetComputed() + + return attrs } func (ServingEndpointsData) GetComplexFieldTypes(context.Context) map[string]reflect.Type { diff --git a/internal/providers/pluginfw/products/sharing/data_shares.go b/internal/providers/pluginfw/products/sharing/data_shares.go index 457ba8633d..bdc5301d0e 100644 --- a/internal/providers/pluginfw/products/sharing/data_shares.go +++ b/internal/providers/pluginfw/products/sharing/data_shares.go @@ -18,7 +18,13 @@ import ( const dataSourceNameShares = "shares" type SharesList struct { - Shares types.List `tfsdk:"shares" tf:"computed,optional,slice_set"` + Shares types.List `tfsdk:"shares"` +} + +func (SharesList) ApplySchemaCustomizations(attrs map[string]tfschema.AttributeBuilder) map[string]tfschema.AttributeBuilder { + attrs["shares"] = attrs["shares"].SetComputed().SetOptional() + + return attrs } func (SharesList) GetComplexFieldTypes(context.Context) map[string]reflect.Type { diff --git a/internal/providers/pluginfw/tfschema/customizable_schema_test.go b/internal/providers/pluginfw/tfschema/customizable_schema_test.go index c2d691da44..42f065e679 100644 --- a/internal/providers/pluginfw/tfschema/customizable_schema_test.go +++ b/internal/providers/pluginfw/tfschema/customizable_schema_test.go @@ -15,10 +15,10 @@ import ( ) type TestTfSdk struct { - Description types.String `tfsdk:"description" tf:""` - Nested types.List `tfsdk:"nested" tf:"optional"` - NestedSliceObject types.List `tfsdk:"nested_slice_object" tf:"optional,object"` - Map types.Map `tfsdk:"map" tf:"optional"` + Description types.String `tfsdk:"description"` + Nested types.List `tfsdk:"nested"` + NestedSliceObject types.List `tfsdk:"nested_slice_object" tf:"object"` + Map types.Map `tfsdk:"map"` } func (TestTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect.Type { @@ -30,8 +30,8 @@ func (TestTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect.Type { } type NestedTfSdk struct { - Name types.String `tfsdk:"name" tf:"optional"` - Enabled types.Bool `tfsdk:"enabled" tf:"optional"` + Name types.String `tfsdk:"name"` + Enabled types.Bool `tfsdk:"enabled"` } type stringLengthBetweenValidator struct { diff --git a/internal/providers/pluginfw/tfschema/struct_to_schema.go b/internal/providers/pluginfw/tfschema/struct_to_schema.go index 9a26dca10e..690941f81b 100644 --- a/internal/providers/pluginfw/tfschema/struct_to_schema.go +++ b/internal/providers/pluginfw/tfschema/struct_to_schema.go @@ -22,8 +22,6 @@ type CustomizableSchemaProvider interface { } type structTag struct { - optional bool - computed bool singleObject bool } @@ -46,7 +44,7 @@ func typeToSchema(ctx context.Context, v reflect.Value) NestedBlockObject { if fieldName == "-" { continue } - structTag, hasStructTag := getStructTag(typeField) + structTag := getStructTag(typeField) value := field.Value.Interface() if _, ok := value.(attr.Value); !ok { panic(fmt.Errorf("unexpected type %T in tfsdk structs, expected a plugin framework value type. %s", value, common.TerraformBugErrorMessage)) @@ -145,27 +143,6 @@ func typeToSchema(ctx context.Context, v reflect.Value) NestedBlockObject { } panic(fmt.Errorf("unexpected type %T in tfsdk structs, expected a plugin framework value type. %s", value, common.TerraformBugErrorMessage)) } - - attr := scmAttr[fieldName] - - if hasStructTag && !implementsSchemaProvider { - if structTag.computed { - // Computed attributes are always computed and may be optional. - attr = attr.SetComputed() - if structTag.optional { - attr = attr.SetOptional() - } - } else { - // Non-computed attributes must be either optional or required. - if structTag.optional { - attr = attr.SetOptional() - } else { - attr = attr.SetRequired() - } - } - } - - scmAttr[fieldName] = attr } if implementsSchemaProvider { @@ -174,13 +151,15 @@ func typeToSchema(ctx context.Context, v reflect.Value) NestedBlockObject { return NestedBlockObject{Attributes: scmAttr} } -func getStructTag(field reflect.StructField) (structTag, bool) { +func getStructTag(field reflect.StructField) structTag { tagValue := field.Tag.Get("tf") + if tagValue != "" && tagValue != "object" { + panic(`"tf:..." annotations are no longer supported. You should implement the CustomizableSchemaProvider interface on the struct and apply the appropriate schema customizations there.`) + } + return structTag{ - optional: strings.Contains(tagValue, "optional"), - computed: strings.Contains(tagValue, "computed"), singleObject: strings.Contains(tagValue, "object"), - }, tagValue != "" + } } // ResourceStructToSchema builds a resource schema from a tfsdk struct, with custoimzations applied. diff --git a/internal/providers/pluginfw/tfschema/struct_to_schema_test.go b/internal/providers/pluginfw/tfschema/struct_to_schema_test.go index 7eda21249f..dd06920e97 100644 --- a/internal/providers/pluginfw/tfschema/struct_to_schema_test.go +++ b/internal/providers/pluginfw/tfschema/struct_to_schema_test.go @@ -16,29 +16,61 @@ import ( ) type TestStringTfSdk struct { - Description types.String `tfsdk:"description" tf:"optional"` + Description types.String `tfsdk:"description"` +} + +func (TestStringTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["description"] = attrs["description"].SetOptional() + return attrs } type TestBoolTfSdk struct { - Enabled types.Bool `tfsdk:"enabled" tf:"required"` + Enabled types.Bool `tfsdk:"enabled"` +} + +func (TestBoolTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["enabled"] = attrs["enabled"].SetRequired() + return attrs } type TestIntTfSdk struct { - Workers types.Int64 `tfsdk:"workers" tf:"optional"` + Workers types.Int64 `tfsdk:"workers"` +} + +func (TestIntTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["workers"] = attrs["workers"].SetOptional() + return attrs } type TestComputedTfSdk struct { - ComputedTag types.String `tfsdk:"computedtag" tf:"computed"` - MultipleTags types.String `tfsdk:"multipletags" tf:"computed,optional"` - NonComputed types.String `tfsdk:"noncomputed" tf:"optional"` + ComputedTag types.String `tfsdk:"computedtag"` + MultipleTags types.String `tfsdk:"multipletags"` + NonComputed types.String `tfsdk:"noncomputed"` +} + +func (TestComputedTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["computedtag"] = attrs["computedtag"].SetComputed() + attrs["multipletags"] = attrs["multipletags"].SetComputed().SetOptional() + attrs["noncomputed"] = attrs["noncomputed"].SetOptional() + return attrs } type TestFloatTfSdk struct { - Float types.Float64 `tfsdk:"float" tf:"optional"` + Float types.Float64 `tfsdk:"float"` +} + +func (TestFloatTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["float"] = attrs["float"].SetOptional() + return attrs } type TestListTfSdk struct { - Repeated types.List `tfsdk:"repeated" tf:"optional"` + Repeated types.List `tfsdk:"repeated"` +} + +func (TestListTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["repeated"] = attrs["repeated"].SetOptional() + return attrs } func (TestListTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect.Type { @@ -48,7 +80,12 @@ func (TestListTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect.Ty } type TestMapTfSdk struct { - Attributes types.Map `tfsdk:"attributes" tf:"optional"` + Attributes types.Map `tfsdk:"attributes"` +} + +func (TestMapTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["attributes"] = attrs["attributes"].SetOptional() + return attrs } func (TestMapTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect.Type { @@ -58,7 +95,12 @@ func (TestMapTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect.Typ } type TestObjectTfSdk struct { - Object types.Object `tfsdk:"object" tf:"optional"` + Object types.Object `tfsdk:"object"` +} + +func (TestObjectTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["object"] = attrs["object"].SetOptional() + return attrs } func (TestObjectTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect.Type { @@ -68,7 +110,12 @@ func (TestObjectTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect. } type TestNestedListTfSdk struct { - NestedList types.List `tfsdk:"nested_list" tf:"optional"` + NestedList types.List `tfsdk:"nested_list"` +} + +func (TestNestedListTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["nested_list"] = attrs["nested_list"].SetOptional() + return attrs } func (TestNestedListTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect.Type { @@ -78,12 +125,23 @@ func (TestNestedListTfSdk) GetComplexFieldTypes(context.Context) map[string]refl } type DummyNested struct { - Name types.String `tfsdk:"name" tf:"optional"` - Enabled types.Bool `tfsdk:"enabled" tf:"optional"` + Name types.String `tfsdk:"name"` + Enabled types.Bool `tfsdk:"enabled"` +} + +func (DummyNested) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["name"] = attrs["name"].SetOptional() + attrs["enabled"] = attrs["enabled"].SetOptional() + return attrs } type TestNestedMapTfSdk struct { - NestedMap types.Map `tfsdk:"nested_map" tf:"optional"` + NestedMap types.Map `tfsdk:"nested_map"` +} + +func (TestNestedMapTfSdk) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["nested_map"] = attrs["nested_map"].SetOptional() + return attrs } func (TestNestedMapTfSdk) GetComplexFieldTypes(context.Context) map[string]reflect.Type { @@ -94,6 +152,10 @@ func (TestNestedMapTfSdk) GetComplexFieldTypes(context.Context) map[string]refle var dummyType = tfcommon.NewObjectTyper(DummyNested{}).Type(context.Background()).(types.ObjectType) +type TestDeprecatedTfTags struct { + Foo types.String `tfsdk:"foo" tf:"computed"` +} + var tests = []struct { name string testStruct any @@ -225,15 +287,30 @@ func testStructToSchemaPanics(t *testing.T, testStruct any, expectedError string } type TestTfSdkList struct { - Description types.List `tfsdk:"description" tf:"optional"` + Description types.List `tfsdk:"description"` +} + +func (TestTfSdkList) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["description"] = attrs["description"].SetOptional() + return attrs } type TestTfSdkMapWithoutMetadata struct { - Description types.Map `tfsdk:"description" tf:"optional"` + Description types.Map `tfsdk:"description"` +} + +func (TestTfSdkMapWithoutMetadata) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["description"] = attrs["description"].SetOptional() + return attrs } type TestSliceOfSlice struct { - NestedList [][]string `tfsdk:"nested_list" tf:"optional"` + NestedList [][]string `tfsdk:"nested_list"` +} + +func (TestSliceOfSlice) ApplySchemaCustomizations(attrs map[string]AttributeBuilder) map[string]AttributeBuilder { + attrs["nested_list"] = attrs["nested_list"].SetOptional() + return attrs } var error_tests = []struct { @@ -278,3 +355,12 @@ func TestComputedField(t *testing.T) { // Test that NonComputed field is not computed assert.True(t, !scm.Attributes["noncomputed"].IsComputed()) } + +func TestDeprecatedTagsPanics(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("Expected panic for TestDeprecatedTfTags") + } + }() + ResourceStructToSchema(context.Background(), TestDeprecatedTfTags{}, nil) +}