introduce site overlays

site overlays are place in sysconfdir/overlays and take
precedence over distribution overlays with the same name.

Every `wwctl overlay` command changing overlays will create
an site overlay. distribution overlays can't be deleted or
modified with wwctl.

Signed-off-by: Christian Goll <cgoll@suse.com>
This commit is contained in:
Christian Goll
2024-11-21 14:52:46 +01:00
parent e8f7c01283
commit d5fc7e9320
12 changed files with 106 additions and 64 deletions

View File

@@ -21,8 +21,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
if err != nil {
return fmt.Errorf("could not convert requested mode: %s", err)
}
overlaySourceDir = overlay.OverlaySourceDir(overlayName)
err = overlay.CreateSiteOverlay(overlayName)
if err != nil {
return err
}
overlaySourceDir, _ = overlay.OverlaySourceDir(overlayName)
if !util.IsDir(overlaySourceDir) {
return fmt.Errorf("overlay does not exist: %s", overlayName)

View File

@@ -34,8 +34,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
} else {
gid = -1
}
overlaySourceDir = overlay.OverlaySourceDir(overlayName)
err = overlay.CreateSiteOverlay(overlayName)
if err != nil {
return err
}
overlaySourceDir, _ = overlay.OverlaySourceDir(overlayName)
if !util.IsDir(overlaySourceDir) {
return fmt.Errorf("overlay does not exist: %s", overlayName)

View File

@@ -22,8 +22,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
fileName = args[1]
}
overlayPath = overlay.OverlaySourceDir(overlayName)
overlayPath, isSite := overlay.OverlaySourceDir(overlayName)
if !isSite {
return fmt.Errorf("distribution overlay can't deleted")
}
if overlayPath == "" {
return fmt.Errorf("overlay name did not resolve: '%s'", overlayName)
}

View File

