Remove Chdir from overlay.BuildOverlayIndir
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
@@ -14,7 +14,6 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
@@ -23,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDoesNotExist = errors.New("overlay does not exist")
|
||||
ErrDoesNotExist = fmt.Errorf("overlay does not exist")
|
||||
)
|
||||
|
||||
// Overlay represents an overlay directory path.
|
||||
@@ -231,155 +230,160 @@ func BuildOverlay(nodeConf node.Node, context string, overlayNames []string) err
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
Build the given overlays for a node in the given directory. If the given does not
|
||||
exists it will be created.
|
||||
*/
|
||||
var regFile *regexp.Regexp
|
||||
var regLink *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
regFile = regexp.MustCompile(`.*{{\s*/\*\s*file\s*["'](.*)["']\s*\*/\s*}}.*`)
|
||||
regLink = regexp.MustCompile(`.*{{\s*/\*\s*softlink\s*["'](.*)["']\s*\*/\s*}}.*`)
|
||||
}
|
||||
|
||||
// Build the given overlays for a node in the given directory.
|
||||
func BuildOverlayIndir(nodeData node.Node, overlayNames []string, outputDir string) error {
|
||||
if len(overlayNames) == 0 {
|
||||
return nil
|
||||
}
|
||||
if !util.IsDir(outputDir) {
|
||||
return errors.Errorf("output must a be a directory: %s", outputDir)
|
||||
return fmt.Errorf("output must a be a directory: %s", outputDir)
|
||||
}
|
||||
|
||||
if !util.ValidString(strings.Join(overlayNames, ""), "^[a-zA-Z0-9-._:]+$") {
|
||||
return errors.Errorf("overlay names contains illegal characters: %v", overlayNames)
|
||||
return fmt.Errorf("overlay names contains illegal characters: %v", overlayNames)
|
||||
}
|
||||
|
||||
// Temporarily set umask to 0000, so directories in the overlay retain permissions
|
||||
defer syscall.Umask(syscall.Umask(0))
|
||||
|
||||
wwlog.Verbose("Processing node/overlay: %s/%s", nodeData.Id(), strings.Join(overlayNames, "-"))
|
||||
wwlog.Verbose("Processing node/overlays: %s/%s", nodeData.Id(), strings.Join(overlayNames, ","))
|
||||
for _, overlayName := range overlayNames {
|
||||
wwlog.Verbose("Building overlay %s for node %s in %s", overlayName, nodeData.Id(), outputDir)
|
||||
overlaySourceDir := GetOverlay(overlayName).Rootfs()
|
||||
wwlog.Debug("Changing directory to OverlayDir: %s", overlaySourceDir)
|
||||
err := os.Chdir(overlaySourceDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("directory: %s name: %s err: %w", overlaySourceDir, overlayName, ErrDoesNotExist)
|
||||
overlayRootfs := GetOverlay(overlayName).Rootfs()
|
||||
if !util.IsDir(overlayRootfs) {
|
||||
return fmt.Errorf("overlay %s: %w", overlayName, ErrDoesNotExist)
|
||||
}
|
||||
|
||||
wwlog.Verbose("Walking the overlay structure: %s", overlaySourceDir)
|
||||
err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error {
|
||||
wwlog.Debug("Walking the overlay structure: %s", overlayRootfs)
|
||||
err := filepath.Walk(overlayRootfs, func(walkPath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("error for %s: %w", location, err)
|
||||
return fmt.Errorf("error for %s: %w", walkPath, err)
|
||||
}
|
||||
wwlog.Debug("Found overlay file: %s", walkPath)
|
||||
|
||||
wwlog.Debug("Found overlay file: %s", location)
|
||||
relPath, relErr := filepath.Rel(overlayRootfs, walkPath)
|
||||
if relErr != nil {
|
||||
wwlog.Warn("Error computing relative path for %s: %v", walkPath, relErr)
|
||||
return relErr
|
||||
}
|
||||
outputPath := path.Join(outputDir, relPath)
|
||||
|
||||
if info.IsDir() {
|
||||
wwlog.Debug("Found directory: %s", location)
|
||||
wwlog.Debug("Found directory: %s", walkPath)
|
||||
|
||||
err = os.MkdirAll(path.Join(outputDir, location), info.Mode())
|
||||
if err != nil {
|
||||
if err = os.MkdirAll(outputPath, info.Mode()); err != nil {
|
||||
return fmt.Errorf("could not create directory within overlay: %w", err)
|
||||
}
|
||||
err = util.CopyUIDGID(location, path.Join(outputDir, location))
|
||||
if err != nil {
|
||||
if err = util.CopyUIDGID(walkPath, outputPath); err != nil {
|
||||
return fmt.Errorf("failed setting permissions on overlay directory: %w", err)
|
||||
}
|
||||
|
||||
wwlog.Debug("Created directory in overlay: %s", location)
|
||||
wwlog.Debug("Created directory in overlay: %s", outputPath)
|
||||
|
||||
} else if filepath.Ext(location) == ".ww" {
|
||||
} else if filepath.Ext(walkPath) == ".ww" {
|
||||
originalOutputPath := outputPath
|
||||
outputPath := strings.TrimSuffix(outputPath, ".ww")
|
||||
tstruct, err := InitStruct(overlayName, nodeData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initial data for %s: %w", nodeData.Id(), err)
|
||||
}
|
||||
tstruct.BuildSource = path.Join(overlaySourceDir, location)
|
||||
wwlog.Verbose("Evaluating overlay template file: %s", location)
|
||||
destFile := strings.TrimSuffix(location, ".ww")
|
||||
tstruct.BuildSource = walkPath
|
||||
wwlog.Verbose("Evaluating overlay template file: %s", walkPath)
|
||||
|
||||
buffer, backupFile, writeFile, err := RenderTemplateFile(location, tstruct)
|
||||
buffer, backupFile, writeFile, err := RenderTemplateFile(walkPath, tstruct)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to render template %s: %w", location, err)
|
||||
return fmt.Errorf("failed to render template %s: %w", walkPath, err)
|
||||
}
|
||||
if writeFile {
|
||||
destFileName := destFile
|
||||
var fileBuffer bytes.Buffer
|
||||
// search for magic file name comment
|
||||
fileScanner := bufio.NewScanner(bytes.NewReader(buffer.Bytes()))
|
||||
fileScanner.Split(ScanLines)
|
||||
regFile := regexp.MustCompile(`.*{{\s*/\*\s*file\s*["'](.*)["']\s*\*/\s*}}.*`)
|
||||
regLink := regexp.MustCompile(`.*{{\s*/\*\s*softlink\s*["'](.*)["']\s*\*/\s*}}.*`)
|
||||
foundFileComment := false
|
||||
for fileScanner.Scan() {
|
||||
line := fileScanner.Text()
|
||||
filenameFromTemplate := regFile.FindAllStringSubmatch(line, -1)
|
||||
softlinkFromTemplate := regLink.FindAllStringSubmatch(line, -1)
|
||||
if len(softlinkFromTemplate) != 0 {
|
||||
wwlog.Debug("Creating soft link %s -> %s", destFileName, softlinkFromTemplate[0][1])
|
||||
return os.Symlink(softlinkFromTemplate[0][1], path.Join(outputDir, destFileName))
|
||||
} else if len(filenameFromTemplate) != 0 {
|
||||
wwlog.Debug("Writing file %s", filenameFromTemplate[0][1])
|
||||
if foundFileComment {
|
||||
err = CarefulWriteBuffer(path.Join(outputDir, destFileName),
|
||||
fileBuffer, backupFile, info.Mode())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write file from template: %w", err)
|
||||
}
|
||||
err = util.CopyUIDGID(location, path.Join(outputDir, destFileName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed setting permissions on template output file: %w", err)
|
||||
}
|
||||
fileBuffer.Reset()
|
||||
if !writeFile {
|
||||
return nil
|
||||
}
|
||||
var fileBuffer bytes.Buffer
|
||||
// search for magic file name comment
|
||||
fileScanner := bufio.NewScanner(bytes.NewReader(buffer.Bytes()))
|
||||
fileScanner.Split(ScanLines)
|
||||
foundFileComment := false
|
||||
for fileScanner.Scan() {
|
||||
line := fileScanner.Text()
|
||||
filenameFromTemplate := regFile.FindAllStringSubmatch(line, -1)
|
||||
softlinkFromTemplate := regLink.FindAllStringSubmatch(line, -1)
|
||||
if len(softlinkFromTemplate) != 0 {
|
||||
wwlog.Debug("Creating soft link %s -> %s", outputPath, softlinkFromTemplate[0][1])
|
||||
return os.Symlink(softlinkFromTemplate[0][1], outputPath)
|
||||
} else if len(filenameFromTemplate) != 0 {
|
||||
wwlog.Debug("Writing file %s", filenameFromTemplate[0][1])
|
||||
if foundFileComment {
|
||||
err = CarefulWriteBuffer(outputPath, fileBuffer, backupFile, info.Mode())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write file from template: %w", err)
|
||||
}
|
||||
destFileName = path.Join(path.Dir(destFile), filenameFromTemplate[0][1])
|
||||
foundFileComment = true
|
||||
} else {
|
||||
_, _ = fileBuffer.WriteString(line)
|
||||
err = util.CopyUIDGID(walkPath, outputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed setting permissions on template output file: %w", err)
|
||||
}
|
||||
fileBuffer.Reset()
|
||||
}
|
||||
outputPath = path.Join(path.Dir(originalOutputPath), filenameFromTemplate[0][1])
|
||||
foundFileComment = true
|
||||
} else {
|
||||
if _, err = fileBuffer.WriteString(line); err != nil {
|
||||
return fmt.Errorf("could not write to template buffer: %w", err)
|
||||
}
|
||||
}
|
||||
err = CarefulWriteBuffer(path.Join(outputDir, destFileName), fileBuffer, backupFile, info.Mode())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write file from template: %w", err)
|
||||
}
|
||||
err = util.CopyUIDGID(location, path.Join(outputDir, destFileName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed setting permissions on template output file: %w", err)
|
||||
}
|
||||
|
||||
wwlog.Debug("Wrote template file into overlay: %s", destFile)
|
||||
|
||||
// } else if b, _ := regexp.MatchString(`\.ww[a-zA-Z0-9\-\._]*$`, location); b {
|
||||
// wwlog.Debug("Ignoring WW template file: %s", location)
|
||||
}
|
||||
err = CarefulWriteBuffer(outputPath, fileBuffer, backupFile, info.Mode())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write file from template: %w", err)
|
||||
}
|
||||
err = util.CopyUIDGID(walkPath, outputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed setting permissions on template output file: %w", err)
|
||||
}
|
||||
wwlog.Debug("Wrote template file into overlay: %s", outputPath)
|
||||
|
||||
} else if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
wwlog.Debug("Found symlink %s", location)
|
||||
destination, err := os.Readlink(location)
|
||||
wwlog.Debug("Found symlink %s", walkPath)
|
||||
target, err := os.Readlink(walkPath)
|
||||
if err != nil {
|
||||
wwlog.ErrorExc(err, "")
|
||||
return fmt.Errorf("failed reading symlink: %w", err)
|
||||
}
|
||||
if util.IsFile(path.Join(outputDir, location)) {
|
||||
if !util.IsFile(path.Join(outputDir, location+".wwbackup")) {
|
||||
wwlog.Debug("Target exists, creating backup file")
|
||||
err = os.Rename(path.Join(outputDir, location), path.Join(outputDir, location+".wwbackup"))
|
||||
if util.IsFile(outputPath) {
|
||||
backupPath := outputPath + ".wwbackup"
|
||||
if !util.IsFile(backupPath) {
|
||||
wwlog.Debug("Output file already exists: moving to backup file")
|
||||
if err = os.Rename(outputPath, backupPath); err != nil {
|
||||
return fmt.Errorf("failed renaming to backup file: %w", err)
|
||||
}
|
||||
} else {
|
||||
wwlog.Debug("%s exists, keeping the backup file", path.Join(outputDir, location+".wwbackup"))
|
||||
err = os.Remove(path.Join(outputDir, location))
|
||||
}
|
||||
if err != nil {
|
||||
wwlog.ErrorExc(err, "")
|
||||
wwlog.Debug("%s exists, keeping the backup file", backupPath)
|
||||
if err = os.Remove(outputPath); err != nil {
|
||||
return fmt.Errorf("failed removing existing file: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
err = os.Symlink(destination, path.Join(outputDir, location))
|
||||
if err != nil {
|
||||
wwlog.ErrorExc(err, "")
|
||||
if err = os.Symlink(target, outputPath); err != nil {
|
||||
return fmt.Errorf("failed creating symlink: %w", err)
|
||||
}
|
||||
wwlog.Debug("Created symlink file: %s", outputPath)
|
||||
} else {
|
||||
err := util.CopyFile(location, path.Join(outputDir, location))
|
||||
if err == nil {
|
||||
wwlog.Debug("Copied file into overlay: %s", location)
|
||||
} else {
|
||||
if err := util.CopyFile(walkPath, outputPath); err != nil {
|
||||
return fmt.Errorf("could not copy file into overlay: %w", err)
|
||||
}
|
||||
wwlog.Debug("Copied overlay file: %s", outputPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build overlay working directory: %w", err)
|
||||
return fmt.Errorf("failed to build overlay image directory: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/util"
|
||||
)
|
||||
|
||||
@@ -110,122 +110,263 @@ func Test_OverlayMethods(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var buildOverlayTests = []struct {
|
||||
description string
|
||||
nodeName string
|
||||
context string
|
||||
overlays []string
|
||||
image string
|
||||
contents []string
|
||||
hasFiles bool
|
||||
}{
|
||||
{
|
||||
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 context named image is generated",
|
||||
nodeName: "",
|
||||
context: "system",
|
||||
overlays: nil,
|
||||
image: "__SYSTEM__.img",
|
||||
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 context pointed overlay is generated",
|
||||
nodeName: "node1",
|
||||
context: "system",
|
||||
overlays: nil,
|
||||
image: "node1/__SYSTEM__.img",
|
||||
contents: nil,
|
||||
},
|
||||
{
|
||||
description: "if no node runtime overlays are specified, then context pointed overlay is generated",
|
||||
nodeName: "node1",
|
||||
context: "runtime",
|
||||
overlays: nil,
|
||||
image: "node1/__RUNTIME__.img",
|
||||
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_BuildOverlayIndir(t *testing.T) {
|
||||
var tests = map[string]struct {
|
||||
node node.Node
|
||||
overlays []string
|
||||
overlayFiles map[string]string
|
||||
overlayDirs []string
|
||||
overlaySymlinks map[string]string
|
||||
outputDir string
|
||||
outputFiles map[string]string
|
||||
outputDirs []string
|
||||
outputSymlinks map[string]string
|
||||
}{
|
||||
"empty": {
|
||||
outputDir: "/image",
|
||||
},
|
||||
"empty directory": {
|
||||
overlays: []string{"o1"},
|
||||
overlayDirs: []string{"/var/lib/warewulf/overlays/o1/rootfs/testdir"},
|
||||
outputDir: "/image",
|
||||
outputDirs: []string{"testdir"},
|
||||
},
|
||||
"single flat file": {
|
||||
overlays: []string{"o1"},
|
||||
overlayFiles: map[string]string{
|
||||
"/var/lib/warewulf/overlays/o1/rootfs/testfile": "A test file from o1",
|
||||
},
|
||||
outputDir: "/image",
|
||||
outputFiles: map[string]string{
|
||||
"testfile": "A test file from o1",
|
||||
},
|
||||
},
|
||||
"multiple overlays": {
|
||||
overlays: []string{"o1", "o2"},
|
||||
overlayFiles: map[string]string{
|
||||
"/var/lib/warewulf/overlays/o1/rootfs/t1.txt": "A test file from o1",
|
||||
"/var/lib/warewulf/overlays/o2/rootfs/t2.txt": "A test file from o2",
|
||||
},
|
||||
outputDir: "/image",
|
||||
outputFiles: map[string]string{
|
||||
"t1.txt": "A test file from o1",
|
||||
"t2.txt": "A test file from o2",
|
||||
},
|
||||
},
|
||||
"template": {
|
||||
node: node.Node{Profile: node.Profile{Comment: "A node comment"}},
|
||||
overlays: []string{"o1"},
|
||||
overlayFiles: map[string]string{
|
||||
"/var/lib/warewulf/overlays/o1/rootfs/t1.txt.ww": "{{ .Comment }}",
|
||||
"/image/t1.txt": "Previous content",
|
||||
},
|
||||
outputDir: "/image",
|
||||
outputFiles: map[string]string{
|
||||
"t1.txt": "A node comment",
|
||||
"t1.txt.wwbackup": "Previous content",
|
||||
},
|
||||
},
|
||||
"multifile": {
|
||||
overlays: []string{"o1"},
|
||||
overlayFiles: map[string]string{
|
||||
"/var/lib/warewulf/overlays/o1/rootfs/file.txt.ww": `
|
||||
{{- file "t1.txt" }}
|
||||
T1
|
||||
{{ file "t2.txt" }}
|
||||
T2
|
||||
{{ file "t3.txt" }}
|
||||
T3
|
||||
`,
|
||||
},
|
||||
outputDir: "/image",
|
||||
outputFiles: map[string]string{
|
||||
"t1.txt": "T1\n",
|
||||
"t2.txt": "T2\n",
|
||||
"t3.txt": "T3\n",
|
||||
},
|
||||
},
|
||||
"symlink": {
|
||||
overlays: []string{"o1"},
|
||||
overlaySymlinks: map[string]string{
|
||||
"/var/lib/warewulf/overlays/o1/rootfs/test-link": "/test-target",
|
||||
},
|
||||
outputDir: "/image",
|
||||
outputSymlinks: map[string]string{
|
||||
"test-link": "/test-target",
|
||||
},
|
||||
},
|
||||
"symlink from template": {
|
||||
overlays: []string{"o1"},
|
||||
overlayFiles: map[string]string{
|
||||
"/var/lib/warewulf/overlays/o1/rootfs/test-link.ww": `
|
||||
{{- softlink "/test-target" }}
|
||||
`,
|
||||
},
|
||||
outputDir: "/image",
|
||||
outputSymlinks: map[string]string{
|
||||
"test-link": "/test-target",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tt := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll(t)
|
||||
|
||||
for fileName, content := range tt.overlayFiles {
|
||||
env.WriteFile(t, fileName, content)
|
||||
}
|
||||
for _, dirName := range tt.overlayDirs {
|
||||
env.MkdirAll(t, dirName)
|
||||
}
|
||||
for linkName, target := range tt.overlaySymlinks {
|
||||
env.Symlink(t, target, linkName)
|
||||
}
|
||||
|
||||
env.MkdirAll(t, tt.outputDir)
|
||||
|
||||
assert.NoError(t, BuildOverlayIndir(tt.node, tt.overlays, env.GetPath(tt.outputDir)))
|
||||
dirFiles := tt.outputDirs
|
||||
for outputFile, _ := range tt.outputFiles {
|
||||
dirFiles = append(dirFiles, outputFile)
|
||||
}
|
||||
for outputFile, _ := range tt.outputSymlinks {
|
||||
dirFiles = append(dirFiles, outputFile)
|
||||
}
|
||||
sort.Strings(dirFiles)
|
||||
assert.Equal(t, dirFiles, env.ReadDir(t, tt.outputDir))
|
||||
for fileName, content := range tt.outputFiles {
|
||||
assert.Equal(t, content, env.ReadFile(t, path.Join(tt.outputDir, fileName)))
|
||||
}
|
||||
for _, dirName := range tt.outputDirs {
|
||||
assert.True(t, util.IsDir(env.GetPath(path.Join(tt.outputDir, dirName))), fmt.Sprintf("%s is not a directory", dirName))
|
||||
}
|
||||
for linkName, expTarget := range tt.outputSymlinks {
|
||||
target, err := os.Readlink(env.GetPath(path.Join(tt.outputDir, linkName)))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expTarget, target)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_BuildOverlay(t *testing.T) {
|
||||
var tests = []struct {
|
||||
description string
|
||||
nodeName string
|
||||
context string
|
||||
overlays []string
|
||||
image string
|
||||
contents []string
|
||||
hasFiles bool
|
||||
}{
|
||||
{
|
||||
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 context named image is generated",
|
||||
nodeName: "",
|
||||
context: "system",
|
||||
overlays: nil,
|
||||
image: "__SYSTEM__.img",
|
||||
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 context pointed overlay is generated",
|
||||
nodeName: "node1",
|
||||
context: "system",
|
||||
overlays: nil,
|
||||
image: "node1/__SYSTEM__.img",
|
||||
contents: nil,
|
||||
},
|
||||
{
|
||||
description: "if no node runtime overlays are specified, then context pointed overlay is generated",
|
||||
nodeName: "node1",
|
||||
context: "runtime",
|
||||
overlays: nil,
|
||||
image: "node1/__RUNTIME__.img",
|
||||
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"},
|
||||
},
|
||||
}
|
||||
|
||||
conf := warewulfconf.Get()
|
||||
overlayDir, overlayDirErr := os.MkdirTemp(os.TempDir(), "ww-test-overlay-*")
|
||||
assert.NoError(t, overlayDirErr)
|
||||
@@ -242,7 +383,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
for _, tt := range buildOverlayTests {
|
||||
for _, tt := range tests {
|
||||
nodeInfo := node.NewNode(tt.nodeName)
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
provisionDir, provisionDirErr := os.MkdirTemp(os.TempDir(), "ww-test-provision-*")
|
||||
@@ -269,83 +410,79 @@ 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
|
||||
createdOverlays []string
|
||||
}{
|
||||
{
|
||||
description: "empty input creates no overlays",
|
||||
nodes: nil,
|
||||
systemOverlays: nil,
|
||||
runtimeOverlays: nil,
|
||||
createdOverlays: nil,
|
||||
},
|
||||
{
|
||||
description: "a node with no overlays creates default system/runtime overlays",
|
||||
nodes: []string{"node1"},
|
||||
systemOverlays: [][]string{{"o1"}},
|
||||
runtimeOverlays: [][]string{{"o1"}},
|
||||
createdOverlays: []string{"node1/__SYSTEM__.img.gz", "node1/__RUNTIME__.img.gz"},
|
||||
},
|
||||
{
|
||||
description: "multiple nodes with no overlays creates default system/runtime overlays",
|
||||
nodes: []string{"node1", "node2"},
|
||||
systemOverlays: [][]string{{"o1"}, {"o2"}},
|
||||
runtimeOverlays: [][]string{{"o1"}, {"o2"}},
|
||||
createdOverlays: []string{"node1/__SYSTEM__.img.gz", "node1/__RUNTIME__.img.gz", "node2/__SYSTEM__.img.gz", "node2/__RUNTIME__.img.gz"},
|
||||
},
|
||||
{
|
||||
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) {
|
||||
var tests = []struct {
|
||||
description string
|
||||
nodes []string
|
||||
systemOverlays [][]string
|
||||
runtimeOverlays [][]string
|
||||
createdOverlays []string
|
||||
}{
|
||||
{
|
||||
description: "empty input creates no overlays",
|
||||
nodes: nil,
|
||||
systemOverlays: nil,
|
||||
runtimeOverlays: nil,
|
||||
createdOverlays: nil,
|
||||
},
|
||||
{
|
||||
description: "a node with no overlays creates default system/runtime overlays",
|
||||
nodes: []string{"node1"},
|
||||
systemOverlays: [][]string{{"o1"}},
|
||||
runtimeOverlays: [][]string{{"o1"}},
|
||||
createdOverlays: []string{"node1/__SYSTEM__.img.gz", "node1/__RUNTIME__.img.gz"},
|
||||
},
|
||||
{
|
||||
description: "multiple nodes with no overlays creates default system/runtime overlays",
|
||||
nodes: []string{"node1", "node2"},
|
||||
systemOverlays: [][]string{{"o1"}, {"o2"}},
|
||||
runtimeOverlays: [][]string{{"o1"}, {"o2"}},
|
||||
createdOverlays: []string{"node1/__SYSTEM__.img.gz", "node1/__RUNTIME__.img.gz", "node2/__SYSTEM__.img.gz", "node2/__RUNTIME__.img.gz"},
|
||||
},
|
||||
{
|
||||
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"},
|
||||
},
|
||||
}
|
||||
|
||||
conf := warewulfconf.Get()
|
||||
overlayDir, overlayDirErr := os.MkdirTemp(os.TempDir(), "ww-test-overlay-*")
|
||||
assert.NoError(t, overlayDirErr)
|
||||
@@ -354,7 +491,7 @@ func Test_BuildAllOverlays(t *testing.T) {
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0700))
|
||||
|
||||
for _, tt := range buildAllOverlaysTests {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
provisionDir, provisionDirErr := os.MkdirTemp(os.TempDir(), "ww-test-provision-*")
|
||||
assert.NoError(t, provisionDirErr)
|
||||
@@ -385,65 +522,65 @@ func Test_BuildAllOverlays(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var buildSpecificOverlaysTests = []struct {
|
||||
description string
|
||||
nodes []string
|
||||
overlays []string
|
||||
images []string
|
||||
succeed bool
|
||||
}{
|
||||
{
|
||||
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) {
|
||||
var tests = []struct {
|
||||
description string
|
||||
nodes []string
|
||||
overlays []string
|
||||
images []string
|
||||
succeed bool
|
||||
}{
|
||||
{
|
||||
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,
|
||||
},
|
||||
}
|
||||
|
||||
conf := warewulfconf.Get()
|
||||
overlayDir, overlayDirErr := os.MkdirTemp(os.TempDir(), "ww-test-overlay-*")
|
||||
assert.NoError(t, overlayDirErr)
|
||||
@@ -452,7 +589,7 @@ func Test_BuildSpecificOverlays(t *testing.T) {
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0700))
|
||||
|
||||
for _, tt := range buildSpecificOverlaysTests {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
provisionDir, provisionDirErr := os.MkdirTemp(os.TempDir(), "ww-test-provision-*")
|
||||
assert.NoError(t, provisionDirErr)
|
||||
|
||||
Reference in New Issue
Block a user