mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Resolve flakes with TestMultiContainerDestroy
Some processes are reparented to the root container depending on the kill order and the root container would not reap in time. So some zombie processes were still present when the test checked. Fix it by running the second container inside a PID namespace. PiperOrigin-RevId: 267278591
This commit is contained in:
committed by
gVisor bot
parent
bcddd0a477
commit
0f5cdc1e00
+22
-27
@@ -760,26 +760,34 @@ func (l *Loader) destroyContainer(cid string) error {
|
||||
if err := l.signalAllProcesses(cid, int32(linux.SIGKILL)); err != nil {
|
||||
return fmt.Errorf("sending SIGKILL to all container processes: %v", err)
|
||||
}
|
||||
// Wait for all processes that belong to the container to exit (including
|
||||
// exec'd processes).
|
||||
for _, t := range l.k.TaskSet().Root.Tasks() {
|
||||
if t.ContainerID() == cid {
|
||||
t.ThreadGroup().WaitExited()
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, all processes inside of the container have exited,
|
||||
// releasing all references to the container's MountNamespace and
|
||||
// causing all submounts and overlays to be unmounted.
|
||||
//
|
||||
// Since the container's MountNamespace has been released,
|
||||
// MountNamespace.destroy() will have executed, but that function may
|
||||
// trigger async close operations. We must wait for those to complete
|
||||
// before returning, otherwise the caller may kill the gofer before
|
||||
// they complete, causing a cascade of failing RPCs.
|
||||
fs.AsyncBarrier()
|
||||
}
|
||||
|
||||
// Remove all container thread groups from the map.
|
||||
// No more failure from this point on. Remove all container thread groups
|
||||
// from the map.
|
||||
for key := range l.processes {
|
||||
if key.cid == cid {
|
||||
delete(l.processes, key)
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, all processes inside of the container have exited,
|
||||
// releasing all references to the container's MountNamespace and
|
||||
// causing all submounts and overlays to be unmounted.
|
||||
//
|
||||
// Since the container's MountNamespace has been released,
|
||||
// MountNamespace.destroy() will have executed, but that function may
|
||||
// trigger async close operations. We must wait for those to complete
|
||||
// before returning, otherwise the caller may kill the gofer before
|
||||
// they complete, causing a cascade of failing RPCs.
|
||||
fs.AsyncBarrier()
|
||||
|
||||
log.Debugf("Container destroyed %q", cid)
|
||||
return nil
|
||||
}
|
||||
@@ -1037,21 +1045,8 @@ func (l *Loader) signalAllProcesses(cid string, signo int32) error {
|
||||
// the signal is delivered. This prevents process leaks when SIGKILL is
|
||||
// sent to the entire container.
|
||||
l.k.Pause()
|
||||
if err := l.k.SendContainerSignal(cid, &arch.SignalInfo{Signo: signo}); err != nil {
|
||||
l.k.Unpause()
|
||||
return err
|
||||
}
|
||||
l.k.Unpause()
|
||||
|
||||
// If SIGKILLing all processes, wait for them to exit.
|
||||
if linux.Signal(signo) == linux.SIGKILL {
|
||||
for _, t := range l.k.TaskSet().Root.Tasks() {
|
||||
if t.ContainerID() == cid {
|
||||
t.ThreadGroup().WaitExited()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
defer l.k.Unpause()
|
||||
return l.k.SendContainerSignal(cid, &arch.SignalInfo{Signo: signo})
|
||||
}
|
||||
|
||||
// threadGroupFromID same as threadGroupFromIDLocked except that it acquires
|
||||
|
||||
@@ -549,10 +549,16 @@ func TestMultiContainerDestroy(t *testing.T) {
|
||||
t.Logf("Running test with conf: %+v", conf)
|
||||
|
||||
// First container will remain intact while the second container is killed.
|
||||
specs, ids := createSpecs(
|
||||
[]string{app, "reaper"},
|
||||
podSpecs, ids := createSpecs(
|
||||
[]string{"sleep", "100"},
|
||||
[]string{app, "fork-bomb"})
|
||||
containers, cleanup, err := startContainers(conf, specs, ids)
|
||||
|
||||
// Run the fork bomb in a PID namespace to prevent processes to be
|
||||
// re-parented to PID=1 in the root container.
|
||||
podSpecs[1].Linux = &specs.Linux{
|
||||
Namespaces: []specs.LinuxNamespace{{Type: "pid"}},
|
||||
}
|
||||
containers, cleanup, err := startContainers(conf, podSpecs, ids)
|
||||
if err != nil {
|
||||
t.Fatalf("error starting containers: %v", err)
|
||||
}
|
||||
@@ -580,7 +586,7 @@ func TestMultiContainerDestroy(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("error getting process data from sandbox: %v", err)
|
||||
}
|
||||
expectedPL := []*control.Process{{PID: 1, Cmd: "test_app"}}
|
||||
expectedPL := []*control.Process{{PID: 1, Cmd: "sleep"}}
|
||||
if !procListsEqual(pss, expectedPL) {
|
||||
t.Errorf("container got process list: %s, want: %s", procListToString(pss), procListToString(expectedPL))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user