@@ -30,16 +30,20 @@ const initialTemplate = `# This is a Warewulf Template file.
# Keep the following for better reference:
# ---
# This file is autogenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source: {{.BuildSource}}
`
func CobraRunE(cmd *cobra.Command, args []string) error {
func CobraRunE(cmd *cobra.Command, args []string) (err error) {
overlayName := args[0]
fileName := args[1]
overlaySourceDir := overlay.OverlaySourceDir(overlayName)
createdSite := false
overlaySourceDir, isSite := overlay.OverlaySourceDir(overlayName)
if !isSite {
err = overlay.CreateSiteOverlay(overlayName)
if err != nil {
return err
}
createdSite = true
}
if !util.IsDir(overlaySourceDir) {
return fmt.Errorf("overlay does not exist: %s", overlayName)
}
@@ -102,12 +106,15 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
} else {
if startTime == fileInfo.ModTime() {
wwlog.Debug("No change detected. Not updating overlay.")
os.Exit(0)
if createdSite {
os.Remove(overlaySourceDir)
}
return nil
}
}
// try renaming the tempfile to overlayfile first
err := os.Rename(tempFile.Name(), overlayFile)
err = os.Rename(tempFile.Name(), overlayFile)
if err != nil {
// if it fails, which probably means that they exists on different partitions
// fallback to data copy

View File

@@ -27,7 +27,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
}
wwlog.Verbose("Copying '%s' into overlay '%s:%s'", source, overlayName, dest)
overlaySource = overlay.OverlaySourceDir(overlayName)
overlaySource, _ = overlay.OverlaySourceDir(overlayName)
if !util.IsDir(overlaySource) {
return fmt.Errorf("overlay does not exist: %s", overlayName)

View File

@@ -25,14 +25,14 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
}
if ListLong {
wwlog.Info("%-10s %5s %-5s %-18s %s\n", "PERM MODE", "UID", "GID", "SYSTEM-OVERLAY", "FILE PATH")
wwlog.Info("%-10s %5s %-5s %-18s %s\n", "PERM MODE", "UID", "GID", "SYSTEM-OVERLAY", "FILE PATH", "SITE")
} else {
wwlog.Info("%-30s %-12s\n", "OVERLAY NAME", "FILES/DIRS")
wwlog.Info("%-30s %-12s-%12s\n", "OVERLAY NAME", "FILES/DIRS", "SITE")
}
for o := range overlays {
name := overlays[o]
path := overlay.OverlaySourceDir(name)
path, isSite := overlay.OverlaySourceDir(name)
if util.IsDir(path) {
files := util.FindFiles(path)
@@ -50,7 +50,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
sys := s.Sys()
wwlog.Info("%v %5d %-5d %-18s /%s\n", perms, sys.(*syscall.Stat_t).Uid, sys.(*syscall.Stat_t).Gid, overlays[o], files[file])
wwlog.Info("%v %5d %-5d %-18s /%s\n", perms, sys.(*syscall.Stat_t).Uid, sys.(*syscall.Stat_t).Gid, overlays[o], files[file], isSite)
}
} else if ListContents {
var fileCount int
@@ -62,7 +62,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
wwlog.Info("%-30s %-12d\n", name, 0)
}
} else {
wwlog.Info("%-30s %-12d\n", name, len(files))
wwlog.Info("%-30s %-12d\n", name, len(files), isSite)
}
} else {

View File

@@ -11,13 +11,16 @@ import (
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
func CobraRunE(cmd *cobra.Command, args []string) error {
func CobraRunE(cmd *cobra.Command, args []string) (err error) {
var overlaySourceDir string
overlayName := args[0]
dirName := args[1]
overlaySourceDir = overlay.OverlaySourceDir(overlayName)
err = overlay.CreateSiteOverlay(overlayName)
if err != nil {
return err
}
overlaySourceDir, _ = overlay.OverlaySourceDir(overlayName)
if !util.IsDir(overlaySourceDir) {
return fmt.Errorf("overlay does not exist: %s", overlayName)
@@ -27,7 +30,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
wwlog.Debug("Will create directory in overlay: %s:%s", overlayName, dirName)
err := os.MkdirAll(overlayDir, os.FileMode(PermMode))
err = os.MkdirAll(overlayDir, os.FileMode(PermMode))
if err != nil {
return fmt.Errorf("could not create directory: %s", path.Dir(overlayDir))
}

View File

@@ -22,7 +22,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
overlayName := args[0]
fileName := args[1]
overlaySourceDir = overlay.OverlaySourceDir(overlayName)
overlaySourceDir, _ = overlay.OverlaySourceDir(overlayName)
if !util.IsDir(overlaySourceDir) {
return fmt.Errorf("overlay dir: %s does not exist", overlaySourceDir)
@@ -66,6 +66,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
return err
}
tstruct.BuildSource = overlayFile
tstruct.Overlay = overlayName
buffer, backupFile, writeFile, err := overlay.RenderTemplateFile(overlayFile, tstruct)
if err != nil {
return err

View File

@@ -14,7 +14,8 @@ import (
Creates '/etc/hosts' from the host template.
*/
func Hostfile() (err error) {
hostTemplate := path.Join(overlay.OverlaySourceDir("host"), "/etc/hosts.ww")
overlaySourceDir, _ := overlay.OverlaySourceDir("host")
hostTemplate := path.Join(overlaySourceDir, "/etc/hosts.ww")
if !(util.IsFile(hostTemplate)) {
return fmt.Errorf("'the overlay template '/etc/hosts.ww' does not exists in 'host' overlay")
}

View File

@@ -1,31 +1,40 @@
package overlay
import (
"fmt"
"os"
"path"
"strings"
"github.com/containers/storage/drivers/copy"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
"github.com/warewulf/warewulf/internal/pkg/util"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
func OverlaySourceTopDir() string {
conf := warewulfconf.Get()
return conf.Paths.WWOverlaydir
}
/*
Return the path for the base of the overlay, strips rootfs
Return the path for the base of the overlay, adds rootfs
prefix in the overlay dir if this it exists
*/
func OverlaySourceDir(overlayName string) string {
func OverlaySourceDir(overlayName string) (overlaypath string, isSite bool) {
controller := warewulfconf.Get()
/* Assume using old style overlay dir without rootfs */
var overlaypath = path.Join(OverlaySourceTopDir(), overlayName)
overlaypath = path.Join(controller.Paths.Sysconfdir, "overlays", overlayName)
if _, err := os.Stat(path.Join(overlaypath, "rootfs")); err == nil {
/* rootfs exists, use it. */
overlaypath = path.Join(overlaypath, "rootfs")
}
return overlaypath
if _, err := os.Stat(overlaypath); err == nil {
return overlaypath, true
}
overlaypath = path.Join(controller.Paths.WWOverlaydir, overlayName)
if _, err := os.Stat(path.Join(overlaypath, "rootfs")); err == nil {
/* rootfs exists, use it. */
overlaypath = path.Join(overlaypath, "rootfs")
}
wwlog.Debug("found overlay %s in path: %s", overlayName, overlaypath)
return overlaypath, false
}
// OverlayImage returns the full path to an overlay image based on the
@@ -57,3 +66,24 @@ func OverlayImage(nodeName string, context string, overlayNames []string) string
conf := warewulfconf.Get()
return path.Join(conf.Paths.OverlayProvisiondir(), nodeName, name)
}
type OverlayDoesNotExist struct {
Name string
}
func (e *OverlayDoesNotExist) Error() string {
return fmt.Sprintf("overlay %s does not exist", e.Name)
}
// Creates a site overlay from an existing overlay and give back
// OverlayDoesNotExist error if distribution overlay doesn't exsist
func CreateSiteOverlay(name string) (err error) {
controller := warewulfconf.Get()
distroPath := path.Join(controller.Paths.WWOverlaydir, name)
sitePath := path.Join(controller.Paths.Sysconfdir, "overlays", name)
if !util.IsDir(distroPath) {
return &OverlayDoesNotExist{Name: name}
}
err = copy.DirCopy(distroPath, sitePath, copy.Content, true)
return err
}

View File

@@ -14,6 +14,7 @@ import (
"text/template"
"github.com/Masterminds/sprig/v3"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
"github.com/pkg/errors"
"github.com/warewulf/warewulf/internal/pkg/node"
@@ -70,7 +71,7 @@ func BuildHostOverlay() error {
hostname, _ := os.Hostname()
hostData := node.NewNode(hostname)
wwlog.Info("Building overlay for %s: host", hostname)
hostdir := OverlaySourceDir("host")
hostdir, _ := OverlaySourceDir("host")
stats, err := os.Stat(hostdir)
if err != nil {
return fmt.Errorf("could not build host overlay: %w ", err)
@@ -84,38 +85,40 @@ func BuildHostOverlay() error {
/*
Get all overlays present in warewulf
*/
func FindOverlays() ([]string, error) {
var ret []string
func FindOverlays() (overlayList []string, err error) {
dotfilecheck, _ := regexp.Compile(`^\..*`)
files, err := os.ReadDir(OverlaySourceTopDir())
controller := warewulfconf.Get()
files, err := os.ReadDir(controller.Paths.WWOverlaydir)
if err != nil {
return ret, fmt.Errorf("could not get list of overlays: %w", err)
return overlayList, fmt.Errorf("could not get list of distribution overlays: %w", err)
}
sitefiles, err := os.ReadDir(path.Join(controller.Paths.Sysconfdir, "overlays"))
if err == nil { // we don't care if there are no site overlays
files = append(files, sitefiles...)
}
for _, file := range files {
wwlog.Debug("Evaluating overlay source: %s", file.Name())
isdotfile := dotfilecheck.MatchString(file.Name())
if (file.IsDir()) && !(isdotfile) {
ret = append(ret, file.Name())
overlayList = append(overlayList, file.Name())
}
}
return ret, nil
return overlayList, nil
}
/*
Creates an empty overlay
*/
func OverlayInit(overlayName string) error {
path := OverlaySourceDir(overlayName)
if util.IsDir(path) {
return errors.New("Overlay already exists: " + overlayName)
controller := warewulfconf.Get()
overlayPath := path.Join(controller.Paths.Sysconfdir, "overlays", overlayName)
if util.IsDir(overlayPath) {
return fmt.Errorf("overlay already exists: %s", overlayName)
}
err := os.MkdirAll(path, 0755)
err := os.MkdirAll(path.Join(overlayPath, "rootfs"), 0755)
return err
}
@@ -190,7 +193,7 @@ func BuildOverlayIndir(nodeData node.Node, overlayNames []string, outputDir stri
wwlog.Verbose("Processing node/overlay: %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 := OverlaySourceDir(overlayName)
overlaySourceDir, _ := OverlaySourceDir(overlayName)
wwlog.Debug("Changing directory to OverlayDir: %s", overlaySourceDir)
err := os.Chdir(overlaySourceDir)
if err != nil {
@@ -358,19 +361,6 @@ func RenderTemplateFile(fileName string, data TemplateStruct) (
err error) {
backupFile = true
writeFile = true
// parse the overlay name out of the absolute path
overlayPath, _ := filepath.Rel(OverlaySourceTopDir(), fileName)
withoutRootfs, err := filepath.Rel("rootfs", overlayPath)
if err != nil {
overlayPath = withoutRootfs
}
overlayPathElem := strings.Split(overlayPath, "/")
if len(overlayPathElem) > 1 {
data.Overlay = overlayPathElem[0]
} else {
data.Overlay = ""
}
// Build our FuncMap
funcMap := template.FuncMap{
"Include": templateFileInclude,
@@ -442,7 +432,7 @@ func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
// Get all the files as a string slice for a given overlay
func OverlayGetFiles(name string) (files []string, err error) {
baseDir := OverlaySourceDir(name)
baseDir, _ := OverlaySourceDir(name)
if !util.IsDir(baseDir) {
err = fmt.Errorf("overlay %s doesn't exist", name)
return

View File

@@ -54,7 +54,8 @@ func getOverlayFile(n node.Node, context string, stage_overlays []string, autobu
build = util.PathIsNewer(stage_file, config.Get().Paths.NodesConf())
for _, overlayname := range stage_overlays {
build = build || util.PathIsNewer(stage_file, overlay.OverlaySourceDir(overlayname))
overlayDir, _ := overlay.OverlaySourceDir(overlayname)
build = build || util.PathIsNewer(stage_file, overlayDir)
}
}