Support for creating and updating overlay file in wwapi

Signed-off-by: jason yang <jasonyangshadow@gmail.com>
This commit is contained in:
jason yang
2025-05-01 06:29:40 +00:00
committed by Jonathon Anderson
parent 7b2c0901ed
commit 62d1aa654d
8 changed files with 231 additions and 37 deletions

View File

@@ -114,6 +114,25 @@ func (overlay Overlay) IsDistributionOverlay() bool {
return path.Dir(overlay.Path()) == config.Get().Paths.DistributionOverlaydir()
}
func (overlay Overlay) CreateOverlayFile(filePath string, content []byte, force bool) error {
wwlog.Info("Creating file %s in overlay %s, force: %v", filePath, overlay.Name(), force)
fullPath := overlay.File(filePath)
// create necessary parent directories
if err := os.MkdirAll(path.Dir(fullPath), 0o755); err != nil {
return fmt.Errorf("failed to create parent directories for %s: %w", fullPath, err)
}
// if the file already exists and force is false, return an error
if util.IsFile(fullPath) {
if force {
return os.WriteFile(fullPath, content, 0o644)
}
return fmt.Errorf("file %s already exists in overlay %s", filePath, overlay.Name())
}
return os.WriteFile(fullPath, content, 0o644)
}
// DeleteFile deletes a file or the entire overlay directory.
// If the file belongs to a distribution overlay, it will be cloned to a site overlay
// before deletion.

View File

