浏览代码

Merge pull request #33075 from x1022as/stop

fix inconsistent state string with containerd
Brian Goff 8 年之前
父节点
当前提交
4dd3e5b77c
共有 37 个文件被更改,包括 1989 次插入18 次删除
  1. 1 1
      daemon/daemon.go
  2. 1 1
      hack/dockerfile/binaries-commits
  3. 3 2
      libcontainerd/client_linux.go
  4. 1 1
      libcontainerd/client_unix.go
  5. 1 1
      libcontainerd/container_unix.go
  6. 1 1
      libcontainerd/process_unix.go
  7. 1 1
      libcontainerd/remote_unix.go
  8. 1 1
      libcontainerd/types.go
  9. 1 1
      libcontainerd/types_linux.go
  10. 1 1
      libcontainerd/types_solaris.go
  11. 1 1
      libcontainerd/utils_linux.go
  12. 1 1
      libcontainerd/utils_solaris.go
  13. 3 2
      vendor.conf
  14. 0 0
      vendor/github.com/containerd/containerd/LICENSE.code
  15. 0 0
      vendor/github.com/containerd/containerd/LICENSE.docs
  16. 0 0
      vendor/github.com/containerd/containerd/NOTICE
  17. 0 0
      vendor/github.com/containerd/containerd/README.md
  18. 0 0
      vendor/github.com/containerd/containerd/api/grpc/types/api.pb.go
  19. 0 0
      vendor/github.com/containerd/containerd/api/grpc/types/api.proto
  20. 18 0
      vendor/github.com/containerd/containerd/osutils/fds.go
  21. 15 0
      vendor/github.com/containerd/containerd/osutils/pdeathsig_linux.go
  22. 8 0
      vendor/github.com/containerd/containerd/osutils/pdeathsig_solaris.go
  23. 48 0
      vendor/github.com/containerd/containerd/osutils/prctl.go
  24. 19 0
      vendor/github.com/containerd/containerd/osutils/prctl_solaris.go
  25. 51 0
      vendor/github.com/containerd/containerd/osutils/reaper.go
  26. 749 0
      vendor/github.com/containerd/containerd/runtime/container.go
  27. 190 0
      vendor/github.com/containerd/containerd/runtime/container_linux.go
  28. 48 0
      vendor/github.com/containerd/containerd/runtime/container_solaris.go
  29. 476 0
      vendor/github.com/containerd/containerd/runtime/process.go
  30. 22 0
      vendor/github.com/containerd/containerd/runtime/process_linux.go
  31. 34 0
      vendor/github.com/containerd/containerd/runtime/process_solaris.go
  32. 132 0
      vendor/github.com/containerd/containerd/runtime/runtime.go
  33. 87 0
      vendor/github.com/containerd/containerd/runtime/stats.go
  34. 12 0
      vendor/github.com/containerd/containerd/specs/spec_linux.go
  35. 10 0
      vendor/github.com/containerd/containerd/specs/spec_solaris.go
  36. 3 3
      vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unix.go
  37. 50 0
      vendor/github.com/pmezard/go-difflib/README.md

+ 1 - 1
daemon/daemon.go

@@ -19,7 +19,7 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"

+ 1 - 1
hack/dockerfile/binaries-commits

@@ -4,7 +4,7 @@ TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
 
 # When updating RUNC_COMMIT, also update runc in vendor.conf accordingly
 RUNC_COMMIT=992a5be178a62e026f4069f443c6164912adbf09
-CONTAINERD_COMMIT=8ef7df579710405c4bb6e0812495671002ce08e0
+CONTAINERD_COMMIT=3addd840653146c90a254301d6c3a663c7fd6429
 TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574
 LIBNETWORK_COMMIT=7b2b1feb1de4817d522cc372af149ff48d25028e
 VNDR_COMMIT=c56e082291115e369f77601f9c071dd0b87c7120

+ 3 - 2
libcontainerd/client_linux.go

@@ -9,7 +9,8 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
+	containerd_runtime_types "github.com/containerd/containerd/runtime"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/mount"
 	"github.com/golang/protobuf/ptypes"
