diff --git a/internal/pkg/node/mergo.go b/internal/pkg/node/mergo.go index 3a0b2c33..171cf558 100644 --- a/internal/pkg/node/mergo.go +++ b/internal/pkg/node/mergo.go @@ -123,46 +123,15 @@ func (config *NodesYaml) MergeNode(id string) (node Node, fields fieldMap, err e continue } else { profile := deepcopy.Copy(profile) - if err = mergo.Merge(&node.Profile, profile, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(InterfaceTransformer{})); err != nil { + if err = merge(&node.Profile, profile, fields, profileID, profileID); err != nil { return node, fields, err } - for _, fieldName := range listFields(profile) { - if value, err := getNestedFieldValue(profile, fieldName); err == nil && valueStr(value) != "" { - source := profileID - prevSource := fields.Source(fieldName) - if value.Kind() == reflect.Slice && prevSource != "" { - source = strings.Join([]string{prevSource, source}, ",") - } - if value, err := getNestedFieldString(node, fieldName); err == nil { - fields.Set(fieldName, source, value) - } - } - } } } - if err = mergo.Merge(&node, originalNode, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(InterfaceTransformer{})); err != nil { + if err = merge(&node, originalNode, fields, "", id); err != nil { return node, fields, err } - for _, fieldName := range listFields(originalNode) { - if value, err := getNestedFieldValue(originalNode, fieldName); err == nil && valueStr(value) != "" { - source := "" - prevSource := fields.Source(fieldName) - if prevSource != "" { - switch value.Kind() { - case reflect.Slice: - source = strings.Join([]string{prevSource, id}, ",") - case reflect.Interface: - if _, ok := value.Interface().([]interface{}); ok { - source = strings.Join([]string{prevSource, id}, ",") - } - } - } - if value, err := getNestedFieldString(node, fieldName); err == nil { - fields.Set(fieldName, source, value) - } - } - } node.Profiles = originalNode.Profiles if len(node.Profiles) > 0 { @@ -177,3 +146,41 @@ func (config *NodesYaml) MergeNode(id string) (node Node, fields fieldMap, err e node.updatePrimaryNetDev() return node, fields, nil } + + +// merge merges the fields of src (a data object) into dst (a pointer) associated with it. Used by +// MergeNode to provide consistent behavior when merging profiles and nodes. +// +// merge further tracks the source of each field in the provided fields. +// +// Because the source label behavior differs between multi-valued fields (e.g., slices) and +// single-valued slices, two source names must be provided: srcName is used for single-valued +// fields, and multipleSrcName is used for multi-sourced fields. +// +// Returns an error if the merging operation fails. +func merge(dest, src interface{}, fields fieldMap, srcName string, multipleSrcName string) error { + if err := mergo.Merge(dest, src, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(InterfaceTransformer{})); err != nil { + return err + } + + for _, fieldName := range listFields(src) { + if value, err := getNestedFieldValue(src, fieldName); err == nil && valueStr(value) != "" { + srcName := srcName + prevSource := fields.Source(fieldName) + if prevSource != "" { + switch value.Kind() { + case reflect.Slice: + srcName = strings.Join([]string{prevSource, multipleSrcName}, ",") + case reflect.Interface: + if _, ok := value.Interface().([]interface{}); ok { + srcName = strings.Join([]string{prevSource, multipleSrcName}, ",") + } + } + } + if value, err := getNestedFieldString(reflect.ValueOf(dest).Elem().Interface(), fieldName); err == nil { + fields.Set(fieldName, srcName, value) + } + } + } + return nil +}