SHA256
1
0
forked from pool/warewulf4
warewulf4/CreateMt-Targets.patch

1005 lines
33 KiB
Diff
Raw Normal View History

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04a40983..a36b77a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -56,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The environment variable `WW_CONTAINER_SHELL` is defined in a `wwctl
container shell` environment to indicate the container in use. #579
- Network interface configuration (`ifcfg`) files now include the
- interface name and type. #574
+ interface name and type. #457
### Fixed
diff --git a/Makefile b/Makefile
index b494dd69..6363b653 100644
--- a/Makefile
+++ b/Makefile
@@ -96,7 +96,7 @@ export GOPROXY
WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree
# Default target
-all: config vendor wwctl wwclient bash_completion.d man_pages config_defaults print_defaults wwapid wwapic wwapird
+all: config vendor wwctl wwclient bash_completion.d man_pages config_defaults print_defaults wwapid wwapic wwapird print_mnts
# Validate source and build all packages
build: lint test-it vet all
@@ -247,6 +247,7 @@ config_defaults: vendor cmd/config_defaults/config_defaults.go
print_defaults: vendor cmd/print_defaults/print_defaults.go
cd cmd/print_defaults && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'" -o ../../print_defaults
+
update_configuration: vendor cmd/update_configuration/update_configuration.go
cd cmd/update_configuration && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'\
-X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\
diff --git a/etc/warewulf.conf b/etc/warewulf.conf
index d0a0c4db..a5b9a346 100644
--- a/etc/warewulf.conf
+++ b/etc/warewulf.conf
@@ -29,3 +29,7 @@ nfs:
mount options: defaults
mount: false
systemd name: nfs-server
+container mounts:
+ - source: /etc/resolv.conf
+ dest: /etc/resolv.conf
+ readonly: true
diff --git a/internal/app/wwctl/container/exec/child/main.go b/internal/app/wwctl/container/exec/child/main.go
index dda8b757..bf32ad17 100644
--- a/internal/app/wwctl/container/exec/child/main.go
+++ b/internal/app/wwctl/container/exec/child/main.go
@@ -7,11 +7,15 @@ import (
"fmt"
"os"
"path"
+ "path/filepath"
+ "strings"
"syscall"
- "time"
"github.com/hpcng/warewulf/internal/pkg/container"
+ "github.com/hpcng/warewulf/internal/pkg/node"
+ "github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/hpcng/warewulf/internal/pkg/util"
+ "github.com/hpcng/warewulf/internal/pkg/warewulfconf"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -29,43 +33,130 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
wwlog.Error("Unknown Warewulf container: %s", containerName)
os.Exit(1)
}
+ conf, err := warewulfconf.New()
+ if err != nil {
+ wwlog.Verbose("Couldn't get warewulf ocnfiguration: %s", err)
+ }
+ mountPts := conf.MountsContainer
+ mountPts = append(container.InitMountPnts(binds), mountPts...)
+ // check for valid mount points
+ lowerObjects := checkMountPoints(containerName, mountPts)
+ if len(lowerObjects) != 0 {
+ if tempDir == "" {
+ tempDir, err = os.MkdirTemp(os.TempDir(), "overlay")
+ if err != nil {
+ wwlog.Warn("couldn't create temp dir for overlay", err)
+ lowerObjects = []string{}
+ tempDir = ""
+ }
+ }
+ // need to create a overlay, where the lower layer contains
+ // the missing mount points
+ if tempDir != "" {
+ wwlog.Verbose("for ephermal mount use tempdir %s", tempDir)
+ // ignore errors as we are doomed if a tmp dir couldn't be written
+ _ = os.Mkdir(path.Join(tempDir, "work"), os.ModePerm)
+ _ = os.Mkdir(path.Join(tempDir, "lower"), os.ModePerm)
+ _ = os.Mkdir(path.Join(tempDir, "nodeoverlay"), os.ModePerm)
+ for _, obj := range lowerObjects {
+ newFile := ""
+ if !strings.HasSuffix(obj, "/") {
+ newFile = filepath.Base(obj)
+ obj = filepath.Dir(obj)
+ }
+ err = os.MkdirAll(filepath.Join(tempDir, "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(tempDir, "lower", obj, newFile))
+ if err != nil {
+ wwlog.Warn("couldn't create directory for mounts: %s", err)
+ }
+ defer desc.Close()
+ }
+ }
+ }
+ }
containerPath := container.RootFsDir(containerName)
- fileStat, _ := os.Stat(path.Join(containerPath, "/etc/passwd"))
- unixStat := fileStat.Sys().(*syscall.Stat_t)
- passwdTime := time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec))
- fileStat, _ = os.Stat(path.Join(containerPath, "/etc/group"))
- unixStat = fileStat.Sys().(*syscall.Stat_t)
- groupTime := time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec))
- wwlog.Debug("passwd: %v", passwdTime)
- wwlog.Debug("group: %v", groupTime)
-
- err := syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "")
+ 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(tempDir, "lower"), containerPath, path.Join(tempDir, "work"))
+ 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))
+ }
+ } else if nodename != "" {
+ nodeDB, err := node.New()
+ if err != nil {
+ wwlog.Error("Could not open node configuration: %s", err)
+ os.Exit(1)
+ }
+
+ nodes, err := nodeDB.FindAllNodes()
+ if err != nil {
+ wwlog.Error("Could not get node list: %s", err)
+ os.Exit(1)
+ }
+ nodes = node.FilterByName(nodes, []string{nodename})
+ if len(nodes) != 1 {
+ wwlog.Error("No single node idendified with %s", nodename)
+ os.Exit(1)
+ }
+ overlays := nodes[0].SystemOverlay.GetSlice()
+ overlays = append(overlays, nodes[0].RuntimeOverlay.GetSlice()...)
+ err = overlay.BuildOverlayIndir(nodes[0], overlays, path.Join(tempDir, "nodeoverlay"))
+ if err != nil {
+ wwlog.Error("Could not build overlay: %s", err)
+ os.Exit(1)
+ }
+ options := fmt.Sprintf("lowerdir=%s:%s:%s",
+ path.Join(tempDir, "lower"), containerPath, path.Join(tempDir, "nodeoverlay"))
+ 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 node render overlay: %s", err))
+ os.Exit(1)
+ }
+ ps1Str = fmt.Sprintf("[%s|ro|%s] Warewulf> ", containerName, nodename)
+ }
+ if !util.IsWriteAble(containerPath) && nodename == "" {
+ wwlog.Verbose("mounting %s ro", containerPath)
+ ps1Str = fmt.Sprintf("[%s|ro] Warewulf> ", containerName)
+ err = syscall.Mount(containerPath, containerPath, "", syscall.MS_BIND, "")
+ if err != nil {
+ return errors.Wrap(err, "failed to prepare bind mount")
+ }
+ err = syscall.Mount(containerPath, containerPath, "", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, "")
+ if err != nil {
+ return errors.Wrap(err, "failed to remount ro")
+ }
+ }
err = syscall.Mount("/dev", path.Join(containerPath, "/dev"), "", syscall.MS_BIND, "")
if err != nil {
return errors.Wrap(err, "failed to mount /dev")
}
- for _, b := range binds {
- var source string
- var dest string
-
- bind := util.SplitValidPaths(b, ":")
- source = bind[0]
-
- if len(bind) == 1 {
- dest = source
- } else {
- dest = bind[1]
- }
-
- err := syscall.Mount(source, path.Join(containerPath, dest), "", syscall.MS_BIND, "")
+ for _, mntPnt := range mountPts {
+ err = syscall.Mount(mntPnt.Source, path.Join(containerPath, mntPnt.Dest), "", syscall.MS_BIND, "")
if err != nil {
- fmt.Printf("BIND ERROR: %s\n", err)
- os.Exit(1)
+ wwlog.Warn("Couldn't mount %s to %s: %s", mntPnt.Source, mntPnt.Dest, err)
+ } else if mntPnt.ReadOnly {
+ err = syscall.Mount(mntPnt.Source, path.Join(containerPath, mntPnt.Dest), "", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, "")
+ if err != nil {
+ wwlog.Warn("failed to following mount readonly: %s", mntPnt.Source)
+ } else {
+ wwlog.Verbose("mounted readonly from host to container: %s:%s", mntPnt.Source, mntPnt.Dest)
+ }
+ } else {
+ wwlog.Verbose("mounted from host to container: %s:%s", mntPnt.Source, mntPnt.Dest)
}
}
@@ -84,27 +175,39 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
return errors.Wrap(err, "failed to mount proc")
}
- os.Setenv("PS1", fmt.Sprintf("[%s] Warewulf> ", containerName))
+ os.Setenv("PS1", ps1Str)
os.Setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin")
os.Setenv("HISTFILE", "/dev/null")
- err = syscall.Exec(args[1], args[1:], os.Environ())
- if err != nil {
- wwlog.Error("%s", err)
- os.Exit(1)
- }
- fileStat, _ = os.Stat(path.Join(containerPath, "/etc/passwd"))
- unixStat = fileStat.Sys().(*syscall.Stat_t)
- if passwdTime.Before(time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec))) {
- wwlog.Warn("/etc/passwd has been modified, maybe you want to run syncuser")
- }
- wwlog.Debug("passwd: %v", time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec)))
- fileStat, _ = os.Stat(path.Join(containerPath, "/etc/group"))
- unixStat = fileStat.Sys().(*syscall.Stat_t)
- if groupTime.Before(time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec))) {
- wwlog.Warn("/etc/group has been modified, maybe you want to run syncuser")
- }
- wwlog.Debug("group: %v", time.Unix(int64(unixStat.Ctim.Sec), int64(unixStat.Ctim.Nsec)))
-
+ _ = syscall.Exec(args[1], args[1:], os.Environ())
+ /*
+ Exec replaces the actual program, so nothing to do here afterwards
+ */
return nil
}
+
+/*
+Check if the bind mount points exists in the given container. Returns
+the invalid mount points. Directories always have '/' as suffix
+*/
+func checkMountPoints(containerName string, binds []*warewulfconf.MountEntry) (overlayObjects []string) {
+ overlayObjects = []string{}
+ for _, b := range binds {
+ _, err := os.Stat(b.Source)
+ if err != nil {
+ wwlog.Debug("Couldn't stat %s create no mount point in container", b.Source)
+ continue
+ }
+ wwlog.Debug("Checking in container for %s", path.Join(container.RootFsDir(containerName), b.Dest))
+ if _, err = os.Stat(path.Join(container.RootFsDir(containerName), b.Dest)); err != nil {
+ if os.IsNotExist(err) {
+ if util.IsDir(b.Dest) && !strings.HasSuffix(b.Dest, "/") {
+ b.Dest += "/"
+ }
+ overlayObjects = append(overlayObjects, b.Dest)
+ wwlog.Debug("Container %s, needs following path: %s", containerName, b.Dest)
+ }
+ }
+ }
+ return overlayObjects
+}
diff --git a/internal/app/wwctl/container/exec/child/root.go b/internal/app/wwctl/container/exec/child/root.go
index 0b7dde7c..d7060995 100644
--- a/internal/app/wwctl/container/exec/child/root.go
+++ b/internal/app/wwctl/container/exec/child/root.go
@@ -5,17 +5,21 @@ import "github.com/spf13/cobra"
var (
baseCmd = &cobra.Command{
DisableFlagsInUseLine: true,
- Use: "__child",
- Hidden: true,
- RunE: CobraRunE,
- Args: cobra.MinimumNArgs(1),
- FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
+ Use: "__child",
+ Hidden: true,
+ RunE: CobraRunE,
+ Args: cobra.MinimumNArgs(1),
+ FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
}
- binds []string
+ binds []string
+ tempDir string
+ nodename string
)
func init() {
- baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, "bind points")
+ baseCmd.Flags().StringVarP(&nodename, "node", "n", "", "create ro overlay for given node")
+ baseCmd.Flags().StringArrayVarP(&binds, "bind", "b", []string{}, "bind points")
+ baseCmd.Flags().StringVar(&tempDir, "tempdir", "", "tempdir")
}
// GetRootCommand returns the root cobra.Command for the application.
diff --git a/internal/app/wwctl/container/exec/main.go b/internal/app/wwctl/container/exec/main.go
index 722ba479..bc622de2 100644
--- a/internal/app/wwctl/container/exec/main.go
+++ b/internal/app/wwctl/container/exec/main.go
@@ -17,9 +17,26 @@ import (
"github.com/spf13/cobra"
)
+/*
+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)
+ }
+ defer func() {
+ err = os.RemoveAll(tempDir)
+ if err != nil {
+ wwlog.Warn("Couldn't remove temp dir for ephermal mounts:", err)
+ }
+ }()
+ }
+ logStr := fmt.Sprint(wwlog.GetLogLevel())
wwlog.Verbose("Running contained command: %s", args[1:])
- c := exec.Command("/proc/self/exe", append([]string{"container", "exec", "__child"}, args...)...)
+ c := exec.Command("/proc/self/exe", append([]string{"--loglevel", logStr, "--tempdir", tempDir, "container", "exec", "__child"}, args...)...)
c.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
@@ -30,6 +47,11 @@ func runContainedCmd(args []string) error {
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
@@ -38,6 +60,8 @@ func runContainedCmd(args []string) error {
func CobraRunE(cmd *cobra.Command, args []string) error {
containerName := args[0]
+ os.Setenv("WW_CONTAINER_SHELL", containerName)
+
var allargs []string
if !container.ValidSource(containerName) {
@@ -48,6 +72,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
for _, b := range binds {
allargs = append(allargs, "--bind", b)
}
+ if nodeName != "" {
+ allargs = append(allargs, "--node", nodeName)
+ }
allargs = append(allargs, args...)
containerPath := container.RootFsDir(containerName)
@@ -109,3 +136,10 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
return nil
}
+func SetBinds(myBinds []string) {
+ binds = append(binds, myBinds...)
+}
+
+func SetNode(myNode string) {
+ nodeName = myNode
+}
diff --git a/internal/app/wwctl/container/exec/root.go b/internal/app/wwctl/container/exec/root.go
index db8bea41..e5fc577b 100644
--- a/internal/app/wwctl/container/exec/root.go
+++ b/internal/app/wwctl/container/exec/root.go
@@ -27,12 +27,16 @@ 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")
}
// GetRootCommand returns the root cobra.Command for the application.
diff --git a/internal/app/wwctl/container/shell/main.go b/internal/app/wwctl/container/shell/main.go
index 04a095ec..b7c3fbde 100644
--- a/internal/app/wwctl/container/shell/main.go
+++ b/internal/app/wwctl/container/shell/main.go
@@ -4,11 +4,10 @@
package shell
import (
- "fmt"
"os"
- "os/exec"
- "syscall"
+ "path"
+ cntexec "github.com/hpcng/warewulf/internal/app/wwctl/container/exec"
"github.com/hpcng/warewulf/internal/pkg/container"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/spf13/cobra"
@@ -23,28 +22,32 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
wwlog.Error("Unknown Warewulf container: %s", containerName)
os.Exit(1)
}
-
- for _, b := range binds {
- allargs = append(allargs, "--bind", b)
+ /*
+ for _, b := range binds {
+ allargs = append(allargs, "--bind", b)
+ }
+ */
+ shellName := os.Getenv("SHELL")
+ if !container.ValidSource(containerName) {
+ wwlog.Error("Unknown Warewulf container: %s", containerName)
+ os.Exit(1)
}
- allargs = append(allargs, args...)
- allargs = append(allargs, "/usr/bin/bash")
-
- c := exec.Command("/proc/self/exe", append([]string{"container", "exec"}, allargs...)...)
-
- //c := exec.Command("/bin/sh")
- c.SysProcAttr = &syscall.SysProcAttr{
- Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
+ var shells []string
+ if shellName == "" {
+ shells = append(shells, "/bin/bash")
+ } else {
+ shells = append(shells, shellName, "/bin/bash")
}
- c.Stdin = os.Stdin
- c.Stdout = os.Stdout
- c.Stderr = os.Stderr
-
- os.Setenv("WW_CONTAINER_SHELL", containerName)
-
- if err := c.Run(); err != nil {
- fmt.Println(err)
- os.Exit(1)
+ for _, s := range shells {
+ if _, err := os.Stat(path.Join(container.RootFsDir(containerName), s)); err == nil {
+ shellName = s
+ break
+ }
}
- return nil
+ args = append(args, shellName)
+ allargs = append(allargs, args...)
+ wwlog.Debug("Calling exec with args: %s", allargs)
+ cntexec.SetBinds(binds)
+ cntexec.SetNode(nodeName)
+ return cntexec.CobraRunE(cmd, allargs)
}
diff --git a/internal/app/wwctl/container/shell/root.go b/internal/app/wwctl/container/shell/root.go
index 59f6c56b..dcec3f06 100644
--- a/internal/app/wwctl/container/shell/root.go
+++ b/internal/app/wwctl/container/shell/root.go
@@ -22,11 +22,13 @@ var (
},
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
}
- binds []string
+ binds []string
+ nodeName string
)
func init() {
baseCmd.PersistentFlags().StringArrayVarP(&binds, "bind", "b", []string{}, "Bind a local path into the container (must exist)")
+ baseCmd.PersistentFlags().StringVarP(&nodeName, "node", "n", "", "Create a read only view of the container for the given node")
}
// GetRootCommand returns the root cobra.Command for the application.
diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go
index 81fe2a26..cc8927d6 100644
--- a/internal/app/wwctl/root.go
+++ b/internal/app/wwctl/root.go
@@ -31,12 +31,14 @@ var (
}
verboseArg bool
DebugFlag bool
+ LogLevel int
)
func init() {
rootCmd.PersistentFlags().BoolVarP(&verboseArg, "verbose", "v", false, "Run with increased verbosity.")
rootCmd.PersistentFlags().BoolVarP(&DebugFlag, "debug", "d", false, "Run with debugging messages enabled.")
-
+ rootCmd.PersistentFlags().IntVar(&LogLevel, "loglevel", wwlog.INFO, "Set log level to given string")
+ _ = rootCmd.PersistentFlags().MarkHidden("loglevel")
rootCmd.SetUsageTemplate(help.UsageTemplate)
rootCmd.SetHelpTemplate(help.HelpTemplate)
@@ -65,6 +67,9 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error {
} else {
wwlog.SetLogLevel(wwlog.INFO)
}
+ if LogLevel != wwlog.INFO {
+ wwlog.SetLogLevel(LogLevel)
+ }
return nil
}
diff --git a/internal/pkg/api/apiconfig/container/container.go b/internal/pkg/api/apiconfig/container/container.go
index 8bc2ba23..beb29544 100644
--- a/internal/pkg/api/apiconfig/container/container.go
+++ b/internal/pkg/api/apiconfig/container/container.go
@@ -192,12 +192,6 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin
return
}
- fmt.Printf("Updating the container's /etc/resolv.conf\n")
- err = util.CopyFile("/etc/resolv.conf", path.Join(container.RootFsDir(cip.Name), "/etc/resolv.conf"))
- if err != nil {
- wwlog.Warn("Could not copy /etc/resolv.conf into container: %s", err)
- }
-
fmt.Printf("Building container: %s\n", cip.Name)
err = container.Build(cip.Name, true)
if err != nil {
diff --git a/internal/pkg/api/container/container.go b/internal/pkg/api/container/container.go
index 757ea873..6683c75c 100644
--- a/internal/pkg/api/container/container.go
+++ b/internal/pkg/api/container/container.go
@@ -197,12 +197,6 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin
return
}
- wwlog.Info("Updating the container's /etc/resolv.conf")
- err = util.CopyFile("/etc/resolv.conf", path.Join(container.RootFsDir(cip.Name), "/etc/resolv.conf"))
- if err != nil {
- wwlog.Warn("Could not copy /etc/resolv.conf into container: %s", err)
- }
-
err = container.SyncUids(cip.Name, !cip.SyncUser)
if err != nil && !cip.SyncUser {
err = fmt.Errorf("error in user sync, fix error and run 'syncuser' manually: %s", err)
diff --git a/internal/pkg/buildconfig/defaults.go b/internal/pkg/buildconfig/defaults.go
index 24cb2d40..332a2f6d 100644
--- a/internal/pkg/buildconfig/defaults.go
+++ b/internal/pkg/buildconfig/defaults.go
@@ -19,6 +19,7 @@ var (
release string = "UNDEF"
wwclientdir string = "UNDEF"
datadir string = "UNDEF"
+ tmpdir string = "UNDEF"
)
func BINDIR() string {
@@ -90,3 +91,8 @@ func WWCLIENTDIR() string {
wwlog.Debug("WWCLIENTDIR = '%s'", wwclientdir)
return wwclientdir
}
+
+func TMPDIR() string {
+ wwlog.Debug("TMPDIR = '%s'", tmpdir)
+ return tmpdir
+}
diff --git a/internal/pkg/buildconfig/setconfigs.go.in b/internal/pkg/buildconfig/setconfigs.go.in
index e10303e3..f049ffeb 100644
--- a/internal/pkg/buildconfig/setconfigs.go.in
+++ b/internal/pkg/buildconfig/setconfigs.go.in
@@ -15,4 +15,5 @@ func init() {
version = "@VERSION@"
release = "@RELEASE@"
wwclientdir = "@WWCLIENTDIR@"
+ tmpdir = "@TMPDIR@"
}
diff --git a/internal/pkg/container/mountpoints.go b/internal/pkg/container/mountpoints.go
new file mode 100644
index 00000000..727ff35c
--- /dev/null
+++ b/internal/pkg/container/mountpoints.go
@@ -0,0 +1,35 @@
+package container
+
+import (
+ "strings"
+
+ "github.com/hpcng/warewulf/internal/pkg/warewulfconf"
+ "github.com/hpcng/warewulf/internal/pkg/wwlog"
+)
+
+/*
+Create a slice iof MntDetails from a string slice with following
+format "source:[:destination][:readonly]" if destination is not
+given, the source is used as destination
+*/
+func InitMountPnts(binds []string) (mounts []*warewulfconf.MountEntry) {
+ wwlog.Debug("Trying to mount following mount points: %s", mounts)
+ for _, b := range binds {
+ bind := strings.Split(b, ":")
+ dest := bind[0]
+ if len(bind) >= 2 {
+ dest = bind[1]
+ }
+ readonly := false
+ if len(bind) >= 3 && bind[2] == "ro" {
+ readonly = true
+ }
+ mntPnt := warewulfconf.MountEntry{
+ Source: bind[0],
+ Dest: dest,
+ ReadOnly: readonly,
+ }
+ mounts = append(mounts, &mntPnt)
+ }
+ return mounts
+}
diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go
index 3f97fbb5..6ad445cb 100644
--- a/internal/pkg/util/util.go
+++ b/internal/pkg/util/util.go
@@ -24,12 +24,16 @@ import (
// reserve some number of cpus for system/warwulfd usage
var processLimitedReserve int = 4
+
// maximum number of concurrent spawned processes
-var processLimitedMax = MaxInt(1, runtime.NumCPU() - processLimitedReserve)
+var processLimitedMax = MaxInt(1, runtime.NumCPU()-processLimitedReserve)
+
// Channel used as semaphore to specififed processLimitedMax
var processLimitedChan = make(chan int, processLimitedMax)
+
// Current number of processes started + queued
var processLimitedNum int32 = 0
+
// Counter over total history of started processes
var processLimitedCounter uint32 = 0
@@ -53,7 +57,7 @@ func ProcessLimitedStatus() (running int32, queued int32) {
return
}
-func MaxInt( a int, b int ) int {
+func MaxInt(a int, b int) int {
if a > b {
return a
}
@@ -65,7 +69,7 @@ func FirstError(errs ...error) (err error) {
for _, e := range errs {
if err == nil {
err = e
- }else if e != nil {
+ } else if e != nil {
wwlog.ErrorExc(e, "Unhandled error")
}
}
@@ -207,7 +211,7 @@ func ValidateOrDie(message string, pattern string, expr string) {
}
}
-//******************************************************************************
+// ******************************************************************************
func FindFiles(path string) []string {
var ret []string
@@ -244,7 +248,7 @@ func FindFiles(path string) []string {
return ret
}
-//******************************************************************************
+// ******************************************************************************
func FindFilterFiles(
path string,
include []string,
@@ -265,7 +269,7 @@ func FindFilterFiles(
return ofiles, errors.Wrapf(err, "Failed to change path: %s", path)
}
- for i := range(ignore) {
+ for i := range ignore {
ignore[i] = strings.TrimLeft(ignore[i], "/")
ignore[i] = strings.TrimPrefix(ignore[i], "./")
wwlog.Debug("Ignore pattern (%d): %s", i, ignore[i])
@@ -282,7 +286,6 @@ func FindFilterFiles(
dev := path_stat.Sys().(*syscall.Stat_t).Dev
-
includeDirs := []string{}
ignoreDirs := []string{}
err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error {
@@ -306,13 +309,13 @@ func FindFilterFiles(
return nil
}
- for _, ignoreDir := range(ignoreDirs) {
+ for _, ignoreDir := range ignoreDirs {
if strings.HasPrefix(location, ignoreDir) {
wwlog.Debug("Ignored (dir): %s", file)
return nil
}
}
- for i, pattern := range(ignore) {
+ for i, pattern := range ignore {
m, err := filepath.Match(pattern, location)
if err != nil {
return err
@@ -325,14 +328,14 @@ func FindFilterFiles(
}
}
- for _, includeDir := range(includeDirs) {
+ for _, includeDir := range includeDirs {
if strings.HasPrefix(location, includeDir) {
wwlog.Debug("Included (dir): %s", file)
ofiles = append(ofiles, location)
return nil
}
}
- for i, pattern := range(include) {
+ for i, pattern := range include {
m, err := filepath.Match(pattern, location)
if err != nil {
return err
@@ -352,7 +355,7 @@ func FindFilterFiles(
return ofiles, err
}
-//******************************************************************************
+// ******************************************************************************
func ExecInteractive(command string, a ...string) error {
wwlog.Debug("ExecInteractive(%s, %s)", command, a)
c := exec.Command(command, a...)
@@ -538,20 +541,22 @@ func AppendLines(fileName string, lines []string) error {
return nil
}
-/*******************************************************************************
+/*
+******************************************************************************
+
Create an archive using cpio
*/
func CpioCreate(
ifiles []string,
ofile string,
format string,
- cpio_args ...string ) (err error) {
+ cpio_args ...string) (err error) {
args := []string{
"--quiet",
"--create",
"-H", format,
- "--file=" + ofile }
+ "--file=" + ofile}
args = append(args, cpio_args...)
@@ -574,14 +579,16 @@ func CpioCreate(
wwlog.Debug(string(out))
}
- return FirstError(err, <- err_in)
+ return FirstError(err, <-err_in)
}
-/*******************************************************************************
+/*
+******************************************************************************
+
Compress a file using gzip or pigz
*/
func FileGz(
- file string ) (err error) {
+ file string) (err error) {
file_gz := file + ".gz"
@@ -608,41 +615,41 @@ func FileGz(
proc := exec.Command(
compressor,
"--keep",
- file )
+ file)
out, err := proc.CombinedOutput()
if len(out) > 0 {
outStr := string(out[:])
if err != nil && strings.HasSuffix(compressor, "gzip") && strings.Contains(outStr, "unrecognized option") {
- var gzippedFile *os.File
- var gzipStderr io.ReadCloser
-
+ var gzippedFile *os.File
+ var gzipStderr io.ReadCloser
+
/* Older version of gzip, try it another way: */
wwlog.Verbose("%s does not recognize the --keep flag, trying redirected stdout", compressor)
-
+
/* Open the output file for writing: */
gzippedFile, err = os.Create(file_gz)
if err != nil {
return errors.Wrapf(err, "Unable to open compressed image file for writing: %s", file_gz)
}
-
+
/* We'll execute gzip with output to stdout and attach stdout to the compressed file we just
created:
- */
+ */
proc = exec.Command(
compressor,
"--stdout",
- file )
+ file)
proc.Stdout = gzippedFile
gzipStderr, err = proc.StderrPipe()
if err != nil {
return errors.Wrapf(err, "Unable to open stderr pipe for compression program: %s", compressor)
}
-
+
/* Execute the command: */
err = proc.Start()
if err != nil {
- _ = proc.Wait()
+ _ = proc.Wait()
gzippedFile.Close()
os.Remove(file_gz)
err = errors.Wrapf(err, "Unable to successfully execute compression program: %s", compressor)
@@ -664,7 +671,9 @@ func FileGz(
return err
}
-/*******************************************************************************
+/*
+******************************************************************************
+
Create an archive using cpio
*/
func BuildFsImage(
@@ -675,7 +684,7 @@ func BuildFsImage(
ignore []string,
ignore_xdev bool,
format string,
- cpio_args ...string ) (err error) {
+ cpio_args ...string) (err error) {
err = os.MkdirAll(path.Dir(imagePath), 0755)
if err != nil {
@@ -709,16 +718,16 @@ func BuildFsImage(
".",
include,
ignore,
- ignore_xdev )
+ ignore_xdev)
if err != nil {
return errors.Wrapf(err, "Failed discovering files for %s: %s", name, rootfsPath)
}
err = CpioCreate(
files,
- imagePath,
+ imagePath,
format,
- cpio_args...)
+ cpio_args...)
if err != nil {
return errors.Wrapf(err, "Failed creating image for %s: %s", name, imagePath)
}
@@ -727,15 +736,17 @@ func BuildFsImage(
err = FileGz(imagePath)
if err != nil {
- return errors.Wrapf(err, "Failed to compress image for %s: %s", name, imagePath + ".gz")
+ return errors.Wrapf(err, "Failed to compress image for %s: %s", name, imagePath+".gz")
}
- wwlog.Info("Compressed image for %s: %s", name, imagePath + ".gz")
+ wwlog.Info("Compressed image for %s: %s", name, imagePath+".gz")
return nil
}
-/*******************************************************************************
+/*
+******************************************************************************
+
Runs wwctl command
*/
func RunWWCTL(args ...string) (out []byte, err error) {
@@ -744,7 +755,7 @@ func RunWWCTL(args ...string) (out []byte, err error) {
running, queued := ProcessLimitedStatus()
wwlog.Verbose("Starting wwctl process %d (%d running, %d queued): %v",
- index, running, queued, args )
+ index, running, queued, args)
proc := exec.Command("wwctl", args...)
@@ -787,3 +798,21 @@ func ByteToString(b int64) string {
}
return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp])
}
+
+/*
+Check if the w-bit of a file/dir. unix.Access(file,unix.W_OK) will
+not show this.
+*/
+func IsWriteAble(path string) bool {
+ info, err := os.Stat(path)
+ if err != nil {
+ return false
+ }
+
+ // Check if the user bit is enabled in file permission
+ if info.Mode().Perm()&(1<<(uint(7))) == 0 {
+ wwlog.Debug("Write permission bit is not set for: %s", path)
+ return false
+ }
+ return true
+}
diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go
index c3eebf8c..170c94cd 100644
--- a/internal/pkg/warewulfconf/datastructure.go
+++ b/internal/pkg/warewulfconf/datastructure.go
@@ -7,19 +7,20 @@ import (
)
type ControllerConf struct {
- WWInternal int `yaml:"WW_INTERNAL"`
- Comment string `yaml:"comment,omitempty"`
- Ipaddr string `yaml:"ipaddr"`
- Ipaddr6 string `yaml:"ipaddr6,omitempty"`
- Netmask string `yaml:"netmask"`
- Network string `yaml:"network,omitempty"`
- Ipv6net string `yaml:"ipv6net,omitempty"`
- Fqdn string `yaml:"fqdn,omitempty"`
- Warewulf *WarewulfConf `yaml:"warewulf"`
- Dhcp *DhcpConf `yaml:"dhcp"`
- Tftp *TftpConf `yaml:"tftp"`
- Nfs *NfsConf `yaml:"nfs"`
- current bool
+ WWInternal int `yaml:"WW_INTERNAL"`
+ Comment string `yaml:"comment,omitempty"`
+ Ipaddr string `yaml:"ipaddr"`
+ Ipaddr6 string `yaml:"ipaddr6,omitempty"`
+ Netmask string `yaml:"netmask"`
+ Network string `yaml:"network,omitempty"`
+ Ipv6net string `yaml:"ipv6net,omitempty"`
+ Fqdn string `yaml:"fqdn,omitempty"`
+ Warewulf *WarewulfConf `yaml:"warewulf"`
+ Dhcp *DhcpConf `yaml:"dhcp"`
+ Tftp *TftpConf `yaml:"tftp"`
+ Nfs *NfsConf `yaml:"nfs"`
+ MountsContainer []*MountEntry `yaml:"container mounts"`
+ current bool
}
type WarewulfConf struct {
@@ -59,6 +60,16 @@ type NfsExportConf struct {
Mount bool `default:"true" yaml:"mount"`
}
+/*
+Describe a mount point for a container exec
+*/
+type MountEntry struct {
+ Source string `yaml:"source" default:"/etc/resolv.conf"`
+ Dest string `yaml:"dest,omitempty" default:"/etc/resolv.conf"`
+ ReadOnly bool `yaml:"readonly,omitempty" default:"false"`
+ Options string `yaml:"options,omitempty"` // ignored at the moment
+}
+
func (s *NfsConf) Unmarshal(unmarshal func(interface{}) error) error {
if err := defaults.Set(s); err != nil {
return err
diff --git a/print_mnts b/print_mnts
new file mode 100755
index 00000000..28986c71
Binary files /dev/null and b/print_mnts differ