@@ -467,7 +468,7 @@ func (clnt *client) Restore(containerID string, attachStdio StdioCallback, optio
 	cont, err := clnt.getContainerdContainer(containerID)
 	// Get its last event
 	ev, eerr := clnt.getContainerLastEvent(containerID)
-	if err != nil || cont.Status == "Stopped" {
+	if err != nil || containerd_runtime_types.State(cont.Status) == containerd_runtime_types.Stopped {
 		if err != nil {
 			logrus.Warnf("libcontainerd: failed to retrieve container %s state: %v", containerID, err)
 		}

+ 1 - 1
libcontainerd/client_unix.go

@@ -11,7 +11,7 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/docker/docker/pkg/idtools"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"golang.org/x/net/context"

+ 1 - 1
libcontainerd/container_unix.go

@@ -13,7 +13,7 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/docker/docker/pkg/ioutils"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/tonistiigi/fifo"

+ 1 - 1
libcontainerd/process_unix.go

@@ -10,7 +10,7 @@ import (
 	goruntime "runtime"
 	"strings"
 
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/tonistiigi/fifo"
 	"golang.org/x/net/context"
 	"golang.org/x/sys/unix"

+ 1 - 1
libcontainerd/remote_unix.go

@@ -19,7 +19,7 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/docker/docker/pkg/locker"
 	"github.com/docker/docker/pkg/system"
 	"github.com/golang/protobuf/ptypes"

+ 1 - 1
libcontainerd/types.go

@@ -3,7 +3,7 @@ package libcontainerd
 import (
 	"io"
 
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/opencontainers/runtime-spec/specs-go"
 	"golang.org/x/net/context"
 )

+ 1 - 1
libcontainerd/types_linux.go

@@ -1,7 +1,7 @@
 package libcontainerd
 
 import (
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/opencontainers/runtime-spec/specs-go"
 )
 

+ 1 - 1
libcontainerd/types_solaris.go

@@ -1,7 +1,7 @@
 package libcontainerd
 
 import (
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/opencontainers/runtime-spec/specs-go"
 )
 

+ 1 - 1
libcontainerd/utils_linux.go

@@ -3,7 +3,7 @@ package libcontainerd
 import (
 	"syscall"
 
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/opencontainers/runtime-spec/specs-go"
 )
 

+ 1 - 1
libcontainerd/utils_solaris.go

@@ -3,7 +3,7 @@ package libcontainerd
 import (
 	"syscall"
 
-	containerd "github.com/docker/containerd/api/grpc/types"
+	containerd "github.com/containerd/containerd/api/grpc/types"
 	"github.com/opencontainers/runtime-spec/specs-go"
 )
 

+ 3 - 2
vendor.conf

@@ -19,6 +19,7 @@ github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
 github.com/docker/go-connections e15c02316c12de00874640cd76311849de2aeed5
 golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
 github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
+github.com/pmezard/go-difflib v1.0.0
 
 github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
 github.com/imdario/mergo 0.2.1
@@ -60,7 +61,7 @@ google.golang.org/grpc v1.0.4
 github.com/miekg/pkcs11 df8ae6ca730422dba20c768ff38ef7d79077a59f
 
 # When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly
-github.com/opencontainers/runc b6b70e53451794e8333e9b602cc096b47a20bd0f
+github.com/opencontainers/runc 992a5be178a62e026f4069f443c6164912adbf09
 github.com/opencontainers/runtime-spec v1.0.0-rc5 # specs
 github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
 
@@ -102,7 +103,7 @@ google.golang.org/genproto b3e7c2fb04031add52c4817f53f43757ccbf9c18
 github.com/docker/docker-credential-helpers v0.5.0
 
 # containerd
-github.com/docker/containerd 8ef7df579710405c4bb6e0812495671002ce08e0
+github.com/containerd/containerd 3addd840653146c90a254301d6c3a663c7fd6429
 github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
 
 # cluster

+ 0 - 0
vendor/github.com/docker/containerd/LICENSE.code → vendor/github.com/containerd/containerd/LICENSE.code


+ 0 - 0
vendor/github.com/docker/containerd/LICENSE.docs → vendor/github.com/containerd/containerd/LICENSE.docs


+ 0 - 0
vendor/github.com/docker/containerd/NOTICE → vendor/github.com/containerd/containerd/NOTICE


+ 0 - 0
vendor/github.com/docker/containerd/README.md → vendor/github.com/containerd/containerd/README.md


+ 0 - 0
vendor/github.com/docker/containerd/api/grpc/types/api.pb.go → vendor/github.com/containerd/containerd/api/grpc/types/api.pb.go


+ 0 - 0
vendor/github.com/docker/containerd/api/grpc/types/api.proto → vendor/github.com/containerd/containerd/api/grpc/types/api.proto


+ 18 - 0
vendor/github.com/containerd/containerd/osutils/fds.go

@@ -0,0 +1,18 @@
+// +build !windows,!darwin
+
+package osutils
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"strconv"
+)
+
+// GetOpenFds returns the number of open fds for the process provided by pid
+func GetOpenFds(pid int) (int, error) {
+	dirs, err := ioutil.ReadDir(filepath.Join("/proc", strconv.Itoa(pid), "fd"))
+	if err != nil {
+		return -1, err
+	}
+	return len(dirs), nil
+}

+ 15 - 0
vendor/github.com/containerd/containerd/osutils/pdeathsig_linux.go

@@ -0,0 +1,15 @@
+// +build !solaris
+
+package osutils
+
+import (
+	"syscall"
+)
+
+// SetPDeathSig sets the parent death signal to SIGKILL so that if the
+// shim dies the container process also dies.
+func SetPDeathSig() *syscall.SysProcAttr {
+	return &syscall.SysProcAttr{
+		Pdeathsig: syscall.SIGKILL,
+	}
+}

+ 8 - 0
vendor/github.com/containerd/containerd/osutils/pdeathsig_solaris.go

@@ -0,0 +1,8 @@
+// +build solaris
+
+package osutils
+
+// SetPDeathSig is a no-op on Solaris as Pdeathsig is not defined.
+func SetPDeathSig() *syscall.SysProcAttr {
+	return nil
+}

+ 48 - 0
vendor/github.com/containerd/containerd/osutils/prctl.go

@@ -0,0 +1,48 @@
+// +build linux
+
+// Package osutils provide access to the Get Child and Set Child prctl
+// flags.
+// See http://man7.org/linux/man-pages/man2/prctl.2.html
+package osutils
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+// PR_SET_CHILD_SUBREAPER allows setting the child subreaper.
+// If arg2 is nonzero, set the "child subreaper" attribute of the
+// calling process; if arg2 is zero, unset the attribute.  When a
+// process is marked as a child subreaper, all of the children
+// that it creates, and their descendants, will be marked as
+// having a subreaper.  In effect, a subreaper fulfills the role
+// of init(1) for its descendant processes.  Upon termination of
+// a process that is orphaned (i.e., its immediate parent has
+// already terminated) and marked as having a subreaper, the
+// nearest still living ancestor subreaper will receive a SIGCHLD
+// signal and be able to wait(2) on the process to discover its
+// termination status.
+const prSetChildSubreaper = 36
+
+// PR_GET_CHILD_SUBREAPER allows retrieving the current child
+// subreaper.
+// Return the "child subreaper" setting of the caller, in the
+// location pointed to by (int *) arg2.
+const prGetChildSubreaper = 37
+
+// GetSubreaper returns the subreaper setting for the calling process
+func GetSubreaper() (int, error) {
+	var i uintptr
+	if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, prGetChildSubreaper, uintptr(unsafe.Pointer(&i)), 0); err != 0 {
+		return -1, err
+	}
+	return int(i), nil
+}
+
+// SetSubreaper sets the value i as the subreaper setting for the calling process
+func SetSubreaper(i int) error {
+	if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, prSetChildSubreaper, uintptr(i), 0); err != 0 {
+		return err
+	}
+	return nil
+}

+ 19 - 0
vendor/github.com/containerd/containerd/osutils/prctl_solaris.go

@@ -0,0 +1,19 @@
+// +build solaris
+
+package osutils
+
+import (
+	"errors"
+)
+
+//Solaris TODO
+
+// GetSubreaper returns the subreaper setting for the calling process
+func GetSubreaper() (int, error) {
+	return 0, errors.New("osutils GetSubreaper not implemented on Solaris")
+}
+
+// SetSubreaper sets the value i as the subreaper setting for the calling process
+func SetSubreaper(i int) error {
+	return nil
+}

+ 51 - 0
vendor/github.com/containerd/containerd/osutils/reaper.go

@@ -0,0 +1,51 @@
+// +build !windows
+
+package osutils
+
+import "syscall"
+
+// Exit is the wait4 information from an exited process
+type Exit struct {
+	Pid    int
+	Status int
+}
+
+// Reap reaps all child processes for the calling process and returns their
+// exit information
+func Reap(wait bool) (exits []Exit, err error) {
+	var (
+		ws  syscall.WaitStatus
+		rus syscall.Rusage
+	)
+	flag := syscall.WNOHANG
+	if wait {
+		flag = 0
+	}
+	for {
+		pid, err := syscall.Wait4(-1, &ws, flag, &rus)
+		if err != nil {
+			if err == syscall.ECHILD {
+				return exits, nil
+			}
+			return exits, err
+		}
+		if pid <= 0 {
+			return exits, nil
+		}
+		exits = append(exits, Exit{
+			Pid:    pid,
+			Status: exitStatus(ws),
+		})
+	}
+}
+
+const exitSignalOffset = 128
+
+// exitStatus returns the correct exit status for a process based on if it
+// was signaled or exited cleanly
+func exitStatus(status syscall.WaitStatus) int {
+	if status.Signaled() {
+		return exitSignalOffset + int(status.Signal())
+	}
+	return status.ExitStatus()
+}

+ 749 - 0
vendor/github.com/containerd/containerd/runtime/container.go

@@ -0,0 +1,749 @@
+package runtime
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"syscall"
+	"time"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/containerd/containerd/specs"
+	ocs "github.com/opencontainers/runtime-spec/specs-go"
+	"golang.org/x/net/context"
+	"golang.org/x/sys/unix"
+)
+
+// Container defines the operations allowed on a container
+type Container interface {
+	// ID returns the container ID
+	ID() string
+	// Path returns the path to the bundle
+	Path() string
+	// Start starts the init process of the container
+	Start(ctx context.Context, checkpointPath string, s Stdio) (Process, error)
+	// Exec starts another process in an existing container
+	Exec(context.Context, string, specs.ProcessSpec, Stdio) (Process, error)
+	// Delete removes the container's state and any resources
+	Delete() error
+	// Processes returns all the containers processes that have been added
+	Processes() ([]Process, error)
+	// State returns the containers runtime state
+	State() State
+	// Resume resumes a paused container
+	Resume() error
+	// Pause pauses a running container
+	Pause() error
+	// RemoveProcess removes the specified process from the container
+	RemoveProcess(string) error
+	// Checkpoints returns all the checkpoints for a container
+	Checkpoints(checkpointDir string) ([]Checkpoint, error)
+	// Checkpoint creates a new checkpoint
+	Checkpoint(checkpoint Checkpoint, checkpointDir string) error
+	// DeleteCheckpoint deletes the checkpoint for the provided name
+	DeleteCheckpoint(name string, checkpointDir string) error
+	// Labels are user provided labels for the container
+	Labels() []string
+	// Pids returns all pids inside the container
+	Pids() ([]int, error)
+	// Stats returns realtime container stats and resource information
+	Stats() (*Stat, error)
+	// Name or path of the OCI compliant runtime used to execute the container
+	Runtime() string
+	// OOM signals the channel if the container received an OOM notification
+	OOM() (OOM, error)
+	// UpdateResource updates the containers resources to new values
+	UpdateResources(*Resource) error
+
+	// Status return the current status of the container.
+	Status() (State, error)
+}
+
+// OOM wraps a container OOM.
+type OOM interface {
+	io.Closer
+	FD() int
+	ContainerID() string
+	Flush()
+	Removed() bool
+}
+
+// Stdio holds the path to the 3 pipes used for the standard ios.
+type Stdio struct {
+	Stdin  string
+	Stdout string
+	Stderr string
+}
+
+// NewStdio wraps the given standard io path into an Stdio struct.
+// If a given parameter is the empty string, it is replaced by "/dev/null"
+func NewStdio(stdin, stdout, stderr string) Stdio {
+	for _, s := range []*string{
+		&stdin, &stdout, &stderr,
+	} {
+		if *s == "" {
+			*s = "/dev/null"
+		}
+	}
+	return Stdio{
+		Stdin:  stdin,
+		Stdout: stdout,
+		Stderr: stderr,
+	}
+}
+
+// ContainerOpts keeps the options passed at container creation
+type ContainerOpts struct {
+	Root        string
+	ID          string
+	Bundle      string
+	Runtime     string
+	RuntimeArgs []string
+	Shim        string
+	Labels      []string
+	NoPivotRoot bool
+	Timeout     time.Duration
+}
+
+// New returns a new container
+func New(opts ContainerOpts) (Container, error) {
+	c := &container{
+		root:        opts.Root,
+		id:          opts.ID,
+		bundle:      opts.Bundle,
+		labels:      opts.Labels,
+		processes:   make(map[string]*process),
+		runtime:     opts.Runtime,
+		runtimeArgs: opts.RuntimeArgs,
+		shim:        opts.Shim,
+		noPivotRoot: opts.NoPivotRoot,
+		timeout:     opts.Timeout,
+	}
+	if err := os.Mkdir(filepath.Join(c.root, c.id), 0755); err != nil {
+		return nil, err
+	}
+	f, err := os.Create(filepath.Join(c.root, c.id, StateFile))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	if err := json.NewEncoder(f).Encode(state{
+		Bundle:      c.bundle,
+		Labels:      c.labels,
+		Runtime:     c.runtime,
+		RuntimeArgs: c.runtimeArgs,
+		Shim:        c.shim,
+		NoPivotRoot: opts.NoPivotRoot,
+	}); err != nil {
+		return nil, err
+	}
+	return c, nil
+}
+
+// Load return a new container from the matchin state file on disk.
+func Load(root, id, shimName string, timeout time.Duration) (Container, error) {
+	var s state
+	f, err := os.Open(filepath.Join(root, id, StateFile))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	if err := json.NewDecoder(f).Decode(&s); err != nil {
+		return nil, err
+	}
+	c := &container{
+		root:        root,
+		id:          id,
+		bundle:      s.Bundle,
+		labels:      s.Labels,
+		runtime:     s.Runtime,
+		runtimeArgs: s.RuntimeArgs,
+		shim:        s.Shim,
+		noPivotRoot: s.NoPivotRoot,
+		processes:   make(map[string]*process),
+		timeout:     timeout,
+	}
+
+	if c.shim == "" {
+		c.shim = shimName
+	}
+
+	dirs, err := ioutil.ReadDir(filepath.Join(root, id))
+	if err != nil {
+		return nil, err
+	}
+	for _, d := range dirs {
+		if !d.IsDir() {
+			continue
+		}
+		pid := d.Name()
+		s, err := readProcessState(filepath.Join(root, id, pid))
+		if err != nil {
+			return nil, err
+		}
+		p, err := loadProcess(filepath.Join(root, id, pid), pid, c, s)
+		if err != nil {
+			logrus.WithField("id", id).WithField("pid", pid).Debugf("containerd: error loading process %s", err)
+			continue
+		}
+		c.processes[pid] = p
+	}
+
+	_, err = os.Stat(c.bundle)
+	if err != nil && !os.IsExist(err) {
+		for key, p := range c.processes {
+			if key == InitProcessID {
+				p.Delete()
+				break
+			}
+		}
+		return nil, fmt.Errorf("bundle dir %s don't exist", c.bundle)
+	}
+	return c, nil
+}
+
+func readProcessState(dir string) (*ProcessState, error) {
+	f, err := os.Open(filepath.Join(dir, "process.json"))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	var s ProcessState
+	if err := json.NewDecoder(f).Decode(&s); err != nil {
+		return nil, err
+	}
+	return &s, nil
+}
+
+type container struct {
+	// path to store runtime state information
+	root        string
+	id          string
+	bundle      string
+	runtime     string
+	runtimeArgs []string
+	shim        string
+	processes   map[string]*process
+	labels      []string
+	oomFds      []int
+	noPivotRoot bool
+	timeout     time.Duration
+}
+
+func (c *container) ID() string {
+	return c.id
+}
+
+func (c *container) Path() string {
+	return c.bundle
+}
+
+func (c *container) Labels() []string {
+	return c.labels
+}
+
+func (c *container) readSpec() (*specs.Spec, error) {
+	var spec specs.Spec
+	f, err := os.Open(filepath.Join(c.bundle, "config.json"))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	if err := json.NewDecoder(f).Decode(&spec); err != nil {
+		return nil, err
+	}
+	return &spec, nil
+}
+
+func (c *container) Delete() error {
+	var err error
+	args := append(c.runtimeArgs, "delete", c.id)
+	if b, derr := exec.Command(c.runtime, args...).CombinedOutput(); derr != nil && !strings.Contains(string(b), "does not exist") {
+		err = fmt.Errorf("%s: %q", derr, string(b))
+	}
+	if rerr := os.RemoveAll(filepath.Join(c.root, c.id)); rerr != nil {
+		if err != nil {
+			err = fmt.Errorf("%s; failed to remove %s: %s", err, filepath.Join(c.root, c.id), rerr)
+		} else {
+			err = rerr
+		}
+	}
+	return err
+}
+
+func (c *container) Processes() ([]Process, error) {
+	out := []Process{}
+	for _, p := range c.processes {
+		out = append(out, p)
+	}
+	return out, nil
+}
+
+func (c *container) RemoveProcess(pid string) error {
+	delete(c.processes, pid)
+	return os.RemoveAll(filepath.Join(c.root, c.id, pid))
+}
+
+func (c *container) State() State {
+	proc := c.processes[InitProcessID]
+	if proc == nil {
+		return Stopped
+	}
+	return proc.State()
+}
+
+func (c *container) Runtime() string {
+	return c.runtime
+}
+
+func (c *container) Pause() error {
+	args := c.runtimeArgs
+	args = append(args, "pause", c.id)
+	b, err := exec.Command(c.runtime, args...).CombinedOutput()
+	if err != nil {
+		return fmt.Errorf("%s: %q", err.Error(), string(b))
+	}
+	return nil
+}
+
+func (c *container) Resume() error {
+	args := c.runtimeArgs
+	args = append(args, "resume", c.id)
+	b, err := exec.Command(c.runtime, args...).CombinedOutput()
+	if err != nil {
+		return fmt.Errorf("%s: %q", err.Error(), string(b))
+	}
+	return nil
+}
+
+func (c *container) Checkpoints(checkpointDir string) ([]Checkpoint, error) {
+	if checkpointDir == "" {
+		checkpointDir = filepath.Join(c.bundle, "checkpoints")
+	}
+
+	dirs, err := ioutil.ReadDir(checkpointDir)
+	if err != nil {
+		return nil, err
+	}
+	var out []Checkpoint
+	for _, d := range dirs {
+		if !d.IsDir() {
+			continue
+		}
+		path := filepath.Join(checkpointDir, d.Name(), "config.json")
+		data, err := ioutil.ReadFile(path)
+		if err != nil {
+			return nil, err
+		}
+		var cpt Checkpoint
+		if err := json.Unmarshal(data, &cpt); err != nil {
+			return nil, err
+		}
+		out = append(out, cpt)
+	}
+	return out, nil
+}
+
+func (c *container) Checkpoint(cpt Checkpoint, checkpointDir string) error {
+	if checkpointDir == "" {
+		checkpointDir = filepath.Join(c.bundle, "checkpoints")
+	}
+
+	if err := os.MkdirAll(checkpointDir, 0755); err != nil {
+		return err
+	}
+
+	path := filepath.Join(checkpointDir, cpt.Name)
+	if err := os.Mkdir(path, 0755); err != nil {
+		return err
+	}
+	f, err := os.Create(filepath.Join(path, "config.json"))
+	if err != nil {
+		return err
+	}
+	cpt.Created = time.Now()
+	err = json.NewEncoder(f).Encode(cpt)
+	f.Close()
+	if err != nil {
+		return err
+	}
+	args := []string{
+		"checkpoint",
+		"--image-path", path,
+		"--work-path", filepath.Join(path, "criu.work"),
+	}
+	add := func(flags ...string) {
+		args = append(args, flags...)
+	}
+	add(c.runtimeArgs...)
+	if !cpt.Exit {
+		add("--leave-running")
+	}
+	if cpt.Shell {
+		add("--shell-job")
+	}
+	if cpt.TCP {
+		add("--tcp-established")
+	}
+	if cpt.UnixSockets {
+		add("--ext-unix-sk")
+	}
+	for _, ns := range cpt.EmptyNS {
+		add("--empty-ns", ns)
+	}
+	add(c.id)
+	out, err := exec.Command(c.runtime, args...).CombinedOutput()
+	if err != nil {
+		return fmt.Errorf("%s: %q", err.Error(), string(out))
+	}
+	return err
+}
+
+func (c *container) DeleteCheckpoint(name string, checkpointDir string) error {
+	if checkpointDir == "" {
+		checkpointDir = filepath.Join(c.bundle, "checkpoints")
+	}
+	return os.RemoveAll(filepath.Join(checkpointDir, name))
+}
+
+func (c *container) Start(ctx context.Context, checkpointPath string, s Stdio) (Process, error) {
+	processRoot := filepath.Join(c.root, c.id, InitProcessID)
+	if err := os.Mkdir(processRoot, 0755); err != nil {
+		return nil, err
+	}
+	cmd := exec.Command(c.shim,
+		c.id, c.bundle, c.runtime,
+	)
+	cmd.Dir = processRoot
+	cmd.SysProcAttr = &syscall.SysProcAttr{
+		Setpgid: true,
+	}
+	spec, err := c.readSpec()
+	if err != nil {
+		return nil, err
+	}
+	config := &processConfig{
+		checkpoint:  checkpointPath,
+		root:        processRoot,
+		id:          InitProcessID,
+		c:           c,
+		stdio:       s,
+		spec:        spec,
+		processSpec: specs.ProcessSpec(spec.Process),
+	}
+	p, err := newProcess(config)
+	if err != nil {
+		return nil, err
+	}
+	if err := c.createCmd(ctx, InitProcessID, cmd, p); err != nil {
+		return nil, err
+	}
+	return p, nil
+}
+
+func (c *container) Exec(ctx context.Context, pid string, pspec specs.ProcessSpec, s Stdio) (pp Process, err error) {
+	processRoot := filepath.Join(c.root, c.id, pid)
+	if err := os.Mkdir(processRoot, 0755); err != nil {
+		return nil, err
+	}
+	defer func() {
+		if err != nil {
+			c.RemoveProcess(pid)
+		}
+	}()
+	cmd := exec.Command(c.shim,
+		c.id, c.bundle, c.runtime,
+	)
+	cmd.Dir = processRoot
+	cmd.SysProcAttr = &syscall.SysProcAttr{
+		Setpgid: true,
+	}
+	spec, err := c.readSpec()
+	if err != nil {
+		return nil, err
+	}
+	config := &processConfig{
+		exec:        true,
+		id:          pid,
+		root:        processRoot,
+		c:           c,
+		processSpec: pspec,
+		spec:        spec,
+		stdio:       s,
+	}
+	p, err := newProcess(config)
+	if err != nil {
+		return nil, err
+	}
+	if err := c.createCmd(ctx, pid, cmd, p); err != nil {
+		return nil, err
+	}
+	return p, nil
+}
+
+func (c *container) createCmd(ctx context.Context, pid string, cmd *exec.Cmd, p *process) error {
+	p.cmd = cmd
+	if err := cmd.Start(); err != nil {
+		close(p.cmdDoneCh)
+		if exErr, ok := err.(*exec.Error); ok {
+			if exErr.Err == exec.ErrNotFound || exErr.Err == os.ErrNotExist {
+				return fmt.Errorf("%s not installed on system", c.shim)
+			}
+		}
+		return err
+	}
+	// We need the pid file to have been written to run
+	defer func() {
+		go func() {
+			err := p.cmd.Wait()
+			if err == nil {
+				p.cmdSuccess = true
+			}
+
+			if same, err := p.isSameProcess(); same && p.pid > 0 {
+				// The process changed its PR_SET_PDEATHSIG, so force
+				// kill it
+				logrus.Infof("containerd: %s:%s (pid %v) has become an orphan, killing it", p.container.id, p.id, p.pid)
+				err = unix.Kill(p.pid, syscall.SIGKILL)
+				if err != nil && err != syscall.ESRCH {
+					logrus.Errorf("containerd: unable to SIGKILL %s:%s (pid %v): %v", p.container.id, p.id, p.pid, err)
+				} else {
+					for {
+						err = unix.Kill(p.pid, 0)
+						if err != nil {
+							break
+						}
+						time.Sleep(5 * time.Millisecond)
+					}
+				}
+			}
+			close(p.cmdDoneCh)
+		}()
+	}()
+
+	ch := make(chan error)
+	go func() {
+		if err := c.waitForCreate(p, cmd); err != nil {
+			ch <- err
+			return
+		}
+		c.processes[pid] = p
+		ch <- nil
+	}()
+	select {
+	case <-ctx.Done():
+		cmd.Process.Kill()
+		cmd.Wait()
+		<-ch
+		return ctx.Err()
+	case err := <-ch:
+		return err
+	}
+	return nil
+}
+
+func hostIDFromMap(id uint32, mp []ocs.LinuxIDMapping) int {
+	for _, m := range mp {
+		if (id >= m.ContainerID) && (id <= (m.ContainerID + m.Size - 1)) {
+			return int(m.HostID + (id - m.ContainerID))
+		}
+	}
+	return 0
+}
+
+func (c *container) Stats() (*Stat, error) {
+	now := time.Now()
+	args := c.runtimeArgs
+	args = append(args, "events", "--stats", c.id)
+	out, err := exec.Command(c.runtime, args...).CombinedOutput()
+	if err != nil {
+		return nil, fmt.Errorf("%s: %q", err.Error(), out)
+	}
+	s := struct {
+		Data *Stat `json:"data"`
+	}{}
+	if err := json.Unmarshal(out, &s); err != nil {
+		return nil, err
+	}
+	s.Data.Timestamp = now
+	return s.Data, nil
+}
+
+// Status implements the runtime Container interface.
+func (c *container) Status() (State, error) {
+	args := c.runtimeArgs
+	args = append(args, "state", c.id)
+
+	out, err := exec.Command(c.runtime, args...).CombinedOutput()
+	if err != nil {
+		return "", fmt.Errorf("%s: %q", err.Error(), out)
+	}
+
+	// We only require the runtime json output to have a top level Status field.
+	var s struct {
+		Status State `json:"status"`
+	}
+	if err := json.Unmarshal(out, &s); err != nil {
+		return "", err
+	}
+	return s.Status, nil
+}
+
+func (c *container) writeEventFD(root string, cfd, efd int) error {
+	f, err := os.OpenFile(filepath.Join(root, "cgroup.event_control"), os.O_WRONLY, 0)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	_, err = f.WriteString(fmt.Sprintf("%d %d", efd, cfd))
+	return err
+}
+
+type waitArgs struct {
+	pid int
+	err error
+}
+
+func (c *container) waitForCreate(p *process, cmd *exec.Cmd) error {
+	wc := make(chan error, 1)
+	go func() {
+		for {
+			if _, err := p.getPidFromFile(); err != nil {
+				if os.IsNotExist(err) || err == errInvalidPidInt || err == errContainerNotFound {
+					alive, err := isAlive(cmd)
+					if err != nil {
+						wc <- err
+						return
+					}
+					if !alive {
+						// runc could have failed to run the container so lets get the error
+						// out of the logs or the shim could have encountered an error
+						messages, err := readLogMessages(filepath.Join(p.root, "shim-log.json"))
+						if err != nil {
+							wc <- err
+							return
+						}
+						for _, m := range messages {
+							if m.Level == "error" {
+								wc <- fmt.Errorf("shim error: %v", m.Msg)
+								return
+							}
+						}
+						// no errors reported back from shim, check for runc/runtime errors
+						messages, err = readLogMessages(filepath.Join(p.root, "log.json"))
+						if err != nil {
+							if os.IsNotExist(err) {
+								err = ErrContainerNotStarted
+							}
+							wc <- err
+							return
+						}
+						for _, m := range messages {
+							if m.Level == "error" {
+								wc <- fmt.Errorf("oci runtime error: %v", m.Msg)
+								return
+							}
+						}
+						wc <- ErrContainerNotStarted
+						return
+					}
+					time.Sleep(15 * time.Millisecond)
+					continue
+				}
+				wc <- err
+				return
+			}
+			// the pid file was read successfully
+			wc <- nil
+			return
+		}
+	}()
+	select {
+	case err := <-wc:
+		if err != nil {
+			return err
+		}
+		err = p.saveStartTime()
+		if err != nil && !os.IsNotExist(err) {
+			logrus.Warnf("containerd: unable to save %s:%s starttime: %v", p.container.id, p.id, err)
+		}
+		return nil
+	case <-time.After(c.timeout):
+		cmd.Process.Kill()
+		cmd.Wait()
+		return ErrContainerStartTimeout
+	}
+}
+
+// isAlive checks if the shim that launched the container is still alive
+func isAlive(cmd *exec.Cmd) (bool, error) {
+	if _, err := syscall.Wait4(cmd.Process.Pid, nil, syscall.WNOHANG, nil); err == nil {
+		return true, nil
+	}
+	if err := syscall.Kill(cmd.Process.Pid, 0); err != nil {
+		if err == syscall.ESRCH {
+			return false, nil
+		}
+		return false, err
+	}
+	return true, nil
+}
+
+type oom struct {
+	id      string
+	root    string
+	eventfd int
+}
+
+func (o *oom) ContainerID() string {
+	return o.id
+}
+
+func (o *oom) FD() int {
+	return o.eventfd
+}
+
+func (o *oom) Flush() {
+	buf := make([]byte, 8)
+	syscall.Read(o.eventfd, buf)
+}
+
+func (o *oom) Removed() bool {
+	_, err := os.Lstat(filepath.Join(o.root, "cgroup.event_control"))
+	return os.IsNotExist(err)
+}
+
+func (o *oom) Close() error {
+	return syscall.Close(o.eventfd)
+}
+
+type message struct {
+	Level string `json:"level"`
+	Msg   string `json:"msg"`
+}
+
+func readLogMessages(path string) ([]message, error) {
+	var out []message
+	f, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	dec := json.NewDecoder(f)
+	for {
+		var m message
+		if err := dec.Decode(&m); err != nil {
+			if err == io.EOF {
+				break
+			}
+			return nil, err
+		}
+		out = append(out, m)
+	}
+	return out, nil
+}

