Recognize terminated runsc gofers during teardown
This commit is contained in:
parent
8cef037fa3
commit
6c570da60e
1 changed files with 97 additions and 8 deletions
82
patches/gvisor-9f8f6c917-gofer-zombie-state.patch
Normal file
82
patches/gvisor-9f8f6c917-gofer-zombie-state.patch
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
diff --git a/runsc/container/container.go b/runsc/container/container.go
|
||||
index f0bfbf2..9fe6cf5 100644
|
||||
--- a/runsc/container/container.go
|
||||
+++ b/runsc/container/container.go
|
||||
@@ -1321,6 +1321,14 @@ func (c *Container) waitForStopped() error {
|
||||
defer cancel()
|
||||
b := backoff.WithContext(backoff.NewConstantBackOff(100*time.Millisecond), ctx)
|
||||
op := func() error {
|
||||
+ stopped, err := processIsTerminal(goferPid)
|
||||
+ if err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+ if stopped {
|
||||
+ c.GoferPid.Store(0)
|
||||
+ return nil
|
||||
+ }
|
||||
if err := unix.Kill(goferPid, 0); err == nil {
|
||||
return fmt.Errorf("gofer is still running")
|
||||
}
|
||||
@@ -1330,6 +1338,25 @@ func (c *Container) waitForStopped() error {
|
||||
return backoff.Retry(op, b)
|
||||
}
|
||||
|
||||
+func processIsTerminal(pid int) (bool, error) {
|
||||
+ status, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
|
||||
+ if errors.Is(err, os.ErrNotExist) {
|
||||
+ return true, nil
|
||||
+ }
|
||||
+ if err != nil {
|
||||
+ return false, fmt.Errorf("reading process state: %v", err)
|
||||
+ }
|
||||
+ return processStateIsTerminal(status)
|
||||
+}
|
||||
+
|
||||
+func processStateIsTerminal(status []byte) (bool, error) {
|
||||
+ nameEnd := strings.LastIndexByte(string(status), ')')
|
||||
+ if nameEnd < 0 || nameEnd+2 >= len(status) {
|
||||
+ return false, fmt.Errorf("process state is malformed")
|
||||
+ }
|
||||
+ return status[nameEnd+2] == 'Z' || status[nameEnd+2] == 'X', nil
|
||||
+}
|
||||
+
|
||||
// shouldCreateDeviceGofer indicates whether a device gofer connection should
|
||||
// be created.
|
||||
func shouldCreateDeviceGofer(spec *specs.Spec, conf *config.Config) bool {
|
||||
diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go
|
||||
index 8b282c4..299745f 100644
|
||||
--- a/runsc/container/container_test.go
|
||||
+++ b/runsc/container/container_test.go
|
||||
@@ -2965,6 +2965,32 @@ func TestGoferExits(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
+func TestProcessStateIsTerminal(t *testing.T) {
|
||||
+ tests := []struct {
|
||||
+ name string
|
||||
+ status string
|
||||
+ terminal bool
|
||||
+ valid bool
|
||||
+ }{
|
||||
+ {name: "running", status: "123 (gofer) S 1", valid: true},
|
||||
+ {name: "zombie", status: "123 (gofer) Z 1", terminal: true, valid: true},
|
||||
+ {name: "dead", status: "123 (gofer) X 1", terminal: true, valid: true},
|
||||
+ {name: "closing parenthesis in name", status: "123 (gofer)) Z 1", terminal: true, valid: true},
|
||||
+ {name: "malformed", status: "123 gofer Z 1"},
|
||||
+ }
|
||||
+ for _, test := range tests {
|
||||
+ t.Run(test.name, func(t *testing.T) {
|
||||
+ terminal, err := processStateIsTerminal([]byte(test.status))
|
||||
+ if (err == nil) != test.valid {
|
||||
+ t.Fatalf("processStateIsTerminal() error = %v, want valid %t", err, test.valid)
|
||||
+ }
|
||||
+ if terminal != test.terminal {
|
||||
+ t.Errorf("processStateIsTerminal() = %t, want %t", terminal, test.terminal)
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
func TestRootNotMount(t *testing.T) {
|
||||
appSym, err := testutil.FindFile("test/cmd/test_app/test_app")
|
||||
if err != nil {
|
||||
Loading…
Reference in a new issue