Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix overlay directory location/creation #1182

Merged
merged 3 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,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

Expand All @@ -70,6 +72,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
Expand Down
39 changes: 23 additions & 16 deletions internal/app/wwctl/container/exec/child/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,45 +34,55 @@ 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)
}
defer desc.Close()
}
}
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")
}
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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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())
Comment on lines +178 to +179
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mslacken I remembered you made this change in the dracut branch but it didn't get merged there; so I brought it in here.

}

/*
Expand Down
42 changes: 19 additions & 23 deletions internal/app/wwctl/container/exec/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
package exec

import (
"errors"
"fmt"
"os"
"os/exec"
"path"
"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"
Expand All @@ -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,
Expand All @@ -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 {
Expand Down
2 changes: 0 additions & 2 deletions internal/app/wwctl/container/exec/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,13 @@ var (
}
SyncUser bool
binds []string
tempDir string
nodeName string
)

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")
}

Expand Down
12 changes: 8 additions & 4 deletions internal/pkg/config/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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
}
1 change: 1 addition & 0 deletions internal/pkg/config/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions internal/pkg/container/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/")
Expand Down