- Warewulf v4.5.8 simplifies the "wwinit" boot process for SELinux and configures tmpfs to spread the node image across all available NUMA nodes. It also improves the detection of kernels in the container image to more reliably detect the newest available kernel and to avoid debug / rescue kernels. - Warewulf v4.5.7 fixes the ability to override overlay files configured in profiles with overlays configured per-node; fixes a template processing bug bug in development-time overlay rendering; and improves the preview dracut-based boot process to better support a "secure" boot process. - rmeoved added-cow-option-to-bind.patch as now in upstream OBS-URL: https://build.opensuse.org/package/show/network:cluster/warewulf4?expand=0&rev=90
255 lines
10 KiB
Diff
255 lines
10 KiB
Diff
diff --git a/internal/app/wwctl/container/exec/child/main.go b/internal/app/wwctl/container/exec/child/main.go
|
|
index 253a6ad2..0785ba74 100644
|
|
--- a/internal/app/wwctl/container/exec/child/main.go
|
|
+++ b/internal/app/wwctl/container/exec/child/main.go
|
|
@@ -92,7 +92,7 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) {
|
|
wwlog.Debug("overlay options: %s", options)
|
|
err = syscall.Mount("overlay", containerPath, "overlay", 0, options)
|
|
if err != nil {
|
|
- wwlog.Warn(fmt.Sprintf("Couldn't create overlay for ephermal mount points: %s", err))
|
|
+ wwlog.Warn("Couldn't create overlay for ephermal mount points: %s", err)
|
|
}
|
|
} else if nodename != "" {
|
|
nodeDB, err := node.New()
|
|
@@ -142,6 +142,10 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) {
|
|
}
|
|
|
|
for _, mntPnt := range mountPts {
|
|
+ if mntPnt.Copy {
|
|
+ continue
|
|
+ }
|
|
+ wwlog.Debug("bind mounting: %s -> %s", mntPnt.Source, path.Join(containerPath, mntPnt.Dest))
|
|
err = syscall.Mount(mntPnt.Source, path.Join(containerPath, mntPnt.Dest), "", syscall.MS_BIND, "")
|
|
if err != nil {
|
|
wwlog.Warn("Couldn't mount %s to %s: %s", mntPnt.Source, mntPnt.Dest, err)
|
|
@@ -195,6 +199,9 @@ the invalid mount points. Directories always have '/' as suffix
|
|
func checkMountPoints(containerName string, binds []*warewulfconf.MountEntry) (overlayObjects []string) {
|
|
overlayObjects = []string{}
|
|
for _, b := range binds {
|
|
+ if b.Copy {
|
|
+ continue
|
|
+ }
|
|
_, err := os.Stat(b.Source)
|
|
if err != nil {
|
|
wwlog.Debug("Couldn't stat %s create no mount point in container", b.Source)
|
|
diff --git a/internal/app/wwctl/container/exec/main.go b/internal/app/wwctl/container/exec/main.go
|
|
index 60cf9d48..336218a9 100644
|
|
--- a/internal/app/wwctl/container/exec/main.go
|
|
+++ b/internal/app/wwctl/container/exec/main.go
|
|
@@ -54,7 +54,6 @@ func runContainedCmd(cmd *cobra.Command, containerName string, args []string) (e
|
|
}()
|
|
|
|
logStr := fmt.Sprint(wwlog.GetLogLevel())
|
|
-
|
|
childArgs := []string{"--warewulfconf", conf.GetWarewulfConf(), "--loglevel", logStr, "container", "exec", "__child"}
|
|
childArgs = append(childArgs, containerName)
|
|
for _, b := range binds {
|
|
@@ -64,8 +63,32 @@ func runContainedCmd(cmd *cobra.Command, containerName string, args []string) (e
|
|
childArgs = append(childArgs, "--node", nodeName)
|
|
}
|
|
childArgs = append(childArgs, args...)
|
|
+ // copy the files into the container at this stage, es in __child the
|
|
+ // command syscall.Exec which replaces the __child process with the
|
|
+ // exec command in the container. All the mounts, have to be done in
|
|
+ // __child so that the used mounts don't propagate outside on the host
|
|
+ // (see the CLONE attributes), but as for the copy option we need
|
|
+ // to see if a file was modified after it was copied into the container
|
|
+ // so do this here.
|
|
+ // At first read out conf, the parse commandline, as copy files has the
|
|
+ // same synatx as mount points
|
|
+ mountPts := append(container.InitMountPnts(binds), conf.MountsContainer...)
|
|
+ filesToCpy := getCopyFiles(mountPts)
|
|
+ for _, cpyFile := range filesToCpy {
|
|
+ if err := (cpyFile).copyToContainer(containerName); err != nil {
|
|
+ wwlog.Warn("couldn't copy file: %s", err)
|
|
+ }
|
|
+ }
|
|
wwlog.Verbose("Running contained command: %s", childArgs)
|
|
- return childCommandFunc(cmd, childArgs)
|
|
+ retVal := childCommandFunc(cmd, childArgs)
|
|
+ for _, cpyFile := range filesToCpy {
|
|
+ if cpyFile.shouldRemoveFromContainer(containerName) {
|
|
+ if err := cpyFile.removeFromContainer(containerName); err != nil {
|
|
+ wwlog.Warn("couldn't remove file: %s", err)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return retVal
|
|
}
|
|
|
|
func CobraRunE(cmd *cobra.Command, args []string) error {
|
|
@@ -154,3 +177,68 @@ func SetBinds(myBinds []string) {
|
|
func SetNode(myNode string) {
|
|
nodeName = myNode
|
|
}
|
|
+
|
|
+// file name and last modification time so we can remove the file if it wasn't modified
|
|
+type copyFile struct {
|
|
+ fileName string
|
|
+ src string
|
|
+ modTime time.Time
|
|
+}
|
|
+
|
|
+func (this *copyFile) containerDest(containerName string) string {
|
|
+ return path.Join(container.RootFsDir(containerName), this.fileName)
|
|
+}
|
|
+
|
|
+func (this *copyFile) copyToContainer(containerName string) error {
|
|
+ containerDest := this.containerDest(containerName)
|
|
+ if _, err := os.Stat(path.Dir(containerDest)); err != nil {
|
|
+ return err
|
|
+ } else if _, err := os.Stat(containerDest); err == nil {
|
|
+ return err
|
|
+ } else if _, err := os.Stat(this.src); err != nil {
|
|
+ return err
|
|
+ } else if err := util.CopyFile(this.src, containerDest); err != nil {
|
|
+ return err
|
|
+ } else if stat, err := os.Stat(containerDest); err != nil {
|
|
+ return err
|
|
+ } else {
|
|
+ this.modTime = stat.ModTime()
|
|
+ return nil
|
|
+ }
|
|
+}
|
|
+
|
|
+func (this *copyFile) shouldRemoveFromContainer(containerName string) bool {
|
|
+ containerDest := this.containerDest(containerName)
|
|
+ if this.modTime.IsZero() {
|
|
+ wwlog.Debug("file was not previously copied: %s", this.fileName)
|
|
+ return false
|
|
+ } else if destStat, err := os.Stat(containerDest); err != nil {
|
|
+ wwlog.Verbose("file is no longer present: %s (%s)", this.fileName, err)
|
|
+ return false
|
|
+ } else if destStat.ModTime() == this.modTime {
|
|
+ wwlog.Verbose("don't remove modified file:", this.fileName)
|
|
+ return false
|
|
+ } else {
|
|
+ return true
|
|
+ }
|
|
+}
|
|
+
|
|
+func (this *copyFile) removeFromContainer(containerName string) error {
|
|
+ containerDest := this.containerDest(containerName)
|
|
+ return os.Remove(containerDest)
|
|
+}
|
|
+
|
|
+/*
|
|
+Check the objects we want to copy in, instead of mounting
|
|
+*/
|
|
+func getCopyFiles(binds []*warewulfconf.MountEntry) (copyObjects []*copyFile) {
|
|
+ for _, bind := range binds {
|
|
+ if bind.Copy {
|
|
+ copyObjects = append(copyObjects, ©File{
|
|
+ fileName: bind.Dest,
|
|
+ src: bind.Source,
|
|
+ })
|
|
+ }
|
|
+ }
|
|
+ return
|
|
+}
|
|
diff --git a/internal/app/wwctl/container/exec/root.go b/internal/app/wwctl/container/exec/root.go
|
|
index 4b74c941..1152fd8e 100644
|
|
--- a/internal/app/wwctl/container/exec/root.go
|
|
+++ b/internal/app/wwctl/container/exec/root.go
|
|
@@ -32,7 +32,10 @@ var (
|
|
|
|
func init() {
|
|
baseCmd.AddCommand(child.GetCommand())
|
|
- baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, "Bind a local path into the container (must exist)")
|
|
+ baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, `source[:destination[:{ro|copy}]]
|
|
+Bind a local path which must exist into the container. If destination is not
|
|
+set, uses the same path as source. "ro" binds read-only. "copy" temporarily
|
|
+copies the file into the container.`)
|
|
baseCmd.PersistentFlags().BoolVar(&SyncUser, "syncuser", false, "Synchronize UIDs/GIDs from host to container")
|
|
baseCmd.PersistentFlags().StringVarP(&nodeName, "node", "n", "", "Create a read only view of the container for the given node")
|
|
}
|
|
diff --git a/internal/app/wwctl/container/shell/root.go b/internal/app/wwctl/container/shell/root.go
|
|
index cceb7512..e1ae01d3 100644
|
|
--- a/internal/app/wwctl/container/shell/root.go
|
|
+++ b/internal/app/wwctl/container/shell/root.go
|
|
@@ -28,7 +28,10 @@ var (
|
|
)
|
|
|
|
func init() {
|
|
- baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, "Bind a local path into the container (must exist)")
|
|
+ baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, `source[:destination[:{ro|copy}]]
|
|
+Bind a local path which must exist into the container. If destination is not
|
|
+set, uses the same path as source. "ro" binds read-only. "copy" temporarily
|
|
+copies the file into the container.`)
|
|
baseCmd.PersistentFlags().StringVarP(&nodeName, "node", "n", "", "Create a read only view of the container for the given node")
|
|
}
|
|
|
|
diff --git a/internal/pkg/config/mounts.go b/internal/pkg/config/mounts.go
|
|
index 2eb5060b..305fbad4 100644
|
|
--- a/internal/pkg/config/mounts.go
|
|
+++ b/internal/pkg/config/mounts.go
|
|
@@ -7,4 +7,5 @@ type MountEntry struct {
|
|
Dest string `yaml:"dest,omitempty"`
|
|
ReadOnly bool `yaml:"readonly,omitempty"`
|
|
Options string `yaml:"options,omitempty"` // ignored at the moment
|
|
+ Copy bool `yaml:"copy,omitempty"` // temporarily copy the file into the container
|
|
}
|
|
diff --git a/internal/pkg/container/mountpoints.go b/internal/pkg/container/mountpoints.go
|
|
index 5de00c75..961513fb 100644
|
|
--- a/internal/pkg/container/mountpoints.go
|
|
+++ b/internal/pkg/container/mountpoints.go
|
|
@@ -21,13 +21,19 @@ func InitMountPnts(binds []string) (mounts []*warewulfconf.MountEntry) {
|
|
dest = bind[1]
|
|
}
|
|
readonly := false
|
|
- if len(bind) >= 3 && bind[2] == "ro" {
|
|
- readonly = true
|
|
+ copy_ := false
|
|
+ if len(bind) >= 3 {
|
|
+ if bind[2] == "ro" {
|
|
+ readonly = true
|
|
+ } else if bind[2] == "copy" {
|
|
+ copy_ = true
|
|
+ }
|
|
}
|
|
mntPnt := warewulfconf.MountEntry{
|
|
Source: bind[0],
|
|
Dest: dest,
|
|
ReadOnly: readonly,
|
|
+ Copy: copy_,
|
|
}
|
|
mounts = append(mounts, &mntPnt)
|
|
}
|
|
diff --git a/internal/pkg/warewulfd/copyshim.go b/internal/pkg/warewulfd/copyshim.go
|
|
index 4f085017..13a32d64 100644
|
|
--- a/internal/pkg/warewulfd/copyshim.go
|
|
+++ b/internal/pkg/warewulfd/copyshim.go
|
|
@@ -16,7 +16,6 @@ import (
|
|
Copies the default shim, which is the shim located on host
|
|
to the tftp directory
|
|
*/
|
|
-
|
|
func CopyShimGrub() (err error) {
|
|
conf := warewulfconf.Get()
|
|
wwlog.Debug("copy shim and grub binaries from host")
|
|
diff --git a/userdocs/contents/containers.rst b/userdocs/contents/containers.rst
|
|
index 2e84921f..2ee7a236 100644
|
|
--- a/userdocs/contents/containers.rst
|
|
+++ b/userdocs/contents/containers.rst
|
|
@@ -213,6 +213,20 @@ when using the exec command. This works as follows:
|
|
location, as it is almost always present and empty in every Linux
|
|
distribution (as prescribed by the LSB file hierarchy standard).
|
|
|
|
+Files which should always be present in a container image like ``resolv.conf``
|
|
+can be specified in ``warewulf.conf``:
|
|
+
|
|
+.. code-block:: yaml
|
|
+ container mounts:
|
|
+ - source: /etc/resolv.conf
|
|
+ dest: /etc/resolv.conf
|
|
+ readonly: true
|
|
+
|
|
+.. note::
|
|
+ Instead of ``readonly: true`` you can set ``copy: true``. This causes the
|
|
+ source file to be copied to the container and removed if it was not
|
|
+ modified. This can be useful for files used for registrations.
|
|
+
|
|
When the command completes, if anything within the container changed,
|
|
the container will be rebuilt into a bootable static object
|
|
automatically.
|