Merge pull request #1647 from anderbubble/enhanced-field-formatting
Enhancements for node.Field formatting
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -223,33 +223,51 @@ func listReflectedFields(t reflect.Type, v reflect.Value, prefix string) (fields
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// valueStr converts a reflect.Value into a string. For slices, it joins all elements into a comma-separated
|
// valueStr converts a reflect.Value into a string.
|
||||||
// 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.
|
|
||||||
func valueStr(value reflect.Value) (output string) {
|
func valueStr(value reflect.Value) (output string) {
|
||||||
if !value.IsValid() {
|
if !value.IsValid() {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if value.Kind() == reflect.Pointer {
|
if value.Kind() == reflect.Pointer {
|
||||||
if value.IsZero() {
|
if value.IsZero() {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
value = value.Elem()
|
value = value.Elem()
|
||||||
}
|
}
|
||||||
if value.Type() == reflect.TypeOf(net.IP{}) {
|
|
||||||
if !value.IsZero() {
|
switch value.Kind() {
|
||||||
output = fmt.Sprintf("%s", value)
|
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
|
var sliceStrs []string
|
||||||
for i := 0; i < value.Len(); i++ {
|
for i := 0; i < value.Len(); i++ {
|
||||||
sliceStrs = append(sliceStrs, fmt.Sprintf("%v", value.Index(i)))
|
sliceStrs = append(sliceStrs, fmt.Sprintf("%v", value.Index(i)))
|
||||||
}
|
}
|
||||||
output = strings.Join(sliceStrs, ",")
|
return strings.Join(sliceStrs, ",")
|
||||||
} else {
|
|
||||||
output = fmt.Sprintf("%s", value)
|
|
||||||
}
|
}
|
||||||
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
|
// sortValues sorts a slice of reflect.Values. Currently, it only supports sorting string values and
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ func Test_getNestedFieldString(t *testing.T) {
|
|||||||
node string
|
node string
|
||||||
field string
|
field string
|
||||||
value string
|
value string
|
||||||
|
jsonValue string
|
||||||
}{
|
}{
|
||||||
"comment (simple)": {
|
"comment (simple)": {
|
||||||
nodesConf: `
|
nodesConf: `
|
||||||
@@ -67,6 +68,53 @@ nodes:
|
|||||||
field: "NetDevs[default].Tags[tag]",
|
field: "NetDevs[default].Tags[tag]",
|
||||||
value: "n1 netdev 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 {
|
for name, tt := range tests {
|
||||||
@@ -80,7 +128,12 @@ nodes:
|
|||||||
node := registry.Nodes[tt.node]
|
node := registry.Nodes[tt.node]
|
||||||
value, err := getNestedFieldString(node, tt.field)
|
value, err := getNestedFieldString(node, tt.field)
|
||||||
assert.NoError(t, err)
|
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)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,15 +194,16 @@ nodeprofiles:
|
|||||||
|
|
||||||
func Test_MergeNode(t *testing.T) {
|
func Test_MergeNode(t *testing.T) {
|
||||||
var tests = map[string]struct {
|
var tests = map[string]struct {
|
||||||
nodesConf string
|
nodesConf string
|
||||||
node string
|
node string
|
||||||
field string
|
field string
|
||||||
source string
|
source string
|
||||||
value string
|
value string
|
||||||
nodes []string
|
nodes []string
|
||||||
fields []string
|
fields []string
|
||||||
sources []string
|
sources []string
|
||||||
values []string
|
values []string
|
||||||
|
jsonValues []string
|
||||||
}{
|
}{
|
||||||
"node comment": {
|
"node comment": {
|
||||||
nodesConf: `
|
nodesConf: `
|
||||||
@@ -779,10 +780,10 @@ nodes:
|
|||||||
file: /opt
|
file: /opt
|
||||||
vfstype: nfs
|
vfstype: nfs
|
||||||
`,
|
`,
|
||||||
nodes: []string{"n1"},
|
nodes: []string{"n1"},
|
||||||
fields: []string{"Resources[fstab]"},
|
fields: []string{"Resources[fstab]"},
|
||||||
sources: []string{"p1,n1"},
|
sources: []string{"p1,n1"},
|
||||||
values: []string{"[map[file:/home spec:warewulf:/home vfstype:nfs] map[file:/opt spec:warewulf:/opt vfstype:nfs]]"},
|
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])
|
_, fields, _ := registry.MergeNode(tt.nodes[i])
|
||||||
value, valueErr := getNestedFieldString(nodes[i], tt.fields[i])
|
value, valueErr := getNestedFieldString(nodes[i], tt.fields[i])
|
||||||
assert.NoError(t, valueErr)
|
assert.NoError(t, valueErr)
|
||||||
assert.Equal(t, tt.values[i], value)
|
if len(tt.values) > i {
|
||||||
assert.Equal(t, tt.values[i], fields.Value(tt.fields[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]))
|
assert.Equal(t, tt.sources[i], fields.Source(tt.fields[i]))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user