diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b60e132..49f43138 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Refine error handling for `wwctl configure`. #1273 - Updated dracut guidance for building initramfs. #1369 +### Fixed + +- Fixed application of node overlays such that they override overlapping files from profile overlays. #1259 + ## v4.5.6, 2024-08-05 ### Added diff --git a/internal/app/wwctl/node/list/main_test.go b/internal/app/wwctl/node/list/main_test.go index d9600cad..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,39 @@ 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 +`}, + { + inDb: `WW_INTERNAL: 45 +nodeprofiles: + p1: + system overlay: + - profileinit +nodes: + n01: + profiles: + - p1 + system overlay: + - nodeinit +`, + name: "prefer profile system overlay over node overlay", + args: []string{"-a"}, + wantErr: false, + stdout: `NODE FIELD PROFILE VALUE +n01 Id -- n01 +n01 Ipxe -- (default) +n01 RuntimeOverlay -- (generic) +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 Profiles -- p1 `}, } @@ -472,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()) } }) } @@ -485,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 @@ -495,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/methods.go b/internal/pkg/node/methods.go index 574e3c38..e10fb4f9 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -277,7 +277,7 @@ 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...) + retval := append(ent.altvalue, ent.value...) if len(retval) != 0 { return cleanList(retval) } @@ -370,7 +370,7 @@ func (ent *Entry) Print() string { } else { var ret string if len(ent.value) != 0 || len(ent.altvalue) != 0 { - combList := append(ent.value, ent.altvalue...) + combList := append(ent.altvalue, ent.value...) ret = strings.Join(cleanList(combList), ",") if len(negList(combList)) > 0 { ret += " ~{" + strings.Join(negList(combList), ",") + "}"