+ 190 - 0
vendor/github.com/containerd/containerd/runtime/container_linux.go

@@ -0,0 +1,190 @@
+package runtime
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"syscall"
+
+	"github.com/containerd/containerd/specs"
+	ocs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func findCgroupMountpointAndRoot(pid int, subsystem string) (string, string, error) {
+	f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
+	if err != nil {
+		return "", "", err
+	}
+	defer f.Close()
+
+	scanner := bufio.NewScanner(f)
+	for scanner.Scan() {
+		txt := scanner.Text()
+		fields := strings.Split(txt, " ")
+		for _, opt := range strings.Split(fields[len(fields)-1], ",") {
+			if opt == subsystem {
+				return fields[4], fields[3], nil
+			}
+		}
+	}
+	if err := scanner.Err(); err != nil {
+		return "", "", err
+	}
+
+	return "", "", fmt.Errorf("cgroup path for %s not found", subsystem)
+}
+
+func parseCgroupFile(path string) (map[string]string, error) {
+	f, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	s := bufio.NewScanner(f)
+	cgroups := make(map[string]string)
+
+	for s.Scan() {
+		if err := s.Err(); err != nil {
+			return nil, err
+		}
+
+		text := s.Text()
+		parts := strings.Split(text, ":")
+
+		for _, subs := range strings.Split(parts[1], ",") {
+			cgroups[subs] = parts[2]
+		}
+	}
+	return cgroups, nil
+}
+
+func (c *container) OOM() (OOM, error) {
+	p := c.processes[InitProcessID]
+	if p == nil {
+		return nil, fmt.Errorf("no init process found")
+	}
+
+	mountpoint, hostRoot, err := findCgroupMountpointAndRoot(os.Getpid(), "memory")
+	if err != nil {
+		return nil, err
+	}
+
+	cgroups, err := parseCgroupFile(fmt.Sprintf("/proc/%d/cgroup", p.pid))
+	if err != nil {
+		return nil, err
+	}
+
+	root, ok := cgroups["memory"]
+	if !ok {
+		return nil, fmt.Errorf("no memory cgroup for container %s", c.ID())
+	}
+
+	// Take care of the case were we're running inside a container
+	// ourself
+	root = strings.TrimPrefix(root, hostRoot)
+
+	return c.getMemoryEventFD(filepath.Join(mountpoint, root))
+}
+
+func (c *container) Pids() ([]int, error) {
+	var pids []int
+	args := c.runtimeArgs
+	args = append(args, "ps", "--format=json", c.id)
+	out, err := exec.Command(c.runtime, args...).CombinedOutput()
+	if err != nil {
+		return nil, fmt.Errorf("%s: %q", err.Error(), out)
+	}
+	if err := json.Unmarshal(out, &pids); err != nil {
+		return nil, err
+	}
+	return pids, nil
+}
+
+func u64Ptr(i uint64) *uint64 { return &i }
+func i64Ptr(i int64) *int64   { return &i }
+
+func (c *container) UpdateResources(r *Resource) error {
+	sr := ocs.LinuxResources{
+		Memory: &ocs.LinuxMemory{
+			Limit:       u64Ptr(uint64(r.Memory)),
+			Reservation: u64Ptr(uint64(r.MemoryReservation)),
+			Swap:        u64Ptr(uint64(r.MemorySwap)),
+			Kernel:      u64Ptr(uint64(r.KernelMemory)),
+			KernelTCP:   u64Ptr(uint64(r.KernelTCPMemory)),
+		},
+		CPU: &ocs.LinuxCPU{
+			Shares: u64Ptr(uint64(r.CPUShares)),
+			Quota:  i64Ptr(int64(r.CPUQuota)),
+			Period: u64Ptr(uint64(r.CPUPeriod)),
+			Cpus:   r.CpusetCpus,
+			Mems:   r.CpusetMems,
+		},
+		BlockIO: &ocs.LinuxBlockIO{
+			Weight: &r.BlkioWeight,
+		},
+		Pids: &ocs.LinuxPids{
+			Limit: r.PidsLimit,
+		},
+	}
+
+	srStr := bytes.NewBuffer(nil)
+	if err := json.NewEncoder(srStr).Encode(&sr); err != nil {
+		return err
+	}
+
+	args := c.runtimeArgs
+	args = append(args, "update", "-r", "-", c.id)
+	cmd := exec.Command(c.runtime, args...)
+	cmd.Stdin = srStr
+	b, err := cmd.CombinedOutput()
+	if err != nil {
+		return fmt.Errorf(string(b))
+	}
+	return nil
+}
+
+func getRootIDs(s *specs.Spec) (int, int, error) {
+	if s == nil {
+		return 0, 0, nil
+	}
+	var hasUserns bool
+	for _, ns := range s.Linux.Namespaces {
+		if ns.Type == ocs.UserNamespace {
+			hasUserns = true
+			break
+		}
+	}
+	if !hasUserns {
+		return 0, 0, nil
+	}
+	uid := hostIDFromMap(0, s.Linux.UIDMappings)
+	gid := hostIDFromMap(0, s.Linux.GIDMappings)
+	return uid, gid, nil
+}
+
+func (c *container) getMemoryEventFD(root string) (*oom, error) {
+	f, err := os.Open(filepath.Join(root, "memory.oom_control"))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	fd, _, serr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
+	if serr != 0 {
+		return nil, serr
+	}
+	if err := c.writeEventFD(root, int(f.Fd()), int(fd)); err != nil {
+		syscall.Close(int(fd))
+		return nil, err
+	}
+	return &oom{
+		root:    root,
+		id:      c.id,
+		eventfd: int(fd),
+	}, nil
+}

