From e90616cafbd92303b0d35855716d8cdbc3c9bbbe Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 16 Aug 2023 16:49:10 +0200 Subject: [PATCH 1/4] calling the overlay build functions direclty The warewulfd process called `wwctl build -O [system|runtime] node` if autobuild was set. This created [system|runtime].img which isn't supported with the build model. Calling now BuildOverlay directly Signed-off-by: Christian Goll --- internal/pkg/overlay/overlay.go | 20 -------------------- internal/pkg/warewulfd/util.go | 27 +++++++++++++++------------ 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index 6ef5278e..2dd9003d 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -19,26 +19,6 @@ import ( "github.com/pkg/errors" ) -/* - -func BuildSystemOverlay(nodeList []node.NodeInfo) error { - return nil -} - -func BuildRuntimeOverlay(nodeList []node.NodeInfo) error { - return nil -} - - -func FindSystemOverlays() ([]string, error) { - return findAllOverlays("system") -} - -func FindRuntimeOverlays() ([]string, error) { - return findAllOverlays("runtime") -} -*/ - /* Build all overlays (runtime and generic) for a node */ diff --git a/internal/pkg/warewulfd/util.go b/internal/pkg/warewulfd/util.go index e55bf553..9f2cd4a4 100644 --- a/internal/pkg/warewulfd/util.go +++ b/internal/pkg/warewulfd/util.go @@ -1,9 +1,11 @@ package warewulfd import ( + "fmt" "net/http" "os" + "github.com/hpcng/warewulf/internal/pkg/node" nodepkg "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/overlay" "github.com/hpcng/warewulf/internal/pkg/util" @@ -61,21 +63,22 @@ func getOverlayFile( } if build { - wwlog.Serv("BUILD %15s, overlays %v", nodeId, stage_overlays) - - args := []string{"overlay", "build"} - - for _, overlayname := range stage_overlays { - args = append(args, "-O", overlayname) + nodeDB, errNested := node.New() + if err != nil { + return stage_file, errNested } - - args = append(args, nodeId) - - out, err := util.RunWWCTL(args...) - + myNode, errNested := nodeDB.FindAllNodes() + if err != nil { + return stage_file, errNested + } + myNode = node.FilterByName(myNode, []string{nodeId}) + if len(myNode) != 1 { + return stage_file, fmt.Errorf("couldn't find node %s", nodeId) + } + err = overlay.BuildOverlay(myNode[0], context, stage_overlays) if err != nil { wwlog.Error("Failed to build overlay: %s, %s, %s\n%s", - nodeId, stage_overlays, stage_file, string(out)) + nodeId, stage_overlays, stage_file, err) } } From 242276bba9727689e5a8457702644e5d8aa681ac Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 20 Sep 2023 11:24:37 +0200 Subject: [PATCH 2/4] fixed tests and added explcit error handling Signed-off-by: Christian Goll --- internal/pkg/overlay/overlay.go | 10 +++++----- internal/pkg/warewulfd/provision.go | 10 +++++++++- internal/pkg/warewulfd/util.go | 24 +++++------------------- internal/pkg/warewulfd/util_test.go | 25 +++++++++++++++---------- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index 2dd9003d..b22545c4 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -19,6 +19,10 @@ import ( "github.com/pkg/errors" ) +var ( + ErrDoesNotExist = errors.New("overlay does not exist") +) + /* Build all overlays (runtime and generic) for a node */ @@ -187,11 +191,7 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir wwlog.Debug("Starting to build overlay %s\nChanging directory to OverlayDir: %s", overlayName, overlaySourceDir) err := os.Chdir(overlaySourceDir) if err != nil { - return errors.Wrap(err, "could not change directory to overlay dir") - } - wwlog.Debug("Checking to see if overlay directory exists: %s", overlaySourceDir) - if !util.IsDir(overlaySourceDir) { - return errors.New("overlay does not exist: " + overlayName) + return errors.Wrapf(ErrDoesNotExist, " name: %s", overlayName) } wwlog.Verbose("Walking the overlay structure: %s", overlaySourceDir) diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index 03b2798c..f2851a26 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -2,6 +2,7 @@ package warewulfd import ( "bytes" + "errors" "net/http" "path" "strconv" @@ -11,6 +12,7 @@ import ( warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/kernel" + "github.com/hpcng/warewulf/internal/pkg/overlay" "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -135,13 +137,19 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } else { context = rinfo.stage } + stage_file, err = getOverlayFile( - node.Id.Get(), + node, context, request_overlays, conf.Warewulf.AutobuildOverlays) if err != nil { + if errors.Is(err, overlay.ErrDoesNotExist) { + w.WriteHeader(http.StatusNotFound) + wwlog.ErrorExc(err, "") + return + } w.WriteHeader(http.StatusInternalServerError) wwlog.ErrorExc(err, "") return diff --git a/internal/pkg/warewulfd/util.go b/internal/pkg/warewulfd/util.go index 9f2cd4a4..3290a678 100644 --- a/internal/pkg/warewulfd/util.go +++ b/internal/pkg/warewulfd/util.go @@ -1,7 +1,6 @@ package warewulfd import ( - "fmt" "net/http" "os" @@ -44,16 +43,15 @@ func sendFile( } func getOverlayFile( - nodeId string, + n node.NodeInfo, context string, stage_overlays []string, autobuild bool) (stage_file string, err error) { - stage_file = overlay.OverlayImage(nodeId, context, stage_overlays) + stage_file = overlay.OverlayImage(n.Id.Get(), context, stage_overlays) err = nil - build := !util.IsFile(stage_file) - + wwlog.Verbose("stage file: %s", stage_file) if !build && autobuild { build = util.PathIsNewer(stage_file, nodepkg.ConfigFile) @@ -63,22 +61,10 @@ func getOverlayFile( } if build { - nodeDB, errNested := node.New() - if err != nil { - return stage_file, errNested - } - myNode, errNested := nodeDB.FindAllNodes() - if err != nil { - return stage_file, errNested - } - myNode = node.FilterByName(myNode, []string{nodeId}) - if len(myNode) != 1 { - return stage_file, fmt.Errorf("couldn't find node %s", nodeId) - } - err = overlay.BuildOverlay(myNode[0], context, stage_overlays) + err = overlay.BuildOverlay(n, context, stage_overlays) if err != nil { wwlog.Error("Failed to build overlay: %s, %s, %s\n%s", - nodeId, stage_overlays, stage_file, err) + n.Id.Get(), stage_overlays, stage_file, err) } } diff --git a/internal/pkg/warewulfd/util_test.go b/internal/pkg/warewulfd/util_test.go index 036a938e..c2853982 100644 --- a/internal/pkg/warewulfd/util_test.go +++ b/internal/pkg/warewulfd/util_test.go @@ -2,6 +2,8 @@ package warewulfd import ( warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/stretchr/testify/assert" "testing" ) @@ -12,24 +14,27 @@ var getOverlayFileTests = []struct { context string overlays []string result string - succeed bool + succeed bool // getOverlayFile will always fail if no overlay dir is defined! }{ - {"empty", "", "", nil, "", true}, - {"empty node", "node1", "", nil, "", true}, - {"specific overlays without node", "", "", []string{"o1", "o2"}, "p/overlays/o1-o2.img", true}, - {"system overlay", "node1", "system", nil, "p/overlays/node1/__SYSTEM__.img", true}, - {"runtime overlay", "node1", "runtime", nil, "p/overlays/node1/__RUNTIME__.img", true}, - {"specific overlay", "node1", "", []string{"o1"}, "p/overlays/node1/o1.img", true}, - {"multiple specific overlays", "node1", "", []string{"o1", "o2"}, "p/overlays/node1/o1-o2.img", true}, - {"multiple specific overlays with context", "node1", "system", []string{"o1", "o2"}, "p/overlays/node1/__SYSTEM__.img", true}, + {"empty", "", "", nil, "", false}, + {"empty node", "node1", "", nil, "", false}, + {"specific overlays without node", "", "", []string{"o1", "o2"}, "p/overlays/node1/o1-o2.img", false}, + {"system overlay", "node1", "system", nil, "p/overlays/node1/__SYSTEM__.img", false}, + {"runtime overlay", "node1", "runtime", nil, "p/overlays/node1/__RUNTIME__.img", false}, + {"specific overlay", "node1", "", []string{"o1"}, "p/overlays/node1/o1.img", false}, + {"multiple specific overlays", "node1", "", []string{"o1", "o2"}, "p/overlays/node1/o1-o2.img", false}, + {"multiple specific overlays with context", "node1", "system", []string{"o1", "o2"}, "p/overlays/node1/__SYSTEM__.img", false}, } func Test_getOverlayFile(t *testing.T) { + wwlog.SetLogLevel(wwlog.VERBOSE) conf := warewulfconf.Get() conf.Paths.WWProvisiondir = "p" + var n node.NodeInfo for _, tt := range getOverlayFileTests { + n.Id.Set(tt.node) t.Run(tt.description, func(t *testing.T) { - result, err := getOverlayFile(tt.node, tt.context, tt.overlays, false) + result, err := getOverlayFile(n, tt.context, tt.overlays, false) if !tt.succeed { assert.Error(t, err) } else { From 767be4fd8cf46391cdd323aabb600057155a6c95 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 21 Sep 2023 15:52:15 +0200 Subject: [PATCH 3/4] refactored util_test.go to test on real files Signed-off-by: Christian Goll --- internal/pkg/overlay/overlay.go | 9 +++-- internal/pkg/overlay/overlay_test.go | 54 ++++++++++++++-------------- internal/pkg/warewulfd/util_test.go | 49 ++++++++++++++++--------- 3 files changed, 67 insertions(+), 45 deletions(-) diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index b22545c4..51e8f70f 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -125,6 +125,9 @@ func OverlayInit(overlayName string) error { Build the given overlays for a node and create a Image for them */ func BuildOverlay(nodeInfo node.NodeInfo, context string, overlayNames []string) error { + if len(overlayNames) == 0 { + return nil + } // create the dir where the overlay images will reside name := fmt.Sprintf("overlay %s/%v", nodeInfo.Id.Get(), overlayNames) overlayImage := OverlayImage(nodeInfo.Id.Get(), context, overlayNames) @@ -171,7 +174,7 @@ exists it will be created. */ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir string) error { if len(overlayNames) == 0 { - return errors.New("At least one valid overlay is needed to build for a node") + return nil } if !util.IsDir(outputDir) { return errors.Errorf("output must a be a directory: %s", outputDir) @@ -188,10 +191,10 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir for _, overlayName := range overlayNames { wwlog.Verbose("Building overlay %s for node %s in %s", overlayName, nodeInfo.Id.Get(), outputDir) overlaySourceDir := OverlaySourceDir(overlayName) - wwlog.Debug("Starting to build overlay %s\nChanging directory to OverlayDir: %s", overlayName, overlaySourceDir) + wwlog.Debug("Changing directory to OverlayDir: %s", overlaySourceDir) err := os.Chdir(overlaySourceDir) if err != nil { - return errors.Wrapf(ErrDoesNotExist, " name: %s", overlayName) + return errors.Wrapf(ErrDoesNotExist, "directory: %s name: %s", overlaySourceDir, overlayName) } wwlog.Verbose("Walking the overlay structure: %s", overlaySourceDir) diff --git a/internal/pkg/overlay/overlay_test.go b/internal/pkg/overlay/overlay_test.go index 2e7d446b..9a24680b 100644 --- a/internal/pkg/overlay/overlay_test.go +++ b/internal/pkg/overlay/overlay_test.go @@ -20,19 +20,20 @@ var buildOverlayTests = []struct { overlays []string image string contents []string + hasFiles bool }{ - {"empty", "", "", nil, "", nil}, - {"empty node", "node1", "", nil, "", nil}, - {"empty context", "", "system", nil, "", nil}, - {"empty overlay", "", "", []string{"o1"}, "o1.img", []string{"o1.txt"}}, - {"single overlay", "node1", "", []string{"o1"}, "node1/o1.img", []string{"o1.txt"}}, - {"multiple overlays", "node1", "", []string{"o1", "o2"}, "node1/o1-o2.img", []string{"o1.txt", "o2.txt"}}, - {"empty system overlay", "node1", "system", nil, "", nil}, - {"empty runtime overlay", "node1", "runtime", nil, "", nil}, - {"single system overlay", "node1", "system", []string{"o1"}, "node1/__SYSTEM__.img", []string{"o1.txt"}}, - {"single runtime overlay", "node1", "runtime", []string{"o1"}, "node1/__RUNTIME__.img", []string{"o1.txt"}}, - {"two system overlays", "node1", "system", []string{"o1", "o2"}, "node1/__SYSTEM__.img", []string{"o1.txt", "o2.txt"}}, - {"two runtime overlays", "node1", "runtime", []string{"o1", "o2"}, "node1/__RUNTIME__.img", []string{"o1.txt", "o2.txt"}}, + {"empty", "", "", nil, "", nil, false}, + {"empty node", "node1", "", nil, "", nil, false}, + {"empty context", "", "system", nil, "", nil, false}, + {"empty overlay", "", "", []string{"o1"}, "o1.img", []string{"o1.txt"}, true}, + {"single overlay", "node1", "", []string{"o1"}, "node1/o1.img", []string{"o1.txt"}, true}, + {"multiple overlays", "node1", "", []string{"o1", "o2"}, "node1/o1-o2.img", []string{"o1.txt", "o2.txt"}, true}, + {"empty system overlay", "node1", "system", nil, "", nil, false}, + {"empty runtime overlay", "node1", "runtime", nil, "", nil, false}, + {"single system overlay", "node1", "system", []string{"o1"}, "node1/__SYSTEM__.img", []string{"o1.txt"}, true}, + {"single runtime overlay", "node1", "runtime", []string{"o1"}, "node1/__RUNTIME__.img", []string{"o1.txt"}, true}, + {"two system overlays", "node1", "system", []string{"o1", "o2"}, "node1/__SYSTEM__.img", []string{"o1.txt", "o2.txt"}, true}, + {"two runtime overlays", "node1", "runtime", []string{"o1", "o2"}, "node1/__RUNTIME__.img", []string{"o1.txt", "o2.txt"}, true}, } func Test_BuildOverlay(t *testing.T) { @@ -65,7 +66,7 @@ func Test_BuildOverlay(t *testing.T) { conf.Paths.WWProvisiondir = provisionDir err := BuildOverlay(nodeInfo, tt.context, tt.overlays) - if len(tt.image) > 0 { + if tt.hasFiles { image := path.Join(provisionDir, "overlays", tt.image) assert.FileExists(t, image) assert.NoError(t, err) @@ -75,7 +76,6 @@ func Test_BuildOverlay(t *testing.T) { sort.Strings(files) assert.Equal(t, tt.contents, files) } else { - assert.Error(t, err) dirName := path.Join(provisionDir, "overlays", tt.nodeName) isEmpty := dirIsEmpty(t, dirName) assert.True(t, isEmpty, "%v should be empty, but isn't", dirName) @@ -90,22 +90,23 @@ var buildAllOverlaysTests = []struct { systemOverlays []string runtimeOverlays []string succeed bool + createdOverlays []string }{ - {"no nodes", nil, nil, nil, true}, - {"single empty node", []string{"node1"}, nil, nil, false}, - {"two empty node", []string{"node1", "node2"}, nil, nil, false}, + {"no nodes", nil, nil, nil, true, nil}, + {"single empty node", []string{"node1"}, nil, nil, true, nil}, + {"two empty node", []string{"node1", "node2"}, nil, nil, true, nil}, {"single node with system overlay", []string{"node1"}, - []string{"o1"}, nil, false}, + []string{"o1"}, nil, true, []string{"__SYSTEM__.img.gz"}}, {"two nodes with system overlays", []string{"node1", "node2"}, - []string{"o1", "o1,o2"}, nil, false}, + []string{"o1", "o1,o2"}, nil, true, []string{"__SYSTEM__.img.gz"}}, {"single node with runtime overlay", []string{"node1"}, - nil, []string{"o1"}, false}, + nil, []string{"o1"}, true, []string{"__RUNTIME__.img.gz"}}, {"two nodes with runtime overlays", []string{"node1", "node2"}, - nil, []string{"o1", "o1,o2"}, false}, - {"stingle node with full overlays", []string{"node1"}, - []string{"o1"}, []string{"o2"}, true}, + nil, []string{"o1", "o1,o2"}, true, []string{"__RUNTIME__.img.gz"}}, + {"single node with full overlays", []string{"node1"}, + []string{"o1"}, []string{"o2"}, true, []string{"__RUNTIME__.img.gz", "__SYSTEM__.img.gz"}}, {"two nodes with full overlays", []string{"node1", "node2"}, - []string{"o1", "o1,o2"}, []string{"o2", "o2"}, true}, + []string{"o1", "o1,o2"}, []string{"o2", "o2"}, true, []string{"__RUNTIME__.img.gz", "__SYSTEM__.img.gz"}}, } func Test_BuildAllOverlays(t *testing.T) { @@ -142,8 +143,9 @@ func Test_BuildAllOverlays(t *testing.T) { } else { assert.NoError(t, err) for _, nodeName := range tt.nodes { - assert.FileExists(t, path.Join(provisionDir, "overlays", nodeName, "__SYSTEM__.img")) - assert.FileExists(t, path.Join(provisionDir, "overlays", nodeName, "__RUNTIME__.img")) + for _, file := range tt.createdOverlays { + assert.FileExists(t, path.Join(provisionDir, "overlays", nodeName, file)) + } } } }) diff --git a/internal/pkg/warewulfd/util_test.go b/internal/pkg/warewulfd/util_test.go index c2853982..9f4999a8 100644 --- a/internal/pkg/warewulfd/util_test.go +++ b/internal/pkg/warewulfd/util_test.go @@ -1,11 +1,14 @@ package warewulfd import ( + "os" + "path" + "testing" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/stretchr/testify/assert" - "testing" ) var getOverlayFileTests = []struct { @@ -14,33 +17,47 @@ var getOverlayFileTests = []struct { context string overlays []string result string - succeed bool // getOverlayFile will always fail if no overlay dir is defined! + succeed bool }{ - {"empty", "", "", nil, "", false}, - {"empty node", "node1", "", nil, "", false}, - {"specific overlays without node", "", "", []string{"o1", "o2"}, "p/overlays/node1/o1-o2.img", false}, - {"system overlay", "node1", "system", nil, "p/overlays/node1/__SYSTEM__.img", false}, - {"runtime overlay", "node1", "runtime", nil, "p/overlays/node1/__RUNTIME__.img", false}, - {"specific overlay", "node1", "", []string{"o1"}, "p/overlays/node1/o1.img", false}, - {"multiple specific overlays", "node1", "", []string{"o1", "o2"}, "p/overlays/node1/o1-o2.img", false}, - {"multiple specific overlays with context", "node1", "system", []string{"o1", "o2"}, "p/overlays/node1/__SYSTEM__.img", false}, + {"empty", "", "", nil, "", true}, + {"empty node", "node1", "", nil, "", true}, + {"specific overlays without node", "", "", []string{"o1", "o2"}, "overlays/o1-o2.img", true}, // will fail as node is empty + {"system overlay", "node1", "system", []string{"o1"}, "overlays/node1/__SYSTEM__.img", true}, + {"runtime overlay", "node1", "runtime", nil, "overlays/node1/__RUNTIME__.img", true}, + {"specific overlay", "node1", "", []string{"o1"}, "overlays/node1/o1.img", true}, + {"multiple specific overlays", "node1", "", []string{"o1", "o2"}, "overlays/node1/o1-o2.img", true}, + {"multiple specific overlays with context", "node1", "system", []string{"o1", "o2"}, "overlays/node1/__SYSTEM__.img", true}, } func Test_getOverlayFile(t *testing.T) { - wwlog.SetLogLevel(wwlog.VERBOSE) + wwlog.SetLogLevel(wwlog.DEBUG) conf := warewulfconf.Get() - conf.Paths.WWProvisiondir = "p" - var n node.NodeInfo + overlayPDir, overlayPDirErr := os.MkdirTemp(os.TempDir(), "ww-test-overlay-*") + assert.NoError(t, overlayPDirErr) + conf.Paths.WWProvisiondir = overlayPDir + overlayDir, overlayDirErr := os.MkdirTemp(os.TempDir(), "ww-test-provision-*") + assert.NoError(t, overlayDirErr) + conf.Paths.WWOverlaydir = overlayDir + defer os.RemoveAll(overlayDir) + assert.NoError(t, os.MkdirAll(path.Join(overlayDir, "o1"), 0700)) + assert.NoError(t, os.WriteFile(path.Join(overlayDir, "o1", "test_file_o1"), []byte("test file"), 0600)) + assert.NoError(t, os.MkdirAll(path.Join(overlayDir, "o2"), 0700)) + for _, tt := range getOverlayFileTests { - n.Id.Set(tt.node) t.Run(tt.description, func(t *testing.T) { - result, err := getOverlayFile(n, tt.context, tt.overlays, false) + var nodeInfo node.NodeInfo + nodeInfo.Id.Set(tt.node) + nodeInfo.RuntimeOverlay.SetSlice(tt.overlays) + nodeInfo.SystemOverlay.SetSlice(tt.overlays) + result, err := getOverlayFile(nodeInfo, tt.context, tt.overlays, false) if !tt.succeed { assert.Error(t, err) } else { assert.NoError(t, err) } - + if tt.result != "" { + tt.result = path.Join(overlayPDir, tt.result) + } assert.Equal(t, tt.result, result) }) } From 2b49bcad2728704862326dd7eb15f717b1842bea Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Wed, 25 Oct 2023 22:16:53 -0600 Subject: [PATCH 4/4] Expand test descriptions and simplify some tests As part of reviewing this PR, I expanded the test definitions to make it easier to discern the intent of each test at a glance. In so doing I understood most tests behaving as normal, but I did make a few small changes: - Some tests no longer produce any errors, so remove this variable from the test definitions. - Replace comma-separated overlay definitions with explicit slices. Splitting comma-separated strings led to erroneous behavior where a null overlay list was processed as a single null overlay rather than an empty slice. Signed-off-by: Jonathon Anderson --- internal/pkg/overlay/overlay_test.go | 292 +++++++++++++++++++++------ internal/pkg/warewulfd/util_test.go | 71 +++++-- 2 files changed, 292 insertions(+), 71 deletions(-) diff --git a/internal/pkg/overlay/overlay_test.go b/internal/pkg/overlay/overlay_test.go index 9a24680b..11e76295 100644 --- a/internal/pkg/overlay/overlay_test.go +++ b/internal/pkg/overlay/overlay_test.go @@ -9,7 +9,6 @@ import ( "os" "path" "sort" - "strings" "testing" ) @@ -22,18 +21,110 @@ var buildOverlayTests = []struct { contents []string hasFiles bool }{ - {"empty", "", "", nil, "", nil, false}, - {"empty node", "node1", "", nil, "", nil, false}, - {"empty context", "", "system", nil, "", nil, false}, - {"empty overlay", "", "", []string{"o1"}, "o1.img", []string{"o1.txt"}, true}, - {"single overlay", "node1", "", []string{"o1"}, "node1/o1.img", []string{"o1.txt"}, true}, - {"multiple overlays", "node1", "", []string{"o1", "o2"}, "node1/o1-o2.img", []string{"o1.txt", "o2.txt"}, true}, - {"empty system overlay", "node1", "system", nil, "", nil, false}, - {"empty runtime overlay", "node1", "runtime", nil, "", nil, false}, - {"single system overlay", "node1", "system", []string{"o1"}, "node1/__SYSTEM__.img", []string{"o1.txt"}, true}, - {"single runtime overlay", "node1", "runtime", []string{"o1"}, "node1/__RUNTIME__.img", []string{"o1.txt"}, true}, - {"two system overlays", "node1", "system", []string{"o1", "o2"}, "node1/__SYSTEM__.img", []string{"o1.txt", "o2.txt"}, true}, - {"two runtime overlays", "node1", "runtime", []string{"o1", "o2"}, "node1/__RUNTIME__.img", []string{"o1.txt", "o2.txt"}, true}, + { + description: "if no node, context, or overlays are specified then no overlay image is generated", + nodeName: "", + context: "", + overlays: nil, + image: "", + contents: nil, + }, + { + description: "if only node is specified then no overlay image is generated", + nodeName: "node1", + context: "", + overlays: nil, + image: "", + contents: nil, + }, + { + description: "if only context is specified then no overlay image is generated", + nodeName: "", + context: "system", + overlays: nil, + image: "", + contents: nil, + }, + { + description: "if an overlay is specified without a node, then the overlay is built directly in the overlay directory", + nodeName: "", + context: "", + overlays: []string{"o1"}, + image: "o1.img", + contents: []string{"o1.txt"}, + }, + { + description: "if multiple overlays are specified without a node, then the combined overlay is built directly in the overlay directory", + nodeName: "", + context: "", + overlays: []string{"o1", "o2"}, + image: "o1-o2.img", + contents: []string{"o1.txt", "o2.txt"}, + }, + { + description: "if a single node overlay is specified, then the overlay is built in a node overlay directory", + nodeName: "node1", + context: "", + overlays: []string{"o1"}, + image: "node1/o1.img", + contents: []string{"o1.txt"}, + }, + { + description: "if multiple node overlays are specified, then the combined overlay is built in a node overlay directory", + nodeName: "node1", + context: "", + overlays: []string{"o1", "o2"}, + image: "node1/o1-o2.img", + contents: []string{"o1.txt", "o2.txt"}, + }, + { + description: "if no node system overlays are specified, then no overlay image is generated", + nodeName: "node1", + context: "system", + overlays: nil, + image: "", + contents: nil, + }, + { + description: "if no node runtime overlays are specified, then no overlay image is generated", + nodeName: "node1", + context: "runtime", + overlays: nil, + image: "", + contents: nil, + }, + { + description: "if a single node system overlay is specified, then a system overlay image is generated in a node overlay directory", + nodeName: "node1", + context: "system", + overlays: []string{"o1"}, + image: "node1/__SYSTEM__.img", + contents: []string{"o1.txt"}, + }, + { + description: "if a single node runtime overlay is specified, then a runtime overlay image is generated in a node overlay directory", + nodeName: "node1", + context: "runtime", + overlays: []string{"o1"}, + image: "node1/__RUNTIME__.img", + contents: []string{"o1.txt"}, + }, + { + description: "if multiple node system overlays are specified, then a system overlay image is generated with the contents of both overlays", + nodeName: "node1", + context: "system", + overlays: []string{"o1", "o2"}, + image: "node1/__SYSTEM__.img", + contents: []string{"o1.txt", "o2.txt"}, + }, + { + description: "if multiple node runtime overlays are specified, then a runtime overlay image is generated with the contents of both overlays", + nodeName: "node1", + context: "runtime", + overlays: []string{"o1", "o2"}, + image: "node1/__RUNTIME__.img", + contents: []string{"o1.txt", "o2.txt"}, + }, } func Test_BuildOverlay(t *testing.T) { @@ -66,10 +157,10 @@ func Test_BuildOverlay(t *testing.T) { conf.Paths.WWProvisiondir = provisionDir err := BuildOverlay(nodeInfo, tt.context, tt.overlays) - if tt.hasFiles { + assert.NoError(t, err) + if tt.image != "" { image := path.Join(provisionDir, "overlays", tt.image) assert.FileExists(t, image) - assert.NoError(t, err) sort.Strings(tt.contents) files := cpioFiles(t, image) @@ -84,29 +175,80 @@ func Test_BuildOverlay(t *testing.T) { } } +// Although these tests specify system and runtime overlays for the +// nodes, these overlays define the overlays that are defined in the +// configuration. BuildAllOverlays doesn't receive these as arguments, +// but builds all the overlays that are configured on the given node. var buildAllOverlaysTests = []struct { description string nodes []string - systemOverlays []string - runtimeOverlays []string - succeed bool + systemOverlays [][]string + runtimeOverlays [][]string createdOverlays []string }{ - {"no nodes", nil, nil, nil, true, nil}, - {"single empty node", []string{"node1"}, nil, nil, true, nil}, - {"two empty node", []string{"node1", "node2"}, nil, nil, true, nil}, - {"single node with system overlay", []string{"node1"}, - []string{"o1"}, nil, true, []string{"__SYSTEM__.img.gz"}}, - {"two nodes with system overlays", []string{"node1", "node2"}, - []string{"o1", "o1,o2"}, nil, true, []string{"__SYSTEM__.img.gz"}}, - {"single node with runtime overlay", []string{"node1"}, - nil, []string{"o1"}, true, []string{"__RUNTIME__.img.gz"}}, - {"two nodes with runtime overlays", []string{"node1", "node2"}, - nil, []string{"o1", "o1,o2"}, true, []string{"__RUNTIME__.img.gz"}}, - {"single node with full overlays", []string{"node1"}, - []string{"o1"}, []string{"o2"}, true, []string{"__RUNTIME__.img.gz", "__SYSTEM__.img.gz"}}, - {"two nodes with full overlays", []string{"node1", "node2"}, - []string{"o1", "o1,o2"}, []string{"o2", "o2"}, true, []string{"__RUNTIME__.img.gz", "__SYSTEM__.img.gz"}}, + { + description: "empty input creates no overlays", + nodes: nil, + systemOverlays: nil, + runtimeOverlays: nil, + createdOverlays: nil, + }, + { + description: "a node with no overlays creates no overlays", + nodes: []string{"node1"}, + systemOverlays: nil, + runtimeOverlays: nil, + createdOverlays: nil, + }, + { + description: "multiple nodes with no overlays creates no overlays", + nodes: []string{"node1", "node2"}, + systemOverlays: nil, + runtimeOverlays: nil, + createdOverlays: nil, + }, + { + description: "a system overlay for a node generates a system overlay for that node", + nodes: []string{"node1"}, + systemOverlays: [][]string{{"o1"}}, + runtimeOverlays: nil, + createdOverlays: []string{"node1/__SYSTEM__.img.gz"}, + }, + { + description: "two nodes with different system overlays generates a system overlay for each node", + nodes: []string{"node1", "node2"}, + systemOverlays: [][]string{{"o1"}, {"o1", "o2"}}, + runtimeOverlays: nil, + createdOverlays: []string{"node1/__SYSTEM__.img.gz", "node2/__SYSTEM__.img.gz"}, + }, + { + description: "two nodes with a single runtime overlay generates a runtime overlay for the first node", + nodes: []string{"node1"}, + systemOverlays: nil, + runtimeOverlays: [][]string{{"o1"}}, + createdOverlays: []string{"node1/__RUNTIME__.img.gz"}, + }, + { + description: "two nodes with different runtime overlays generates a system overlay for each node", + nodes: []string{"node1", "node2"}, + systemOverlays: nil, + runtimeOverlays: [][]string{{"o1"}, {"o1", "o2"}}, + createdOverlays: []string{"node1/__RUNTIME__.img.gz", "node2/__RUNTIME__.img.gz"}, + }, + { + description: "a node with both a runtime and system overlay generates an image for each", + nodes: []string{"node1"}, + systemOverlays: [][]string{{"o1"}}, + runtimeOverlays: [][]string{{"o2"}}, + createdOverlays: []string{"node1/__RUNTIME__.img.gz", "node1/__SYSTEM__.img.gz"}, + }, + { + description: "two nodes with both runtime and system overlays generates each image for each node", + nodes: []string{"node1", "node2"}, + systemOverlays: [][]string{{"o1"}, {"o1", "o2"}}, + runtimeOverlays: [][]string{{"o2"}, {"o2"}}, + createdOverlays: []string{"node1/__RUNTIME__.img.gz", "node1/__SYSTEM__.img.gz", "node2/__RUNTIME__.img.gz", "node2/__SYSTEM__.img.gz"}, + }, } func Test_BuildAllOverlays(t *testing.T) { @@ -130,23 +272,21 @@ func Test_BuildAllOverlays(t *testing.T) { nodeInfo := node.NodeInfo{} nodeInfo.Id.Set(nodeName) if tt.systemOverlays != nil { - nodeInfo.SystemOverlay.SetSlice(strings.Split(tt.systemOverlays[i], ",")) + nodeInfo.SystemOverlay.SetSlice(tt.systemOverlays[i]) } if tt.runtimeOverlays != nil { - nodeInfo.RuntimeOverlay.SetSlice(strings.Split(tt.runtimeOverlays[i], ",")) + nodeInfo.RuntimeOverlay.SetSlice(tt.runtimeOverlays[i]) } nodes = append(nodes, nodeInfo) } err := BuildAllOverlays(nodes) - if !tt.succeed { - assert.Error(t, err) - } else { - assert.NoError(t, err) - for _, nodeName := range tt.nodes { - for _, file := range tt.createdOverlays { - assert.FileExists(t, path.Join(provisionDir, "overlays", nodeName, file)) - } - } + assert.NoError(t, err) + if tt.createdOverlays == nil { + dirName := path.Join(provisionDir, "overlays") + assert.True(t, dirIsEmpty(t, dirName), "%v should be empty, but isn't", dirName) + } + for _, overlayPath := range tt.createdOverlays { + assert.FileExists(t, path.Join(provisionDir, "overlays", overlayPath)) } }) } @@ -155,21 +295,59 @@ func Test_BuildAllOverlays(t *testing.T) { var buildSpecificOverlaysTests = []struct { description string nodes []string - overlays string + overlays []string images []string succeed bool }{ - {"no nodes", nil, "", nil, true}, - {"single empty node", []string{"node1"}, "", nil, false}, - {"two empty node", []string{"node1", "node2"}, "", nil, false}, - {"single node with single overlay", []string{"node1"}, "o1", - []string{"node1/o1.img"}, true}, - {"two nodes with single overlay", []string{"node1", "node2"}, "o1", - []string{"node1/o1.img", "node2/o1.img"}, true}, - {"single node with multi overlay", []string{"node1"}, "o1,o2", - []string{"node1/o1.img", "node1/o2.img"}, true}, - {"two nodes with multi overlays", []string{"node1", "node2"}, "o1,o2", - []string{"node1/o1.img", "node1/o2.img", "node2/o1.img", "node2/o2.img"}, true}, + { + description: "building no overlays for no nodes generates no error and no images", + nodes: nil, + overlays: nil, + images: nil, + succeed: true, + }, + { + description: "building no overlays for a node generates no error and no images", + nodes: []string{"node1"}, + overlays: nil, + images: nil, + succeed: true, + }, + { + description: "building no overlays for two nodes generates no error and no images", + nodes: []string{"node1", "node2"}, + overlays: nil, + images: nil, + succeed: true, + }, + { + description: "building an overlay for a node generates an overlay image in that node's overlay directory", + nodes: []string{"node1"}, + overlays: []string{"o1"}, + images: []string{"node1/o1.img"}, + succeed: true, + }, + { + description: "building an overlay for two nodes generates an overlay image in each node's overlay directory", + nodes: []string{"node1", "node2"}, + overlays: []string{"o1"}, + images: []string{"node1/o1.img", "node2/o1.img"}, + succeed: true, + }, + { + description: "building multiple overlays for a node generates an overlay image for each overlay in the node's overlay directory", + nodes: []string{"node1"}, + overlays: []string{"o1", "o2"}, + images: []string{"node1/o1.img", "node1/o2.img"}, + succeed: true, + }, + { + description: "building multiple overlays for two nodes generates an overlay image for each overlay in each node's overlay directory", + nodes: []string{"node1", "node2"}, + overlays: []string{"o1", "o2"}, + images: []string{"node1/o1.img", "node1/o2.img", "node2/o1.img", "node2/o2.img"}, + succeed: true, + }, } func Test_BuildSpecificOverlays(t *testing.T) { @@ -194,7 +372,7 @@ func Test_BuildSpecificOverlays(t *testing.T) { nodeInfo.Id.Set(nodeName) nodes = append(nodes, nodeInfo) } - err := BuildSpecificOverlays(nodes, strings.Split(tt.overlays, ",")) + err := BuildSpecificOverlays(nodes, tt.overlays) if !tt.succeed { assert.Error(t, err) } else { diff --git a/internal/pkg/warewulfd/util_test.go b/internal/pkg/warewulfd/util_test.go index 9f4999a8..3bfc0295 100644 --- a/internal/pkg/warewulfd/util_test.go +++ b/internal/pkg/warewulfd/util_test.go @@ -17,16 +17,63 @@ var getOverlayFileTests = []struct { context string overlays []string result string - succeed bool }{ - {"empty", "", "", nil, "", true}, - {"empty node", "node1", "", nil, "", true}, - {"specific overlays without node", "", "", []string{"o1", "o2"}, "overlays/o1-o2.img", true}, // will fail as node is empty - {"system overlay", "node1", "system", []string{"o1"}, "overlays/node1/__SYSTEM__.img", true}, - {"runtime overlay", "node1", "runtime", nil, "overlays/node1/__RUNTIME__.img", true}, - {"specific overlay", "node1", "", []string{"o1"}, "overlays/node1/o1.img", true}, - {"multiple specific overlays", "node1", "", []string{"o1", "o2"}, "overlays/node1/o1-o2.img", true}, - {"multiple specific overlays with context", "node1", "system", []string{"o1", "o2"}, "overlays/node1/__SYSTEM__.img", true}, + { + description: "empty inputs produces no result", + node: "", + context: "", + overlays: nil, + result: "", + }, + { + description: "a node with no context or overlays produces no result", + node: "node1", + context: "", + overlays: nil, + result: "", + }, + { + description: "overlays with no node or context points to a combined overlay image", + node: "", + context: "", + overlays: []string{"o1", "o2"}, + result: "overlays/o1-o2.img", + }, + { + description: "system overlay for a node points to the node's system overlay image", + node: "node1", + context: "system", + overlays: []string{"o1"}, + result: "overlays/node1/__SYSTEM__.img", + }, + { + description: "runtime overlay for a node points to the node's runtime overlay image", + node: "node1", + context: "runtime", + overlays: nil, + result: "overlays/node1/__RUNTIME__.img", + }, + { + description: "a specific overlay for a node points to that specific overlay image for that node", + node: "node1", + context: "", + overlays: []string{"o1"}, + result: "overlays/node1/o1.img", + }, + { + description: "a specific set of overlays for a node points to a combined overlay image for that node", + node: "node1", + context: "", + overlays: []string{"o1", "o2"}, + result: "overlays/node1/o1-o2.img", + }, + { + description: "a specific set of overlays for a node while also specifying a context points to the contextual overlay image for that node", + node: "node1", + context: "system", + overlays: []string{"o1", "o2"}, + result: "overlays/node1/__SYSTEM__.img", + }, } func Test_getOverlayFile(t *testing.T) { @@ -50,11 +97,7 @@ func Test_getOverlayFile(t *testing.T) { nodeInfo.RuntimeOverlay.SetSlice(tt.overlays) nodeInfo.SystemOverlay.SetSlice(tt.overlays) result, err := getOverlayFile(nodeInfo, tt.context, tt.overlays, false) - if !tt.succeed { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } + assert.NoError(t, err) if tt.result != "" { tt.result = path.Join(overlayPDir, tt.result) }