Refactor gRPC-based node functions
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
88
internal/pkg/node/update.go
Normal file
88
internal/pkg/node/update.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// UpdateFrom copies fields from src to dst, but only those fields whose
|
||||
// corresponding cobra flag (identified by the "lopt" struct tag) reports
|
||||
// as changed. This replaces the old pattern of marshaling to YAML (to strip
|
||||
// zero values via omitempty) and then merging with mergo.
|
||||
//
|
||||
// The changed function should typically be cmd.Flags().Changed.
|
||||
func (dst *Node) UpdateFrom(src *Node, changed func(string) bool) {
|
||||
recursiveUpdateFrom(reflect.ValueOf(dst).Elem(), reflect.ValueOf(src).Elem(), changed)
|
||||
}
|
||||
|
||||
// UpdateFrom copies fields from src to dst for profiles.
|
||||
func (dst *Profile) UpdateFrom(src *Profile, changed func(string) bool) {
|
||||
recursiveUpdateFrom(reflect.ValueOf(dst).Elem(), reflect.ValueOf(src).Elem(), changed)
|
||||
}
|
||||
|
||||
func recursiveUpdateFrom(dst, src reflect.Value, changed func(string) bool) {
|
||||
srcType := src.Type()
|
||||
|
||||
for i := 0; i < src.NumField(); i++ {
|
||||
field := srcType.Field(i)
|
||||
srcField := src.Field(i)
|
||||
dstField := dst.Field(i)
|
||||
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
|
||||
if lopt := field.Tag.Get("lopt"); lopt != "" && field.Tag.Get("comment") != "" {
|
||||
// Leaf field with a cobra flag — copy if the flag was changed
|
||||
if changed(lopt) {
|
||||
dstField.Set(srcField)
|
||||
}
|
||||
} else if field.Anonymous {
|
||||
// Embedded struct (e.g., Profile in Node)
|
||||
recursiveUpdateFrom(dstField, srcField, changed)
|
||||
} else if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
|
||||
// Pointer-to-struct (e.g., *IpmiConf, *KernelConf)
|
||||
if srcField.IsNil() {
|
||||
continue
|
||||
}
|
||||
if dstField.IsNil() {
|
||||
dstField.Set(reflect.New(field.Type.Elem()))
|
||||
}
|
||||
recursiveUpdateFrom(dstField.Elem(), srcField.Elem(), changed)
|
||||
} else if field.Type.Kind() == reflect.Struct {
|
||||
// Direct struct
|
||||
recursiveUpdateFrom(dstField, srcField, changed)
|
||||
} else if field.Type.Kind() == reflect.Map {
|
||||
// Map fields (e.g., NetDevs, Disks, FileSystems)
|
||||
switch field.Type.Elem().Kind() {
|
||||
case reflect.String, reflect.Interface:
|
||||
// Tags map[string]string — handled separately by tag add/del operations
|
||||
continue
|
||||
case reflect.Pointer:
|
||||
// map[string]*Struct — update entries from src into dst
|
||||
if srcField.IsNil() {
|
||||
continue
|
||||
}
|
||||
if dstField.IsNil() {
|
||||
dstField.Set(reflect.MakeMap(field.Type))
|
||||
}
|
||||
for _, key := range srcField.MapKeys() {
|
||||
srcEntry := srcField.MapIndex(key)
|
||||
if srcEntry.IsNil() {
|
||||
continue
|
||||
}
|
||||
dstEntry := dstField.MapIndex(key)
|
||||
if !dstEntry.IsValid() || dstEntry.IsNil() {
|
||||
// Create new entry in dst
|
||||
dstEntry = reflect.New(field.Type.Elem().Elem())
|
||||
dstField.SetMapIndex(key, dstEntry)
|
||||
}
|
||||
// Map values are not addressable, so we need to work with a copy
|
||||
tmpDst := reflect.New(field.Type.Elem().Elem()).Elem()
|
||||
tmpDst.Set(dstEntry.Elem())
|
||||
recursiveUpdateFrom(tmpDst, srcEntry.Elem(), changed)
|
||||
dstField.SetMapIndex(key, tmpDst.Addr())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user