Merge pull request #903 from anderbubble/896-overlay-names
Fix warewulfd for new contextual overlay names
This commit is contained in:
1
go.mod
1
go.mod
@@ -79,6 +79,7 @@ require (
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/rootless-containers/proto v0.1.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sassoftware/go-rpmutils v0.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
|
||||
|
||||
10
go.sum
10
go.sum
@@ -409,6 +409,7 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
@@ -863,6 +864,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/klauspost/compress v1.10.9/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.12.1 h1:/+xsCsk06wE38cyiqOR/o7U2fSftcH72xD+BQXmja/g=
|
||||
github.com/klauspost/compress v1.12.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@@ -1043,6 +1045,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||
github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE=
|
||||
github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
@@ -1116,6 +1120,7 @@ github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKw
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
@@ -1146,6 +1151,7 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
|
||||
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -1174,6 +1180,7 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
|
||||
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -1192,6 +1199,7 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4=
|
||||
@@ -1454,6 +1462,7 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -1504,6 +1513,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
||||
@@ -28,29 +28,32 @@ func OverlaySourceDir(overlayName string) string {
|
||||
return overlaypath
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the overlay name of the image for a given node
|
||||
*/
|
||||
func OverlayImage(nodeName string, overlayName []string, img_context ...string) string {
|
||||
// OverlayImage returns the full path to an overlay image based on the
|
||||
// context and the overlays contained in it.
|
||||
//
|
||||
// If a context is provided, the image file name is based on that
|
||||
// context name, in the form __{CONTEXT}__.
|
||||
//
|
||||
// If the context is empty ("") the image file name is a concatenated
|
||||
// list of the contained overlays joined by "-".
|
||||
//
|
||||
// If the context is empty and no overlays are specified, the empty
|
||||
// string is returned.
|
||||
func OverlayImage(nodeName string, context string, overlayNames []string) string {
|
||||
var name string
|
||||
var context string
|
||||
|
||||
/* Check optional context argument. If missing, default to legacy. */
|
||||
if len(img_context) == 0 {
|
||||
context = "legacy"
|
||||
if context != "" {
|
||||
if len(overlayNames) > 0 {
|
||||
wwlog.Warn("context(%v) and overlays(%v) specified: prioritizing context(%v)",
|
||||
context, overlayNames, context)
|
||||
}
|
||||
name = "__" + strings.ToUpper(context) + "__.img"
|
||||
} else if len(overlayNames) > 0 {
|
||||
name = strings.Join(overlayNames, "-")+".img"
|
||||
} else {
|
||||
context = img_context[0]
|
||||
wwlog.Warn("unable to generate overlay image path: no context or overlays specified")
|
||||
return ""
|
||||
}
|
||||
|
||||
conf := warewulfconf.Get()
|
||||
|
||||
switch context {
|
||||
case "legacy":
|
||||
name = strings.Join(overlayName, "-")+".img"
|
||||
default:
|
||||
wwlog.Warn("Context %s passed to OverlayImage(), using %s to build image name.", context, "__" + strings.ToUpper(context) + "__")
|
||||
name = "__" + strings.ToUpper(context) + "__.img"
|
||||
}
|
||||
|
||||
return path.Join(conf.Paths.WWProvisiondir, "overlays/", nodeName, name)
|
||||
}
|
||||
|
||||
38
internal/pkg/overlay/config_test.go
Normal file
38
internal/pkg/overlay/config_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"testing"
|
||||
warewulfconf "github.com/hpcng/warewulf/internal/pkg/config"
|
||||
)
|
||||
|
||||
var overlayImageTests = []struct{
|
||||
description string
|
||||
node string
|
||||
context string
|
||||
overlays []string
|
||||
image string
|
||||
}{
|
||||
{"all empty", "", "", nil, ""},
|
||||
{"empty with named context", "", "system", nil, "p/overlays/__SYSTEM__.img"},
|
||||
{"empty with named overlay", "", "", []string{"o1"}, "p/overlays/o1.img"},
|
||||
{"empty with two named overlays", "", "", []string{"o1", "o2"}, "p/overlays/o1-o2.img"},
|
||||
{"empty node", "node1", "", nil, ""},
|
||||
{"node system overlay", "node1", "system", nil, "p/overlays/node1/__SYSTEM__.img"},
|
||||
{"node runtime overlay", "node1", "runtime", nil, "p/overlays/node1/__RUNTIME__.img"},
|
||||
{"node single overlay", "node1", "", []string{"o1"}, "p/overlays/node1/o1.img"},
|
||||
{"node two overlays", "node1", "", []string{"o1", "o2"}, "p/overlays/node1/o1-o2.img"},
|
||||
{"node with context and overlays", "node1", "system", []string{"o1", "o2"}, "p/overlays/node1/__SYSTEM__.img"},
|
||||
}
|
||||
|
||||
func Test_OverlayImage(t *testing.T) {
|
||||
conf := warewulfconf.Get()
|
||||
conf.Paths.WWProvisiondir = "p"
|
||||
for _, tt := range overlayImageTests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
out := OverlayImage(tt.node, tt.context, tt.overlays)
|
||||
if tt.image != out {
|
||||
t.Errorf("got %q, want %q", out, tt.image)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -47,13 +47,13 @@ func BuildAllOverlays(nodes []node.NodeInfo) error {
|
||||
|
||||
sysOverlays := n.SystemOverlay.GetSlice()
|
||||
wwlog.Info("Building system overlays for %s: [%s]", n.Id.Get(), strings.Join(sysOverlays, ", "))
|
||||
err := BuildOverlay(n, sysOverlays, "system")
|
||||
err := BuildOverlay(n, "system", sysOverlays)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not build system overlays %v for node %s", sysOverlays, n.Id.Get())
|
||||
}
|
||||
runOverlays := n.RuntimeOverlay.GetSlice()
|
||||
wwlog.Info("Building runtime overlays for %s: [%s]", n.Id.Get(), strings.Join(runOverlays, ", "))
|
||||
err = BuildOverlay(n, runOverlays, "runtime")
|
||||
err = BuildOverlay(n, "runtime", runOverlays)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not build runtime overlays %v for node %s", runOverlays, n.Id.Get())
|
||||
}
|
||||
@@ -67,11 +67,11 @@ func BuildAllOverlays(nodes []node.NodeInfo) error {
|
||||
func BuildSpecificOverlays(nodes []node.NodeInfo, overlayNames []string) error {
|
||||
for _, n := range nodes {
|
||||
wwlog.Info("Building overlay for %s: %v", n.Id.Get(), overlayNames)
|
||||
for _, overlayName := range overlayNames {
|
||||
err := BuildOverlay(n, []string{overlayName})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not build overlay %s for node %s", overlayName, n.Id.Get())
|
||||
}
|
||||
for _, overlayName := range overlayNames {
|
||||
err := BuildOverlay(n, "", []string{overlayName})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not build overlay %s for node %s", overlayName, n.Id.Get())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -140,18 +140,10 @@ func OverlayInit(overlayName string) error {
|
||||
/*
|
||||
Build the given overlays for a node and create a Image for them
|
||||
*/
|
||||
func BuildOverlay(nodeInfo node.NodeInfo, overlayNames []string, img_context ...string) error {
|
||||
var context string
|
||||
/* Check optional context argument. If missing, default to legacy. */
|
||||
if len(img_context) == 0 {
|
||||
context = "legacy"
|
||||
} else {
|
||||
context = img_context[0]
|
||||
}
|
||||
|
||||
func BuildOverlay(nodeInfo node.NodeInfo, context string, overlayNames []string) error {
|
||||
// create the dir where the overlay images will reside
|
||||
name := fmt.Sprintf("overlay %s/%v", nodeInfo.Id.Get(), overlayNames)
|
||||
overlayImage := OverlayImage(nodeInfo.Id.Get(), overlayNames, context)
|
||||
overlayImage := OverlayImage(nodeInfo.Id.Get(), context, overlayNames)
|
||||
overlayImageDir := path.Dir(overlayImage)
|
||||
|
||||
err := os.MkdirAll(overlayImageDir, 0755)
|
||||
|
||||
232
internal/pkg/overlay/overlay_test.go
Normal file
232
internal/pkg/overlay/overlay_test.go
Normal file
@@ -0,0 +1,232 @@
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sort"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/sassoftware/go-rpmutils/cpio"
|
||||
warewulfconf "github.com/hpcng/warewulf/internal/pkg/config"
|
||||
"github.com/hpcng/warewulf/internal/pkg/node"
|
||||
)
|
||||
|
||||
var buildOverlayTests = []struct{
|
||||
description string
|
||||
nodeName string
|
||||
context string
|
||||
overlays []string
|
||||
image string
|
||||
contents []string
|
||||
}{
|
||||
{"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"}},
|
||||
}
|
||||
|
||||
func Test_BuildOverlay(t *testing.T) {
|
||||
conf := warewulfconf.Get()
|
||||
overlayDir, overlayDirErr := os.MkdirTemp(os.TempDir(), "ww-test-overlay-*")
|
||||
assert.NoError(t, overlayDirErr)
|
||||
defer os.RemoveAll(overlayDir)
|
||||
conf.Paths.WWOverlaydir = overlayDir
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0700))
|
||||
{ _, err := os.Create(path.Join(overlayDir, "o1", "o1.txt")); assert.NoError(t, err) }
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0700))
|
||||
{ _, err := os.Create(path.Join(overlayDir, "o2", "o2.txt")); assert.NoError(t, err) }
|
||||
|
||||
for _, tt := range buildOverlayTests {
|
||||
assert.True(t, (tt.image != "" && tt.contents != nil) || (tt.image == "" && tt.contents == nil),
|
||||
"image and contents must eiher be populated or empty together")
|
||||
|
||||
nodeInfo := node.NodeInfo{}
|
||||
nodeInfo.Id.Set(tt.nodeName)
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
provisionDir, provisionDirErr := os.MkdirTemp(os.TempDir(), "ww-test-provision-*")
|
||||
assert.NoError(t, provisionDirErr)
|
||||
defer os.RemoveAll(provisionDir)
|
||||
conf.Paths.WWProvisiondir = provisionDir
|
||||
|
||||
err := BuildOverlay(nodeInfo, tt.context, tt.overlays)
|
||||
if len(tt.image) > 0 {
|
||||
image := path.Join(provisionDir, "overlays", tt.image)
|
||||
assert.FileExists(t, image)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sort.Strings(tt.contents)
|
||||
files := cpioFiles(t, image)
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var buildAllOverlaysTests = []struct{
|
||||
description string
|
||||
nodes []string
|
||||
systemOverlays []string
|
||||
runtimeOverlays []string
|
||||
succeed bool
|
||||
}{
|
||||
{"no nodes", nil, nil, nil, true},
|
||||
{"single empty node", []string{"node1"}, nil, nil, false},
|
||||
{"two empty node", []string{"node1", "node2"}, nil, nil, false},
|
||||
{"single node with system overlay", []string{"node1"},
|
||||
[]string{"o1"}, nil, false},
|
||||
{"two nodes with system overlays", []string{"node1", "node2"},
|
||||
[]string{"o1", "o1,o2"}, nil, false},
|
||||
{"single node with runtime overlay", []string{"node1"},
|
||||
nil, []string{"o1"}, false},
|
||||
{"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},
|
||||
{"two nodes with full overlays", []string{"node1", "node2"},
|
||||
[]string{"o1", "o1,o2"}, []string{"o2", "o2"}, true},
|
||||
}
|
||||
|
||||
|
||||
func Test_BuildAllOverlays(t *testing.T) {
|
||||
conf := warewulfconf.Get()
|
||||
overlayDir, overlayDirErr := os.MkdirTemp(os.TempDir(), "ww-test-overlay-*")
|
||||
assert.NoError(t, overlayDirErr)
|
||||
defer os.RemoveAll(overlayDir)
|
||||
conf.Paths.WWOverlaydir = overlayDir
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0700))
|
||||
|
||||
for _, tt := range buildAllOverlaysTests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
provisionDir, provisionDirErr := os.MkdirTemp(os.TempDir(), "ww-test-provision-*")
|
||||
assert.NoError(t, provisionDirErr)
|
||||
defer os.RemoveAll(provisionDir)
|
||||
conf.Paths.WWProvisiondir = provisionDir
|
||||
|
||||
var nodes []node.NodeInfo
|
||||
for i, nodeName := range tt.nodes {
|
||||
nodeInfo := node.NodeInfo{}
|
||||
nodeInfo.Id.Set(nodeName)
|
||||
if tt.systemOverlays != nil {
|
||||
nodeInfo.SystemOverlay.SetSlice(strings.Split(tt.systemOverlays[i], ","))
|
||||
}
|
||||
if tt.runtimeOverlays != nil {
|
||||
nodeInfo.RuntimeOverlay.SetSlice(strings.Split(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 {
|
||||
assert.FileExists(t, path.Join(provisionDir, "overlays", nodeName, "__SYSTEM__.img"))
|
||||
assert.FileExists(t, path.Join(provisionDir, "overlays", nodeName, "__RUNTIME__.img"))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var buildSpecificOverlaysTests = []struct{
|
||||
description string
|
||||
nodes []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},
|
||||
}
|
||||
|
||||
|
||||
func Test_BuildSpecificOverlays(t *testing.T) {
|
||||
conf := warewulfconf.Get()
|
||||
overlayDir, overlayDirErr := os.MkdirTemp(os.TempDir(), "ww-test-overlay-*")
|
||||
assert.NoError(t, overlayDirErr)
|
||||
defer os.RemoveAll(overlayDir)
|
||||
conf.Paths.WWOverlaydir = overlayDir
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0700))
|
||||
|
||||
for _, tt := range buildSpecificOverlaysTests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
provisionDir, provisionDirErr := os.MkdirTemp(os.TempDir(), "ww-test-provision-*")
|
||||
assert.NoError(t, provisionDirErr)
|
||||
defer os.RemoveAll(provisionDir)
|
||||
conf.Paths.WWProvisiondir = provisionDir
|
||||
|
||||
var nodes []node.NodeInfo
|
||||
for _, nodeName := range tt.nodes {
|
||||
nodeInfo := node.NodeInfo{}
|
||||
nodeInfo.Id.Set(nodeName)
|
||||
nodes = append(nodes, nodeInfo)
|
||||
}
|
||||
err := BuildSpecificOverlays(nodes, strings.Split(tt.overlays, ","))
|
||||
if !tt.succeed {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
for _, image := range tt.images {
|
||||
assert.FileExists(t, path.Join(provisionDir, "overlays", image))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func dirIsEmpty(t *testing.T, name string) bool {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return true
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
dirnames, err2 := f.Readdirnames(1)
|
||||
if err2 == io.EOF {
|
||||
t.Log(err2)
|
||||
return true
|
||||
}
|
||||
t.Log(dirnames)
|
||||
return false
|
||||
}
|
||||
|
||||
func cpioFiles(t *testing.T, name string) (files []string) {
|
||||
f, openErr := os.Open(name)
|
||||
if openErr != nil { return }
|
||||
defer f.Close()
|
||||
|
||||
reader := cpio.NewReader(f)
|
||||
for {
|
||||
header, err := reader.Next()
|
||||
if err != nil { return }
|
||||
files = append(files, header.Filename())
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/container"
|
||||
@@ -29,6 +30,13 @@ type iPxeTemplate struct {
|
||||
KernelOverride string
|
||||
}
|
||||
|
||||
var status_stages = map[string]string{
|
||||
"ipxe": "IPXE",
|
||||
"kernel": "KERNEL",
|
||||
"kmods": "KMODS_OVERLAY",
|
||||
"system": "SYSTEM_OVERLAY",
|
||||
"runtime": "RUNTIME_OVERLAY"}
|
||||
|
||||
func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
conf := warewulfconf.Get()
|
||||
|
||||
@@ -49,17 +57,8 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
status_stages := map[string]string{
|
||||
"ipxe": "IPXE",
|
||||
"kernel": "KERNEL",
|
||||
"kmods": "KMODS_OVERLAY",
|
||||
"system": "SYSTEM_OVERLAY",
|
||||
"runtime": "RUNTIME_OVERLAY"}
|
||||
|
||||
status_stage := status_stages[rinfo.stage]
|
||||
var stage_overlays []string
|
||||
var stage_file string = ""
|
||||
var img_context string = "legacy" /* Default to old image name behavior */
|
||||
var stage_file string
|
||||
|
||||
// TODO: when module version is upgraded to go1.18, should be 'any' type
|
||||
var tmpl_data interface{}
|
||||
@@ -127,33 +126,20 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
wwlog.Warn("No container set for node %s", node.Id.Get())
|
||||
}
|
||||
|
||||
} else if rinfo.stage == "system" {
|
||||
if len(node.SystemOverlay.GetSlice()) != 0 {
|
||||
stage_overlays = node.SystemOverlay.GetSlice()
|
||||
img_context = rinfo.stage
|
||||
} else if rinfo.stage == "system" || rinfo.stage == "runtime" {
|
||||
var context string
|
||||
var request_overlays []string
|
||||
|
||||
if len(rinfo.overlay) > 0 {
|
||||
request_overlays = strings.Split(rinfo.overlay, ",")
|
||||
} else {
|
||||
wwlog.Warn("No system overlay set for node %s", node.Id.Get())
|
||||
context = rinfo.stage
|
||||
}
|
||||
|
||||
} else if rinfo.stage == "runtime" {
|
||||
if rinfo.overlay != "" {
|
||||
stage_overlays = []string{rinfo.overlay}
|
||||
img_context = "legacy"
|
||||
} else if len(node.RuntimeOverlay.GetSlice()) != 0 {
|
||||
stage_overlays = node.RuntimeOverlay.GetSlice()
|
||||
img_context = rinfo.stage
|
||||
} else {
|
||||
wwlog.Warn("No runtime overlay set for node %s", node.Id.Get())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(stage_overlays) > 0 {
|
||||
stage_file, err = getOverlayFile(
|
||||
node.Id.Get(),
|
||||
stage_overlays,
|
||||
conf.Warewulf.AutobuildOverlays,
|
||||
img_context )
|
||||
context,
|
||||
request_overlays,
|
||||
conf.Warewulf.AutobuildOverlays)
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
69
internal/pkg/warewulfd/provision_test.go
Normal file
69
internal/pkg/warewulfd/provision_test.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/node"
|
||||
warewulfconf "github.com/hpcng/warewulf/internal/pkg/config"
|
||||
)
|
||||
|
||||
|
||||
var provisionSendTests = []struct{
|
||||
description string
|
||||
url string
|
||||
body string
|
||||
status int
|
||||
}{
|
||||
{"system overlay", "/overlay-system/00:00:00:ff:ff:ff", "system overlay", 200},
|
||||
{"runtime overlay", "/overlay-runtime/00:00:00:ff:ff:ff", "runtime overlay", 200},
|
||||
{"fake overlay", "/overlay-system/00:00:00:ff:ff:ff?overlay=fake", "", 404},
|
||||
{"specific overlay", "/overlay-system/00:00:00:ff:ff:ff?overlay=o1", "specific overlay", 200},
|
||||
}
|
||||
|
||||
func Test_ProvisionSend(t *testing.T) {
|
||||
file, err := os.CreateTemp(os.TempDir(), "ww-test-nodes.conf-*")
|
||||
assert.NoError(t, err)
|
||||
defer file.Close()
|
||||
{ _, err := file.WriteString(`WW_INTERNAL: 43
|
||||
nodes:
|
||||
n1:
|
||||
network devices:
|
||||
default:
|
||||
hwaddr: 00:00:00:ff:ff:ff`); assert.NoError(t, err) }
|
||||
assert.NoError(t, file.Sync())
|
||||
node.ConfigFile = file.Name()
|
||||
dbErr := LoadNodeDB()
|
||||
assert.NoError(t, dbErr)
|
||||
|
||||
provisionDir, provisionDirErr := os.MkdirTemp(os.TempDir(), "ww-test-provision-*")
|
||||
assert.NoError(t, provisionDirErr)
|
||||
defer os.RemoveAll(provisionDir)
|
||||
conf := warewulfconf.Get()
|
||||
conf.Paths.WWProvisiondir = provisionDir
|
||||
conf.Warewulf.Secure = false
|
||||
assert.NoError(t, os.MkdirAll(path.Join(provisionDir, "overlays", "n1"), 0700))
|
||||
assert.NoError(t, os.WriteFile(path.Join(provisionDir, "overlays", "n1", "__SYSTEM__.img"), []byte("system overlay"), 0600))
|
||||
assert.NoError(t, os.WriteFile(path.Join(provisionDir, "overlays", "n1", "__RUNTIME__.img"), []byte("runtime overlay"), 0600))
|
||||
assert.NoError(t, os.WriteFile(path.Join(provisionDir, "overlays", "n1", "o1.img"), []byte("specific overlay"), 0600))
|
||||
|
||||
for _, tt := range provisionSendTests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, tt.url, nil)
|
||||
w := httptest.NewRecorder()
|
||||
ProvisionSend(w, req)
|
||||
res := w.Result()
|
||||
defer res.Body.Close()
|
||||
|
||||
data, readErr := ioutil.ReadAll(res.Body)
|
||||
assert.NoError(t, readErr)
|
||||
assert.Equal(t, tt.body, string(data))
|
||||
assert.Equal(t, tt.status, res.StatusCode)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -43,11 +43,11 @@ func sendFile(
|
||||
|
||||
func getOverlayFile(
|
||||
nodeId string,
|
||||
context string,
|
||||
stage_overlays []string,
|
||||
autobuild bool,
|
||||
img_context string) (stage_file string, err error) {
|
||||
autobuild bool) (stage_file string, err error) {
|
||||
|
||||
stage_file = overlay.OverlayImage(nodeId, stage_overlays, img_context)
|
||||
stage_file = overlay.OverlayImage(nodeId, context, stage_overlays)
|
||||
err = nil
|
||||
|
||||
build := !util.IsFile(stage_file)
|
||||
|
||||
42
internal/pkg/warewulfd/util_test.go
Normal file
42
internal/pkg/warewulfd/util_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
warewulfconf "github.com/hpcng/warewulf/internal/pkg/config"
|
||||
)
|
||||
|
||||
var getOverlayFileTests = []struct{
|
||||
description string
|
||||
node string
|
||||
context string
|
||||
overlays []string
|
||||
result string
|
||||
succeed bool
|
||||
}{
|
||||
{"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},
|
||||
}
|
||||
|
||||
func Test_getOverlayFile(t *testing.T) {
|
||||
conf := warewulfconf.Get()
|
||||
conf.Paths.WWProvisiondir = "p"
|
||||
for _, tt := range getOverlayFileTests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
result, err := getOverlayFile(tt.node, tt.context, tt.overlays, false)
|
||||
if !tt.succeed {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.result, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user