summaryrefslogtreecommitdiffhomepage
path: root/runsc/container/container.go
diff options
context:
space:
mode:
Diffstat (limited to 'runsc/container/container.go')
-rw-r--r--runsc/container/container.go57
1 files changed, 56 insertions, 1 deletions
diff --git a/runsc/container/container.go b/runsc/container/container.go
index 042c76577..8dca721f6 100644
--- a/runsc/container/container.go
+++ b/runsc/container/container.go
@@ -30,6 +30,7 @@ import (
specs "github.com/opencontainers/runtime-spec/specs-go"
"gvisor.googlesource.com/gvisor/pkg/log"
"gvisor.googlesource.com/gvisor/pkg/sentry/control"
+ "gvisor.googlesource.com/gvisor/pkg/syserror"
"gvisor.googlesource.com/gvisor/runsc/boot"
"gvisor.googlesource.com/gvisor/runsc/sandbox"
"gvisor.googlesource.com/gvisor/runsc/specutils"
@@ -100,11 +101,12 @@ type Container struct {
func Load(rootDir, id string) (*Container, error) {
log.Debugf("Load container %q %q", rootDir, id)
if err := validateID(id); err != nil {
- return nil, err
+ return nil, fmt.Errorf("error validating id: %v", err)
}
cRoot, err := findContainerRoot(rootDir, id)
if err != nil {
+ // Preserve error so that callers can distinguish 'not found' errors.
return nil, err
}
@@ -471,6 +473,32 @@ func (c *Container) Destroy() error {
c.Sandbox = nil
c.Status = Stopped
+
+ return nil
+}
+
+// DestroyAndWait frees all resources associated with the container
+// and waits for destroy to finish before returning.
+func (c *Container) DestroyAndWait() error {
+ sandboxPid := c.Sandbox.Pid
+ goferPid := c.Sandbox.GoferPid
+
+ if err := c.Destroy(); err != nil {
+ return fmt.Errorf("error destroying container %v: %v", c, err)
+ }
+
+ if sandboxPid != 0 {
+ if err := waitForDeath(sandboxPid, 5*time.Second); err != nil {
+ return fmt.Errorf("error waiting for sandbox death: %v", err)
+ }
+ }
+
+ if goferPid != 0 {
+ if err := waitForDeath(goferPid, 5*time.Second); err != nil {
+ return fmt.Errorf("error waiting for gofer death: %v", err)
+ }
+ }
+
return nil
}
@@ -490,3 +518,30 @@ func (c *Container) save() error {
}
return nil
}
+
+// waitForDeath ensures that process is dead before proceeding.
+//
+// This is racy because the kernel can potentially reuse the pid in the time
+// between the process' death and the first check after the process has ended.
+func waitForDeath(pid int, timeout time.Duration) error {
+ backoff := 1 * time.Millisecond
+ for start := time.Now(); time.Now().Sub(start) < timeout; {
+
+ if err := syscall.Kill(pid, 0); err != nil {
+ if err == syserror.ESRCH {
+ // pid does not exist so process must have died
+ return nil
+ }
+ return fmt.Errorf("error killing pid (%d): %v", pid, err)
+ }
+ // pid is still alive.
+
+ // Process continues to run, backoff and retry.
+ time.Sleep(backoff)
+ backoff *= 2
+ if backoff > 1*time.Second {
+ backoff = 1 * time.Second
+ }
+ }
+ return fmt.Errorf("timed out waiting for process (%d)", pid)
+}