Merge pull request #12469 from Luap99/ns-teardown-flake

Fix possible rootless netns cleanup race
This commit is contained in:
OpenShift Merge Robot
2021-12-02 14:40:48 +01:00
committed by GitHub
3 changed files with 20 additions and 18 deletions

View File

@ -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

View File

@ -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()
} }

View File

@ -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()