diff --git a/CHANGELOG.md b/CHANGELOG.md index 44acc838..49f43138 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,7 +51,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Added a link to an example SELinux-enabled node image in documentation. #1305 - Refine error handling for `wwctl configure`. #1273 - Updated dracut guidance for building initramfs. #1369 -- Preferred profile overlay value over node one for node's `SystemOverlay` entry. #1259 + +### Fixed + +- Fixed application of node overlays such that they override overlapping files from profile overlays. #1259 ## v4.5.6, 2024-08-05 diff --git a/internal/app/wwctl/node/list/main_test.go b/internal/app/wwctl/node/list/main_test.go index 002350e7..2d043e4f 100644 --- a/internal/app/wwctl/node/list/main_test.go +++ b/internal/app/wwctl/node/list/main_test.go @@ -4,7 +4,7 @@ import ( "bytes" "io" "os" - "strings" + "regexp" "testing" "github.com/spf13/cobra" @@ -27,8 +27,8 @@ func Test_List(t *testing.T) { name: "single node list", args: []string{}, wantErr: false, - stdout: ` NODE NAME PROFILES NETWORK - n01 default + stdout: `NODE NAME PROFILES NETWORK +n01 default `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -43,9 +43,9 @@ nodes: name: "multiple nodes list", args: []string{}, wantErr: false, - stdout: ` NODE NAME PROFILES NETWORK - n01 default - n02 default + stdout: `NODE NAME PROFILES NETWORK +n01 default +n02 default `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -63,9 +63,9 @@ nodes: name: "node list returns multiple nodes", args: []string{"n01,n02"}, wantErr: false, - stdout: ` NODE NAME PROFILES NETWORK - n01 default - n02 default + stdout: `NODE NAME PROFILES NETWORK +n01 default +n02 default `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -83,9 +83,9 @@ nodes: name: "node list returns multiple nodes (case 2)", args: []string{"n01,n03"}, wantErr: false, - stdout: ` NODE NAME PROFILES NETWORK - n01 default - n03 default + stdout: `NODE NAME PROFILES NETWORK +n01 default +n03 default `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -112,8 +112,8 @@ nodes: name: "node list returns one node", args: []string{"n01,"}, wantErr: false, - stdout: ` NODE NAME PROFILES NETWORK - n01 default + stdout: `NODE NAME PROFILES NETWORK +n01 default `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -131,8 +131,8 @@ nodes: name: "node list profile with network", args: []string{}, wantErr: false, - stdout: ` NODE NAME PROFILES NETWORK - n01 default default + stdout: `NODE NAME PROFILES NETWORK +n01 default default `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -149,16 +149,16 @@ nodes: name: "node list profile with comment", args: []string{"-a"}, wantErr: false, - stdout: `NODE FIELD PROFILE VALUE - n01 Id -- n01 - n01 Comment default profilecomment - n01 Ipxe -- (default) - n01 RuntimeOverlay -- (generic) - n01 SystemOverlay -- (wwinit) - n01 Root -- (initramfs) - n01 Init -- (/sbin/init) - n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) - n01 Profiles -- default + stdout: `NODE FIELD PROFILE VALUE +n01 Id -- n01 +n01 Comment default profilecomment +n01 Ipxe -- (default) +n01 RuntimeOverlay -- (generic) +n01 SystemOverlay -- (wwinit) +n01 Root -- (initramfs) +n01 Init -- (/sbin/init) +n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) +n01 Profiles -- default `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -173,16 +173,16 @@ nodes: name: "node list profile with comment superseded", args: []string{"-a"}, wantErr: false, - stdout: `NODE FIELD PROFILE VALUE - n01 Id -- n01 - n01 Comment SUPERSEDED nodecomment - n01 Ipxe -- (default) - n01 RuntimeOverlay -- (generic) - n01 SystemOverlay -- (wwinit) - n01 Root -- (initramfs) - n01 Init -- (/sbin/init) - n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) - n01 Profiles -- default + stdout: `NODE FIELD PROFILE VALUE +n01 Id -- n01 +n01 Comment SUPERSEDED nodecomment +n01 Ipxe -- (default) +n01 RuntimeOverlay -- (generic) +n01 SystemOverlay -- (wwinit) +n01 Root -- (initramfs) +n01 Init -- (/sbin/init) +n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) +n01 Profiles -- default `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -198,8 +198,8 @@ nodes: name: "node list profile with ipmi user", args: []string{"-i"}, wantErr: false, - stdout: `NODENAME IPMIIPADDR IPMIPORT IPMIUSERNAME IPMIINTERFACE -n01 -- -- admin -- -- + stdout: `NODE NAME IPMI IPADDR IPMI PORT IPMI USERNAME IPMI INTERFACE +n01 -- -- admin -- -- `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -215,8 +215,8 @@ nodes: name: "node list profile with ipmi user superseded", args: []string{"-i"}, wantErr: false, - stdout: `NODENAME IPMIIPADDR IPMIPORT IPMIUSERNAME IPMIINTERFACE -n01 -- -- user -- -- + stdout: `NODE NAME IPMI IPADDR IPMI PORT IPMI USERNAME IPMI INTERFACE +n01 -- -- user -- -- `, inDb: `WW_INTERNAL: 45 nodeprofiles: @@ -244,7 +244,7 @@ nodes: name: "multiple profiles list", args: []string{}, wantErr: false, - stdout: ` NODE NAME PROFILES NETWORK + stdout: `NODE NAME PROFILES NETWORK n01 p1,p2 `}, { @@ -261,14 +261,14 @@ nodes: name: "multiple profiles list all", args: []string{"-a"}, wantErr: false, - stdout: `NODE FIELD PROFILE VALUE + stdout: `NODE FIELD PROFILE VALUE n01 Id -- n01 n01 Ipxe -- (default) n01 RuntimeOverlay -- (generic) -n01 SystemOverlay -- (wwinit) +n01 SystemOverlay -- (wwinit) n01 Root -- (initramfs) n01 Init -- (/sbin/init) -n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) +n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) n01 Profiles -- p1,p2 `}, { @@ -308,7 +308,7 @@ nodes: args: []string{"-l"}, wantErr: false, stdout: `NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R) -n01 -- -- (wwinit)/nop1,rop2 ~{rop1} +n01 -- -- (wwinit)/rop2,nop1 ~{rop1} `}, { inDb: `WW_INTERNAL: 45 @@ -329,13 +329,13 @@ nodes: args: []string{"-a"}, wantErr: false, stdout: `NODE FIELD PROFILE VALUE -n01 Id -- n01 +n01 Id -- n01 n01 Ipxe -- (default) -n01 RuntimeOverlay SUPERSEDED nop1,rop2~{rop1} -n01 SystemOverlay -- (wwinit) +n01 RuntimeOverlay SUPERSEDED rop2,nop1 ~{rop1} +n01 SystemOverlay -- (wwinit) n01 Root -- (initramfs) n01 Init -- (/sbin/init) -n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) +n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) n01 Profiles -- p1 `}, { @@ -355,13 +355,13 @@ nodes: args: []string{"-a"}, wantErr: false, stdout: `NODE FIELD PROFILE VALUE -n01 Id -- n01 +n01 Id -- n01 n01 Ipxe -- (default) n01 RuntimeOverlay -- (generic) -n01 SystemOverlay SUPERSEDED profileinit, nodeinit +n01 SystemOverlay SUPERSEDED profileinit,nodeinit n01 Root -- (initramfs) n01 Init -- (/sbin/init) -n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) +n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238) n01 Profiles -- p1 `}, } @@ -498,8 +498,8 @@ nodes: wwlog.SetLogWriter(buf) err := baseCmd.Execute() assert.NoError(t, err) - for _, output := range tt.output { - assert.Contains(t, buf.String(), output) + for _, expected_output := range tt.output { + assert.Equal(t, expected_output, buf.String()) } }) } @@ -511,6 +511,8 @@ func verifyOutput(t *testing.T, baseCmd *cobra.Command, content string) { err := baseCmd.Execute() assert.NoError(t, err) + strip, _ := regexp.Compile("(?m)(^ *| *$)") + stdoutC := make(chan string) go func() { var buf bytes.Buffer @@ -521,5 +523,5 @@ func verifyOutput(t *testing.T, baseCmd *cobra.Command, content string) { stdout := <-stdoutC assert.NotEmpty(t, stdout, "output should not be empty") - assert.Contains(t, strings.ReplaceAll(stdout, " ", ""), strings.ReplaceAll(content, " ", "")) + assert.Equal(t, strip.ReplaceAllString(content, ""), strip.ReplaceAllString(stdout, "")) } diff --git a/internal/pkg/node/constuctors_test.go b/internal/pkg/node/constuctors_test.go index dc452c20..18ca728c 100644 --- a/internal/pkg/node/constuctors_test.go +++ b/internal/pkg/node/constuctors_test.go @@ -191,19 +191,19 @@ nodes: } assert.Contains(nodemap, "node1") assert.ElementsMatch(nodemap["node1"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p1o2"}) - assert.Equal(nodemap["node1"].RuntimeOverlay.Print(), "p1o1,p1o2") + assert.Equal("p1o1,p1o2", nodemap["node1"].RuntimeOverlay.Print()) assert.Contains(nodemap, "node2") assert.ElementsMatch(nodemap["node2"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p1o2", "p2o1", "p2o2"}) - assert.Equal(nodemap["node2"].RuntimeOverlay.Print(), "p1o1,p1o2,p2o1,p2o2") + assert.Equal("p1o1,p1o2,p2o1,p2o2", nodemap["node2"].RuntimeOverlay.Print()) assert.Contains(nodemap, "node3") assert.ElementsMatch(nodemap["node3"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p1o2", "n1o1", "n1o2"}) - assert.Equal(nodemap["node3"].RuntimeOverlay.Print(), "n1o1,n1o2,p1o1,p1o2") + assert.Equal("p1o1,p1o2,n1o1,n1o2", nodemap["node3"].RuntimeOverlay.Print()) assert.Contains(nodemap, "node4") assert.ElementsMatch(nodemap["node4"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p1o2", "p2o1", "p2o2", "n1o1", "n1o2"}) - assert.Equal(nodemap["node4"].RuntimeOverlay.Print(), "n1o1,n1o2,p1o1,p1o2,p2o1,p2o2") + assert.Equal("p1o1,p1o2,p2o1,p2o2,n1o1,n1o2", nodemap["node4"].RuntimeOverlay.Print()) assert.Contains(nodemap, "node5") assert.ElementsMatch(nodemap["node5"].RuntimeOverlay.GetSlice(), []string{"p1o1", "p2o1", "p2o2", "n1o1"}) - assert.Equal(nodemap["node5"].RuntimeOverlay.Print(), "n1o1,p1o1,p2o1,p2o2 ~{p1o2}") + assert.Equal("p1o1,p2o1,p2o2,n1o1 ~{p1o2}", nodemap["node5"].RuntimeOverlay.Print()) } func Test_negated_list(t *testing.T) { diff --git a/internal/pkg/node/list.go b/internal/pkg/node/list.go index 79612f70..4a728ece 100644 --- a/internal/pkg/node/list.go +++ b/internal/pkg/node/list.go @@ -32,19 +32,11 @@ func recursiveFields(obj interface{}, emptyFields bool, prefix string) (output [ if typeObj.Elem().Field(i).Type == reflect.TypeOf(Entry{}) { myField := valObj.Elem().Field(i).Interface().(Entry) if emptyFields || myField.Get() != "" { - if typeObj.Elem().Field(i).Name == "SystemOverlay" { - output = append(output, NodeFields{ - Field: prefix + typeObj.Elem().Field(i).Name, - Source: myField.Source(), - Value: myField.PrintPreferAlt(), - }) - } else { - output = append(output, NodeFields{ - Field: prefix + typeObj.Elem().Field(i).Name, - Source: myField.Source(), - Value: myField.Print(), - }) - } + output = append(output, NodeFields{ + Field: prefix + typeObj.Elem().Field(i).Name, + Source: myField.Source(), + Value: myField.Print(), + }) } } else if typeObj.Elem().Field(i).Type == reflect.TypeOf(map[string]*Entry{}) { for key, val := range valObj.Elem().Field(i).Interface().(map[string]*Entry) { diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index 308eebf7..e10fb4f9 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -277,17 +277,6 @@ func cleanList(list []string) (ret []string) { Returns a string slice created from a comma separated list of the value. */ func (ent *Entry) GetSlice() []string { - retval := append(ent.value, ent.altvalue...) - if len(retval) != 0 { - return cleanList(retval) - } - if len(ent.def) != 0 { - return ent.def - } - return []string{} -} - -func (ent *Entry) GetSlicePreferAlt() []string { retval := append(ent.altvalue, ent.value...) if len(retval) != 0 { return cleanList(retval) @@ -378,43 +367,6 @@ func (ent *Entry) Print() string { if len(ent.def) != 0 { return "(" + ent.def[0] + ")" } - } else { - var ret string - if len(ent.value) != 0 || len(ent.altvalue) != 0 { - combList := append(ent.value, ent.altvalue...) - ret = strings.Join(cleanList(combList), ",") - if len(negList(combList)) > 0 { - ret += " ~{" + strings.Join(negList(combList), ",") + "}" - } - - } - if ret != "" { - return ret - } - if len(ent.def) != 0 { - return "(" + strings.Join(ent.def, ",") + ")" - } - - } - return "--" -} - -/* Gets the the entry of the value in following order -* profile value if set -* node value if set -* default value if set - */ -func (ent *Entry) PrintPreferAlt() string { - if !ent.isSlice { - if len(ent.altvalue) != 0 { - return ent.altvalue[0] - } - if len(ent.value) != 0 { - return ent.value[0] - } - if len(ent.def) != 0 { - return "(" + ent.def[0] + ")" - } } else { var ret string if len(ent.value) != 0 || len(ent.altvalue) != 0 { diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index 1b22352f..a49c8bb7 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -31,7 +31,7 @@ Build all overlays (runtime and generic) for a node func BuildAllOverlays(nodes []node.NodeInfo) error { for _, n := range nodes { - sysOverlays := n.SystemOverlay.GetSlicePreferAlt() + sysOverlays := n.SystemOverlay.GetSlice() wwlog.Info("Building system overlays for %s: [%s]", n.Id.Get(), strings.Join(sysOverlays, ", ")) err := BuildOverlay(n, "system", sysOverlays) if err != nil {