diff --git a/CHANGELOG.md b/CHANGELOG.md index 73802bb0..b6945751 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix the issue that warewulf.conf parse does not support CIDR format. #1130 - Reduce the number of times syncuser walks the container file system. #1209 - Create ssh key also when calling `wwctl configure --all` #1250 +- Remove the temporary overlay dir. #1180 +- Remove the temporary overlayfs dir and create them besides rootfs #1180 ### Security @@ -71,6 +73,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Allow specification of the ssh-keys to be to be created. #1185 +### Changed + +- The command `wwctl container exec` locks now this container during execution. #830 + ### Fixed - Fix nightly release build failure issue. #1195 diff --git a/internal/app/wwctl/container/exec/child/main.go b/internal/app/wwctl/container/exec/child/main.go index 57cf7192..62d09a20 100644 --- a/internal/app/wwctl/container/exec/child/main.go +++ b/internal/app/wwctl/container/exec/child/main.go @@ -34,30 +34,39 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { os.Exit(1) } conf := warewulfconf.Get() + runDir := container.RunDir(containerName) + if _, err := os.Stat(runDir); os.IsNotExist(err) { + return errors.Wrap(err, "container run directory does not exist") + } mountPts := conf.MountsContainer mountPts = append(container.InitMountPnts(binds), mountPts...) // check for valid mount points lowerObjects := checkMountPoints(containerName, mountPts) - overlayDir := conf.Paths.WWChrootdir + "/overlays" // need to create a overlay, where the lower layer contains // the missing mount points - wwlog.Verbose("for ephermal mount use tempdir %s", overlayDir) - // ignore errors as we are doomed if a tmp dir couldn't be written - _ = os.MkdirAll(path.Join(overlayDir, "work"), os.ModePerm) - _ = os.MkdirAll(path.Join(overlayDir, "lower"), os.ModePerm) - _ = os.MkdirAll(path.Join(overlayDir, "nodeoverlay"), os.ModePerm) + wwlog.Verbose("for ephermal mount use tempdir %s", runDir) + if err = os.Mkdir(path.Join(runDir, "work"), os.ModePerm); err != nil { + return err + } + if err = os.Mkdir(path.Join(runDir, "lower"), os.ModePerm); err != nil { + return err + } + if err = os.Mkdir(path.Join(runDir, "nodeoverlay"), os.ModePerm); err != nil { + return err + } + // handle all lower object, have some extra logic if the object is a file for _, obj := range lowerObjects { newFile := "" if !strings.HasSuffix(obj, "/") { newFile = filepath.Base(obj) obj = filepath.Dir(obj) } - err = os.MkdirAll(filepath.Join(overlayDir, "lower", obj), os.ModePerm) + err = os.Mkdir(filepath.Join(runDir, "lower", obj), os.ModePerm) if err != nil { wwlog.Warn("couldn't create directory for mounts: %s", err) } if newFile != "" { - desc, err := os.Create(filepath.Join(overlayDir, "lower", obj, newFile)) + desc, err := os.Create(filepath.Join(runDir, "lower", obj, newFile)) if err != nil { wwlog.Warn("couldn't create directory for mounts: %s", err) } @@ -65,6 +74,7 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { } } containerPath := container.RootFsDir(containerName) + // running in a private PID space, so also make / private, so that nothing gets out from here err = syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "") if err != nil { return errors.Wrap(err, "failed to mount") @@ -72,7 +82,7 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { ps1Str := fmt.Sprintf("[%s] Warewulf> ", containerName) if len(lowerObjects) != 0 && nodename == "" { options := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", - path.Join(overlayDir, "lower"), containerPath, path.Join(overlayDir, "work")) + path.Join(runDir, "lower"), containerPath, path.Join(runDir, "work")) wwlog.Debug("overlay options: %s", options) err = syscall.Mount("overlay", containerPath, "overlay", 0, options) if err != nil { @@ -97,13 +107,13 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { } overlays := nodes[0].SystemOverlay.GetSlice() overlays = append(overlays, nodes[0].RuntimeOverlay.GetSlice()...) - err = overlay.BuildOverlayIndir(nodes[0], overlays, path.Join(overlayDir, "nodeoverlay")) + err = overlay.BuildOverlayIndir(nodes[0], overlays, path.Join(runDir, "nodeoverlay")) if err != nil { wwlog.Error("Could not build overlay: %s", err) os.Exit(1) } options := fmt.Sprintf("lowerdir=%s:%s:%s", - path.Join(overlayDir, "lower"), containerPath, path.Join(overlayDir, "nodeoverlay")) + path.Join(runDir, "lower"), containerPath, path.Join(runDir, "nodeoverlay")) wwlog.Debug("overlay options: %s", options) err = syscall.Mount("overlay", containerPath, "overlay", 0, options) if err != nil { @@ -165,11 +175,8 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { os.Setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin") os.Setenv("HISTFILE", "/dev/null") - _ = syscall.Exec(args[1], args[1:], os.Environ()) - /* - Exec replaces the actual program, so nothing to do here afterwards - */ - return nil + wwlog.Debug("Exec: %s %s", args[1], args[1:]) + return syscall.Exec(args[1], args[1:], os.Environ()) } /* diff --git a/internal/app/wwctl/container/exec/main.go b/internal/app/wwctl/container/exec/main.go index 44e11e9f..4de99940 100644 --- a/internal/app/wwctl/container/exec/main.go +++ b/internal/app/wwctl/container/exec/main.go @@ -4,6 +4,7 @@ package exec import ( + "errors" "fmt" "os" "os/exec" @@ -11,6 +12,8 @@ import ( "syscall" "time" + warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" + "github.com/spf13/cobra" "github.com/warewulf/warewulf/internal/pkg/container" "github.com/warewulf/warewulf/internal/pkg/util" @@ -20,23 +23,25 @@ import ( /* fork off a process with a new PID space */ -func runContainedCmd(args []string) error { - var err error - if tempDir == "" { - tempDir, err = os.MkdirTemp(os.TempDir(), "overlay") - if err != nil { - wwlog.Warn("couldn't create temp dir for overlay", err) +func runContainedCmd(args []string) (err error) { + conf := warewulfconf.Get() + containerName := args[0] + runDir := container.RunDir(containerName) + if err := os.Mkdir(runDir, 0750); err != nil { + if _, existerr := os.Stat(runDir); !os.IsNotExist(existerr) { + return errors.New("run directory already exists: another container command may already be running") + } else { + return fmt.Errorf("unable to create run directory: %w", err) } - defer func() { - err = os.RemoveAll(tempDir) - if err != nil { - wwlog.Warn("Couldn't remove temp dir for ephermal mounts:", err) - } - }() } + defer func() { + if err := errors.Join(os.RemoveAll(runDir), err); err != nil { + wwlog.Error("error removing run directory: %w", err) + } + }() logStr := fmt.Sprint(wwlog.GetLogLevel()) wwlog.Verbose("Running contained command: %s", args[1:]) - c := exec.Command("/proc/self/exe", append([]string{"--loglevel", logStr, "--tempdir", tempDir, "container", "exec", "__child"}, args...)...) + c := exec.Command("/proc/self/exe", append([]string{"--warewulfconf", conf.GetWarewulfConf(), "--loglevel", logStr, "container", "exec", "__child"}, args...)...) c.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS, @@ -45,16 +50,7 @@ func runContainedCmd(args []string) error { c.Stdout = os.Stdout c.Stderr = os.Stderr - if err := c.Run(); err != nil { - fmt.Printf("Command exited non-zero, not rebuilding/updating VNFS image\n") - // defer is not called before os.Exit(0) - err = os.RemoveAll(tempDir) - if err != nil { - wwlog.Warn("Couldn't remove temp dir for ephermal mounts:", err) - } - os.Exit(0) - } - return nil + return c.Run() } func CobraRunE(cmd *cobra.Command, args []string) error { diff --git a/internal/app/wwctl/container/exec/root.go b/internal/app/wwctl/container/exec/root.go index a59b42c1..4b74c941 100644 --- a/internal/app/wwctl/container/exec/root.go +++ b/internal/app/wwctl/container/exec/root.go @@ -27,7 +27,6 @@ var ( } SyncUser bool binds []string - tempDir string nodeName string ) @@ -35,7 +34,6 @@ func init() { baseCmd.AddCommand(child.GetCommand()) baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, "Bind a local path into the container (must exist)") baseCmd.PersistentFlags().BoolVar(&SyncUser, "syncuser", false, "Synchronize UIDs/GIDs from host to container") - baseCmd.PersistentFlags().StringVar(&tempDir, "tempdir", "", "Use tempdir for constructing the overlay fs (only used if mount points don't exist in 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/root.go b/internal/pkg/config/root.go index c24e84e7..89c6b944 100644 --- a/internal/pkg/config/root.go +++ b/internal/pkg/config/root.go @@ -42,14 +42,14 @@ type RootConf struct { MountsContainer []*MountEntry `yaml:"container mounts" default:"[{\"source\": \"/etc/resolv.conf\", \"dest\": \"/etc/resolv.conf\"}]"` Paths *BuildConfig `yaml:"paths"` - fromFile bool + warewulfconf string } // New caches and returns a new [RootConf] initialized with empty // values, clearing replacing any previously cached value. func New() *RootConf { cachedConf = RootConf{} - cachedConf.fromFile = false + cachedConf.warewulfconf = "" cachedConf.Warewulf = new(WarewulfConf) cachedConf.DHCP = new(DHCPConf) cachedConf.TFTP = new(TFTPConf) @@ -77,12 +77,12 @@ func Get() *RootConf { // file. func (conf *RootConf) Read(confFileName string) error { wwlog.Debug("Reading warewulf.conf from: %s", confFileName) + conf.warewulfconf = confFileName if data, err := os.ReadFile(confFileName); err != nil { return err } else if err := conf.Parse(data); err != nil { return err } else { - conf.fromFile = true return nil } } @@ -200,5 +200,9 @@ func (conf *RootConf) SetDynamicDefaults() (err error) { // InitializedFromFile returns true if [RootConf] memory was read from // a file, or false otherwise. func (conf *RootConf) InitializedFromFile() bool { - return conf.fromFile + return conf.warewulfconf != "" +} + +func (conf *RootConf) GetWarewulfConf() string { + return conf.warewulfconf } diff --git a/internal/pkg/config/root_test.go b/internal/pkg/config/root_test.go index 7cf0812c..4511e47d 100644 --- a/internal/pkg/config/root_test.go +++ b/internal/pkg/config/root_test.go @@ -67,6 +67,7 @@ func TestInitializedFromFile(t *testing.T) { assert.False(t, conf.InitializedFromFile()) assert.NoError(t, conf.Read(tempWarewulfConf.Name())) assert.True(t, conf.InitializedFromFile()) + assert.Equal(t, conf.GetWarewulfConf(), tempWarewulfConf.Name()) } func TestExampleRootConf(t *testing.T) { diff --git a/internal/pkg/container/config.go b/internal/pkg/container/config.go index 902827ea..afbd3b40 100644 --- a/internal/pkg/container/config.go +++ b/internal/pkg/container/config.go @@ -33,6 +33,10 @@ func RootFsDir(name string) string { return path.Join(SourceDir(name), "rootfs") } +func RunDir(name string) string { + return path.Join(SourceDir(name), "run") +} + func ImageParentDir() string { conf := warewulfconf.Get() return path.Join(conf.Paths.WWProvisiondir, "container/")