Skip to content

Commit

Permalink
Improve error reporting and error handling in reflect lib
Browse files Browse the repository at this point in the history
This change improves error checking and reporting in the reflect lib.
Now an error will be returned on both convert directions if either:
- The name of a field is not found in the passed in struct
- reflect called panic for whatever operation failed

Signed-off-by: Shawn Wang <[email protected]>
  • Loading branch information
wsquan171 committed May 30, 2024
1 parent fd9ae7d commit cffdca2
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 16 deletions.
54 changes: 48 additions & 6 deletions nsxt/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,15 @@ func GetSchemaFromExtendedSchema(ext map[string]*ExtendedSchema) map[string]*sch

// StructToSchema converts NSX model struct to terraform schema
// currently supports nested subtype and trivial types
func StructToSchema(elem reflect.Value, d *schema.ResourceData, metadata map[string]*ExtendedSchema, parent string, parentMap map[string]interface{}) {
func StructToSchema(elem reflect.Value, d *schema.ResourceData, metadata map[string]*ExtendedSchema, parent string, parentMap map[string]interface{}) (err error) {
ctx := fmt.Sprintf("[from %s]", elem.Type())
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%s recovered from panic: %v", ctx, r)
logger.Printf("[ERROR] %v", err)
}
}()

for key, item := range metadata {
if item.Metadata.Skip {
continue
Expand All @@ -94,6 +101,14 @@ func StructToSchema(elem reflect.Value, d *schema.ResourceData, metadata map[str
if len(parent) > 0 {
logger.Printf("[TRACE] %s parent %s key %s", ctx, parent, key)
}
if !elem.FieldByName(item.Metadata.SdkFieldName).IsValid() {
// FieldByName can't find the field by name
logger.Printf("[ERROR] %s skip key %s as %s not found in struct",
ctx, key, item.Metadata.SdkFieldName)
err = fmt.Errorf("%s key %s not found in %s",
ctx, key, item.Metadata.SdkFieldName)
return
}
if elem.FieldByName(item.Metadata.SdkFieldName).IsNil() {
logger.Printf("[TRACE] %s skip key %s with nil value", ctx, key)
continue
Expand All @@ -102,7 +117,9 @@ func StructToSchema(elem reflect.Value, d *schema.ResourceData, metadata map[str
nestedObj := elem.FieldByName(item.Metadata.SdkFieldName)
nestedSchema := make(map[string]interface{})
childElem := item.Schema.Elem.(*ExtendedResource)
StructToSchema(nestedObj.Elem(), d, childElem.Schema, key, nestedSchema)
if err = StructToSchema(nestedObj.Elem(), d, childElem.Schema, key, nestedSchema); err != nil {
return
}
logger.Printf("[TRACE] %s assigning struct %+v to %s", ctx, nestedObj, key)
var nestedSlice []map[string]interface{}
nestedSlice = append(nestedSlice, nestedSchema)
Expand All @@ -127,7 +144,9 @@ func StructToSchema(elem reflect.Value, d *schema.ResourceData, metadata map[str
var nestedSlice []map[string]interface{}
for i := 0; i < sliceElem.Len(); i++ {
nestedSchema := make(map[string]interface{})
StructToSchema(sliceElem.Index(i), d, childElem.Schema, key, nestedSchema)
if err = StructToSchema(sliceElem.Index(i), d, childElem.Schema, key, nestedSchema); err != nil {
return
}
nestedSlice = append(nestedSlice, nestedSchema)
logger.Printf("[TRACE] %s appending slice item %+v to %s", ctx, nestedSchema, key)
}
Expand All @@ -149,12 +168,21 @@ func StructToSchema(elem reflect.Value, d *schema.ResourceData, metadata map[str
}
}
}

return
}

// SchemaToStruct converts terraform schema to NSX model struct
// currently supports nested subtype and trivial types
func SchemaToStruct(elem reflect.Value, d *schema.ResourceData, metadata map[string]*ExtendedSchema, parent string, parentMap map[string]interface{}) {
func SchemaToStruct(elem reflect.Value, d *schema.ResourceData, metadata map[string]*ExtendedSchema, parent string, parentMap map[string]interface{}) (err error) {
ctx := fmt.Sprintf("[to %s]", elem.Type())
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%s recovered from panic: %v", ctx, r)
logger.Printf("[ERROR] %v", err)
}
}()

for key, item := range metadata {
if item.Metadata.ReadOnly {
logger.Printf("[TRACE] %s skip key %s as read only", ctx, key)
Expand All @@ -168,6 +196,14 @@ func SchemaToStruct(elem reflect.Value, d *schema.ResourceData, metadata map[str
logger.Printf("[TRACE] %s skip key %s as NSX does not have support", ctx, key)
continue
}
if !elem.FieldByName(item.Metadata.SdkFieldName).IsValid() {
// FieldByName can't find the field by name
logger.Printf("[WARN] %s skip key %s as %s not found in struct",
ctx, key, item.Metadata.SdkFieldName)
err = fmt.Errorf("%s key %s not found in %s",
ctx, key, item.Metadata.SdkFieldName)
return
}

logger.Printf("[TRACE] %s inspecting key %s with type %s", ctx, key, item.Metadata.SchemaType)
if len(parent) > 0 {
Expand Down Expand Up @@ -217,7 +253,9 @@ func SchemaToStruct(elem reflect.Value, d *schema.ResourceData, metadata map[str
nestedSchema := itemList[0].(map[string]interface{})

childElem := item.Schema.Elem.(*ExtendedResource)
SchemaToStruct(nestedObj.Elem(), d, childElem.Schema, key, nestedSchema)
if err = SchemaToStruct(nestedObj.Elem(), d, childElem.Schema, key, nestedSchema); err != nil {
return
}
logger.Printf("[TRACE] %s assigning struct %v to %s", ctx, nestedObj, key)
elem.FieldByName(item.Metadata.SdkFieldName).Set(nestedObj)
}
Expand Down Expand Up @@ -270,11 +308,15 @@ func SchemaToStruct(elem reflect.Value, d *schema.ResourceData, metadata map[str
for i, childItem := range itemList {
nestedObj := reflect.New(item.Metadata.ReflectType)
nestedSchema := childItem.(map[string]interface{})
SchemaToStruct(nestedObj.Elem(), d, childElem.Schema, key, nestedSchema)
if err = SchemaToStruct(nestedObj.Elem(), d, childElem.Schema, key, nestedSchema); err != nil {
return
}
sliceElem.Index(i).Set(nestedObj.Elem())
logger.Printf("[TRACE] %s appending %+v to %s", ctx, nestedObj.Elem(), key)
}
}
}
}

return
}
9 changes: 6 additions & 3 deletions nsxt/metadata/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,8 @@ func TestStructToSchema(t *testing.T) {
t, testSchema, map[string]interface{}{})

elem := reflect.ValueOf(&obj).Elem()
StructToSchema(elem, d, testExtendedSchema, "", nil)
err := StructToSchema(elem, d, testExtendedSchema, "", nil)
assert.NoError(t, err, "unexpected error calling StructToSchema")

t.Run("Base types", func(t *testing.T) {
assert.Equal(t, "test_string", d.Get("string_field").(string))
Expand Down Expand Up @@ -613,7 +614,8 @@ func TestSchemaToStruct(t *testing.T) {

obj := testStruct{}
elem := reflect.ValueOf(&obj).Elem()
SchemaToStruct(elem, d, testExtendedSchema, "", nil)
err := SchemaToStruct(elem, d, testExtendedSchema, "", nil)
assert.NoError(t, err, "unexpected error calling SchemaToStruct")

nestStr1, nestStr2, nestStr3 := "nested_string_1", "nested_string_2", "nested_string_3"
trueVal, falseVal := true, false
Expand Down Expand Up @@ -697,7 +699,8 @@ func TestSchemaToStructEmptySlice(t *testing.T) {
obj := testDeepNestedStruct{}
elem := reflect.ValueOf(&obj).Elem()
testSch := mixedStructSchema().Elem.(*ExtendedResource).Schema
SchemaToStruct(elem, d, testSch, "", nil)
err := SchemaToStruct(elem, d, testSch, "", nil)
assert.NoError(t, err, "unexpected error calling SchemaToStruct")

t.Run("Empty bool list", func(t *testing.T) {
assert.NotNil(t, obj.BoolList)
Expand Down
12 changes: 7 additions & 5 deletions nsxt/resource_nsxt_policy_mac_discovery_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,9 @@ func resourceNsxtPolicyMacDiscoveryProfileCreate(d *schema.ResourceData, m inter
}

elem := reflect.ValueOf(&obj).Elem()
metadata.SchemaToStruct(elem, d, macDiscoveryProfileSchema, "", nil)
if err := metadata.SchemaToStruct(elem, d, macDiscoveryProfileSchema, "", nil); err != nil {
return err
}

// Create the resource using PATCH
log.Printf("[INFO] Creating MacDiscoveryProfile with ID %s", id)
Expand Down Expand Up @@ -213,9 +215,7 @@ func resourceNsxtPolicyMacDiscoveryProfileRead(d *schema.ResourceData, m interfa
d.Set("path", obj.Path)

elem := reflect.ValueOf(&obj).Elem()
metadata.StructToSchema(elem, d, macDiscoveryProfileSchema, "", nil)

return nil
return metadata.StructToSchema(elem, d, macDiscoveryProfileSchema, "", nil)
}

func resourceNsxtPolicyMacDiscoveryProfileUpdate(d *schema.ResourceData, m interface{}) error {
Expand All @@ -241,7 +241,9 @@ func resourceNsxtPolicyMacDiscoveryProfileUpdate(d *schema.ResourceData, m inter
}

elem := reflect.ValueOf(&obj).Elem()
metadata.SchemaToStruct(elem, d, macDiscoveryProfileSchema, "", nil)
if err := metadata.SchemaToStruct(elem, d, macDiscoveryProfileSchema, "", nil); err != nil {
return err
}

// Update the resource using PATCH
boolFalse := false
Expand Down
8 changes: 6 additions & 2 deletions nsxt/resource_nsxt_policy_segment_security_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,9 @@ func resourceNsxtPolicySegmentSecurityProfilePatch(d *schema.ResourceData, m int
BpduFilterAllow: bpduFilterAllow,
}
elem := reflect.ValueOf(&obj).Elem()
metadata.SchemaToStruct(elem, d, segmentSecurityProfileSchema, "", nil)
if err := metadata.SchemaToStruct(elem, d, segmentSecurityProfileSchema, "", nil); err != nil {
return err
}

log.Printf("[INFO] Sending SegmentSecurityProfile with ID %s", id)
client := infra.NewSegmentSecurityProfilesClient(getSessionContext(d, m), connector)
Expand Down Expand Up @@ -323,7 +325,9 @@ func resourceNsxtPolicySegmentSecurityProfileRead(d *schema.ResourceData, m inte
}

elem := reflect.ValueOf(&obj).Elem()
metadata.StructToSchema(elem, d, segmentSecurityProfileSchema, "", nil)
if err := metadata.StructToSchema(elem, d, segmentSecurityProfileSchema, "", nil); err != nil {
return err
}

d.Set("display_name", obj.DisplayName)
d.Set("description", obj.Description)
Expand Down

0 comments on commit cffdca2

Please sign in to comment.