From 7683b98af425de94bf29f657f13c40165f7f7854 Mon Sep 17 00:00:00 2001 From: edwardfeng-db Date: Tue, 20 Aug 2024 17:05:43 +0200 Subject: [PATCH] update --- common/force_send_fields.go | 10 +- common/reflect_resource.go | 12 +- common/util.go | 3 +- .../reflect_utils.go | 12 +- .../{common => converters}/converters_test.go | 112 +++++++----------- .../{common => converters}/go_to_tf.go | 46 +++---- .../{common => converters}/tf_to_go.go | 59 ++++----- 7 files changed, 116 insertions(+), 138 deletions(-) rename internal/{reflect_utils => tfreflect}/reflect_utils.go (53%) rename pluginframework/{common => converters}/converters_test.go (83%) rename pluginframework/{common => converters}/go_to_tf.go (73%) rename pluginframework/{common => converters}/tf_to_go.go (67%) diff --git a/common/force_send_fields.go b/common/force_send_fields.go index 0cb444e471..db102aa6a4 100644 --- a/common/force_send_fields.go +++ b/common/force_send_fields.go @@ -5,7 +5,7 @@ import ( "reflect" "strings" - "github.com/databricks/terraform-provider-databricks/internal/reflect_utils" + "github.com/databricks/terraform-provider-databricks/internal/tfreflect" "golang.org/x/exp/slices" ) @@ -30,22 +30,22 @@ func SetForceSendFields(req any, d attributeGetter, fields []string) { if !ok { panic(fmt.Errorf("request argument to setForceSendFields must have ForceSendFields field of type []string (got %s)", forceSendFieldsField.Type())) } - fs := reflect_utils.ListAllFields(rv) + fs := tfreflect.ListAllFields(rv) for _, fieldName := range fields { found := false var structField reflect.StructField for _, f := range fs { - fn := chooseFieldName(f.Sf) + fn := chooseFieldName(f.StructField) if fn != "-" && fn == fieldName { found = true - structField = f.Sf + structField = f.StructField break } } if !found { allFieldNames := make([]string, 0) for _, f := range fs { - fn := chooseFieldName(f.Sf) + fn := chooseFieldName(f.StructField) if fn == "-" || fn == "force_send_fields" { continue } diff --git a/common/reflect_resource.go b/common/reflect_resource.go index 6398346a95..110b449ff7 100644 --- a/common/reflect_resource.go +++ b/common/reflect_resource.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "github.com/databricks/terraform-provider-databricks/internal/reflect_utils" + "github.com/databricks/terraform-provider-databricks/internal/tfreflect" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -397,9 +397,9 @@ func typeToSchema(v reflect.Value, aliases map[string]map[string]string, tc trac panic(fmt.Errorf("Schema value of Struct is expected, but got %s: %#v", reflectKind(rk), v)) } tc = tc.visit(v) - fields := reflect_utils.ListAllFields(v) + fields := tfreflect.ListAllFields(v) for _, field := range fields { - typeField := field.Sf + typeField := field.StructField if tc.depthExceeded(typeField) { // Skip the field if recursion depth is over the limit. log.Printf("[TRACE] over recursion limit, skipping field: %s, max depth: %d", getNameForType(typeField.Type), tc.getMaxDepthForTypeField(typeField)) @@ -576,9 +576,9 @@ func iterFields(rv reflect.Value, path []string, s map[string]*schema.Schema, al return fmt.Errorf("%s: got invalid reflect value %#v", path, rv) } isGoSDK := isGoSdk(rv) - fields := reflect_utils.ListAllFields(rv) + fields := tfreflect.ListAllFields(rv) for _, field := range fields { - typeField := field.Sf + typeField := field.StructField fieldName := chooseFieldNameWithAliases(typeField, rv.Type(), aliases) if fieldName == "-" { continue @@ -596,7 +596,7 @@ func iterFields(rv reflect.Value, path []string, s map[string]*schema.Schema, al if !isGoSDK && fieldSchema.Optional && defaultEmpty && !omitEmpty { return fmt.Errorf("inconsistency: %s is optional, default is empty, but has no omitempty", fieldName) } - valueField := field.V + valueField := field.Value err := cb(fieldSchema, append(path, fieldName), &valueField) if err != nil { return fmt.Errorf("%s: %s", fieldName, err) diff --git a/common/util.go b/common/util.go index 68723f7aee..62b2ab56b2 100644 --- a/common/util.go +++ b/common/util.go @@ -16,7 +16,8 @@ import ( ) var ( - uuidRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) + uuidRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) + TerraformBugErrorMessage = "This is a bug. Please report this to the maintainers at github.com/databricks/terraform-provider-databricks." ) func StringIsUUID(s string) bool { diff --git a/internal/reflect_utils/reflect_utils.go b/internal/tfreflect/reflect_utils.go similarity index 53% rename from internal/reflect_utils/reflect_utils.go rename to internal/tfreflect/reflect_utils.go index 5f26211f1f..ec140c605e 100644 --- a/internal/reflect_utils/reflect_utils.go +++ b/internal/tfreflect/reflect_utils.go @@ -1,12 +1,14 @@ -package reflect_utils +package tfreflect import "reflect" type Field struct { - Sf reflect.StructField - V reflect.Value + StructField reflect.StructField + Value reflect.Value } +// Given a reflect.Value of a struct, list all of the fields for both struct field and +// the value. This function also extracts and flattens the anonymous fields nested inside. func ListAllFields(v reflect.Value) []Field { t := v.Type() fields := make([]Field, 0, v.NumField()) @@ -16,8 +18,8 @@ func ListAllFields(v reflect.Value) []Field { fields = append(fields, ListAllFields(v.Field(i))...) } else { fields = append(fields, Field{ - Sf: f, - V: v.Field(i), + StructField: f, + Value: v.Field(i), }) } } diff --git a/pluginframework/common/converters_test.go b/pluginframework/converters/converters_test.go similarity index 83% rename from pluginframework/common/converters_test.go rename to pluginframework/converters/converters_test.go index 263f19eb17..f515669fa6 100644 --- a/pluginframework/common/converters_test.go +++ b/pluginframework/converters/converters_test.go @@ -1,4 +1,4 @@ -package pluginframework +package converters import ( "context" @@ -12,8 +12,8 @@ import ( type DummyTfSdk struct { Enabled types.Bool `tfsdk:"enabled" tf:"optional"` - Workers types.Int64 `tfsdk:"workers" tf:""` // Test required field - Floats types.Float64 `tfsdk:"floats" tf:""` // Test required field + 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"` Nested *DummyNestedTfSdk `tfsdk:"nested" tf:"optional"` @@ -26,7 +26,7 @@ type DummyTfSdk struct { Attributes map[string]types.String `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"` // distinct field that the gosdk struct doesn't have + DistinctField types.String `tfsdk:"distinct_field" tf:"optional"` Irrelevant types.String `tfsdk:"-"` } @@ -90,55 +90,52 @@ type DummyNestedGoSdk struct { // Function to construct individual test case with a pair of matching tfSdkStruct and gosdkStruct. // Verifies that the conversion both ways are working as expected. -func ConverterTestCase(t *testing.T, description string, tfSdkStruct DummyTfSdk, goSdkStruct DummyGoSdk) { +func RunConverterTest(t *testing.T, description string, tfSdkStruct DummyTfSdk, goSdkStruct DummyGoSdk) { convertedGoSdkStruct := DummyGoSdk{} - assert.True(t, !TfSdkToGoSdkStruct(tfSdkStruct, &convertedGoSdkStruct, context.Background()).HasError()) + assert.True(t, !TfSdkToGoSdkStruct(context.Background(), tfSdkStruct, &convertedGoSdkStruct).HasError()) assert.True(t, reflect.DeepEqual(convertedGoSdkStruct, goSdkStruct), fmt.Sprintf("tfsdk to gosdk conversion - %s", description)) convertedTfSdkStruct := DummyTfSdk{} - assert.True(t, !GoSdkToTfSdkStruct(goSdkStruct, &convertedTfSdkStruct, context.Background()).HasError()) + assert.True(t, !GoSdkToTfSdkStruct(context.Background(), goSdkStruct, &convertedTfSdkStruct).HasError()) assert.True(t, reflect.DeepEqual(convertedTfSdkStruct, tfSdkStruct), fmt.Sprintf("gosdk to tfsdk conversion - %s", description)) } -func TestConverter(t *testing.T) { - ConverterTestCase( - t, +var tests = []struct { + name string + tfSdkStruct DummyTfSdk + goSdkStruct DummyGoSdk +}{ + { "string conversion", DummyTfSdk{Description: types.StringValue("abc")}, DummyGoSdk{Description: "abc", ForceSendFields: []string{"Description"}}, - ) - ConverterTestCase( - t, + }, + { "bool conversion", DummyTfSdk{Enabled: types.BoolValue(true)}, DummyGoSdk{Enabled: true, ForceSendFields: []string{"Enabled"}}, - ) - ConverterTestCase( - t, + }, + { "int64 conversion", DummyTfSdk{Workers: types.Int64Value(123)}, DummyGoSdk{Workers: 123, ForceSendFields: []string{"Workers"}}, - ) - ConverterTestCase( - t, + }, + { "tf null value conversion", DummyTfSdk{Workers: types.Int64Null()}, DummyGoSdk{}, - ) - ConverterTestCase( - t, + }, + { "float64 conversion", DummyTfSdk{Floats: types.Float64Value(1.1)}, DummyGoSdk{Floats: 1.1, ForceSendFields: []string{"Floats"}}, - ) - ConverterTestCase( - t, + }, + { "enum conversion", DummyTfSdk{EnumField: types.StringValue("TEST_ENUM_A")}, DummyGoSdk{EnumField: TestEnumA}, - ) - ConverterTestCase( - t, + }, + { "struct conversion", DummyTfSdk{NoPointerNested: DummyNestedTfSdk{ Name: types.StringValue("def"), @@ -149,9 +146,8 @@ func TestConverter(t *testing.T) { Enabled: true, ForceSendFields: []string{"Name", "Enabled"}, }}, - ) - ConverterTestCase( - t, + }, + { "pointer conversion", DummyTfSdk{Nested: &DummyNestedTfSdk{ Name: types.StringValue("def"), @@ -162,21 +158,18 @@ func TestConverter(t *testing.T) { Enabled: true, ForceSendFields: []string{"Name", "Enabled"}, }}, - ) - ConverterTestCase( - t, + }, + { "list conversion", DummyTfSdk{Repeated: []types.Int64{types.Int64Value(12), types.Int64Value(34)}}, DummyGoSdk{Repeated: []int64{12, 34}}, - ) - ConverterTestCase( - t, + }, + { "map conversion", DummyTfSdk{Attributes: map[string]types.String{"key": types.StringValue("value")}}, DummyGoSdk{Attributes: map[string]string{"key": "value"}}, - ) - ConverterTestCase( - t, + }, + { "nested list conversion", DummyTfSdk{NestedList: []DummyNestedTfSdk{ { @@ -200,35 +193,8 @@ func TestConverter(t *testing.T) { ForceSendFields: []string{"Name", "Enabled"}, }, }}, - ) - ConverterTestCase( - t, - "nested list conversion", - DummyTfSdk{NestedList: []DummyNestedTfSdk{ - { - Name: types.StringValue("abc"), - Enabled: types.BoolValue(true), - }, - { - Name: types.StringValue("def"), - Enabled: types.BoolValue(false), - }, - }}, - DummyGoSdk{NestedList: []DummyNestedGoSdk{ - { - Name: "abc", - Enabled: true, - ForceSendFields: []string{"Name", "Enabled"}, - }, - { - Name: "def", - Enabled: false, - ForceSendFields: []string{"Name", "Enabled"}, - }, - }}, - ) - ConverterTestCase( - t, + }, + { "nested map conversion", DummyTfSdk{NestedMap: map[string]DummyNestedTfSdk{ "key1": { @@ -252,5 +218,11 @@ func TestConverter(t *testing.T) { ForceSendFields: []string{"Name", "Enabled"}, }, }}, - ) + }, +} + +func TestConverter(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { RunConverterTest(t, test.name, test.tfSdkStruct, test.goSdkStruct) }) + } } diff --git a/pluginframework/common/go_to_tf.go b/pluginframework/converters/go_to_tf.go similarity index 73% rename from pluginframework/common/go_to_tf.go rename to pluginframework/converters/go_to_tf.go index 0ea3d64f84..3728432a1e 100644 --- a/pluginframework/common/go_to_tf.go +++ b/pluginframework/converters/go_to_tf.go @@ -1,4 +1,4 @@ -package pluginframework +package converters import ( "context" @@ -9,7 +9,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/databricks/terraform-provider-databricks/internal/reflect_utils" + "github.com/databricks/terraform-provider-databricks/common" + "github.com/databricks/terraform-provider-databricks/internal/tfreflect" ) // Converts a gosdk struct into a tfsdk struct, with the folowing rules. @@ -27,7 +28,7 @@ import ( // map keys should always be a string // tfsdk structs use types.String for all enum values // non-json fields will be omitted -func GoSdkToTfSdkStruct(gosdk interface{}, tfsdk interface{}, ctx context.Context) diag.Diagnostics { +func GoSdkToTfSdkStruct(ctx context.Context, gosdk interface{}, tfsdk interface{}) diag.Diagnostics { srcVal := reflect.ValueOf(gosdk) destVal := reflect.ValueOf(tfsdk) @@ -55,11 +56,11 @@ func GoSdkToTfSdkStruct(gosdk interface{}, tfsdk interface{}, ctx context.Contex forceSendFieldVal = forceSendField.Interface().([]string) } - for _, field := range reflect_utils.ListAllFields(srcVal) { - srcField := field.V - srcFieldName := field.Sf.Name + for _, field := range tfreflect.ListAllFields(srcVal) { + srcField := field.Value + srcFieldName := field.StructField.Name - srcFieldTag := field.Sf.Tag.Get("json") + srcFieldTag := field.StructField.Tag.Get("json") if srcFieldTag == "-" { continue } @@ -70,7 +71,7 @@ func GoSdkToTfSdkStruct(gosdk interface{}, tfsdk interface{}, ctx context.Contex continue } - err := goSdkToTfSdkSingleField(srcField, destField, fieldInForceSendFields(srcFieldName, forceSendFieldVal), ctx) + err := goSdkToTfSdkSingleField(ctx, srcField, destField, fieldInForceSendFields(srcFieldName, forceSendFieldVal)) if err != nil { return diag.Diagnostics{diag.NewErrorDiagnostic(err.Error(), "gosdk to tfsdk field conversion failure")} } @@ -78,10 +79,10 @@ func GoSdkToTfSdkStruct(gosdk interface{}, tfsdk interface{}, ctx context.Contex return nil } -func goSdkToTfSdkSingleField(srcField reflect.Value, destField reflect.Value, forceSendField bool, ctx context.Context) error { +func goSdkToTfSdkSingleField(ctx context.Context, srcField reflect.Value, destField reflect.Value, forceSendField bool) error { if !destField.CanSet() { - panic(fmt.Errorf("destination field can not be set: %s", destField.Type().Name())) + panic(fmt.Errorf("destination field can not be set: %s. %s", destField.Type().Name(), common.TerraformBugErrorMessage)) } srcFieldValue := srcField.Interface() @@ -99,8 +100,8 @@ func goSdkToTfSdkSingleField(srcField reflect.Value, destField reflect.Value, fo destField.Set(reflect.New(destField.Type().Elem())) // Recursively populate the nested struct. - if diags := GoSdkToTfSdkStruct(srcFieldValue, destField.Interface(), ctx); diags.ErrorsCount() > 0 { - panic("Error converting gosdk to tfsdk struct") + if GoSdkToTfSdkStruct(ctx, srcFieldValue, destField.Interface()).HasError() { + panic(fmt.Sprintf("Error converting gosdk to tfsdk struct. %s", common.TerraformBugErrorMessage)) } case reflect.Bool: boolVal := srcFieldValue.(bool) @@ -112,16 +113,16 @@ func goSdkToTfSdkSingleField(srcField reflect.Value, destField reflect.Value, fo } case reflect.Int64: // convert any kind of integer to int64 - intVal := srcField.Convert(reflect.TypeOf(int64(0))).Interface().(int64) + intVal := srcField.Convert(reflect.TypeOf(int64(0))).Int() // check if the value is non-zero or if the field is in the forceSendFields list if intVal != 0 || forceSendField { - destField.Set(reflect.ValueOf(types.Int64Value(int64(intVal)))) + destField.Set(reflect.ValueOf(types.Int64Value(intVal))) } else { destField.Set(reflect.ValueOf(types.Int64Null())) } case reflect.Float64: // convert any kind of float to float64 - float64Val := srcField.Convert(reflect.TypeOf(float64(0))).Interface().(float64) + float64Val := srcField.Convert(reflect.TypeOf(float64(0))).Float() // check if the value is non-zero or if the field is in the forceSendFields list if float64Val != 0 || forceSendField { destField.Set(reflect.ValueOf(types.Float64Value(float64Val))) @@ -148,8 +149,8 @@ func goSdkToTfSdkSingleField(srcField reflect.Value, destField reflect.Value, fo return nil } // resolve the nested struct by recursively calling the function - if GoSdkToTfSdkStruct(srcFieldValue, destField.Addr().Interface(), ctx).ErrorsCount() > 0 { - panic("Error converting gosdk to tfsdk struct") + if GoSdkToTfSdkStruct(ctx, srcFieldValue, destField.Addr().Interface()).HasError() { + panic(fmt.Sprintf("Error converting gosdk to tfsdk struct. %s", common.TerraformBugErrorMessage)) } case reflect.Slice: if srcField.IsNil() { @@ -162,7 +163,7 @@ func goSdkToTfSdkSingleField(srcField reflect.Value, destField reflect.Value, fo srcElem := srcField.Index(j) destElem := destSlice.Index(j) - if err := goSdkToTfSdkSingleField(srcElem, destElem, true, ctx); err != nil { + if err := goSdkToTfSdkSingleField(ctx, srcElem, destElem, true); err != nil { return err } } @@ -177,14 +178,14 @@ func goSdkToTfSdkSingleField(srcField reflect.Value, destField reflect.Value, fo srcMapValue := srcField.MapIndex(key) destMapValue := reflect.New(destField.Type().Elem()).Elem() destMapKey := reflect.ValueOf(key.Interface()) - if err := goSdkToTfSdkSingleField(srcMapValue, destMapValue, true, ctx); err != nil { + if err := goSdkToTfSdkSingleField(ctx, srcMapValue, destMapValue, true); err != nil { return err } destMap.SetMapIndex(destMapKey, destMapValue) } destField.Set(destMap) default: - panic(fmt.Errorf("unknown type for field: %s", srcField.Type().Name())) + panic(fmt.Errorf("unknown type for field: %s. %s", srcField.Type().Name(), common.TerraformBugErrorMessage)) } return nil } @@ -195,6 +196,7 @@ func getStringFromEnum(srcField reflect.Value) string { if srcField.CanAddr() { stringMethod = srcField.Addr().MethodByName("String") } else { + // This case is for the unit tests because the enum values will be const and we cannot get the address. // If cannot get addr, create a new addressable variable to call the String method addr := reflect.New(srcField.Type()).Elem() addr.Set(srcField) @@ -205,10 +207,10 @@ func getStringFromEnum(srcField reflect.Value) string { if len(stringResult) == 1 { return stringResult[0].Interface().(string) } else { - panic("num get string has more than one result") + panic(fmt.Sprintf("num get string has more than one result. %s", common.TerraformBugErrorMessage)) } } else { - panic("enum does not have valid .String() method") + panic(fmt.Sprintf("enum does not have valid .String() method. %s", common.TerraformBugErrorMessage)) } } diff --git a/pluginframework/common/tf_to_go.go b/pluginframework/converters/tf_to_go.go similarity index 67% rename from pluginframework/common/tf_to_go.go rename to pluginframework/converters/tf_to_go.go index 0e466ef2dd..cd629c8a78 100644 --- a/pluginframework/common/tf_to_go.go +++ b/pluginframework/converters/tf_to_go.go @@ -1,16 +1,17 @@ -package pluginframework +package converters import ( "context" "fmt" - "log" "reflect" "github.com/databricks/databricks-sdk-go/logger" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/databricks/terraform-provider-databricks/internal/reflect_utils" + "github.com/databricks/terraform-provider-databricks/common" + "github.com/databricks/terraform-provider-databricks/internal/tfreflect" ) // Converts a tfsdk struct into a gosdk struct, with the folowing rules. @@ -27,7 +28,7 @@ import ( // types.list and types.map are not supported // map keys should always be a string // tfsdk structs use types.String for all enum values -func TfSdkToGoSdkStruct(tfsdk interface{}, gosdk interface{}, ctx context.Context) diag.Diagnostics { +func TfSdkToGoSdkStruct(ctx context.Context, tfsdk interface{}, gosdk interface{}) diag.Diagnostics { srcVal := reflect.ValueOf(tfsdk) destVal := reflect.ValueOf(gosdk) @@ -46,28 +47,28 @@ func TfSdkToGoSdkStruct(tfsdk interface{}, gosdk interface{}, ctx context.Contex forceSendFieldsField := destVal.FieldByName("ForceSendFields") - allFields := reflect_utils.ListAllFields(srcVal) + allFields := tfreflect.ListAllFields(srcVal) for _, field := range allFields { - srcField := field.V - srcFieldName := field.Sf.Name + srcField := field.Value + srcFieldName := field.StructField.Name - srcFieldTag := field.Sf.Tag.Get("tfsdk") + srcFieldTag := field.StructField.Tag.Get("tfsdk") if srcFieldTag == "-" { continue } destField := destVal.FieldByName(srcFieldName) - err := tfSdkToGoSdkSingleField(srcField, destField, srcFieldName, &forceSendFieldsField, ctx) + err := tfSdkToGoSdkSingleField(ctx, srcField, destField, srcFieldName, &forceSendFieldsField) if err != nil { return diag.Diagnostics{diag.NewErrorDiagnostic(err.Error(), "tfsdk to gosdk field conversion failure")} } } - return diag.Diagnostics{} + return nil } -func tfSdkToGoSdkSingleField(srcField reflect.Value, destField reflect.Value, srcFieldName string, forceSendFieldsField *reflect.Value, ctx context.Context) error { +func tfSdkToGoSdkSingleField(ctx context.Context, srcField reflect.Value, destField reflect.Value, srcFieldName string, forceSendFieldsField *reflect.Value) error { if !destField.IsValid() { // Skip field that destination struct does not have. @@ -76,7 +77,7 @@ func tfSdkToGoSdkSingleField(srcField reflect.Value, destField reflect.Value, sr } if !destField.CanSet() { - panic(fmt.Errorf("destination field can not be set: %s", destField.Type().Name())) + panic(fmt.Errorf("destination field can not be set: %s. %s", destField.Type().Name(), common.TerraformBugErrorMessage)) } srcFieldValue := srcField.Interface() @@ -91,8 +92,8 @@ func tfSdkToGoSdkSingleField(srcField reflect.Value, destField reflect.Value, sr destField.Set(reflect.New(destField.Type().Elem())) // Recursively populate the nested struct. - if TfSdkToGoSdkStruct(srcFieldValue, destField.Interface(), ctx).ErrorsCount() > 0 { - panic("Error converting tfsdk to gosdk struct") + if TfSdkToGoSdkStruct(ctx, srcFieldValue, destField.Interface()).HasError() { + panic(fmt.Sprintf("Error converting tfsdk to gosdk struct. %s", common.TerraformBugErrorMessage)) } } else if srcField.Kind() == reflect.Struct { tfsdkToGoSdkStructField(srcField, destField, srcFieldName, forceSendFieldsField, ctx) @@ -107,7 +108,7 @@ func tfSdkToGoSdkSingleField(srcField reflect.Value, destField reflect.Value, sr srcElem := srcField.Index(j) destElem := destSlice.Index(j) - if err := tfSdkToGoSdkSingleField(srcElem, destElem, "", nil, ctx); err != nil { + if err := tfSdkToGoSdkSingleField(ctx, srcElem, destElem, "", nil); err != nil { return err } } @@ -122,14 +123,14 @@ func tfSdkToGoSdkSingleField(srcField reflect.Value, destField reflect.Value, sr srcMapValue := srcField.MapIndex(key) destMapValue := reflect.New(destField.Type().Elem()).Elem() destMapKey := reflect.ValueOf(key.Interface()) - if err := tfSdkToGoSdkSingleField(srcMapValue, destMapValue, "", nil, ctx); err != nil { + if err := tfSdkToGoSdkSingleField(ctx, srcMapValue, destMapValue, "", nil); err != nil { return err } destMap.SetMapIndex(destMapKey, destMapValue) } destField.Set(destMap) } else { - panic(fmt.Errorf("unknown type for field: %s", srcField.Type().Name())) + panic(fmt.Errorf("unknown type for field: %s. %s", srcField.Type().Name(), common.TerraformBugErrorMessage)) } return nil @@ -141,17 +142,17 @@ func tfsdkToGoSdkStructField(srcField reflect.Value, destField reflect.Value, sr case types.Bool: destField.SetBool(v.ValueBool()) if !v.IsNull() { - addToForceSendFields(srcFieldName, forceSendFieldsField) + addToForceSendFields(ctx, srcFieldName, forceSendFieldsField) } case types.Int64: destField.SetInt(v.ValueInt64()) if !v.IsNull() { - addToForceSendFields(srcFieldName, forceSendFieldsField) + addToForceSendFields(ctx, srcFieldName, forceSendFieldsField) } case types.Float64: destField.SetFloat(v.ValueFloat64()) if !v.IsNull() { - addToForceSendFields(srcFieldName, forceSendFieldsField) + addToForceSendFields(ctx, srcFieldName, forceSendFieldsField) } case types.String: if destField.Type().Name() != "string" { @@ -166,7 +167,7 @@ func tfsdkToGoSdkStructField(srcField reflect.Value, destField reflect.Value, sr destVal := reflect.New(destField.Type()) setMethod := destVal.MethodByName("Set") if !setMethod.IsValid() { - panic(fmt.Sprintf("set method not found on enum type: %s", destField.Type().Name())) + panic(fmt.Sprintf("set method not found on enum type: %s. %s", destField.Type().Name(), common.TerraformBugErrorMessage)) } // Prepare the argument for the Set method @@ -176,7 +177,7 @@ func tfsdkToGoSdkStructField(srcField reflect.Value, destField reflect.Value, sr result := setMethod.Call([]reflect.Value{arg}) if len(result) != 0 { if err, ok := result[0].Interface().(error); ok && err != nil { - panic(err) + panic(fmt.Sprintf("%s. %s", err, common.TerraformBugErrorMessage)) } } // We don't need to set ForceSendFields for enums because the value is never going to be a zero value (empty string). @@ -184,28 +185,28 @@ func tfsdkToGoSdkStructField(srcField reflect.Value, destField reflect.Value, sr } else { destField.SetString(v.ValueString()) if !v.IsNull() { - addToForceSendFields(srcFieldName, forceSendFieldsField) + addToForceSendFields(ctx, srcFieldName, forceSendFieldsField) } } case types.List: - panic("types.List should never be used, use go native slices instead") + panic(fmt.Sprintf("types.List should never be used, use go native slices instead. %s", common.TerraformBugErrorMessage)) case types.Map: - panic("types.Map should never be used, use go native maps instead") + panic(fmt.Sprintf("types.Map should never be used, use go native maps instead. %s", common.TerraformBugErrorMessage)) default: if srcField.IsZero() { // Skip zeros return } // If it is a real stuct instead of a tfsdk type, recursively resolve it. - if TfSdkToGoSdkStruct(srcFieldValue, destField.Addr().Interface(), ctx).ErrorsCount() > 0 { - panic("Error converting tfsdk to gosdk struct") + if TfSdkToGoSdkStruct(ctx, srcFieldValue, destField.Addr().Interface()).HasError() { + panic(fmt.Sprintf("Error converting tfsdk to gosdk struct. %s", common.TerraformBugErrorMessage)) } } } -func addToForceSendFields(fieldName string, forceSendFieldsField *reflect.Value) { +func addToForceSendFields(ctx context.Context, fieldName string, forceSendFieldsField *reflect.Value) { if forceSendFieldsField == nil || !forceSendFieldsField.IsValid() || !forceSendFieldsField.CanSet() { - log.Printf("[Debug] forceSendFieldsField is nil, invalid or not settable. %s", fieldName) + tflog.Debug(ctx, fmt.Sprintf("[Debug] forceSendFieldsField is nil, invalid or not settable. %s", fieldName)) return } // Initialize forceSendFields if it is a zero Value