From d5fc7e93201a90baea6f9215b9eb90d8d9eba9aa Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 21 Nov 2024 14:52:46 +0100 Subject: [PATCH] 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 --- internal/app/wwctl/overlay/chmod/main.go | 7 +++- internal/app/wwctl/overlay/chown/main.go | 7 +++- internal/app/wwctl/overlay/delete/main.go | 5 ++- internal/app/wwctl/overlay/edit/main.go | 23 +++++++---- internal/app/wwctl/overlay/imprt/main.go | 2 +- internal/app/wwctl/overlay/list/main.go | 10 ++--- internal/app/wwctl/overlay/mkdir/main.go | 11 ++++-- internal/app/wwctl/overlay/show/main.go | 3 +- internal/pkg/configure/hostfile.go | 3 +- internal/pkg/overlay/config.go | 48 ++++++++++++++++++----- internal/pkg/overlay/overlay.go | 48 +++++++++-------------- internal/pkg/warewulfd/util.go | 3 +- 12 files changed, 106 insertions(+), 64 deletions(-) diff --git a/internal/app/wwctl/overlay/chmod/main.go b/internal/app/wwctl/overlay/chmod/main.go index 22a92cf7..552b53d9 100644 --- a/internal/app/wwctl/overlay/chmod/main.go +++ b/internal/app/wwctl/overlay/chmod/main.go @@ -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) diff --git a/internal/app/wwctl/overlay/chown/main.go b/internal/app/wwctl/overlay/chown/main.go index 10d82a96..f7b80c89 100644 --- a/internal/app/wwctl/overlay/chown/main.go +++ b/internal/app/wwctl/overlay/chown/main.go @@ -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) diff --git a/internal/app/wwctl/overlay/delete/main.go b/internal/app/wwctl/overlay/delete/main.go index 3ceb26b7..19c708b1 100644 --- a/internal/app/wwctl/overlay/delete/main.go +++ b/internal/app/wwctl/overlay/delete/main.go @@ -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) } diff --git a/internal/app/wwctl/overlay/edit/main.go b/internal/app/wwctl/overlay/edit/main.go index b8a1b820..fa778ef9 100644 --- a/internal/app/wwctl/overlay/edit/main.go +++ b/internal/app/wwctl/overlay/edit/main.go @@ -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 diff --git a/internal/app/wwctl/overlay/imprt/main.go b/internal/app/wwctl/overlay/imprt/main.go index acad75ec..21288502 100644 --- a/internal/app/wwctl/overlay/imprt/main.go +++ b/internal/app/wwctl/overlay/imprt/main.go @@ -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) diff --git a/internal/app/wwctl/overlay/list/main.go b/internal/app/wwctl/overlay/list/main.go index c5ad2040..4852aaf8 100644 --- a/internal/app/wwctl/overlay/list/main.go +++ b/internal/app/wwctl/overlay/list/main.go @@ -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 { diff --git a/internal/app/wwctl/overlay/mkdir/main.go b/internal/app/wwctl/overlay/mkdir/main.go index ee8ffd8c..2c2c8206 100644 --- a/internal/app/wwctl/overlay/mkdir/main.go +++ b/internal/app/wwctl/overlay/mkdir/main.go @@ -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)) } diff --git a/internal/app/wwctl/overlay/show/main.go b/internal/app/wwctl/overlay/show/main.go index 198a2099..2a62709b 100644 --- a/internal/app/wwctl/overlay/show/main.go +++ b/internal/app/wwctl/overlay/show/main.go @@ -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 diff --git a/internal/pkg/configure/hostfile.go b/internal/pkg/configure/hostfile.go index bb7d469b..e2a0309e 100644 --- a/internal/pkg/configure/hostfile.go +++ b/internal/pkg/configure/hostfile.go @@ -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") } diff --git a/internal/pkg/overlay/config.go b/internal/pkg/overlay/config.go index 988519a3..5924de2e 100644 --- a/internal/pkg/overlay/config.go +++ b/internal/pkg/overlay/config.go @@ -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 +} diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index bef2edc3..742be04a 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -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 diff --git a/internal/pkg/warewulfd/util.go b/internal/pkg/warewulfd/util.go index 7c4987b8..59f916aa 100644 --- a/internal/pkg/warewulfd/util.go +++ b/internal/pkg/warewulfd/util.go @@ -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) } }