Fix handling of net.IP during node merge

Because net.IP is derived from []byte, it was being handled by mergo as a
slice. This adds an additional transformer that handles net.IP specifically,
and also watches for this type in field tracking.

Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
Jonathon Anderson
2024-12-28 13:07:45 -07:00
parent dae8d004b3
commit b50fb3f3ef
2 changed files with 140 additions and 5 deletions

View File

@@ -1,6 +1,7 @@
package node
import (
"net"
"reflect"
"strings"
@@ -58,10 +59,18 @@ func (config *NodesYaml) appendProfileProfiles(profiles []string, id string) []s
return profiles
}
type InterfaceTransformer struct{}
type Transformer struct{}
func (t InterfaceTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
if typ.Kind() == reflect.Interface {
func (t Transformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
if typ == reflect.TypeOf(net.IP{}) {
return func(dst, src reflect.Value) error {
if !src.IsValid() || !src.CanSet() {
return nil
}
dst.Set(src)
return nil
}
} else if typ.Kind() == reflect.Interface {
return func(dst, src reflect.Value) error {
if !src.IsValid() || src.IsZero() {
return nil
@@ -159,7 +168,7 @@ func (config *NodesYaml) MergeNode(id string) (node Node, fields fieldMap, err e
//
// 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 {
if err := mergo.Merge(dest, src, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(Transformer{})); err != nil {
return err
}
@@ -170,7 +179,9 @@ func merge(dest, src interface{}, fields fieldMap, srcName string, multipleSrcNa
if prevSource != "" {
switch value.Kind() {
case reflect.Slice:
srcName = strings.Join([]string{prevSource, multipleSrcName}, ",")
if value.Type() != reflect.TypeOf(net.IP{}) {
srcName = strings.Join([]string{prevSource, multipleSrcName}, ",")
}
case reflect.Interface:
if _, ok := value.Interface().([]interface{}); ok {
srcName = strings.Join([]string{prevSource, multipleSrcName}, ",")

View File

@@ -1,6 +1,8 @@
package node
import (
"fmt"
"net"
"testing"
"github.com/stretchr/testify/assert"
@@ -823,6 +825,128 @@ nodes:
}
}
func Test_MergeNodeIP(t *testing.T) {
tests := map[string]struct {
nodesConf string
node string
netdev string
ipaddr net.IP
ipaddrStr string
source string
}{
"empty": {
nodesConf: `
nodes:
n1:
network devices:
default: {}
`,
node: "n1",
netdev: "default",
ipaddr: nil,
ipaddrStr: "",
source: "",
},
"single node value": {
nodesConf: `
nodes:
n1:
network devices:
default:
ipaddr: 192.168.0.1
`,
node: "n1",
netdev: "default",
ipaddr: net.IPv4(192, 168, 0, 1),
ipaddrStr: "192.168.0.1",
source: "",
},
"profile value": {
nodesConf: `
nodeprofiles:
p1:
network devices:
default:
ipaddr: 192.168.0.1
nodes:
n1:
profiles:
- p1
`,
node: "n1",
netdev: "default",
ipaddr: net.IPv4(192, 168, 0, 1),
ipaddrStr: "192.168.0.1",
source: "p1",
},
"multi-profile value": {
nodesConf: `
nodeprofiles:
p1:
network devices:
default:
ipaddr: 192.168.0.1
p2:
network devices:
default:
ipaddr: 192.168.1.1
nodes:
n1:
profiles:
- p1
- p2
`,
node: "n1",
netdev: "default",
ipaddr: net.IPv4(192, 168, 1, 1),
ipaddrStr: "192.168.1.1",
source: "p2",
},
"superseded": {
nodesConf: `
nodeprofiles:
p1:
network devices:
default:
ipaddr: 192.168.0.1
p2:
network devices:
default:
ipaddr: 192.168.1.1
nodes:
n1:
profiles:
- p1
- p2
network devices:
default:
ipaddr: 192.168.2.1
`,
node: "n1",
netdev: "default",
ipaddr: net.IPv4(192, 168, 2, 1),
ipaddrStr: "192.168.2.1",
source: "SUPERSEDED",
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
env := testenv.New(t)
defer env.RemoveAll()
env.WriteFile("/etc/warewulf/nodes.conf", tt.nodesConf)
registry, regErr := New()
assert.NoError(t, regErr)
node, fields, mergeErr := registry.MergeNode(tt.node)
assert.NoError(t, mergeErr)
assert.Equal(t, tt.ipaddr, node.NetDevs[tt.netdev].Ipaddr)
assert.Equal(t, tt.ipaddrStr, fields.Value(fmt.Sprintf("NetDevs[%s].Ipaddr", tt.netdev)))
assert.Equal(t, tt.source, fields.Source(fmt.Sprintf("NetDevs[%s].Ipaddr", tt.netdev)))
})
}
}
func Test_MergeNodeKernel(t *testing.T) {
var tests = map[string]struct {
nodesConf string