Enhancements for node.Field formatting

- Complex values are now marshalled as json
- Booleans are now represented as truth values

Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
Jonathon Anderson
2025-01-19 10:59:26 -07:00
parent d6d1e37c83
commit a8fab3e844
3 changed files with 106 additions and 28 deletions

View File

@@ -1,8 +1,8 @@
package node
import (
"encoding/json"
"fmt"
"net"
"reflect"
"regexp"
"sort"
@@ -223,33 +223,51 @@ func listReflectedFields(t reflect.Type, v reflect.Value, prefix string) (fields
return
}
// valueStr converts a reflect.Value into a string. For slices, it joins all elements into a comma-separated
// string. For pointers, it returns an empty string if the pointer is nil. For other kinds, it returns the
// formatted string representation of the value.
// valueStr converts a reflect.Value into a string.
func valueStr(value reflect.Value) (output string) {
if !value.IsValid() {
return ""
}
if value.Kind() == reflect.Pointer {
if value.IsZero() {
return ""
}
value = value.Elem()
}
if value.Type() == reflect.TypeOf(net.IP{}) {
if !value.IsZero() {
output = fmt.Sprintf("%s", value)
switch value.Kind() {
case reflect.Ptr, reflect.Interface, reflect.Map, reflect.Slice:
if value.IsNil() {
return ""
}
} else if value.Kind() == reflect.Slice {
}
stringerType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
if value.Type().Implements(stringerType) {
return fmt.Sprintf("%s", value)
}
if value.Type() == reflect.TypeOf([]string{}) {
var sliceStrs []string
for i := 0; i < value.Len(); i++ {
sliceStrs = append(sliceStrs, fmt.Sprintf("%v", value.Index(i)))
}
output = strings.Join(sliceStrs, ",")
} else {
output = fmt.Sprintf("%s", value)
return strings.Join(sliceStrs, ",")
}
return output
switch value.Kind() {
case reflect.String, reflect.Int:
return fmt.Sprintf("%s", value)
case reflect.Bool:
return fmt.Sprintf("%t", value.Bool())
}
if jsonBytes, err := json.Marshal(value.Interface()); err == nil {
return string(jsonBytes)
}
return fmt.Sprintf("%s", value)
}
// sortValues sorts a slice of reflect.Values. Currently, it only supports sorting string values and

View File

@@ -14,6 +14,7 @@ func Test_getNestedFieldString(t *testing.T) {
node string
field string
value string
jsonValue string
}{
"comment (simple)": {
nodesConf: `
@@ -67,6 +68,53 @@ nodes:
field: "NetDevs[default].Tags[tag]",
value: "n1 netdev tag",
},
"boolean value (true)": {
nodesConf: `
nodes:
n1:
discoverable: true`,
node: "n1",
field: "Discoverable",
value: "true",
},
"boolean value (false)": {
nodesConf: `
nodes:
n1:
discoverable: false`,
node: "n1",
field: "Discoverable",
value: "false",
},
"fstab resource": {
nodesConf: `
nodes:
n1:
resources:
fstab:
- file: /home
freq: 0
mntops: defaults
passno: 0
spec: warewulf:/home
vfstype: nfs`,
node: "n1",
field: "Resources[fstab]",
jsonValue: `[{"file":"/home","freq":0,"mntops":"defaults","passno":0,"spec":"warewulf:/home","vfstype":"nfs"}]`,
},
"disk partition": {
nodesConf: `
nodes:
n1:
disks:
/dev/vda:
partitions:
rootfs:
resize: false`,
node: "n1",
field: "Disks[/dev/vda].Partitions[rootfs].Resize",
value: "false",
},
}
for name, tt := range tests {
@@ -80,7 +128,12 @@ nodes:
node := registry.Nodes[tt.node]
value, err := getNestedFieldString(node, tt.field)
assert.NoError(t, err)
assert.Equal(t, tt.value, value)
if tt.value != "" {
assert.Equal(t, tt.value, value)
}
if tt.jsonValue != "" {
assert.JSONEq(t, tt.jsonValue, value)
}
})
}
}

View File

@@ -194,15 +194,16 @@ nodeprofiles:
func Test_MergeNode(t *testing.T) {
var tests = map[string]struct {
nodesConf string
node string
field string
source string
value string
nodes []string
fields []string
sources []string
values []string
nodesConf string
node string
field string
source string
value string
nodes []string
fields []string
sources []string
values []string
jsonValues []string
}{
"node comment": {
nodesConf: `
@@ -779,10 +780,10 @@ nodes:
file: /opt
vfstype: nfs
`,
nodes: []string{"n1"},
fields: []string{"Resources[fstab]"},
sources: []string{"p1,n1"},
values: []string{"[map[file:/home spec:warewulf:/home vfstype:nfs] map[file:/opt spec:warewulf:/opt vfstype:nfs]]"},
nodes: []string{"n1"},
fields: []string{"Resources[fstab]"},
sources: []string{"p1,n1"},
jsonValues: []string{`[{"file":"/home","spec":"warewulf:/home","vfstype":"nfs"},{"file":"/opt","spec":"warewulf:/opt","vfstype":"nfs"}]`},
},
}
@@ -817,8 +818,14 @@ nodes:
_, fields, _ := registry.MergeNode(tt.nodes[i])
value, valueErr := getNestedFieldString(nodes[i], tt.fields[i])
assert.NoError(t, valueErr)
assert.Equal(t, tt.values[i], value)
assert.Equal(t, tt.values[i], fields.Value(tt.fields[i]))
if len(tt.values) > i {
assert.Equal(t, tt.values[i], value)
assert.Equal(t, tt.values[i], fields.Value(tt.fields[i]))
}
if len(tt.jsonValues) > i {
assert.JSONEq(t, tt.jsonValues[i], value)
assert.Equal(t, tt.jsonValues[i], fields.Value(tt.fields[i]))
}
assert.Equal(t, tt.sources[i], fields.Source(tt.fields[i]))
}
})