mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Merge pull request #12469 from Luap99/ns-teardown-flake
Fix possible rootless netns cleanup race
This commit is contained in:
@ -35,7 +35,6 @@ Print usage statement
|
|||||||
Join the rootless network namespace used for CNI and netavark networking. It can be used to
|
Join the rootless network namespace used for CNI and netavark networking. It can be used to
|
||||||
connect to a rootless container via IP address (bridge networking). This is otherwise
|
connect to a rootless container via IP address (bridge networking). This is otherwise
|
||||||
not possible from the host network namespace.
|
not possible from the host network namespace.
|
||||||
_Note: Using this option with more than one unshare session can have unexpected results._
|
|
||||||
|
|
||||||
## Exit Codes
|
## Exit Codes
|
||||||
|
|
||||||
@ -57,13 +56,13 @@ the exit codes follow the `chroot` standard, see below:
|
|||||||
|
|
||||||
**127** Executing a _contained command_ and the _command_ cannot be found
|
**127** Executing a _contained command_ and the _command_ cannot be found
|
||||||
|
|
||||||
$ podman run busybox foo; echo $?
|
$ podman unshare foo; echo $?
|
||||||
Error: fork/exec /usr/bin/bogus: no such file or directory
|
Error: fork/exec /usr/bin/bogus: no such file or directory
|
||||||
127
|
127
|
||||||
|
|
||||||
**Exit code** _contained command_ exit code
|
**Exit code** _contained command_ exit code
|
||||||
|
|
||||||
$ podman run busybox /bin/sh -c 'exit 3'; echo $?
|
$ podman unshare /bin/sh -c 'exit 3'; echo $?
|
||||||
3
|
3
|
||||||
|
|
||||||
## EXAMPLE
|
## EXAMPLE
|
||||||
|
@ -322,17 +322,14 @@ func (r *RootlessNetNS) Do(toRun func() error) error {
|
|||||||
|
|
||||||
// Cleanup the rootless network namespace if needed.
|
// Cleanup the rootless network namespace if needed.
|
||||||
// It checks if we have running containers with the bridge network mode.
|
// It checks if we have running containers with the bridge network mode.
|
||||||
// Cleanup() will try to lock RootlessNetNS, therefore you have to call
|
// Cleanup() expects that r.Lock is locked
|
||||||
// it with an unlocked lock.
|
|
||||||
func (r *RootlessNetNS) Cleanup(runtime *Runtime) error {
|
func (r *RootlessNetNS) Cleanup(runtime *Runtime) error {
|
||||||
_, err := os.Stat(r.dir)
|
_, err := os.Stat(r.dir)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
// the directory does not exists no need for cleanup
|
// the directory does not exists no need for cleanup
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
r.Lock.Lock()
|
activeNetns := func(c *Container) bool {
|
||||||
defer r.Lock.Unlock()
|
|
||||||
running := func(c *Container) bool {
|
|
||||||
// no bridge => no need to check
|
// no bridge => no need to check
|
||||||
if !c.config.NetMode.IsBridge() {
|
if !c.config.NetMode.IsBridge() {
|
||||||
return false
|
return false
|
||||||
@ -352,15 +349,18 @@ func (r *RootlessNetNS) Cleanup(runtime *Runtime) error {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
state := c.state.State
|
// only check for an active netns, we cannot use the container state
|
||||||
return state == define.ContainerStateRunning
|
// because not running does not mean that the netns does not need cleanup
|
||||||
|
// only if the netns is empty we know that we do not need cleanup
|
||||||
|
return c.state.NetNS != nil
|
||||||
}
|
}
|
||||||
ctrs, err := runtime.GetContainersWithoutLock(running)
|
ctrs, err := runtime.GetContainersWithoutLock(activeNetns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// no cleanup if we found containers
|
// no cleanup if we found no other containers with a netns
|
||||||
if len(ctrs) > 0 {
|
// we will always find one container (the container cleanup that is currently calling us)
|
||||||
|
if len(ctrs) > 1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
logrus.Debug("Cleaning up rootless network namespace")
|
logrus.Debug("Cleaning up rootless network namespace")
|
||||||
@ -809,10 +809,10 @@ func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error {
|
|||||||
if rootlessNetNS != nil {
|
if rootlessNetNS != nil {
|
||||||
// execute the cni setup in the rootless net ns
|
// execute the cni setup in the rootless net ns
|
||||||
err = rootlessNetNS.Do(tearDownPod)
|
err = rootlessNetNS.Do(tearDownPod)
|
||||||
rootlessNetNS.Lock.Unlock()
|
if cerr := rootlessNetNS.Cleanup(r); cerr != nil {
|
||||||
if err == nil {
|
logrus.WithError(err).Error("failed to cleanup rootless netns")
|
||||||
err = rootlessNetNS.Cleanup(r)
|
|
||||||
}
|
}
|
||||||
|
rootlessNetNS.Lock.Unlock()
|
||||||
} else {
|
} else {
|
||||||
err = tearDownPod()
|
err = tearDownPod()
|
||||||
}
|
}
|
||||||
|
@ -365,9 +365,12 @@ func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// make sure to unlock, unshare can run for a long time
|
// Make sure to unlock, unshare can run for a long time.
|
||||||
rootlessNetNS.Lock.Unlock()
|
rootlessNetNS.Lock.Unlock()
|
||||||
defer rootlessNetNS.Cleanup(ic.Libpod)
|
// We do not want to cleanup the netns after unshare.
|
||||||
|
// The problem is that we cannot know if we need to cleanup and
|
||||||
|
// secondly unshare should allow user to setup the namespace with
|
||||||
|
// special things, e.g. potentially macvlan or something like that.
|
||||||
return rootlessNetNS.Do(unshare)
|
return rootlessNetNS.Do(unshare)
|
||||||
}
|
}
|
||||||
return unshare()
|
return unshare()
|
||||||
|
Reference in New Issue
Block a user