From fdd233a25ffb6e7fbf2544998ea18e4e8a2d9bf2 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 11 Aug 2023 08:00:00 -0600 Subject: [PATCH] Add recursive getter functions When transforming a NodeConf struct to a NodeInfo (get the NodeInfo from the Nodconf) nested structures were seperately handled. This means there were seperate handles for e.g *Netdevs and *IpmiConfig. The new getter traverses through the datastucture and sets the right value for an Entry struct, but calls itself again if a Pointer is detected. This adds the posiblity to add new nested structures to NodeConf and NodeInfo without the need of seperate handling them in the transformer functions. Signed-off-by: Christian Goll --- internal/pkg/node/transformer_test.go | 3 +- internal/pkg/node/transformers.go | 226 +++++++------------------- 2 files changed, 63 insertions(+), 166 deletions(-) diff --git a/internal/pkg/node/transformer_test.go b/internal/pkg/node/transformer_test.go index 3c642e50..0f8746fe 100644 --- a/internal/pkg/node/transformer_test.go +++ b/internal/pkg/node/transformer_test.go @@ -299,13 +299,14 @@ ipmi: {} t.Errorf("Got wrong yml, wanted:\n'%s'\nGot:\n'%s'", wanted, string(ymlByte)) } delete(test_node4.Tags, "foo") + nodeConf = NewConf() nodeConf.GetFrom(test_node4) nodeConf.Flatten() ymlByte, _ = yaml.Marshal(nodeConf) wanted = `{} ` if string(ymlByte) != wanted { - t.Errorf("Couldn't remove tag'%s'", string(ymlByte)) + t.Errorf("Couldn't remove tag, wanted:\n%s\nGot:\n%s", wanted, string(ymlByte)) } }) diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index 9eceac5d..66d24328 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -1,6 +1,7 @@ package node import ( + "fmt" "reflect" "strings" @@ -15,7 +16,7 @@ the underlying entries using GetReal, so just the explicit values go do disk. */ func (nodeConf *NodeConf) GetRealFrom(nodeInfo NodeInfo) { - nodeConf.getterFrom(nodeInfo, (*Entry).GetReal, (*Entry).GetRealSlice) + recursiveGetter(&nodeInfo, nodeConf, (*Entry).GetReal, (*Entry).GetRealSlice) } /* @@ -23,168 +24,67 @@ Populates a NodeConf struct from a NodeInfo, with the combined values from the underlying entries using Get. */ func (nodeConf *NodeConf) GetFrom(nodeInfo NodeInfo) { - nodeConf.getterFrom(nodeInfo, (*Entry).Get, (*Entry).GetSlice) + recursiveGetter(&nodeInfo, nodeConf, (*Entry).Get, (*Entry).GetSlice) } /* Abstract function which populates a NodeConf from the given NodeInfo -via getter functions. +via getter functions. Calls recursive itself for nested structures. +Panics if the NodeConf has fields which are not type of string,[]string,map[string]*ptr */ -func (nodeConf *NodeConf) getterFrom(nodeInfo NodeInfo, +func recursiveGetter( + source, target interface{}, getter func(*Entry) string, getterSlice func(*Entry) []string) { - nodeInfoType := reflect.TypeOf(nodeInfo) - nodeInfoVal := reflect.ValueOf(nodeInfo) - configVal := reflect.ValueOf(nodeConf) - // now iterate of every field - for i := 0; i < nodeInfoType.NumField(); i++ { - // found field with same name for Conf and Info - confField := configVal.Elem().FieldByName(nodeInfoType.Field(i).Name) - if confField.IsValid() { - if nodeInfoVal.Field(i).Type() == reflect.TypeOf(Entry{}) { - if confField.Type().Kind() == reflect.String { - newValue := (confField.Addr().Interface()).(*string) - entryVal := nodeInfoVal.Field(i).Interface().(Entry) - *newValue = getter(&entryVal) - } else if confField.Type() == reflect.TypeOf([]string{}) { - newValue := (confField.Addr().Interface()).(*[]string) - entryVal := nodeInfoVal.Field(i).Interface().(Entry) - *newValue = getterSlice(&entryVal) - } - } else if nodeInfoVal.Field(i).Type() == reflect.TypeOf(map[string]*Entry{}) { - entryMap := nodeInfoVal.Field(i).Interface().(map[string]*Entry) - confMap := confField.Interface().(map[string]string) + sourceValue := reflect.ValueOf(source) + targetType := reflect.TypeOf(target) + targetValue := reflect.ValueOf(target) + if targetValue.Elem().Kind() == reflect.Struct && sourceValue.Elem().Kind() == reflect.Struct { + for i := 0; i < targetType.Elem().NumField(); i++ { + sourceValueMatched := sourceValue.Elem().FieldByName(targetType.Elem().Field(i).Name) + if sourceValueMatched.IsValid() { + if sourceValueMatched.Type() == reflect.TypeOf(Entry{}) { + // get the fields which are part of the struct + switch targetValue.Elem().Field(i).Type() { + case reflect.TypeOf(""): + newValue := (targetValue.Elem().Field(i).Addr().Interface()).(*string) + source := sourceValueMatched.Interface().(Entry) + *newValue = getter(&source) + case reflect.TypeOf([]string{}): + newValue := (targetValue.Elem().Field(i).Addr().Interface()).(*[]string) + source := sourceValueMatched.Interface().(Entry) + *newValue = getterSlice(&source) + default: + panic(fmt.Errorf("can't convert an Entry to %s", targetValue.Elem().Field(i).Type())) + } + } else if sourceValueMatched.Kind() == reflect.Ptr { + // if we get a pointer, initialize if empty and then have a recursive call + if targetValue.Elem().Field(i).IsZero() { + targetValue.Elem().Field(i).Set(reflect.New(targetType.Elem().Field(i).Type.Elem())) + } + recursiveGetter(sourceValueMatched.Interface(), targetValue.Elem().Field(i).Interface(), getter, getterSlice) + } else if sourceValueMatched.Type().Kind() == reflect.Map { + if targetValue.Elem().Field(i).IsZero() { + targetValue.Elem().Field(i).Set(reflect.MakeMap(targetType.Elem().Field(i).Type)) + } + sourceIter := sourceValueMatched.MapRange() + if sourceValueMatched.Type() == reflect.TypeOf(map[string]*Entry{}) { + // go over a simple map with strings + for sourceIter.Next() { + if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() { + str := getter((sourceIter.Value().Interface()).(*Entry)) + targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), reflect.ValueOf(str)) + } + } + } else { + // now the complicated map which contains pointers to objects + for sourceIter.Next() { + if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() { + newPtr := reflect.New(targetType.Elem().Field(i).Type.Elem().Elem()) + targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), newPtr) + } + recursiveGetter(sourceIter.Value().Interface(), targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).Interface(), getter, getterSlice) - if len(confMap) > len(entryMap) { - for confKey := range confMap { - foundKey := false - for entrKey := range entryMap { - if confKey == entrKey { - foundKey = true - } - } - if !foundKey { - delete(confMap, confKey) - } - } - } - for key, val := range entryMap { - confMap[key] = getter(val) - } - } else if nodeInfoVal.Field(i).Type().Kind() == reflect.Ptr && !nodeInfoVal.Field(i).IsNil() { - // initialize the nested NodeConf structs, but only if these will be set - if confField.Addr().Elem().IsZero() { - switch confField.Addr().Elem().Type() { - case reflect.TypeOf((*KernelConf)(nil)): - var newConf KernelConf - newConfPtr := (confField.Addr().Elem().Addr().Interface()).(**KernelConf) - *newConfPtr = &newConf - case reflect.TypeOf((*IpmiConf)(nil)): - var newConf IpmiConf - newConfPtr := (confField.Addr().Elem().Addr().Interface()).(**IpmiConf) - *newConfPtr = &newConf - } - } - nestedInfoType := reflect.TypeOf(nodeInfoVal.Field(i).Interface()) - nestedInfoVal := reflect.ValueOf(nodeInfoVal.Field(i).Interface()) - nestedConfVal := reflect.ValueOf(confField.Interface()) - for j := 0; j < nestedInfoType.Elem().NumField(); j++ { - nestedVal := nestedConfVal.Elem().FieldByName(nestedInfoType.Elem().Field(j).Name) - if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(Entry{}) { - if nestedVal.Type().Kind() == reflect.String { - newValue := (nestedVal.Addr().Interface()).(*string) - entryVal := nestedInfoVal.Elem().Field(j).Interface().(Entry) - *newValue = getter(&entryVal) - } else if nestedVal.Type() == reflect.TypeOf([]string{}) { - newValue := (nestedVal.Addr().Interface()).(*[]string) - entryVal := nestedInfoVal.Elem().Field(j).Interface().(Entry) - *newValue = getterSlice(&entryVal) - - } - } else if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(map[string]*Entry{}) { - if nestedVal.IsNil() { - mapPtr := nestedVal.Addr().Interface().(*map[string]string) - *mapPtr = make(map[string]string) - } - entryMap := nestedInfoVal.Elem().Field(j).Interface().(map[string]*Entry) - confMap := nestedVal.Interface().(map[string]string) - if len(confMap) > len(entryMap) { - for confKey := range confMap { - foundKey := false - for entrKey := range entryMap { - if confKey == entrKey { - foundKey = true - } - } - if !foundKey { - delete(confMap, confKey) - } - } - } - for key, val := range entryMap { - confMap[key] = getter(val) - } - } - } - - } else if nodeInfoVal.Field(i).Type() == reflect.TypeOf(map[string]*NetDevEntry{}) { - if confField.IsNil() { - netMapPtr := confField.Addr().Interface().(*map[string](*NetDevs)) - *netMapPtr = make(map[string](*NetDevs)) - } - nestedMap := nodeInfoVal.Field(i).Interface().(map[string]*NetDevEntry) - netMap := confField.Interface().(map[string](*NetDevs)) - // check if a network was deleted - if len(netMap) > len(nestedMap) { - for netMapKey := range netMap { - foundKey := false - for nestedMapKey := range nestedMap { - if netMapKey == nestedMapKey { - foundKey = true - } - } - if !foundKey { - delete(netMap, netMapKey) - } - } - } - for netName, netVal := range nestedMap { - netValsType := reflect.ValueOf(netVal) - if _, ok := netMap[netName]; !ok { - netMap[netName] = new(NetDevs) - } - netConfType := reflect.TypeOf(*netMap[netName]) - netConfVal := reflect.ValueOf(netMap[netName]) - for j := 0; j < netConfType.NumField(); j++ { - netVal := netValsType.Elem().FieldByName(netConfType.Field(j).Name) - if netVal.IsValid() { - if netVal.Type() == reflect.TypeOf(Entry{}) { - newVal := netConfVal.Elem().Field(j).Addr().Interface().((*string)) - *newVal = getter((netVal.Addr().Interface()).(*Entry)) - } else if netVal.Type() == reflect.TypeOf(map[string]*Entry{}) { - entryMap := netVal.Interface().(map[string](*Entry)) - confMap := netConfVal.Elem().Field(j).Interface().(map[string]string) - if confMap == nil { - confMapPtr := netConfVal.Elem().Field(j).Addr().Interface().(*map[string]string) - *confMapPtr = make(map[string]string) - } - if len(confMap) > len(entryMap) { - for confMapKey := range confMap { - foundKey := false - for entryMapKey := range entryMap { - if confMapKey == entryMapKey { - foundKey = true - } - } - if !foundKey { - delete(netConfVal.Elem().Field(j).Interface().(map[string]string), confMapKey) - } - } - } - for key, val := range entryMap { - netConfVal.Elem().Field(j).Interface().(map[string]string)[key] = getter(val) - } - } } } } @@ -350,8 +250,11 @@ or *KernelConf, these pointer will set to nil. This will remove something like ipmi: {} from nodes.conf */ func (info *NodeConf) Flatten() { - confType := reflect.TypeOf(info) - confVal := reflect.ValueOf(info) + recursiveFlatten(info) +} +func recursiveFlatten(strct interface{}) { + confType := reflect.TypeOf(strct) + confVal := reflect.ValueOf(strct) for j := 0; j < confType.Elem().NumField(); j++ { if confVal.Elem().Field(j).Type().Kind() == reflect.Ptr && !confVal.Elem().Field(j).IsNil() { // iterate now over the ptr fields @@ -371,14 +274,7 @@ func (info *NodeConf) Flatten() { } } if setToNil { - switch confType.Elem().Field(j).Type { - case reflect.TypeOf((*IpmiConf)(nil)): - ptr := confVal.Elem().Field(j).Addr().Interface().(**IpmiConf) - *ptr = (*IpmiConf)(nil) - case reflect.TypeOf((*KernelConf)(nil)): - ptr := confVal.Elem().Field(j).Addr().Interface().(**KernelConf) - *ptr = (*KernelConf)(nil) - } + confVal.Elem().Field(j).Set(reflect.Zero(confVal.Elem().Field(j).Type())) } } }