test: Bump up runc to 9f9c96235cc97674e935002fc3d78361b696a69e
This brings in a fix for a cgroups setup race condition that we hit sometimes in the tests. Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
parent
77561e95cf
commit
970b8d61a7
89 changed files with 3197 additions and 970 deletions
295
vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
generated
vendored
295
vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
generated
vendored
|
@ -5,6 +5,7 @@ package libcontainer
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -21,6 +22,7 @@ import (
|
|||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/runc/libcontainer/criurpc"
|
||||
"github.com/opencontainers/runc/libcontainer/intelrdt"
|
||||
"github.com/opencontainers/runc/libcontainer/system"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
|
||||
|
@ -38,10 +40,14 @@ type linuxContainer struct {
|
|||
root string
|
||||
config *configs.Config
|
||||
cgroupManager cgroups.Manager
|
||||
intelRdtManager intelrdt.Manager
|
||||
initPath string
|
||||
initArgs []string
|
||||
initProcess parentProcess
|
||||
initProcessStartTime uint64
|
||||
criuPath string
|
||||
newuidmapPath string
|
||||
newgidmapPath string
|
||||
m sync.Mutex
|
||||
criuVersion int
|
||||
state containerState
|
||||
|
@ -67,6 +73,9 @@ type State struct {
|
|||
|
||||
// Container's standard descriptors (std{in,out,err}), needed for checkpoint and restore
|
||||
ExternalDescriptors []string `json:"external_descriptors,omitempty"`
|
||||
|
||||
// Intel RDT "resource control" filesystem path
|
||||
IntelRdtPath string `json:"intel_rdt_path"`
|
||||
}
|
||||
|
||||
// Container is a libcontainer container object.
|
||||
|
@ -163,6 +172,11 @@ func (c *linuxContainer) Stats() (*Stats, error) {
|
|||
if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil {
|
||||
return stats, newSystemErrorWithCause(err, "getting container stats from cgroups")
|
||||
}
|
||||
if c.intelRdtManager != nil {
|
||||
if stats.IntelRdtStats, err = c.intelRdtManager.GetStats(); err != nil {
|
||||
return stats, newSystemErrorWithCause(err, "getting container's Intel RDT stats")
|
||||
}
|
||||
}
|
||||
for _, iface := range c.config.Networks {
|
||||
switch iface.Type {
|
||||
case "veth":
|
||||
|
@ -186,8 +200,26 @@ func (c *linuxContainer) Set(config configs.Config) error {
|
|||
if status == Stopped {
|
||||
return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning)
|
||||
}
|
||||
if err := c.cgroupManager.Set(&config); err != nil {
|
||||
// Set configs back
|
||||
if err2 := c.cgroupManager.Set(c.config); err2 != nil {
|
||||
logrus.Warnf("Setting back cgroup configs failed due to error: %v, your state.json and actual configs might be inconsistent.", err2)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if c.intelRdtManager != nil {
|
||||
if err := c.intelRdtManager.Set(&config); err != nil {
|
||||
// Set configs back
|
||||
if err2 := c.intelRdtManager.Set(c.config); err2 != nil {
|
||||
logrus.Warnf("Setting back intelrdt configs failed due to error: %v, your state.json and actual configs might be inconsistent.", err2)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
// After config setting succeed, update config and states
|
||||
c.config = &config
|
||||
return c.cgroupManager.Set(c.config)
|
||||
_, err = c.updateState(nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *linuxContainer) Start(process *Process) error {
|
||||
|
@ -236,20 +268,71 @@ func (c *linuxContainer) Exec() error {
|
|||
|
||||
func (c *linuxContainer) exec() error {
|
||||
path := filepath.Join(c.root, execFifoFilename)
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return newSystemErrorWithCause(err, "open exec fifo for reading")
|
||||
|
||||
fifoOpen := make(chan struct{})
|
||||
select {
|
||||
case <-awaitProcessExit(c.initProcess.pid(), fifoOpen):
|
||||
return errors.New("container process is already dead")
|
||||
case result := <-awaitFifoOpen(path):
|
||||
close(fifoOpen)
|
||||
if result.err != nil {
|
||||
return result.err
|
||||
}
|
||||
f := result.file
|
||||
defer f.Close()
|
||||
if err := readFromExecFifo(f); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Remove(path)
|
||||
}
|
||||
defer f.Close()
|
||||
data, err := ioutil.ReadAll(f)
|
||||
}
|
||||
|
||||
func readFromExecFifo(execFifo io.Reader) error {
|
||||
data, err := ioutil.ReadAll(execFifo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) > 0 {
|
||||
os.Remove(path)
|
||||
return nil
|
||||
if len(data) <= 0 {
|
||||
return fmt.Errorf("cannot start an already running container")
|
||||
}
|
||||
return fmt.Errorf("cannot start an already running container")
|
||||
return nil
|
||||
}
|
||||
|
||||
func awaitProcessExit(pid int, exit <-chan struct{}) <-chan struct{} {
|
||||
isDead := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-exit:
|
||||
return
|
||||
case <-time.After(time.Millisecond * 100):
|
||||
stat, err := system.Stat(pid)
|
||||
if err != nil || stat.State == system.Zombie {
|
||||
close(isDead)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return isDead
|
||||
}
|
||||
|
||||
func awaitFifoOpen(path string) <-chan openResult {
|
||||
fifoOpened := make(chan openResult)
|
||||
go func() {
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
fifoOpened <- openResult{err: newSystemErrorWithCause(err, "open exec fifo for reading")}
|
||||
return
|
||||
}
|
||||
fifoOpened <- openResult{file: f}
|
||||
}()
|
||||
return fifoOpened
|
||||
}
|
||||
|
||||
type openResult struct {
|
||||
file *os.File
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *linuxContainer) start(process *Process, isInit bool) error {
|
||||
|
@ -259,7 +342,7 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
|
|||
}
|
||||
if err := parent.start(); err != nil {
|
||||
// terminate the process to ensure that it properly is reaped.
|
||||
if err := parent.terminate(); err != nil {
|
||||
if err := ignoreTerminateErrors(parent.terminate()); err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
return newSystemErrorWithCause(err, "starting container process")
|
||||
|
@ -277,15 +360,17 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
|
|||
c.initProcessStartTime = state.InitProcessStartTime
|
||||
|
||||
if c.config.Hooks != nil {
|
||||
bundle, annotations := utils.Annotations(c.config.Labels)
|
||||
s := configs.HookState{
|
||||
Version: c.config.Version,
|
||||
ID: c.id,
|
||||
Pid: parent.pid(),
|
||||
Bundle: utils.SearchLabels(c.config.Labels, "bundle"),
|
||||
Version: c.config.Version,
|
||||
ID: c.id,
|
||||
Pid: parent.pid(),
|
||||
Bundle: bundle,
|
||||
Annotations: annotations,
|
||||
}
|
||||
for i, hook := range c.config.Hooks.Poststart {
|
||||
if err := hook.Run(s); err != nil {
|
||||
if err := parent.terminate(); err != nil {
|
||||
if err := ignoreTerminateErrors(parent.terminate()); err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
return newSystemErrorWithCausef(err, "running poststart hook %d", i)
|
||||
|
@ -341,6 +426,23 @@ func (c *linuxContainer) deleteExecFifo() {
|
|||
os.Remove(fifoName)
|
||||
}
|
||||
|
||||
// includeExecFifo opens the container's execfifo as a pathfd, so that the
|
||||
// container cannot access the statedir (and the FIFO itself remains
|
||||
// un-opened). It then adds the FifoFd to the given exec.Cmd as an inherited
|
||||
// fd, with _LIBCONTAINER_FIFOFD set to its fd number.
|
||||
func (c *linuxContainer) includeExecFifo(cmd *exec.Cmd) error {
|
||||
fifoName := filepath.Join(c.root, execFifoFilename)
|
||||
fifoFd, err := unix.Open(fifoName, unix.O_PATH|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, os.NewFile(uintptr(fifoFd), fifoName))
|
||||
cmd.Env = append(cmd.Env,
|
||||
fmt.Sprintf("_LIBCONTAINER_FIFOFD=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) {
|
||||
parentPipe, childPipe, err := utils.NewSockPair("init")
|
||||
if err != nil {
|
||||
|
@ -354,22 +456,20 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces
|
|||
return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
|
||||
}
|
||||
|
||||
// We only set up rootDir if we're not doing a `runc exec`. The reason for
|
||||
// this is to avoid cases where a racing, unprivileged process inside the
|
||||
// container can get access to the statedir file descriptor (which would
|
||||
// allow for container rootfs escape).
|
||||
rootDir, err := os.Open(c.root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// We only set up fifoFd if we're not doing a `runc exec`. The historic
|
||||
// reason for this is that previously we would pass a dirfd that allowed
|
||||
// for container rootfs escape (and not doing it in `runc exec` avoided
|
||||
// that problem), but we no longer do that. However, there's no need to do
|
||||
// this for `runc exec` so we just keep it this way to be safe.
|
||||
if err := c.includeExecFifo(cmd); err != nil {
|
||||
return nil, newSystemErrorWithCause(err, "including execfifo in cmd.Exec setup")
|
||||
}
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, rootDir)
|
||||
cmd.Env = append(cmd.Env,
|
||||
fmt.Sprintf("_LIBCONTAINER_STATEDIR=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
|
||||
return c.newInitProcess(p, cmd, parentPipe, childPipe, rootDir)
|
||||
return c.newInitProcess(p, cmd, parentPipe, childPipe)
|
||||
}
|
||||
|
||||
func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) {
|
||||
cmd := exec.Command(c.initArgs[0], c.initArgs[1:]...)
|
||||
cmd := exec.Command(c.initPath, c.initArgs[1:]...)
|
||||
cmd.Args[0] = c.initArgs[0]
|
||||
cmd.Stdin = p.Stdin
|
||||
cmd.Stdout = p.Stdout
|
||||
cmd.Stderr = p.Stderr
|
||||
|
@ -397,7 +497,7 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
|
|||
return cmd, nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe, rootDir *os.File) (*initProcess, error) {
|
||||
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*initProcess, error) {
|
||||
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard))
|
||||
nsMaps := make(map[configs.NamespaceType]string)
|
||||
for _, ns := range c.config.Namespaces {
|
||||
|
@ -411,16 +511,16 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
|
|||
return nil, err
|
||||
}
|
||||
return &initProcess{
|
||||
cmd: cmd,
|
||||
childPipe: childPipe,
|
||||
parentPipe: parentPipe,
|
||||
manager: c.cgroupManager,
|
||||
config: c.newInitConfig(p),
|
||||
container: c,
|
||||
process: p,
|
||||
bootstrapData: data,
|
||||
sharePidns: sharePidns,
|
||||
rootDir: rootDir,
|
||||
cmd: cmd,
|
||||
childPipe: childPipe,
|
||||
parentPipe: parentPipe,
|
||||
manager: c.cgroupManager,
|
||||
intelRdtManager: c.intelRdtManager,
|
||||
config: c.newInitConfig(p),
|
||||
container: c,
|
||||
process: p,
|
||||
bootstrapData: data,
|
||||
sharePidns: sharePidns,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -439,6 +539,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
|
|||
return &setnsProcess{
|
||||
cmd: cmd,
|
||||
cgroupPaths: c.cgroupManager.GetPaths(),
|
||||
intelRdtPath: state.IntelRdtPath,
|
||||
childPipe: childPipe,
|
||||
parentPipe: parentPipe,
|
||||
config: c.newInitConfig(p),
|
||||
|
@ -477,6 +578,8 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
|
|||
cfg.Rlimits = process.Rlimits
|
||||
}
|
||||
cfg.CreateConsole = process.ConsoleSocket != nil
|
||||
cfg.ConsoleWidth = process.ConsoleWidth
|
||||
cfg.ConsoleHeight = process.ConsoleHeight
|
||||
return cfg
|
||||
}
|
||||
|
||||
|
@ -578,9 +681,24 @@ func (c *linuxContainer) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.
|
|||
logrus.Debugf("Feature check says: %s", criuFeatures)
|
||||
missingFeatures := false
|
||||
|
||||
if *criuFeat.MemTrack && !*criuFeatures.MemTrack {
|
||||
missingFeatures = true
|
||||
logrus.Debugf("CRIU does not support MemTrack")
|
||||
// The outer if checks if the fields actually exist
|
||||
if (criuFeat.MemTrack != nil) &&
|
||||
(criuFeatures.MemTrack != nil) {
|
||||
// The inner if checks if they are set to true
|
||||
if *criuFeat.MemTrack && !*criuFeatures.MemTrack {
|
||||
missingFeatures = true
|
||||
logrus.Debugf("CRIU does not support MemTrack")
|
||||
}
|
||||
}
|
||||
|
||||
// This needs to be repeated for every new feature check.
|
||||
// Is there a way to put this in a function. Reflection?
|
||||
if (criuFeat.LazyPages != nil) &&
|
||||
(criuFeatures.LazyPages != nil) {
|
||||
if *criuFeat.LazyPages && !*criuFeatures.LazyPages {
|
||||
missingFeatures = true
|
||||
logrus.Debugf("CRIU does not support LazyPages")
|
||||
}
|
||||
}
|
||||
|
||||
if missingFeatures {
|
||||
|
@ -610,9 +728,9 @@ func parseCriuVersion(path string) (int, error) {
|
|||
return 0, fmt.Errorf("Unable to parse the CRIU version: %s", path)
|
||||
}
|
||||
|
||||
n, err := fmt.Sscanf(string(version), "GitID: v%d.%d.%d", &x, &y, &z) // 1.5.2
|
||||
n, err := fmt.Sscanf(version, "GitID: v%d.%d.%d", &x, &y, &z) // 1.5.2
|
||||
if err != nil {
|
||||
n, err = fmt.Sscanf(string(version), "GitID: v%d.%d", &x, &y) // 1.6
|
||||
n, err = fmt.Sscanf(version, "GitID: v%d.%d", &x, &y) // 1.6
|
||||
y++
|
||||
} else {
|
||||
z++
|
||||
|
@ -736,6 +854,25 @@ func (c *linuxContainer) addMaskPaths(req *criurpc.CriuReq) error {
|
|||
}
|
||||
req.Opts.ExtMnt = append(req.Opts.ExtMnt, extMnt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitForCriuLazyServer(r *os.File, status string) error {
|
||||
|
||||
data := make([]byte, 1)
|
||||
_, err := r.Read(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fd, err := os.OpenFile(status, os.O_TRUNC|os.O_WRONLY, os.ModeAppend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fd.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fd.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -802,6 +939,8 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
|||
FileLocks: proto.Bool(criuOpts.FileLocks),
|
||||
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
|
||||
OrphanPtsMaster: proto.Bool(true),
|
||||
AutoDedup: proto.Bool(criuOpts.AutoDedup),
|
||||
LazyPages: proto.Bool(criuOpts.LazyPages),
|
||||
}
|
||||
|
||||
fcg := c.cgroupManager.GetPaths()["freezer"]
|
||||
|
@ -852,6 +991,24 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
|||
Opts: &rpcOpts,
|
||||
}
|
||||
|
||||
if criuOpts.LazyPages {
|
||||
// lazy migration requested; check if criu supports it
|
||||
feat := criurpc.CriuFeatures{
|
||||
LazyPages: proto.Bool(true),
|
||||
}
|
||||
|
||||
if err := c.checkCriuFeatures(criuOpts, &rpcOpts, &feat); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
statusRead, statusWrite, err := os.Pipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rpcOpts.StatusFd = proto.Int32(int32(statusWrite.Fd()))
|
||||
go waitForCriuLazyServer(statusRead, criuOpts.StatusFd)
|
||||
}
|
||||
|
||||
//no need to dump these information in pre-dump
|
||||
if !criuOpts.PreDump {
|
||||
for _, m := range c.config.Mounts {
|
||||
|
@ -1003,6 +1160,8 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
|||
FileLocks: proto.Bool(criuOpts.FileLocks),
|
||||
EmptyNs: proto.Uint32(criuOpts.EmptyNs),
|
||||
OrphanPtsMaster: proto.Bool(true),
|
||||
AutoDedup: proto.Bool(criuOpts.AutoDedup),
|
||||
LazyPages: proto.Bool(criuOpts.LazyPages),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1331,11 +1490,13 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
|
|||
}
|
||||
case notify.GetScript() == "setup-namespaces":
|
||||
if c.config.Hooks != nil {
|
||||
bundle, annotations := utils.Annotations(c.config.Labels)
|
||||
s := configs.HookState{
|
||||
Version: c.config.Version,
|
||||
ID: c.id,
|
||||
Pid: int(notify.GetPid()),
|
||||
Bundle: utils.SearchLabels(c.config.Labels, "bundle"),
|
||||
Version: c.config.Version,
|
||||
ID: c.id,
|
||||
Pid: int(notify.GetPid()),
|
||||
Bundle: bundle,
|
||||
Annotations: annotations,
|
||||
}
|
||||
for i, hook := range c.config.Hooks.Prestart {
|
||||
if err := hook.Run(s); err != nil {
|
||||
|
@ -1380,7 +1541,7 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
|
|||
defer master.Close()
|
||||
|
||||
// While we can access console.master, using the API is a good idea.
|
||||
if err := utils.SendFd(process.ConsoleSocket, master); err != nil {
|
||||
if err := utils.SendFd(process.ConsoleSocket, master.Name(), master.Fd()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -1388,7 +1549,9 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
|
|||
}
|
||||
|
||||
func (c *linuxContainer) updateState(process parentProcess) (*State, error) {
|
||||
c.initProcess = process
|
||||
if process != nil {
|
||||
c.initProcess = process
|
||||
}
|
||||
state, err := c.currentState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1493,6 +1656,10 @@ func (c *linuxContainer) currentState() (*State, error) {
|
|||
startTime, _ = c.initProcess.startTime()
|
||||
externalDescriptors = c.initProcess.externalDescriptors()
|
||||
}
|
||||
intelRdtPath, err := intelrdt.GetIntelRdtPath(c.ID())
|
||||
if err != nil {
|
||||
intelRdtPath = ""
|
||||
}
|
||||
state := &State{
|
||||
BaseState: BaseState{
|
||||
ID: c.ID(),
|
||||
|
@ -1503,6 +1670,7 @@ func (c *linuxContainer) currentState() (*State, error) {
|
|||
},
|
||||
Rootless: c.config.Rootless,
|
||||
CgroupPaths: c.cgroupManager.GetPaths(),
|
||||
IntelRdtPath: intelRdtPath,
|
||||
NamespacePaths: make(map[configs.NamespaceType]string),
|
||||
ExternalDescriptors: externalDescriptors,
|
||||
}
|
||||
|
@ -1601,6 +1769,12 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na
|
|||
if !joinExistingUser {
|
||||
// write uid mappings
|
||||
if len(c.config.UidMappings) > 0 {
|
||||
if c.config.Rootless && c.newuidmapPath != "" {
|
||||
r.AddData(&Bytemsg{
|
||||
Type: UidmapPathAttr,
|
||||
Value: []byte(c.newuidmapPath),
|
||||
})
|
||||
}
|
||||
b, err := encodeIDMapping(c.config.UidMappings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1621,6 +1795,12 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na
|
|||
Type: GidmapAttr,
|
||||
Value: b,
|
||||
})
|
||||
if c.config.Rootless && c.newgidmapPath != "" {
|
||||
r.AddData(&Bytemsg{
|
||||
Type: GidmapPathAttr,
|
||||
Value: []byte(c.newgidmapPath),
|
||||
})
|
||||
}
|
||||
// The following only applies if we are root.
|
||||
if !c.config.Rootless {
|
||||
// check if we have CAP_SETGID to setgroup properly
|
||||
|
@ -1652,3 +1832,18 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na
|
|||
|
||||
return bytes.NewReader(r.Serialize()), nil
|
||||
}
|
||||
|
||||
// ignoreTerminateErrors returns nil if the given err matches an error known
|
||||
// to indicate that the terminate occurred successfully or err was nil, otherwise
|
||||
// err is returned unaltered.
|
||||
func ignoreTerminateErrors(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
s := err.Error()
|
||||
switch {
|
||||
case strings.Contains(s, "process already finished"), strings.Contains(s, "Wait was already called"):
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue