updated test for yaml/v3
Signed-off-by: Christian Goll <cgoll@suse.com>
This commit is contained in:
committed by
Jonathon Anderson
parent
9ba1aa8d63
commit
342236b9b9
@@ -1,9 +1,10 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"dario.cat/mergo"
|
||||
@@ -58,15 +59,28 @@ func Parse(data []byte) (nodeList NodeYaml, err error) {
|
||||
Get a node with its merged in nodes
|
||||
*/
|
||||
func (config *NodeYaml) GetNode(id string) (node NodeConf, err error) {
|
||||
|
||||
if _, ok := config.nodes[id]; !ok {
|
||||
return node, ErrNotFound
|
||||
}
|
||||
node = *config.nodes[id]
|
||||
node = EmptyNode()
|
||||
// create a deep copy of the node, as otherwise pointers
|
||||
// and not their contents is merged
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err = enc.Encode(config.nodes[id])
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
err = dec.Decode(&node)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
for _, p := range cleanList(config.nodes[id].Profiles) {
|
||||
includedProfile, err := config.GetProfile(p)
|
||||
if err != nil {
|
||||
return node, err
|
||||
wwlog.Warn("profile not found: %s", p)
|
||||
continue
|
||||
}
|
||||
err = mergo.Merge(&node.ProfileConf, includedProfile, mergo.WithAppendSlice)
|
||||
if err != nil {
|
||||
@@ -76,9 +90,6 @@ func (config *NodeYaml) GetNode(id string) (node NodeConf, err error) {
|
||||
// err = mergo.Merge(&node, config.nodes[id], mergo.WithOverride, mergo.WithoutDereference)
|
||||
// err = mergo.Merge(&node, config.nodes[id], mergo.WithOverride)
|
||||
// err = mergo.Merge(&node, config.nodes[id])
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
/*
|
||||
node = EmptyNode()
|
||||
var buf bytes.Buffer
|
||||
@@ -266,7 +277,7 @@ func (config *NodeYaml) ListAllProfiles() []string {
|
||||
|
||||
/*
|
||||
FindDiscoverableNode returns the first discoverable node and an
|
||||
interface to associate with the discovered interface. If the node has
|
||||
interface to associate with the discovered interface. If the nodUNDEFe has
|
||||
a primary interface, it is returned; otherwise, the first interface
|
||||
without a hardware address is returned.
|
||||
|
||||
@@ -277,7 +288,7 @@ func (config *NodeYaml) FindDiscoverableNode() (NodeConf, string, error) {
|
||||
nodes, _ := config.FindAllNodes()
|
||||
|
||||
for _, node := range nodes {
|
||||
if !node.Discoverable {
|
||||
if !(node.Discoverable.Bool()) {
|
||||
continue
|
||||
}
|
||||
if _, ok := node.NetDevs[node.PrimaryNetDev]; ok {
|
||||
@@ -292,30 +303,3 @@ func (config *NodeYaml) FindDiscoverableNode() (NodeConf, string, error) {
|
||||
|
||||
return EmptyNode(), "", ErrNoUnconfigured
|
||||
}
|
||||
func appendStringSlices(src, dst any) {
|
||||
//srcType := reflect.TypeOf(src)
|
||||
srcVal := reflect.ValueOf(src)
|
||||
dstType := reflect.TypeOf(dst)
|
||||
dstVal := reflect.ValueOf(dst)
|
||||
for i := 0; i < dstType.Elem().NumField(); i++ {
|
||||
// wwlog.Debug("dstType.Name: %s", dstType.Elem().Field(i).Name)
|
||||
srcValField := srcVal.Elem().FieldByName(dstType.Elem().Field(i).Name)
|
||||
if !srcValField.IsZero() {
|
||||
if srcValField.Type() == reflect.TypeOf([]string{}) {
|
||||
// wwlog.Debug("dstType.Name: %s", dstType.Elem().Field(i).Name)
|
||||
if srcValField.Len() > 0 {
|
||||
// wwlog.Debug("srcValField.Len(): %d", srcValField.Len())
|
||||
for _, elem := range srcValField.Interface().([]string) {
|
||||
dstVal.Elem().Field(i).Set(reflect.Append(dstVal.Elem().Field(i), reflect.ValueOf(elem)))
|
||||
wwlog.Debug("elem: %s", elem)
|
||||
}
|
||||
lst := dstVal.Elem().Field(i).Addr().Interface().(*[]string)
|
||||
*lst = cleanList(*lst)
|
||||
}
|
||||
|
||||
}
|
||||
} else if srcValField.Type().Kind() == reflect.Ptr {
|
||||
appendStringSlices(srcValField, dstVal.Elem().Field(i).Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,21 @@ package node
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v2"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func newConstructorPrimaryNetworkTest() NodeYaml {
|
||||
func newConstructorPrimaryNetworkTest(t *testing.T) NodeYaml {
|
||||
var data = `
|
||||
nodeprofiles:
|
||||
default:
|
||||
comment: This profile is automatically included for each node
|
||||
overrideprofile:
|
||||
network devices:
|
||||
override:
|
||||
device: ib0
|
||||
type: profile
|
||||
nodes:
|
||||
test_node1:
|
||||
network devices:
|
||||
@@ -38,15 +43,25 @@ nodes:
|
||||
device: eth0
|
||||
net1:
|
||||
device: eth1
|
||||
test_node5:
|
||||
profiles:
|
||||
- overrideprofile
|
||||
test_node6:
|
||||
profiles:
|
||||
- overrideprofile
|
||||
network devices:
|
||||
override:
|
||||
device: ib1
|
||||
`
|
||||
var ret NodeYaml
|
||||
_ = yaml.Unmarshal([]byte(data), &ret)
|
||||
err := yaml.Unmarshal([]byte(data), &ret)
|
||||
assert.NoError(t, err)
|
||||
return ret
|
||||
}
|
||||
|
||||
func Test_Primary_Network(t *testing.T) {
|
||||
wwlog.SetLogLevel(wwlog.DEBUG)
|
||||
c := newConstructorPrimaryNetworkTest()
|
||||
//wwlog.SetLogLevel(wwlog.DEBUG)
|
||||
c := newConstructorPrimaryNetworkTest(t)
|
||||
test_node1, err := c.GetNode("test_node1")
|
||||
assert.NoError(t, err)
|
||||
test_node2, err := c.GetNode("test_node2")
|
||||
@@ -55,6 +70,10 @@ func Test_Primary_Network(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
test_node4, err := c.GetNode("test_node4")
|
||||
assert.NoError(t, err)
|
||||
test_node5, err := c.GetNode("test_node5")
|
||||
assert.NoError(t, err)
|
||||
test_node6, err := c.GetNode("test_node6")
|
||||
assert.NoError(t, err)
|
||||
t.Run("Primary network with one network, nothing set", func(t *testing.T) {
|
||||
if test_node1.PrimaryNetDev != "net0" {
|
||||
t.Errorf("primary network isn't net0 but: %s", test_node1.PrimaryNetDev)
|
||||
@@ -92,6 +111,14 @@ func Test_Primary_Network(t *testing.T) {
|
||||
t.Errorf("node primary flag isn't set")
|
||||
}
|
||||
})
|
||||
t.Run("defined in profile", func(t *testing.T) {
|
||||
assert.Equal(t, test_node5.NetDevs["override"].Device, "ib0")
|
||||
assert.Equal(t, test_node5.NetDevs["override"].Type, "profile")
|
||||
})
|
||||
t.Run("redefined in profile", func(t *testing.T) {
|
||||
assert.Equal(t, test_node6.NetDevs["override"].Device, "ib1")
|
||||
assert.Equal(t, test_node6.NetDevs["override"].Type, "profile")
|
||||
})
|
||||
}
|
||||
|
||||
var findDiscoverableNodeTests = []struct {
|
||||
@@ -110,16 +137,16 @@ var findDiscoverableNodeTests = []struct {
|
||||
func Test_FindDiscoverableNode(t *testing.T) {
|
||||
for _, tt := range findDiscoverableNodeTests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
config := newConstructorPrimaryNetworkTest()
|
||||
config := newConstructorPrimaryNetworkTest(t)
|
||||
for _, node := range tt.discoverable_nodes {
|
||||
config.Nodes[node].Discoverable = true
|
||||
config.nodes[node].Discoverable = "true"
|
||||
}
|
||||
discovered_node, discovered_interface, err := config.FindDiscoverableNode()
|
||||
if !tt.succeed {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.discovered_node, discovered_node)
|
||||
assert.Equal(t, tt.discovered_node, discovered_node.Id())
|
||||
assert.Equal(t, tt.discovered_interface, discovered_interface)
|
||||
}
|
||||
})
|
||||
@@ -147,8 +174,8 @@ nodes:
|
||||
- profile2
|
||||
node3:
|
||||
runtime overlay:
|
||||
- n1o1
|
||||
- n1o2
|
||||
- n3o1
|
||||
- n3o2
|
||||
profiles:
|
||||
- profile1
|
||||
node4:
|
||||
@@ -170,27 +197,24 @@ nodes:
|
||||
var ymlSrc NodeYaml
|
||||
err := yaml.Unmarshal([]byte(nodesconf), &ymlSrc)
|
||||
assert.NoError(err)
|
||||
wwlog.SetLogLevel(wwlog.DEBUG)
|
||||
nodes, err := ymlSrc.FindAllNodes()
|
||||
assert.NoError(err)
|
||||
nodemap := make(map[string]*NodeInfo)
|
||||
nodemap := make(map[string]*NodeConf)
|
||||
for i := range nodes {
|
||||
nodemap[nodes[i].Id.Get()] = &nodes[i]
|
||||
nodemap[nodes[i].Id()] = &nodes[i]
|
||||
}
|
||||
assert.Contains(nodemap, "node1")
|
||||
assert.ElementsMatch(nodemap["node1"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p1o2"})
|
||||
assert.Equal("p1o1,p1o2", nodemap["node1"].RuntimeOverlay.Print())
|
||||
|
||||
assert.ElementsMatch(nodemap["node1"].RuntimeOverlay, []string{"p1o1", "p1o2"})
|
||||
assert.Contains(nodemap, "node2")
|
||||
assert.ElementsMatch(nodemap["node2"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p1o2", "p2o1", "p2o2"})
|
||||
assert.Equal("p1o1,p1o2,p2o1,p2o2", nodemap["node2"].RuntimeOverlay.Print())
|
||||
assert.ElementsMatch(nodemap["node2"].RuntimeOverlay, []string{"p1o1", "p1o2", "p2o1", "p2o2"})
|
||||
assert.Contains(nodemap, "node3")
|
||||
assert.ElementsMatch(nodemap["node3"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p1o2", "n1o1", "n1o2"})
|
||||
assert.Equal("p1o1,p1o2,n1o1,n1o2", nodemap["node3"].RuntimeOverlay.Print())
|
||||
assert.ElementsMatch(nodemap["node3"].RuntimeOverlay, []string{"p1o1", "p1o2", "n3o1", "n3o2"})
|
||||
assert.Contains(nodemap, "node4")
|
||||
assert.ElementsMatch(nodemap["node4"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p1o2", "p2o1", "p2o2", "n1o1", "n1o2"})
|
||||
assert.Equal("p1o1,p1o2,p2o1,p2o2,n1o1,n1o2", nodemap["node4"].RuntimeOverlay.Print())
|
||||
assert.ElementsMatch(nodemap["node4"].RuntimeOverlay, []string{"p1o1", "p1o2", "p2o1", "p2o2", "n1o1", "n1o2"})
|
||||
assert.Contains(nodemap, "node5")
|
||||
assert.ElementsMatch(nodemap["node5"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p2o1", "p2o2", "n1o1"})
|
||||
assert.Equal("p1o1,p2o1,p2o2,n1o1 ~{p1o2}", nodemap["node5"].RuntimeOverlay.Print())
|
||||
assert.ElementsMatch(nodemap["node5"].RuntimeOverlay, []string{"p1o1", "p1o2", "~p1o2", "p2o1", "p2o2", "n1o1"})
|
||||
}
|
||||
|
||||
func Test_negated_list(t *testing.T) {
|
||||
|
||||
@@ -18,9 +18,6 @@ Structure of which goes to disk
|
||||
*/
|
||||
type NodeYaml struct {
|
||||
WWInternal int `yaml:"WW_INTERNAL,omitempty" json:"WW_INTERNAL,omitempty"`
|
||||
NodeProfiles map[string]*ProfileConf
|
||||
Nodes map[string]*NodeConf
|
||||
WWInternal int `yaml:"WW_INTERNAL"`
|
||||
nodeProfiles map[string]*ProfileConf
|
||||
nodes map[string]*NodeConf
|
||||
}
|
||||
@@ -32,7 +29,7 @@ type NodeConf struct {
|
||||
id string
|
||||
valid bool // Is set true, if called by the constructor
|
||||
// exported values
|
||||
Discoverable bool `yaml:"discoverable,omitempty" lopt:"discoverable" sopt:"e" comment:"Make discoverable in given network (true/false)"`
|
||||
Discoverable wwtype.WWbool `yaml:"discoverable,omitempty" lopt:"discoverable" sopt:"e" comment:"Make discoverable in given network (true/false)"`
|
||||
AssetKey string `yaml:"asset key,omitempty" lopt:"asset" comment:"Set the node's Asset tag (key)"`
|
||||
Profiles []string `yaml:"profiles,omitempty" lopt:"profile" sopt:"P" comment:"Set the node's profile members (comma separated)"`
|
||||
ProfileConf `yaml:"-,inline"` // include all values set in the profile, but inline them in yaml output if these are part of NodeConf
|
||||
|
||||
@@ -83,8 +83,9 @@ Helper function to create the different PersistentFlags() for different types.
|
||||
*/
|
||||
func createFlags(baseCmd *cobra.Command,
|
||||
myType reflect.StructField, myVal *reflect.Value) {
|
||||
var wwbool wwtype.WWbool
|
||||
if myType.Tag.Get("lopt") != "" {
|
||||
if myType.Type.Kind() == reflect.String {
|
||||
if myType.Type == reflect.TypeOf("") {
|
||||
ptr := myVal.Addr().Interface().(*string)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().StringVarP(ptr,
|
||||
@@ -128,21 +129,6 @@ func createFlags(baseCmd *cobra.Command,
|
||||
map[string]string{}, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
} else if myType.Type == reflect.TypeOf(true) {
|
||||
ptr := myVal.Addr().Interface().(*bool)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().BoolVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
false, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().BoolVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
false, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
|
||||
} else if myType.Type == reflect.TypeOf(true) {
|
||||
ptr := myVal.Addr().Interface().(*bool)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
@@ -185,7 +171,7 @@ func createFlags(baseCmd *cobra.Command,
|
||||
net.IPMask{}, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
} else if myType.Type == reflect.TypeOf(wwtype.WWbool{}) {
|
||||
} else if myType.Type == reflect.TypeOf(wwbool) {
|
||||
ptr := myVal.Addr().Interface().(*wwtype.WWbool)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().VarP(ptr,
|
||||
|
||||
@@ -3,7 +3,7 @@ package node
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -16,34 +17,63 @@ type NodeFields struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
type fieldMap map[string]NodeFields
|
||||
func (f *NodeFields) Set(src, val string) {
|
||||
if val == "" {
|
||||
return
|
||||
}
|
||||
if f.Value == "" {
|
||||
f.Value = val
|
||||
f.Source = src
|
||||
} else if f.Source != "" {
|
||||
f.Value = val
|
||||
if src == "" {
|
||||
f.Source = "SUPERSEDED"
|
||||
} else {
|
||||
f.Source = src
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type fieldMap map[string]*NodeFields
|
||||
|
||||
/*
|
||||
Get all the info out of NodeConf. If emptyFields is set true, all fields are shown not only the ones with effective values
|
||||
*/
|
||||
func (nodeYml *NodeYaml) GetFields(node NodeConf, emptyFields bool) (output []NodeFields) {
|
||||
func (nodeYml *NodeYaml) GetFields(node NodeConf) (output []NodeFields) {
|
||||
nodeMap := make(fieldMap)
|
||||
for _, p := range node.Profiles {
|
||||
if profile, ok := nodeYml.nodeProfiles[p]; ok {
|
||||
nodeMap.recursiveFields(profile, emptyFields, "", p)
|
||||
nodeMap.recursiveFields(profile, "", p)
|
||||
}
|
||||
}
|
||||
nodeMap.recursiveFields(&node, emptyFields, "", "")
|
||||
rawNode, _ := nodeYml.GetNodeOnlyPtr(node.id)
|
||||
nodeMap.recursiveFields(rawNode, "", "")
|
||||
for _, elem := range nodeMap {
|
||||
output = append(output, elem)
|
||||
if elem.Value != "" {
|
||||
output = append(output, *elem)
|
||||
}
|
||||
}
|
||||
sort.Slice(output, func(i, j int) bool {
|
||||
return output[i].Field < output[j].Field
|
||||
})
|
||||
return output
|
||||
}
|
||||
|
||||
/*
|
||||
Get all the info out of ProfileConf. If emptyFields is set true, all fields are shown not only the ones with effective values
|
||||
*/
|
||||
func (nodeYml *NodeYaml) GetFieldsProfile(profile ProfileConf, emptyFields bool) (output []NodeFields) {
|
||||
func (nodeYml *NodeYaml) GetFieldsProfile(profile ProfileConf) (output []NodeFields) {
|
||||
profileMap := make(fieldMap)
|
||||
profileMap.recursiveFields(&profile, emptyFields, "", "")
|
||||
profileMap.recursiveFields(&profile, "", "")
|
||||
for _, elem := range profileMap {
|
||||
output = append(output, elem)
|
||||
if elem.Value != "" {
|
||||
output = append(output, *elem)
|
||||
}
|
||||
}
|
||||
sort.Slice(output, func(i, j int) bool {
|
||||
return output[i].Field < output[j].Field
|
||||
})
|
||||
return output
|
||||
}
|
||||
|
||||
@@ -51,52 +81,68 @@ func (nodeYml *NodeYaml) GetFieldsProfile(profile ProfileConf, emptyFields bool)
|
||||
Internal function which travels through all fields of a NodeConf and for this
|
||||
reason needs to be called via interface{}
|
||||
*/
|
||||
func (fieldMap *fieldMap) recursiveFields(obj interface{}, emptyFields bool, prefix string, source string) {
|
||||
func (fieldMap fieldMap) recursiveFields(obj interface{}, prefix string, source string) {
|
||||
valObj := reflect.ValueOf(obj)
|
||||
typeObj := reflect.TypeOf(obj)
|
||||
if valObj.IsNil() {
|
||||
return
|
||||
}
|
||||
for i := 0; i < typeObj.Elem().NumField(); i++ {
|
||||
fmt.Printf("name: %s\n", typeObj.Elem().Field(i).Name)
|
||||
if valObj.Elem().Field(i).IsValid() {
|
||||
if !typeObj.Elem().Field(i).IsExported() {
|
||||
continue
|
||||
}
|
||||
if valObj.Elem().Field(i).Kind() == reflect.String && valObj.Elem().Field(i).String() != "" {
|
||||
fmt.Printf("string: %s\n", valObj.Elem().Field(i).String())
|
||||
(*fieldMap)[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name,
|
||||
Source: source,
|
||||
Value: valObj.Elem().Field(i).String(),
|
||||
}
|
||||
} else if emptyFields {
|
||||
(*fieldMap)[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name + "[]",
|
||||
Source: source,
|
||||
}
|
||||
} else if typeObj.Elem().Field(i).Type == reflect.TypeOf([]string{}) && valObj.Elem().Field(i).Len() != 0 {
|
||||
vals := (valObj.Elem().Field(i).Interface()).([]string)
|
||||
(*fieldMap)[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name,
|
||||
Source: source,
|
||||
Value: strings.Join(vals, ","),
|
||||
}
|
||||
} else if typeObj.Elem().Field(i).Type == reflect.TypeOf(net.IP{}) {
|
||||
val := (valObj.Elem().Field(i).Interface()).(net.IP)
|
||||
(*fieldMap)[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name,
|
||||
Source: source,
|
||||
Value: val.String(),
|
||||
}
|
||||
} else if typeObj.Elem().Field(i).Type.Kind() == reflect.Map {
|
||||
switch typeObj.Elem().Field(i).Type.Kind() {
|
||||
case reflect.Map:
|
||||
mapIter := valObj.Elem().Field(i).MapRange()
|
||||
for mapIter.Next() {
|
||||
fieldMap.recursiveFields(mapIter.Value().Interface(),
|
||||
emptyFields, prefix+typeObj.Elem().Field(i).Name+"["+mapIter.Key().String()+"].", source)
|
||||
fieldMap.recursiveFields(mapIter.Value().Interface(), prefix+typeObj.Elem().Field(i).Name+"["+mapIter.Key().String()+"].", source)
|
||||
}
|
||||
if valObj.Elem().Field(i).Len() == 0 && emptyFields {
|
||||
(*fieldMap)[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
if valObj.Elem().Field(i).Len() == 0 {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name] = &NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name + "[]",
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
fieldMap.recursiveFields(valObj.Elem().Field(i).Addr().Interface(), "", source)
|
||||
case reflect.Ptr:
|
||||
if valObj.Elem().Field(i).Addr().IsValid() {
|
||||
fieldMap.recursiveFields(valObj.Elem().Field(i).Interface(), prefix+typeObj.Elem().Field(i).Name+".", source)
|
||||
}
|
||||
default:
|
||||
if _, ok := fieldMap[prefix+typeObj.Elem().Field(i).Name]; !ok {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name] = &NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name,
|
||||
Source: source,
|
||||
}
|
||||
}
|
||||
|
||||
switch typeObj.Elem().Field(i).Type {
|
||||
case reflect.TypeOf([]string{}):
|
||||
vals := (valObj.Elem().Field(i).Interface()).([]string)
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name] = &NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name,
|
||||
Source: source,
|
||||
Value: strings.Join(vals, ","),
|
||||
}
|
||||
case reflect.TypeOf(net.IP{}):
|
||||
val := (valObj.Elem().Field(i).Interface()).(net.IP)
|
||||
if val != nil {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name].Set(source, val.String())
|
||||
}
|
||||
case reflect.TypeOf(net.IPMask{}):
|
||||
val := (valObj.Elem().Field(i).Interface()).(net.IPMask)
|
||||
if val != nil {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name].Set(source, val.String())
|
||||
}
|
||||
case reflect.TypeOf(true):
|
||||
val := (valObj.Elem().Field(i).Interface()).(bool)
|
||||
if val {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name].Set(source, strconv.FormatBool(val))
|
||||
}
|
||||
default:
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name].Set(source, valObj.Elem().Field(i).String())
|
||||
}
|
||||
|
||||
} /*else if typeObj.Elem().Field(i).Type.Kind() == reflect.Ptr {
|
||||
fieldMap.recursiveFields(valObj.Elem().Field(i).Interface(), emptyFields, prefix+typeObj.Elem().Field(i).Name+".", source)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"net"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@@ -10,7 +8,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/util"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwtype"
|
||||
)
|
||||
|
||||
type sortByName []NodeConf
|
||||
@@ -134,51 +131,92 @@ func (info *ProfileConf) Flatten() {
|
||||
recursiveFlatten(info)
|
||||
}
|
||||
|
||||
// abstract flatten
|
||||
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
|
||||
setToNil := true
|
||||
nestedType := reflect.TypeOf(confVal.Elem().Field(j).Interface())
|
||||
nestedVal := reflect.ValueOf(confVal.Elem().Field(j).Interface())
|
||||
for i := 0; i < nestedType.Elem().NumField(); i++ {
|
||||
// wwlog.Debug("checking %s", nestedType.Elem().Field(i).Type.String())
|
||||
if nestedType.Elem().Field(i).Type.Kind() == reflect.String &&
|
||||
nestedVal.Elem().Field(i).Interface().(string) != "" &&
|
||||
nestedVal.Elem().Field(i).Interface().(string) != undef {
|
||||
setToNil = false
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf([]string{}) &&
|
||||
len(nestedVal.Elem().Field(i).Interface().([]string)) != 0 {
|
||||
setToNil = false
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf(map[string]string{}) &&
|
||||
len(nestedVal.Elem().Field(i).Interface().(map[string]string)) != 0 {
|
||||
setToNil = false
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf(net.IP{}) {
|
||||
val := nestedVal.Elem().Field(i).Interface().(net.IP)
|
||||
if len(val) != 0 && !val.IsUnspecified() {
|
||||
setToNil = false
|
||||
}
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf(wwtype.WWbool{}) {
|
||||
val := nestedVal.Elem().Field(i).Interface().(wwtype.WWbool)
|
||||
if !val.IsZero() {
|
||||
setToNil = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if setToNil {
|
||||
confVal.Elem().Field(j).Set(reflect.Zero(confVal.Elem().Field(j).Type()))
|
||||
}
|
||||
} else if confType.Elem().Field(j).Anonymous {
|
||||
recursiveFlatten(confVal.Elem().Field(j).Addr().Interface())
|
||||
} else if confType.Elem().Field(j).IsExported() && confType.Elem().Field(j).Type.Kind() == reflect.String {
|
||||
if confVal.Elem().Field(j).Interface().(string) == undef {
|
||||
confVal.Elem().Field(j).SetString("")
|
||||
func recursiveFlatten(obj interface{}) (hasContent bool) {
|
||||
valObj := reflect.ValueOf(obj)
|
||||
typeObj := reflect.TypeOf(obj)
|
||||
hasContent = false
|
||||
if valObj.IsNil() {
|
||||
return
|
||||
}
|
||||
for i := 0; i < typeObj.Elem().NumField(); i++ {
|
||||
if valObj.Elem().Field(i).IsValid() {
|
||||
if !typeObj.Elem().Field(i).IsExported() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
switch typeObj.Elem().Field(i).Type.Kind() {
|
||||
case reflect.Map:
|
||||
mapIter := valObj.Elem().Field(i).MapRange()
|
||||
for mapIter.Next() {
|
||||
if mapIter.Value().Kind() == reflect.String {
|
||||
if mapIter.Value().String() != "" {
|
||||
// fmt.Println("map")
|
||||
hasContent = true
|
||||
}
|
||||
} else {
|
||||
ret := recursiveFlatten(mapIter.Value().Interface())
|
||||
hasContent = ret || hasContent
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
if valObj.Elem().Field(i).Addr().IsValid() {
|
||||
ret := recursiveFlatten((valObj.Elem().Field(i).Interface()))
|
||||
if !ret {
|
||||
valObj.Elem().Field(i).Set(reflect.Zero(valObj.Elem().Field(i).Type()))
|
||||
}
|
||||
hasContent = ret || hasContent
|
||||
|
||||
}
|
||||
case reflect.Struct:
|
||||
ret := recursiveFlatten((valObj.Elem().Field(i).Addr().Interface()))
|
||||
hasContent = ret || hasContent
|
||||
case reflect.Slice:
|
||||
if typeObj.Elem().Field(i).Type == reflect.TypeOf([]string{}) {
|
||||
del := false
|
||||
for _, elem := range (valObj.Elem().Field(i).Interface()).([]string) {
|
||||
if strings.EqualFold(elem, undef) {
|
||||
del = true
|
||||
}
|
||||
}
|
||||
if del {
|
||||
valObj.Elem().Field(i).SetLen(0)
|
||||
}
|
||||
}
|
||||
if valObj.Elem().Field(i).Len() > 0 {
|
||||
hasContent = true
|
||||
}
|
||||
case reflect.String:
|
||||
if strings.EqualFold(valObj.Elem().Field(i).String(), undef) {
|
||||
valObj.Elem().Field(i).SetString("")
|
||||
}
|
||||
if valObj.Elem().Field(i).String() != "" {
|
||||
hasContent = true
|
||||
}
|
||||
case reflect.Bool:
|
||||
val := valObj.Elem().Field(i).Interface().(bool)
|
||||
hasContent = hasContent || val
|
||||
default:
|
||||
switch valObj.Elem().Field(i).Type() {
|
||||
case reflect.TypeOf(net.IP{}):
|
||||
val := valObj.Elem().Field(i).Interface().(net.IP)
|
||||
if len(val) != 0 && !val.IsUnspecified() {
|
||||
hasContent = true
|
||||
}
|
||||
case reflect.TypeOf(net.IPMask{}):
|
||||
val := valObj.Elem().Field(i).Interface().(net.IPMask)
|
||||
if len(val) != 0 {
|
||||
// fmt.Println("Mask")
|
||||
hasContent = true
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
if !hasContent {
|
||||
valObj.Elem().Field(i).Set(reflect.Zero(valObj.Elem().Field(i).Type()))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -308,38 +346,3 @@ func cleanList(list []string) (ret []string) {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Clone (deep copy) a node via gob
|
||||
func (src *NodeConf) Clone() (dst NodeConf, err error) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err = enc.Encode(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = dec.Decode(&dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dst.id = src.id
|
||||
dst.valid = src.valid
|
||||
return
|
||||
}
|
||||
|
||||
// Clone (deep copy) a node via gob
|
||||
func (src ProfileConf) Clone() (dst ProfileConf, err error) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err = enc.Encode(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = dec.Decode(&dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dst.id = src.id
|
||||
return
|
||||
}
|
||||
|
||||
@@ -112,12 +112,12 @@ func (config *NodeYaml) Persist() error {
|
||||
out, dumpErr := config.Dump()
|
||||
if dumpErr != nil {
|
||||
wwlog.Error("%s", dumpErr)
|
||||
os.Exit(1)
|
||||
return dumpErr
|
||||
}
|
||||
file, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644)
|
||||
if err != nil {
|
||||
wwlog.Error("%s", err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = file.WriteString(string(out))
|
||||
|
||||
@@ -3,9 +3,9 @@ package node
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v2"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func NewUtilTestNode() (NodeYaml, error) {
|
||||
|
||||
Reference in New Issue
Block a user