3
Makefile
3
Makefile
@@ -96,7 +96,7 @@ export GOPROXY
|
|||||||
WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree
|
WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree
|
||||||
|
|
||||||
# Default target
|
# 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
|
# Validate source and build all packages
|
||||||
build: lint test-it vet all
|
build: lint test-it vet all
|
||||||
@@ -248,6 +248,7 @@ config_defaults: vendor cmd/config_defaults/config_defaults.go
|
|||||||
print_defaults: vendor cmd/print_defaults/print_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
|
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
|
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'\
|
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'"\
|
-X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\
|
||||||
|
|||||||
@@ -29,3 +29,7 @@ nfs:
|
|||||||
mount options: defaults
|
mount options: defaults
|
||||||
mount: false
|
mount: false
|
||||||
systemd name: nfs-server
|
systemd name: nfs-server
|
||||||
|
container mounts:
|
||||||
|
- source: /etc/resolv.conf
|
||||||
|
dest: /etc/resolv.conf
|
||||||
|
readonly: true
|
||||||
|
|||||||
@@ -7,11 +7,15 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hpcng/warewulf/internal/pkg/container"
|
"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/util"
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
|
||||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -29,43 +33,130 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
wwlog.Error("Unknown Warewulf container: %s", containerName)
|
wwlog.Error("Unknown Warewulf container: %s", containerName)
|
||||||
os.Exit(1)
|
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)
|
containerPath := container.RootFsDir(containerName)
|
||||||
fileStat, _ := os.Stat(path.Join(containerPath, "/etc/passwd"))
|
err = syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "")
|
||||||
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, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to mount")
|
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, "")
|
err = syscall.Mount("/dev", path.Join(containerPath, "/dev"), "", syscall.MS_BIND, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to mount /dev")
|
return errors.Wrap(err, "failed to mount /dev")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, b := range binds {
|
for _, mntPnt := range mountPts {
|
||||||
var source string
|
err = syscall.Mount(mntPnt.Source, path.Join(containerPath, mntPnt.Dest), "", syscall.MS_BIND, "")
|
||||||
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, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("BIND ERROR: %s\n", err)
|
wwlog.Warn("Couldn't mount %s to %s: %s", mntPnt.Source, mntPnt.Dest, err)
|
||||||
os.Exit(1)
|
} 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")
|
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("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin")
|
||||||
os.Setenv("HISTFILE", "/dev/null")
|
os.Setenv("HISTFILE", "/dev/null")
|
||||||
|
|
||||||
err = syscall.Exec(args[1], args[1:], os.Environ())
|
_ = syscall.Exec(args[1], args[1:], os.Environ())
|
||||||
if err != nil {
|
/*
|
||||||
wwlog.Error("%s", err)
|
Exec replaces the actual program, so nothing to do here afterwards
|
||||||
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)))
|
|
||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,17 +5,21 @@ import "github.com/spf13/cobra"
|
|||||||
var (
|
var (
|
||||||
baseCmd = &cobra.Command{
|
baseCmd = &cobra.Command{
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
Use: "__child",
|
Use: "__child",
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
RunE: CobraRunE,
|
RunE: CobraRunE,
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||||
}
|
}
|
||||||
binds []string
|
binds []string
|
||||||
|
tempDir string
|
||||||
|
nodename string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
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.
|
// GetRootCommand returns the root cobra.Command for the application.
|
||||||
|
|||||||
@@ -17,9 +17,26 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
fork off a process with a new PID space
|
||||||
|
*/
|
||||||
func runContainedCmd(args []string) error {
|
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:])
|
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{
|
c.SysProcAttr = &syscall.SysProcAttr{
|
||||||
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
|
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 {
|
if err := c.Run(); err != nil {
|
||||||
fmt.Printf("Command exited non-zero, not rebuilding/updating VNFS image\n")
|
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)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -38,6 +60,8 @@ func runContainedCmd(args []string) error {
|
|||||||
func CobraRunE(cmd *cobra.Command, args []string) error {
|
func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
containerName := args[0]
|
containerName := args[0]
|
||||||
|
os.Setenv("WW_CONTAINER_SHELL", containerName)
|
||||||
|
|
||||||
var allargs []string
|
var allargs []string
|
||||||
|
|
||||||
if !container.ValidSource(containerName) {
|
if !container.ValidSource(containerName) {
|
||||||
@@ -48,6 +72,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
for _, b := range binds {
|
for _, b := range binds {
|
||||||
allargs = append(allargs, "--bind", b)
|
allargs = append(allargs, "--bind", b)
|
||||||
}
|
}
|
||||||
|
if nodeName != "" {
|
||||||
|
allargs = append(allargs, "--node", nodeName)
|
||||||
|
}
|
||||||
allargs = append(allargs, args...)
|
allargs = append(allargs, args...)
|
||||||
containerPath := container.RootFsDir(containerName)
|
containerPath := container.RootFsDir(containerName)
|
||||||
|
|
||||||
@@ -109,3 +136,10 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func SetBinds(myBinds []string) {
|
||||||
|
binds = append(binds, myBinds...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetNode(myNode string) {
|
||||||
|
nodeName = myNode
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,12 +27,16 @@ var (
|
|||||||
}
|
}
|
||||||
SyncUser bool
|
SyncUser bool
|
||||||
binds []string
|
binds []string
|
||||||
|
tempDir string
|
||||||
|
nodeName string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
baseCmd.AddCommand(child.GetCommand())
|
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{}, "Bind a local path into the container (must exist)")
|
||||||
baseCmd.PersistentFlags().BoolVar(&SyncUser, "syncuser", false, "Synchronize UIDs/GIDs from host to container")
|
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.
|
// GetRootCommand returns the root cobra.Command for the application.
|
||||||
|
|||||||
@@ -4,11 +4,10 @@
|
|||||||
package shell
|
package shell
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"path"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
|
cntexec "github.com/hpcng/warewulf/internal/app/wwctl/container/exec"
|
||||||
"github.com/hpcng/warewulf/internal/pkg/container"
|
"github.com/hpcng/warewulf/internal/pkg/container"
|
||||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -23,28 +22,32 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
wwlog.Error("Unknown Warewulf container: %s", containerName)
|
wwlog.Error("Unknown Warewulf container: %s", containerName)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
for _, b := range binds {
|
for _, b := range binds {
|
||||||
allargs = append(allargs, "--bind", b)
|
allargs = append(allargs, "--bind", b)
|
||||||
}
|
}
|
||||||
allargs = append(allargs, args...)
|
*/
|
||||||
allargs = append(allargs, "/usr/bin/bash")
|
shellName := os.Getenv("SHELL")
|
||||||
|
if !container.ValidSource(containerName) {
|
||||||
c := exec.Command("/proc/self/exe", append([]string{"container", "exec"}, allargs...)...)
|
wwlog.Error("Unknown Warewulf container: %s", containerName)
|
||||||
|
|
||||||
//c := exec.Command("/bin/sh")
|
|
||||||
c.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
|
|
||||||
}
|
|
||||||
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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return nil
|
var shells []string
|
||||||
|
if shellName == "" {
|
||||||
|
shells = append(shells, "/bin/bash")
|
||||||
|
} else {
|
||||||
|
shells = append(shells, shellName, "/bin/bash")
|
||||||
|
}
|
||||||
|
for _, s := range shells {
|
||||||
|
if _, err := os.Stat(path.Join(container.RootFsDir(containerName), s)); err == nil {
|
||||||
|
shellName = s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ var (
|
|||||||
},
|
},
|
||||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||||
}
|
}
|
||||||
binds []string
|
binds []string
|
||||||
|
nodeName string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
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{}, "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.
|
// GetRootCommand returns the root cobra.Command for the application.
|
||||||
|
|||||||
@@ -31,12 +31,14 @@ var (
|
|||||||
}
|
}
|
||||||
verboseArg bool
|
verboseArg bool
|
||||||
DebugFlag bool
|
DebugFlag bool
|
||||||
|
LogLevel int
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.PersistentFlags().BoolVarP(&verboseArg, "verbose", "v", false, "Run with increased verbosity.")
|
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().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.SetUsageTemplate(help.UsageTemplate)
|
||||||
rootCmd.SetHelpTemplate(help.HelpTemplate)
|
rootCmd.SetHelpTemplate(help.HelpTemplate)
|
||||||
|
|
||||||
@@ -65,6 +67,9 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
wwlog.SetLogLevel(wwlog.INFO)
|
wwlog.SetLogLevel(wwlog.INFO)
|
||||||
}
|
}
|
||||||
|
if LogLevel != wwlog.INFO {
|
||||||
|
wwlog.SetLogLevel(LogLevel)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -192,12 +192,6 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin
|
|||||||
return
|
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)
|
fmt.Printf("Building container: %s\n", cip.Name)
|
||||||
err = container.Build(cip.Name, true)
|
err = container.Build(cip.Name, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -197,12 +197,6 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin
|
|||||||
return
|
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)
|
err = container.SyncUids(cip.Name, !cip.SyncUser)
|
||||||
if err != nil && !cip.SyncUser {
|
if err != nil && !cip.SyncUser {
|
||||||
err = fmt.Errorf("error in user sync, fix error and run 'syncuser' manually: %s", err)
|
err = fmt.Errorf("error in user sync, fix error and run 'syncuser' manually: %s", err)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ var (
|
|||||||
release string = "UNDEF"
|
release string = "UNDEF"
|
||||||
wwclientdir string = "UNDEF"
|
wwclientdir string = "UNDEF"
|
||||||
datadir string = "UNDEF"
|
datadir string = "UNDEF"
|
||||||
|
tmpdir string = "UNDEF"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BINDIR() string {
|
func BINDIR() string {
|
||||||
@@ -90,3 +91,8 @@ func WWCLIENTDIR() string {
|
|||||||
wwlog.Debug("WWCLIENTDIR = '%s'", wwclientdir)
|
wwlog.Debug("WWCLIENTDIR = '%s'", wwclientdir)
|
||||||
return wwclientdir
|
return wwclientdir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TMPDIR() string {
|
||||||
|
wwlog.Debug("TMPDIR = '%s'", tmpdir)
|
||||||
|
return tmpdir
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,4 +15,5 @@ func init() {
|
|||||||
version = "@VERSION@"
|
version = "@VERSION@"
|
||||||
release = "@RELEASE@"
|
release = "@RELEASE@"
|
||||||
wwclientdir = "@WWCLIENTDIR@"
|
wwclientdir = "@WWCLIENTDIR@"
|
||||||
|
tmpdir = "@TMPDIR@"
|
||||||
}
|
}
|
||||||
|
|||||||
35
internal/pkg/container/mountpoints.go
Normal file
35
internal/pkg/container/mountpoints.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -24,12 +24,16 @@ import (
|
|||||||
|
|
||||||
// reserve some number of cpus for system/warwulfd usage
|
// reserve some number of cpus for system/warwulfd usage
|
||||||
var processLimitedReserve int = 4
|
var processLimitedReserve int = 4
|
||||||
|
|
||||||
// maximum number of concurrent spawned processes
|
// 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
|
// Channel used as semaphore to specififed processLimitedMax
|
||||||
var processLimitedChan = make(chan int, processLimitedMax)
|
var processLimitedChan = make(chan int, processLimitedMax)
|
||||||
|
|
||||||
// Current number of processes started + queued
|
// Current number of processes started + queued
|
||||||
var processLimitedNum int32 = 0
|
var processLimitedNum int32 = 0
|
||||||
|
|
||||||
// Counter over total history of started processes
|
// Counter over total history of started processes
|
||||||
var processLimitedCounter uint32 = 0
|
var processLimitedCounter uint32 = 0
|
||||||
|
|
||||||
@@ -53,7 +57,7 @@ func ProcessLimitedStatus() (running int32, queued int32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func MaxInt( a int, b int ) int {
|
func MaxInt(a int, b int) int {
|
||||||
if a > b {
|
if a > b {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
@@ -65,7 +69,7 @@ func FirstError(errs ...error) (err error) {
|
|||||||
for _, e := range errs {
|
for _, e := range errs {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = e
|
err = e
|
||||||
}else if e != nil {
|
} else if e != nil {
|
||||||
wwlog.ErrorExc(e, "Unhandled error")
|
wwlog.ErrorExc(e, "Unhandled error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,7 +211,7 @@ func ValidateOrDie(message string, pattern string, expr string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//******************************************************************************
|
// ******************************************************************************
|
||||||
func FindFiles(path string) []string {
|
func FindFiles(path string) []string {
|
||||||
var ret []string
|
var ret []string
|
||||||
|
|
||||||
@@ -244,7 +248,7 @@ func FindFiles(path string) []string {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
//******************************************************************************
|
// ******************************************************************************
|
||||||
func FindFilterFiles(
|
func FindFilterFiles(
|
||||||
path string,
|
path string,
|
||||||
include []string,
|
include []string,
|
||||||
@@ -265,7 +269,7 @@ func FindFilterFiles(
|
|||||||
return ofiles, errors.Wrapf(err, "Failed to change path: %s", path)
|
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.TrimLeft(ignore[i], "/")
|
||||||
ignore[i] = strings.TrimPrefix(ignore[i], "./")
|
ignore[i] = strings.TrimPrefix(ignore[i], "./")
|
||||||
wwlog.Debug("Ignore pattern (%d): %s", i, 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
|
dev := path_stat.Sys().(*syscall.Stat_t).Dev
|
||||||
|
|
||||||
|
|
||||||
includeDirs := []string{}
|
includeDirs := []string{}
|
||||||
ignoreDirs := []string{}
|
ignoreDirs := []string{}
|
||||||
err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error {
|
err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error {
|
||||||
@@ -306,13 +309,13 @@ func FindFilterFiles(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ignoreDir := range(ignoreDirs) {
|
for _, ignoreDir := range ignoreDirs {
|
||||||
if strings.HasPrefix(location, ignoreDir) {
|
if strings.HasPrefix(location, ignoreDir) {
|
||||||
wwlog.Debug("Ignored (dir): %s", file)
|
wwlog.Debug("Ignored (dir): %s", file)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, pattern := range(ignore) {
|
for i, pattern := range ignore {
|
||||||
m, err := filepath.Match(pattern, location)
|
m, err := filepath.Match(pattern, location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -325,14 +328,14 @@ func FindFilterFiles(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, includeDir := range(includeDirs) {
|
for _, includeDir := range includeDirs {
|
||||||
if strings.HasPrefix(location, includeDir) {
|
if strings.HasPrefix(location, includeDir) {
|
||||||
wwlog.Debug("Included (dir): %s", file)
|
wwlog.Debug("Included (dir): %s", file)
|
||||||
ofiles = append(ofiles, location)
|
ofiles = append(ofiles, location)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, pattern := range(include) {
|
for i, pattern := range include {
|
||||||
m, err := filepath.Match(pattern, location)
|
m, err := filepath.Match(pattern, location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -352,7 +355,7 @@ func FindFilterFiles(
|
|||||||
return ofiles, err
|
return ofiles, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//******************************************************************************
|
// ******************************************************************************
|
||||||
func ExecInteractive(command string, a ...string) error {
|
func ExecInteractive(command string, a ...string) error {
|
||||||
wwlog.Debug("ExecInteractive(%s, %s)", command, a)
|
wwlog.Debug("ExecInteractive(%s, %s)", command, a)
|
||||||
c := exec.Command(command, a...)
|
c := exec.Command(command, a...)
|
||||||
@@ -538,20 +541,22 @@ func AppendLines(fileName string, lines []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*
|
||||||
|
******************************************************************************
|
||||||
|
|
||||||
Create an archive using cpio
|
Create an archive using cpio
|
||||||
*/
|
*/
|
||||||
func CpioCreate(
|
func CpioCreate(
|
||||||
ifiles []string,
|
ifiles []string,
|
||||||
ofile string,
|
ofile string,
|
||||||
format string,
|
format string,
|
||||||
cpio_args ...string ) (err error) {
|
cpio_args ...string) (err error) {
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--quiet",
|
"--quiet",
|
||||||
"--create",
|
"--create",
|
||||||
"-H", format,
|
"-H", format,
|
||||||
"--file=" + ofile }
|
"--file=" + ofile}
|
||||||
|
|
||||||
args = append(args, cpio_args...)
|
args = append(args, cpio_args...)
|
||||||
|
|
||||||
@@ -574,14 +579,16 @@ func CpioCreate(
|
|||||||
wwlog.Debug(string(out))
|
wwlog.Debug(string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
return FirstError(err, <- err_in)
|
return FirstError(err, <-err_in)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*
|
||||||
|
******************************************************************************
|
||||||
|
|
||||||
Compress a file using gzip or pigz
|
Compress a file using gzip or pigz
|
||||||
*/
|
*/
|
||||||
func FileGz(
|
func FileGz(
|
||||||
file string ) (err error) {
|
file string) (err error) {
|
||||||
|
|
||||||
file_gz := file + ".gz"
|
file_gz := file + ".gz"
|
||||||
|
|
||||||
@@ -608,41 +615,41 @@ func FileGz(
|
|||||||
proc := exec.Command(
|
proc := exec.Command(
|
||||||
compressor,
|
compressor,
|
||||||
"--keep",
|
"--keep",
|
||||||
file )
|
file)
|
||||||
|
|
||||||
out, err := proc.CombinedOutput()
|
out, err := proc.CombinedOutput()
|
||||||
if len(out) > 0 {
|
if len(out) > 0 {
|
||||||
outStr := string(out[:])
|
outStr := string(out[:])
|
||||||
if err != nil && strings.HasSuffix(compressor, "gzip") && strings.Contains(outStr, "unrecognized option") {
|
if err != nil && strings.HasSuffix(compressor, "gzip") && strings.Contains(outStr, "unrecognized option") {
|
||||||
var gzippedFile *os.File
|
var gzippedFile *os.File
|
||||||
var gzipStderr io.ReadCloser
|
var gzipStderr io.ReadCloser
|
||||||
|
|
||||||
/* Older version of gzip, try it another way: */
|
/* Older version of gzip, try it another way: */
|
||||||
wwlog.Verbose("%s does not recognize the --keep flag, trying redirected stdout", compressor)
|
wwlog.Verbose("%s does not recognize the --keep flag, trying redirected stdout", compressor)
|
||||||
|
|
||||||
/* Open the output file for writing: */
|
/* Open the output file for writing: */
|
||||||
gzippedFile, err = os.Create(file_gz)
|
gzippedFile, err = os.Create(file_gz)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Unable to open compressed image file for writing: %s", file_gz)
|
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
|
/* We'll execute gzip with output to stdout and attach stdout to the compressed file we just
|
||||||
created:
|
created:
|
||||||
*/
|
*/
|
||||||
proc = exec.Command(
|
proc = exec.Command(
|
||||||
compressor,
|
compressor,
|
||||||
"--stdout",
|
"--stdout",
|
||||||
file )
|
file)
|
||||||
proc.Stdout = gzippedFile
|
proc.Stdout = gzippedFile
|
||||||
gzipStderr, err = proc.StderrPipe()
|
gzipStderr, err = proc.StderrPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Unable to open stderr pipe for compression program: %s", compressor)
|
return errors.Wrapf(err, "Unable to open stderr pipe for compression program: %s", compressor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Execute the command: */
|
/* Execute the command: */
|
||||||
err = proc.Start()
|
err = proc.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = proc.Wait()
|
_ = proc.Wait()
|
||||||
gzippedFile.Close()
|
gzippedFile.Close()
|
||||||
os.Remove(file_gz)
|
os.Remove(file_gz)
|
||||||
err = errors.Wrapf(err, "Unable to successfully execute compression program: %s", compressor)
|
err = errors.Wrapf(err, "Unable to successfully execute compression program: %s", compressor)
|
||||||
@@ -664,7 +671,9 @@ func FileGz(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*
|
||||||
|
******************************************************************************
|
||||||
|
|
||||||
Create an archive using cpio
|
Create an archive using cpio
|
||||||
*/
|
*/
|
||||||
func BuildFsImage(
|
func BuildFsImage(
|
||||||
@@ -675,7 +684,7 @@ func BuildFsImage(
|
|||||||
ignore []string,
|
ignore []string,
|
||||||
ignore_xdev bool,
|
ignore_xdev bool,
|
||||||
format string,
|
format string,
|
||||||
cpio_args ...string ) (err error) {
|
cpio_args ...string) (err error) {
|
||||||
|
|
||||||
err = os.MkdirAll(path.Dir(imagePath), 0755)
|
err = os.MkdirAll(path.Dir(imagePath), 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -709,16 +718,16 @@ func BuildFsImage(
|
|||||||
".",
|
".",
|
||||||
include,
|
include,
|
||||||
ignore,
|
ignore,
|
||||||
ignore_xdev )
|
ignore_xdev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Failed discovering files for %s: %s", name, rootfsPath)
|
return errors.Wrapf(err, "Failed discovering files for %s: %s", name, rootfsPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = CpioCreate(
|
err = CpioCreate(
|
||||||
files,
|
files,
|
||||||
imagePath,
|
imagePath,
|
||||||
format,
|
format,
|
||||||
cpio_args...)
|
cpio_args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Failed creating image for %s: %s", name, imagePath)
|
return errors.Wrapf(err, "Failed creating image for %s: %s", name, imagePath)
|
||||||
}
|
}
|
||||||
@@ -727,15 +736,17 @@ func BuildFsImage(
|
|||||||
|
|
||||||
err = FileGz(imagePath)
|
err = FileGz(imagePath)
|
||||||
if err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*
|
||||||
|
******************************************************************************
|
||||||
|
|
||||||
Runs wwctl command
|
Runs wwctl command
|
||||||
*/
|
*/
|
||||||
func RunWWCTL(args ...string) (out []byte, err error) {
|
func RunWWCTL(args ...string) (out []byte, err error) {
|
||||||
@@ -744,7 +755,7 @@ func RunWWCTL(args ...string) (out []byte, err error) {
|
|||||||
running, queued := ProcessLimitedStatus()
|
running, queued := ProcessLimitedStatus()
|
||||||
|
|
||||||
wwlog.Verbose("Starting wwctl process %d (%d running, %d queued): %v",
|
wwlog.Verbose("Starting wwctl process %d (%d running, %d queued): %v",
|
||||||
index, running, queued, args )
|
index, running, queued, args)
|
||||||
|
|
||||||
proc := exec.Command("wwctl", 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])
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,19 +7,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ControllerConf struct {
|
type ControllerConf struct {
|
||||||
WWInternal int `yaml:"WW_INTERNAL"`
|
WWInternal int `yaml:"WW_INTERNAL"`
|
||||||
Comment string `yaml:"comment,omitempty"`
|
Comment string `yaml:"comment,omitempty"`
|
||||||
Ipaddr string `yaml:"ipaddr"`
|
Ipaddr string `yaml:"ipaddr"`
|
||||||
Ipaddr6 string `yaml:"ipaddr6,omitempty"`
|
Ipaddr6 string `yaml:"ipaddr6,omitempty"`
|
||||||
Netmask string `yaml:"netmask"`
|
Netmask string `yaml:"netmask"`
|
||||||
Network string `yaml:"network,omitempty"`
|
Network string `yaml:"network,omitempty"`
|
||||||
Ipv6net string `yaml:"ipv6net,omitempty"`
|
Ipv6net string `yaml:"ipv6net,omitempty"`
|
||||||
Fqdn string `yaml:"fqdn,omitempty"`
|
Fqdn string `yaml:"fqdn,omitempty"`
|
||||||
Warewulf *WarewulfConf `yaml:"warewulf"`
|
Warewulf *WarewulfConf `yaml:"warewulf"`
|
||||||
Dhcp *DhcpConf `yaml:"dhcp"`
|
Dhcp *DhcpConf `yaml:"dhcp"`
|
||||||
Tftp *TftpConf `yaml:"tftp"`
|
Tftp *TftpConf `yaml:"tftp"`
|
||||||
Nfs *NfsConf `yaml:"nfs"`
|
Nfs *NfsConf `yaml:"nfs"`
|
||||||
current bool
|
MountsContainer []*MountEntry `yaml:"container mounts"`
|
||||||
|
current bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type WarewulfConf struct {
|
type WarewulfConf struct {
|
||||||
@@ -61,6 +62,16 @@ type NfsExportConf struct {
|
|||||||
Mount bool `default:"true" yaml:"mount"`
|
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 {
|
func (s *NfsConf) Unmarshal(unmarshal func(interface{}) error) error {
|
||||||
if err := defaults.Set(s); err != nil {
|
if err := defaults.Set(s); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
BIN
print_mnts
Executable file
BIN
print_mnts
Executable file
Binary file not shown.
Reference in New Issue
Block a user