+ 48 - 0
vendor/github.com/containerd/containerd/runtime/container_solaris.go

@@ -0,0 +1,48 @@
+package runtime
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"os/exec"
+	"strings"
+
+	"github.com/containerd/containerd/specs"
+	ocs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func getRootIDs(s *specs.Spec) (int, int, error) {
+	return 0, 0, nil
+}
+
+func (c *container) OOM() (OOM, error) {
+	return nil, nil
+}
+
+func (c *container) Pids() ([]int, error) {
+	var pids []int
+
+	// TODO: This could be racy. Needs more investigation.
+	//we get this information from runz state
+	cmd := exec.Command(c.runtime, "state", c.id)
+	outBuf, errBuf := new(bytes.Buffer), new(bytes.Buffer)
+	cmd.Stdout, cmd.Stderr = outBuf, errBuf
+
+	if err := cmd.Run(); err != nil {
+		if strings.Contains(errBuf.String(), "Container not found") {
+			return nil, errContainerNotFound
+		}
+		return nil, fmt.Errorf("Error is: %+v\n", err)
+	}
+	response := ocs.State{}
+	decoder := json.NewDecoder(outBuf)
+	if err := decoder.Decode(&response); err != nil {
+		return nil, fmt.Errorf("unable to decode json response: %+v", err)
+	}
+	pids = append(pids, response.Pid)
+	return pids, nil
+}
+
+func (c *container) UpdateResources(r *Resource) error {
+	return nil
+}