@@ -20,7 +20,7 @@ import (
)
func Test_FindOverlays(t *testing.T) {
var tests = map[string]struct {
tests := map[string]struct {
distOverlays []string
siteOverlays []string
overlayList []string
@@ -75,7 +75,7 @@ func Test_OverlayMethods(t *testing.T) {
env.WriteFile(path.Join(sitedir, "both/rootfs/testfile"), "the site version")
env.WriteFile(path.Join(distdir, "both/rootfs/testfile"), "the distribution version")
var tests = map[string]struct {
tests := map[string]struct {
name string
path string
rootfs string
@@ -157,7 +157,7 @@ func Test_OverlayMethods(t *testing.T) {
}
func Test_BuildOverlayIndir(t *testing.T) {
var tests = map[string]struct {
tests := map[string]struct {
node node.Node
overlays []string
overlayFiles map[string]string
@@ -336,7 +336,7 @@ Tags:map[]
}
func Test_BuildOverlay(t *testing.T) {
var tests = []struct {
tests := []struct {
description string
nodeName string
context string
@@ -377,7 +377,7 @@ func Test_BuildOverlay(t *testing.T) {
overlays: []string{"o1"},
image: "o1.img",
contents: []string{"o1.txt"},
perms: []int{0644},
perms: []int{0o644},
},
{
description: "if multiple overlays are specified without a node, then the combined overlay is built directly in the overlay directory",
@@ -386,7 +386,7 @@ func Test_BuildOverlay(t *testing.T) {
overlays: []string{"o1", "o2"},
image: "o1-o2.img",
contents: []string{"o1.txt", "o2.txt"},
perms: []int{0644, 0644},
perms: []int{0o644, 0o644},
},
{
description: "if a single node overlay is specified, then the overlay is built in a node overlay directory",
@@ -395,7 +395,7 @@ func Test_BuildOverlay(t *testing.T) {
overlays: []string{"o1"},
image: "node1/o1.img",
contents: []string{"o1.txt"},
perms: []int{0644},
perms: []int{0o644},
},
{
description: "if multiple node overlays are specified, then the combined overlay is built in a node overlay directory",
@@ -404,7 +404,7 @@ func Test_BuildOverlay(t *testing.T) {
overlays: []string{"o1", "o2"},
image: "node1/o1-o2.img",
contents: []string{"o1.txt", "o2.txt"},
perms: []int{0644, 0644},
perms: []int{0o644, 0o644},
},
{
description: "if no node system overlays are specified, then context pointed overlay is generated",
@@ -429,7 +429,7 @@ func Test_BuildOverlay(t *testing.T) {
overlays: []string{"o1"},
image: "node1/__SYSTEM__.img",
contents: []string{"o1.txt"},
perms: []int{0644},
perms: []int{0o644},
},
{
description: "if a single node runtime overlay is specified, then a runtime overlay image is generated in a node overlay directory",
@@ -438,7 +438,7 @@ func Test_BuildOverlay(t *testing.T) {
overlays: []string{"o1"},
image: "node1/__RUNTIME__.img",
contents: []string{"o1.txt"},
perms: []int{0644},
perms: []int{0o644},
},
{
description: "if multiple node system overlays are specified, then a system overlay image is generated with the contents of both overlays",
@@ -447,7 +447,7 @@ func Test_BuildOverlay(t *testing.T) {
overlays: []string{"o1", "o2"},
image: "node1/__SYSTEM__.img",
contents: []string{"o1.txt", "o2.txt"},
perms: []int{0644, 0644},
perms: []int{0o644, 0o644},
},
{
description: "if multiple node runtime overlays are specified, then a runtime overlay image is generated with the contents of both overlays",
@@ -456,7 +456,7 @@ func Test_BuildOverlay(t *testing.T) {
overlays: []string{"o1", "o2"},
image: "node1/__RUNTIME__.img",
contents: []string{"o1.txt", "o2.txt"},
perms: []int{0644, 0644},
perms: []int{0o644, 0o644},
},
{
description: "validating altered permissions are retained",
@@ -465,7 +465,7 @@ func Test_BuildOverlay(t *testing.T) {
overlays: []string{"o3"},
image: "node1/__RUNTIME__.img",
contents: []string{"subdir", "subdir/o3.txt"},
perms: []int{0700, 0600},
perms: []int{0o700, 0o600},
},
}
@@ -473,12 +473,12 @@ func Test_BuildOverlay(t *testing.T) {
defer env.RemoveAll()
env.CreateFile("var/lib/warewulf/overlays/o1/rootfs/o1.txt")
env.Chmod("var/lib/warewulf/overlays/o1/rootfs/o1.txt", 0644)
env.Chmod("var/lib/warewulf/overlays/o1/rootfs/o1.txt", 0o644)
env.CreateFile("var/lib/warewulf/overlays/o2/rootfs/o2.txt")
env.Chmod("var/lib/warewulf/overlays/o2/rootfs/o2.txt", 0644)
env.Chmod("var/lib/warewulf/overlays/o2/rootfs/o2.txt", 0o644)
env.CreateFile("var/lib/warewulf/overlays/o3/rootfs/subdir/o3.txt.ww")
env.Chmod("var/lib/warewulf/overlays/o3/rootfs/subdir", 0700)
env.Chmod("var/lib/warewulf/overlays/o3/rootfs/subdir/o3.txt.ww", 0600)
env.Chmod("var/lib/warewulf/overlays/o3/rootfs/subdir", 0o700)
env.Chmod("var/lib/warewulf/overlays/o3/rootfs/subdir/o3.txt.ww", 0o600)
for _, tt := range tests {
nodeInfo := node.NewNode(tt.nodeName)
@@ -508,7 +508,7 @@ func Test_BuildOverlay(t *testing.T) {
}
func Test_BuildAllOverlays(t *testing.T) {
var tests = []struct {
tests := []struct {
description string
nodes []string
systemOverlays [][]string
@@ -585,8 +585,8 @@ func Test_BuildAllOverlays(t *testing.T) {
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))
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0o700))
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0o700))
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
@@ -620,7 +620,7 @@ func Test_BuildAllOverlays(t *testing.T) {
}
func Test_BuildSpecificOverlays(t *testing.T) {
var tests = []struct {
tests := []struct {
description string
nodes []string
overlays []string
@@ -683,8 +683,8 @@ func Test_BuildSpecificOverlays(t *testing.T) {
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))
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0o700))
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0o700))
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
@@ -711,6 +711,39 @@ func Test_BuildSpecificOverlays(t *testing.T) {
}
}
func Test_CreateOverlayFile(t *testing.T) {
tests := []struct {
name string
overlayName string
filePath string
content []byte
force bool
}{
{"create file", "test", "newfile.ww", []byte("new file"), false},
{"overwrite existing file", "test", "existingfile.ww", []byte("overwrite file"), true},
}
conf := warewulfconf.Get()
overlayDir, overlayDirErr := os.MkdirTemp(os.TempDir(), "ww-test-overlay-*")
assert.NoError(t, overlayDirErr)
defer os.RemoveAll(overlayDir)
conf.Paths.WWOverlaydir = overlayDir
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
newOverlay := GetSiteOverlay(tt.overlayName)
err := newOverlay.CreateOverlayFile(tt.filePath, tt.content, tt.force)
assert.NoError(t, err)
newFile := newOverlay.File(tt.filePath)
assert.FileExists(t, newFile)
readContent, err := os.ReadFile(newFile)
assert.NoError(t, err)
assert.Equal(t, tt.content, readContent)
})
}
}
func dirIsEmpty(t *testing.T, name string) bool {
f, err := os.Open(name)
if err != nil {