+ 476 - 0
vendor/github.com/containerd/containerd/runtime/process.go

@@ -0,0 +1,476 @@
+package runtime
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"sync"
+	"syscall"
+	"time"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/containerd/containerd/osutils"
+	"github.com/containerd/containerd/specs"
+	"golang.org/x/sys/unix"
+)
+
+// Process holds the operation allowed on a container's process
+type Process interface {
+	io.Closer
+
+	// ID of the process.
+	// This is either "init" when it is the container's init process or
+	// it is a user provided id for the process similar to the container id
+	ID() string
+	// Start unblocks the associated container init process.
+	// This should only be called on the process with ID "init"
+	Start() error
+	CloseStdin() error
+	Resize(int, int) error
+	// ExitFD returns the fd the provides an event when the process exits
+	ExitFD() int
+	// ExitStatus returns the exit status of the process or an error if it
+	// has not exited
+	ExitStatus() (uint32, error)
+	// Spec returns the process spec that created the process
+	Spec() specs.ProcessSpec
+	// Signal sends the provided signal to the process
+	Signal(os.Signal) error
+	// Container returns the container that the process belongs to
+	Container() Container
+	// Stdio of the container
+	Stdio() Stdio
+	// SystemPid is the pid on the system
+	SystemPid() int
+	// State returns if the process is running or not
+	State() State
+	// Wait reaps the shim process if avaliable
+	Wait()
+}
+
+type processConfig struct {
+	id          string
+	root        string
+	processSpec specs.ProcessSpec
+	spec        *specs.Spec
+	c           *container
+	stdio       Stdio
+	exec        bool
+	checkpoint  string
+}
+
+func newProcess(config *processConfig) (*process, error) {
+	p := &process{
+		root:      config.root,
+		id:        config.id,
+		container: config.c,
+		spec:      config.processSpec,
+		stdio:     config.stdio,
+		cmdDoneCh: make(chan struct{}),
+		state:     Running,
+	}
+	uid, gid, err := getRootIDs(config.spec)
+	if err != nil {
+		return nil, err
+	}
+	f, err := os.Create(filepath.Join(config.root, "process.json"))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	ps := ProcessState{
+		ProcessSpec: config.processSpec,
+		Exec:        config.exec,
+		PlatformProcessState: PlatformProcessState{
+			Checkpoint: config.checkpoint,
+			RootUID:    uid,
+			RootGID:    gid,
+		},
+		Stdin:       config.stdio.Stdin,
+		Stdout:      config.stdio.Stdout,
+		Stderr:      config.stdio.Stderr,
+		RuntimeArgs: config.c.runtimeArgs,
+		NoPivotRoot: config.c.noPivotRoot,
+	}
+
+	if err := json.NewEncoder(f).Encode(ps); err != nil {
+		return nil, err
+	}
+	exit, err := getExitPipe(filepath.Join(config.root, ExitFile))
+	if err != nil {
+		return nil, err
+	}
+	control, err := getControlPipe(filepath.Join(config.root, ControlFile))
+	if err != nil {
+		return nil, err
+	}
+	p.exitPipe = exit
+	p.controlPipe = control
+	return p, nil
+}
+
+func loadProcess(root, id string, c *container, s *ProcessState) (*process, error) {
+	p := &process{
+		root:      root,
+		id:        id,
+		container: c,
+		spec:      s.ProcessSpec,
+		stdio: Stdio{
+			Stdin:  s.Stdin,
+			Stdout: s.Stdout,
+			Stderr: s.Stderr,
+		},
+		state: Stopped,
+	}
+
+	startTime, err := ioutil.ReadFile(filepath.Join(p.root, StartTimeFile))
+	if err != nil && !os.IsNotExist(err) {
+		return nil, err
+	}
+	p.startTime = string(startTime)
+
+	if _, err := p.getPidFromFile(); err != nil {
+		return nil, err
+	}
+	if _, err := p.ExitStatus(); err != nil {
+		if err == ErrProcessNotExited {
+			exit, err := getExitPipe(filepath.Join(root, ExitFile))
+			if err != nil {
+				return nil, err
+			}
+			p.exitPipe = exit
+
+			control, err := getControlPipe(filepath.Join(root, ControlFile))
+			if err != nil {
+				return nil, err
+			}
+			p.controlPipe = control
+
+			p.state = Running
+			return p, nil
+		}
+		return nil, err
+	}
+	return p, nil
+}
+
+func readProcStatField(pid int, field int) (string, error) {
+	data, err := ioutil.ReadFile(filepath.Join(string(filepath.Separator), "proc", strconv.Itoa(pid), "stat"))
+	if err != nil {
+		return "", err
+	}
+
+	if field > 2 {
+		// First, split out the name since he could contains spaces.
+		parts := strings.Split(string(data), ") ")
+		// Now split out the rest, we end up with 2 fields less
+		parts = strings.Split(parts[1], " ")
+		return parts[field-2-1], nil // field count start at 1 in manual
+	}
+
+	parts := strings.Split(string(data), " (")
+
+	if field == 1 {
+		return parts[0], nil
+	}
+
+	parts = strings.Split(parts[1], ") ")
+	return parts[0], nil
+}
+
+type process struct {
+	root        string
+	id          string
+	pid         int
+	exitPipe    *os.File
+	controlPipe *os.File
+	container   *container
+	spec        specs.ProcessSpec
+	stdio       Stdio
+	cmd         *exec.Cmd
+	cmdSuccess  bool
+	cmdDoneCh   chan struct{}
+	state       State
+	stateLock   sync.Mutex
+	startTime   string
+}
+
+func (p *process) ID() string {
+	return p.id
+}
+
+func (p *process) Container() Container {
+	return p.container
+}
+
+func (p *process) SystemPid() int {
+	return p.pid
+}
+
+// ExitFD returns the fd of the exit pipe
+func (p *process) ExitFD() int {
+	return int(p.exitPipe.Fd())
+}
+
+func (p *process) CloseStdin() error {
+	_, err := fmt.Fprintf(p.controlPipe, "%d %d %d\n", 0, 0, 0)
+	return err
+}
+
+func (p *process) Resize(w, h int) error {
+	_, err := fmt.Fprintf(p.controlPipe, "%d %d %d\n", 1, w, h)
+	return err
+}
+
+func (p *process) updateExitStatusFile(status uint32) (uint32, error) {
+	p.stateLock.Lock()
+	p.state = Stopped
+	p.stateLock.Unlock()
+	err := ioutil.WriteFile(filepath.Join(p.root, ExitStatusFile), []byte(fmt.Sprintf("%u", status)), 0644)
+	return status, err
+}
+
+func (p *process) handleSigkilledShim(rst uint32, rerr error) (uint32, error) {
+	if p.cmd == nil || p.cmd.Process == nil {
+		e := unix.Kill(p.pid, 0)
+		if e == syscall.ESRCH {
+			logrus.Warnf("containerd: %s:%s (pid %d) does not exist", p.container.id, p.id, p.pid)
+			// The process died while containerd was down (probably of
+			// SIGKILL, but no way to be sure)
+			return p.updateExitStatusFile(UnknownStatus)
+		}
+
+		// If it's not the same process, just mark it stopped and set
+		// the status to the UnknownStatus value (i.e. 255)
+		if same, err := p.isSameProcess(); !same {
+			logrus.Warnf("containerd: %s:%s (pid %d) is not the same process anymore (%v)", p.container.id, p.id, p.pid, err)
+			// Create the file so we get the exit event generated once monitor kicks in
+			// without having to go through all this process again
+			return p.updateExitStatusFile(UnknownStatus)
+		}
+
+		ppid, err := readProcStatField(p.pid, 4)
+		if err != nil {
+			return rst, fmt.Errorf("could not check process ppid: %v (%v)", err, rerr)
+		}
+		if ppid == "1" {
+			logrus.Warnf("containerd: %s:%s shim died, killing associated process", p.container.id, p.id)
+			unix.Kill(p.pid, syscall.SIGKILL)
+			if err != nil && err != syscall.ESRCH {
+				return UnknownStatus, fmt.Errorf("containerd: unable to SIGKILL %s:%s (pid %v): %v", p.container.id, p.id, p.pid, err)
+			}
+
+			// wait for the process to die
+			for {
+				e := unix.Kill(p.pid, 0)
+				if e == syscall.ESRCH {
+					break
+				}
+				time.Sleep(5 * time.Millisecond)
+			}
+			// Create the file so we get the exit event generated once monitor kicks in
+			// without having to go through all this process again
+			return p.updateExitStatusFile(128 + uint32(syscall.SIGKILL))
+		}
+
+		return rst, rerr
+	}
+
+	// Possible that the shim was SIGKILLED
+	e := unix.Kill(p.cmd.Process.Pid, 0)
+	if e != syscall.ESRCH {
+		return rst, rerr
+	}
+
+	// Ensure we got the shim ProcessState
+	<-p.cmdDoneCh
+
+	shimStatus := p.cmd.ProcessState.Sys().(syscall.WaitStatus)
+	if shimStatus.Signaled() && shimStatus.Signal() == syscall.SIGKILL {
+		logrus.Debugf("containerd: ExitStatus(container: %s, process: %s): shim was SIGKILL'ed reaping its child with pid %d", p.container.id, p.id, p.pid)
+
+		rerr = nil
+		rst = 128 + uint32(shimStatus.Signal())
+
+		p.stateLock.Lock()
+		p.state = Stopped
+		p.stateLock.Unlock()
+	}
+
+	return rst, rerr
+}
+
+func (p *process) ExitStatus() (rst uint32, rerr error) {
+	data, err := ioutil.ReadFile(filepath.Join(p.root, ExitStatusFile))
+	defer func() {
+		if rerr != nil {
+			rst, rerr = p.handleSigkilledShim(rst, rerr)
+		}
+	}()
+	if err != nil {
+		if os.IsNotExist(err) {
+			return UnknownStatus, ErrProcessNotExited
+		}
+		return UnknownStatus, err
+	}
+	if len(data) == 0 {
+		return UnknownStatus, ErrProcessNotExited
+	}
+	p.stateLock.Lock()
+	p.state = Stopped
+	p.stateLock.Unlock()
+
+	i, err := strconv.ParseUint(string(data), 10, 32)
+	return uint32(i), err
+}
+
+func (p *process) Spec() specs.ProcessSpec {
+	return p.spec
+}
+
+func (p *process) Stdio() Stdio {
+	return p.stdio
+}
+
+// Close closes any open files and/or resouces on the process
+func (p *process) Close() error {
+	err := p.exitPipe.Close()
+	if cerr := p.controlPipe.Close(); err == nil {
+		err = cerr
+	}
+	return err
+}
+
+func (p *process) State() State {
+	p.stateLock.Lock()
+	defer p.stateLock.Unlock()
+	return p.state
+}
+
+func (p *process) readStartTime() (string, error) {
+	return readProcStatField(p.pid, 22)
+}
+
+func (p *process) saveStartTime() error {
+	startTime, err := p.readStartTime()
+	if err != nil {
+		return err
+	}
+
+	p.startTime = startTime
+	return ioutil.WriteFile(filepath.Join(p.root, StartTimeFile), []byte(startTime), 0644)
+}
+
+func (p *process) isSameProcess() (bool, error) {
+	if p.pid == 0 {
+		_, err := p.getPidFromFile()
+		if err != nil {
+			return false, err
+		}
+	}
+
+	// for backward compat assume it's the same if startTime wasn't set
+	if p.startTime == "" {
+		// Sometimes the process dies before we can get the starttime,
+		// check that the process actually exists
+		if err := unix.Kill(p.pid, 0); err != syscall.ESRCH {
+			return true, nil
+		}
+		return false, nil
+	}
+
+	startTime, err := p.readStartTime()
+	if err != nil {
+		return false, err
+	}
+
+	return startTime == p.startTime, nil
+}
+
+// Wait will reap the shim process
+func (p *process) Wait() {
+	if p.cmdDoneCh != nil {
+		<-p.cmdDoneCh
+	}
+}
+
+func getExitPipe(path string) (*os.File, error) {
+	if err := unix.Mkfifo(path, 0755); err != nil && !os.IsExist(err) {
+		return nil, err
+	}
+	// add NONBLOCK in case the other side has already closed or else
+	// this function would never return
+	return os.OpenFile(path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
+}
+
+func getControlPipe(path string) (*os.File, error) {
+	if err := unix.Mkfifo(path, 0755); err != nil && !os.IsExist(err) {
+		return nil, err
+	}
+	return os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0)
+}
+
+// Signal sends the provided signal to the process
+func (p *process) Signal(s os.Signal) error {
+	return syscall.Kill(p.pid, s.(syscall.Signal))
+}
+
+// Start unblocks the associated container init process.
+// This should only be called on the process with ID "init"
+func (p *process) Start() error {
+	if p.ID() == InitProcessID {
+		var (
+			errC = make(chan error, 1)
+			args = append(p.container.runtimeArgs, "start", p.container.id)
+			cmd  = exec.Command(p.container.runtime, args...)
+		)
+		go func() {
+			out, err := cmd.CombinedOutput()
+			if err != nil {
+				errC <- fmt.Errorf("%s: %q", err.Error(), out)
+			}
+			errC <- nil
+		}()
+		select {
+		case err := <-errC:
+			if err != nil {
+				return err
+			}
+		case <-p.cmdDoneCh:
+			if !p.cmdSuccess {
+				if cmd.Process != nil {
+					cmd.Process.Kill()
+				}
+				cmd.Wait()
+				return ErrShimExited
+			}
+			err := <-errC
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+// Delete delete any resources held by the container
+func (p *process) Delete() error {
+	var (
+		args = append(p.container.runtimeArgs, "delete", "-f", p.container.id)
+		cmd  = exec.Command(p.container.runtime, args...)
+	)
+
+	cmd.SysProcAttr = osutils.SetPDeathSig()
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		return fmt.Errorf("%s: %v", out, err)
+	}
+	return nil
+}

+ 22 - 0
vendor/github.com/containerd/containerd/runtime/process_linux.go

@@ -0,0 +1,22 @@
+// +build linux
+
+package runtime
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"strconv"
+)
+
+func (p *process) getPidFromFile() (int, error) {
+	data, err := ioutil.ReadFile(filepath.Join(p.root, "pid"))
+	if err != nil {
+		return -1, err
+	}
+	i, err := strconv.Atoi(string(data))
+	if err != nil {
+		return -1, errInvalidPidInt
+	}
+	p.pid = i
+	return i, nil
+}

+ 34 - 0
vendor/github.com/containerd/containerd/runtime/process_solaris.go

@@ -0,0 +1,34 @@
+// +build solaris
+
+package runtime
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"os/exec"
+
+	runtimespec "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// On Solaris we already have a state file maintained by the framework.
+// This is read by runz state. We just call that instead of maintaining
+// a separate file.
+func (p *process) getPidFromFile() (int, error) {
+	//we get this information from runz state
+	cmd := exec.Command("runc", "state", p.container.ID())
+	outBuf, errBuf := new(bytes.Buffer), new(bytes.Buffer)
+	cmd.Stdout, cmd.Stderr = outBuf, errBuf
+
+	if err := cmd.Run(); err != nil {
+		// TODO: Improve logic
+		return -1, errContainerNotFound
+	}
+	response := runtimespec.State{}
+	decoder := json.NewDecoder(outBuf)
+	if err := decoder.Decode(&response); err != nil {
+		return -1, fmt.Errorf("unable to decode json response: %+v", err)
+	}
+	p.pid = response.Pid
+	return p.pid, nil
+}

+ 132 - 0
vendor/github.com/containerd/containerd/runtime/runtime.go

@@ -0,0 +1,132 @@
+package runtime
+
+import (
+	"errors"
+	"time"
+
+	"github.com/containerd/containerd/specs"
+)
+
+var (
+	// ErrContainerExited is returned when access to an exited
+	// container is attempted
+	ErrContainerExited = errors.New("containerd: container has exited")
+	// ErrProcessNotExited is returned when trying to retrieve the exit
+	// status of an alive process
+	ErrProcessNotExited = errors.New("containerd: process has not exited")
+	// ErrContainerNotStarted is returned when a container fails to
+	// start without error from the shim or the OCI runtime
+	ErrContainerNotStarted = errors.New("containerd: container not started")
+	// ErrContainerStartTimeout is returned if a container takes too
+	// long to start
+	ErrContainerStartTimeout = errors.New("containerd: container did not start before the specified timeout")
+	// ErrShimExited is returned if the shim or the contianer's init process
+	// exits before completing
+	ErrShimExited = errors.New("containerd: shim exited before container process was started")
+
+	errNoPidFile         = errors.New("containerd: no process pid file found")
+	errInvalidPidInt     = errors.New("containerd: process pid is invalid")
+	errContainerNotFound = errors.New("containerd: container not found")
+	errNotImplemented    = errors.New("containerd: not implemented")
+)
+
+const (
+	// ExitFile holds the name of the pipe used to monitor process
+	// exit
+	ExitFile = "exit"
+	// ExitStatusFile holds the name of the file where the container
+	// exit code is to be written
+	ExitStatusFile = "exitStatus"
+	// StateFile holds the name of the file where the container state
+	// is written
+	StateFile = "state.json"
+	// ControlFile holds the name of the pipe used to control the shim
+	ControlFile = "control"
+	// InitProcessID holds the special ID used for the very first
+	// container's process
+	InitProcessID = "init"
+	// StartTimeFile holds the name of the file in which the process
+	// start time is saved
+	StartTimeFile = "starttime"
+
+	// UnknownStatus is the value returned when a process exit
+	// status cannot be determined
+	UnknownStatus = 255
+)
+
+// Checkpoint holds information regarding a container checkpoint
+type Checkpoint struct {
+	// Timestamp is the time that checkpoint happened
+	Created time.Time `json:"created"`
+	// Name is the name of the checkpoint
+	Name string `json:"name"`
+	// TCP checkpoints open tcp connections
+	TCP bool `json:"tcp"`
+	// UnixSockets persists unix sockets in the checkpoint
+	UnixSockets bool `json:"unixSockets"`
+	// Shell persists tty sessions in the checkpoint
+	Shell bool `json:"shell"`
+	// Exit exits the container after the checkpoint is finished
+	Exit bool `json:"exit"`
+	// EmptyNS tells CRIU to omit a specified namespace
+	EmptyNS []string `json:"emptyNS,omitempty"`
+}
+
+// PlatformProcessState container platform-specific fields in the ProcessState structure
+type PlatformProcessState struct {
+	Checkpoint string `json:"checkpoint"`
+	RootUID    int    `json:"rootUID"`
+	RootGID    int    `json:"rootGID"`
+}
+
+// State represents a container state
+type State string
+
+// Resource regroups the various container limits that can be updated
+type Resource struct {
+	CPUShares         int64
+	BlkioWeight       uint16
+	CPUPeriod         int64
+	CPUQuota          int64
+	CpusetCpus        string
+	CpusetMems        string
+	KernelMemory      int64
+	KernelTCPMemory   int64
+	Memory            int64
+	MemoryReservation int64
+	MemorySwap        int64
+	PidsLimit         int64
+}
+
+// Possible container states
+const (
+	Paused  = State("paused")
+	Stopped = State("stopped")
+	Running = State("running")
+)
+
+type state struct {
+	Bundle      string   `json:"bundle"`
+	Labels      []string `json:"labels"`
+	Stdin       string   `json:"stdin"`
+	Stdout      string   `json:"stdout"`
+	Stderr      string   `json:"stderr"`
+	Runtime     string   `json:"runtime"`
+	RuntimeArgs []string `json:"runtimeArgs"`
+	Shim        string   `json:"shim"`
+	NoPivotRoot bool     `json:"noPivotRoot"`
+}
+
+// ProcessState holds the process OCI specs along with various fields
+// required by containerd
+type ProcessState struct {
+	specs.ProcessSpec
+	Exec        bool     `json:"exec"`
+	Stdin       string   `json:"containerdStdin"`
+	Stdout      string   `json:"containerdStdout"`
+	Stderr      string   `json:"containerdStderr"`
+	RuntimeArgs []string `json:"runtimeArgs"`
+	NoPivotRoot bool     `json:"noPivotRoot"`
+
+	PlatformProcessState
+}

+ 87 - 0
vendor/github.com/containerd/containerd/runtime/stats.go

@@ -0,0 +1,87 @@
+package runtime
+
+import "time"
+
+// Stat holds a container statistics
+type Stat struct {
+	// Timestamp is the time that the statistics where collected
+	Timestamp time.Time
+	CPU       CPU                `json:"cpu"`
+	Memory    Memory             `json:"memory"`
+	Pids      Pids               `json:"pids"`
+	Blkio     Blkio              `json:"blkio"`
+	Hugetlb   map[string]Hugetlb `json:"hugetlb"`
+}
+
+// Hugetlb holds information regarding a container huge tlb usage
+type Hugetlb struct {
+	Usage   uint64 `json:"usage,omitempty"`
+	Max     uint64 `json:"max,omitempty"`
+	Failcnt uint64 `json:"failcnt"`
+}
+
+// BlkioEntry represents a single record for a Blkio stat
+type BlkioEntry struct {
+	Major uint64 `json:"major,omitempty"`
+	Minor uint64 `json:"minor,omitempty"`
+	Op    string `json:"op,omitempty"`
+	Value uint64 `json:"value,omitempty"`
+}
+
+// Blkio regroups all the Blkio related stats
+type Blkio struct {
+	IoServiceBytesRecursive []BlkioEntry `json:"ioServiceBytesRecursive,omitempty"`
+	IoServicedRecursive     []BlkioEntry `json:"ioServicedRecursive,omitempty"`
+	IoQueuedRecursive       []BlkioEntry `json:"ioQueueRecursive,omitempty"`
+	IoServiceTimeRecursive  []BlkioEntry `json:"ioServiceTimeRecursive,omitempty"`
+	IoWaitTimeRecursive     []BlkioEntry `json:"ioWaitTimeRecursive,omitempty"`
+	IoMergedRecursive       []BlkioEntry `json:"ioMergedRecursive,omitempty"`
+	IoTimeRecursive         []BlkioEntry `json:"ioTimeRecursive,omitempty"`
+	SectorsRecursive        []BlkioEntry `json:"sectorsRecursive,omitempty"`
+}
+
+// Pids holds the stat of the pid usage of the machine
+type Pids struct {
+	Current uint64 `json:"current,omitempty"`
+	Limit   uint64 `json:"limit,omitempty"`
+}
+
+// Throttling holds a cpu throttling information
+type Throttling struct {
+	Periods          uint64 `json:"periods,omitempty"`
+	ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"`
+	ThrottledTime    uint64 `json:"throttledTime,omitempty"`
+}
+
+// CPUUsage holds information regarding cpu usage
+type CPUUsage struct {
+	// Units: nanoseconds.
+	Total  uint64   `json:"total,omitempty"`
+	Percpu []uint64 `json:"percpu,omitempty"`
+	Kernel uint64   `json:"kernel"`
+	User   uint64   `json:"user"`
+}
+
+// CPU regroups both a CPU usage and throttling information
+type CPU struct {
+	Usage      CPUUsage   `json:"usage,omitempty"`
+	Throttling Throttling `json:"throttling,omitempty"`
+}
+
+// MemoryEntry regroups statistic about a given type of memory
+type MemoryEntry struct {
+	Limit   uint64 `json:"limit"`
+	Usage   uint64 `json:"usage,omitempty"`
+	Max     uint64 `json:"max,omitempty"`
+	Failcnt uint64 `json:"failcnt"`
+}
+
+// Memory holds information regarding the different type of memories available
+type Memory struct {
+	Cache     uint64            `json:"cache,omitempty"`
+	Usage     MemoryEntry       `json:"usage,omitempty"`
+	Swap      MemoryEntry       `json:"swap,omitempty"`
+	Kernel    MemoryEntry       `json:"kernel,omitempty"`
+	KernelTCP MemoryEntry       `json:"kernelTCP,omitempty"`
+	Raw       map[string]uint64 `json:"raw,omitempty"`
+}

+ 12 - 0
vendor/github.com/containerd/containerd/specs/spec_linux.go

@@ -0,0 +1,12 @@
+package specs
+
+import oci "github.com/opencontainers/runtime-spec/specs-go"
+
+type (
+	// ProcessSpec aliases the platform process specs
+	ProcessSpec oci.Process
+	// Spec aliases the platform oci spec
+	Spec oci.Spec
+	// Rlimit aliases the platform resource limit
+	Rlimit oci.LinuxRlimit
+)

+ 10 - 0
vendor/github.com/containerd/containerd/specs/spec_solaris.go

@@ -0,0 +1,10 @@
+package specs
+
+import ocs "github.com/opencontainers/runtime-spec/specs-go"
+
+type (
+	// ProcessSpec aliases the platform process specs
+	ProcessSpec ocs.Process
+	// Spec aliases the platform oci spec
+	Spec ocs.Spec
+)

+ 3 - 3
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unix.go

@@ -64,12 +64,12 @@ func IsNamespaceSupported(ns NamespaceType) bool {
 
 func NamespaceTypes() []NamespaceType {
 	return []NamespaceType{
+		NEWUSER, // Keep user NS always first, don't move it.
+		NEWIPC,
+		NEWUTS,
 		NEWNET,
 		NEWPID,
 		NEWNS,
-		NEWUTS,
-		NEWIPC,
-		NEWUSER,
 	}
 }
 

+ 50 - 0
vendor/github.com/pmezard/go-difflib/README.md

@@ -0,0 +1,50 @@
+go-difflib
+==========
+
+[![Build Status](https://travis-ci.org/pmezard/go-difflib.png?branch=master)](https://travis-ci.org/pmezard/go-difflib)
+[![GoDoc](https://godoc.org/github.com/pmezard/go-difflib/difflib?status.svg)](https://godoc.org/github.com/pmezard/go-difflib/difflib)
+
+Go-difflib is a partial port of python 3 difflib package. Its main goal
+was to make unified and context diff available in pure Go, mostly for
+testing purposes.
+
+The following class and functions (and related tests) have be ported:
+
+* `SequenceMatcher`
+* `unified_diff()`
+* `context_diff()`
+
+## Installation
+
+```bash
+$ go get github.com/pmezard/go-difflib/difflib
+```
+
+### Quick Start
+
+Diffs are configured with Unified (or ContextDiff) structures, and can
+be output to an io.Writer or returned as a string.
+
+```Go
+diff := UnifiedDiff{
+    A:        difflib.SplitLines("foo\nbar\n"),
+    B:        difflib.SplitLines("foo\nbaz\n"),
+    FromFile: "Original",
+    ToFile:   "Current",
+    Context:  3,
+}
+text, _ := GetUnifiedDiffString(diff)
+fmt.Printf(text)
+```
+
+would output:
+
+```
+--- Original
++++ Current
+@@ -1,3 +1,3 @@
+ foo
+-bar
++baz
+```
+