Browse code

Bump containerd to cc969fb42f427a68a8cc6870ef47f17

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Michael Crosby authored on 2017/12/05 04:14:42
Showing 29 changed files
... ...
@@ -4,7 +4,7 @@ TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
4 4
 
5 5
 # When updating RUNC_COMMIT, also update runc in vendor.conf accordingly
6 6
 RUNC_COMMIT=b2567b37d7b75eb4cf325b77297b140ea686ce8f
7
-CONTAINERD_COMMIT=6bff39c643886dfa3d546e83a90a527b64ddeacf
7
+CONTAINERD_COMMIT=cc969fb42f427a68a8cc6870ef47f17304b83962
8 8
 TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574
9 9
 LIBNETWORK_COMMIT=7b2b1feb1de4817d522cc372af149ff48d25028e
10 10
 VNDR_COMMIT=a6e196d8b4b0cbbdc29aebdb20c59ac6926bb384
... ...
@@ -103,7 +103,7 @@ github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7
103 103
 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
104 104
 
105 105
 # containerd
106
-github.com/containerd/containerd 6bff39c643886dfa3d546e83a90a527b64ddeacf
106
+github.com/containerd/containerd cc969fb42f427a68a8cc6870ef47f17304b83962
107 107
 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
108 108
 github.com/containerd/continuity 35d55c5e8dd23b32037d56cf97174aff3efdfa83
109 109
 github.com/containerd/cgroups 29da22c6171a4316169f9205ab6c49f59b5b852f
... ...
@@ -111,7 +111,7 @@ github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e
111 111
 github.com/containerd/go-runc ed1cbe1fc31f5fb2359d3a54b6330d1a097858b7
112 112
 github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788
113 113
 github.com/dmcgowan/go-tar go1.10
114
-github.com/stevvooe/ttrpc 8c92e22ce0c492875ccaac3ab06143a77d8ed0c1
114
+github.com/stevvooe/ttrpc 76e68349ad9ab4d03d764c713826d31216715e4f
115 115
 
116 116
 # cluster
117 117
 github.com/docker/swarmkit de950a7ed842c7b7e47e9451cde9bf8f96031894
... ...
@@ -15,6 +15,28 @@ containerd is designed to be embedded into a larger system, rather than being us
15 15
 
16 16
 If you are interested in trying out containerd please see our [Getting Started Guide](docs/getting-started.md).
17 17
 
18
+## Runtime Requirements
19
+
20
+Runtime requirements for containerd are very minimal. Most interactions with
21
+the Linux and Windows container feature sets are handled via [runc](https://github.com/opencontainers/runc) and/or
22
+OS-specific libraries (e.g. [hcsshim](https://github.com/Microsoft/hcsshim) for Microsoft). There are specific features
23
+used by containerd core code and snapshotters that will require a minimum kernel
24
+version on Linux. With the understood caveat of distro kernel versioning, a
25
+reasonable starting point for Linux is a minimum 4.x kernel version.
26
+
27
+The overlay filesystem snapshotter, used by default, uses features that were
28
+finalized in the 4.x kernel series. If you choose to use btrfs, there may
29
+be more flexibility in kernel version (minimum recommended is 3.18), but will
30
+require the btrfs kernel module and btrfs tools to be installed on your Linux
31
+distribution.
32
+
33
+To use Linux checkpoint and restore features, you will need `criu` installed on
34
+your system. See more details in [Checkpoint and Restore](#checkpoint-and-restore).
35
+
36
+The current required version of runc is always listed in [RUNC.md](/RUNC.md).
37
+
38
+Build requirements for developers are listed in the [Developer Quick-Start](#developer-quick-start) section.
39
+
18 40
 ## Features
19 41
 
20 42
 ### Client
... ...
@@ -93,7 +115,6 @@ image, err := client.Pull(context, "docker.io/library/redis:latest", containerd.
93 93
 redis, err := client.NewContainer(context, "redis-master",
94 94
 	containerd.WithNewSnapshot("redis-rootfs", image),
95 95
 	containerd.WithNewSpec(oci.WithImageConfig(image)),
96
-
97 96
 )
98 97
 
99 98
 // use a readonly filesystem with multiple containers
... ...
@@ -150,7 +171,7 @@ defer task.Delete(context)
150 150
 err := task.Start(context)
151 151
 ```
152 152
 
153
-## Developer Quick-Start
153
+## Developer Quick Start
154 154
 
155 155
 To build the daemon and `ctr` simple test client, the following build system dependencies are required:
156 156
 
... ...
@@ -162,11 +162,17 @@ func (c *container) Image(ctx context.Context) (Image, error) {
162 162
 	}, nil
163 163
 }
164 164
 
165
-func (c *container) NewTask(ctx context.Context, ioCreate cio.Creation, opts ...NewTaskOpts) (Task, error) {
165
+func (c *container) NewTask(ctx context.Context, ioCreate cio.Creation, opts ...NewTaskOpts) (_ Task, err error) {
166 166
 	i, err := ioCreate(c.id)
167 167
 	if err != nil {
168 168
 		return nil, err
169 169
 	}
170
+	defer func() {
171
+		if err != nil && i != nil {
172
+			i.Cancel()
173
+			i.Close()
174
+		}
175
+	}()
170 176
 	cfg := i.Config()
171 177
 	request := &tasks.CreateTaskRequest{
172 178
 		ContainerID: c.id,
... ...
@@ -24,7 +24,6 @@ import (
24 24
 	"github.com/opencontainers/image-spec/identity"
25 25
 	"github.com/opencontainers/image-spec/specs-go/v1"
26 26
 	"github.com/pkg/errors"
27
-	"golang.org/x/sys/unix"
28 27
 )
29 28
 
30 29
 // WithCheckpoint allows a container to be created from the checkpointed information
... ...
@@ -193,14 +192,17 @@ func remapRootFS(mounts []mount.Mount, uid, gid uint32) error {
193 193
 	if err != nil {
194 194
 		return err
195 195
 	}
196
-	defer os.RemoveAll(root)
196
+	defer os.Remove(root)
197 197
 	for _, m := range mounts {
198 198
 		if err := m.Mount(root); err != nil {
199 199
 			return err
200 200
 		}
201 201
 	}
202
-	defer unix.Unmount(root, 0)
203
-	return filepath.Walk(root, incrementFS(root, uid, gid))
202
+	err = filepath.Walk(root, incrementFS(root, uid, gid))
203
+	if uerr := mount.Unmount(root, 0); err == nil {
204
+		err = uerr
205
+	}
206
+	return err
204 207
 }
205 208
 
206 209
 func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc {
... ...
@@ -62,7 +62,7 @@ func NewStore(root string) (content.Store, error) {
62 62
 // require labels and should use `NewStore`. `NewLabeledStore` is primarily
63 63
 // useful for tests or standalone implementations.
64 64
 func NewLabeledStore(root string, ls LabelStore) (content.Store, error) {
65
-	if err := os.MkdirAll(filepath.Join(root, "ingest"), 0777); err != nil && !os.IsExist(err) {
65
+	if err := os.MkdirAll(filepath.Join(root, "ingest"), 0777); err != nil {
66 66
 		return nil, err
67 67
 	}
68 68
 
... ...
@@ -147,7 +147,7 @@ func (i *image) getLayers(ctx context.Context, platform string) ([]rootfs.Layer,
147 147
 
148 148
 	manifest, err := images.Manifest(ctx, cs, i.i.Target, platform)
149 149
 	if err != nil {
150
-		return nil, errors.Wrap(err, "")
150
+		return nil, err
151 151
 	}
152 152
 
153 153
 	diffIDs, err := i.i.RootFS(ctx, cs, platform)
... ...
@@ -187,13 +187,13 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
187 187
 			return descs, nil
188 188
 
189 189
 		}
190
-		return nil, errors.Wrap(errdefs.ErrNotFound, "could not resolve manifest")
190
+		return nil, errors.Wrapf(errdefs.ErrNotFound, "unexpected media type %v for %v", desc.MediaType, desc.Digest)
191 191
 	}), image); err != nil {
192 192
 		return ocispec.Manifest{}, err
193 193
 	}
194 194
 
195 195
 	if m == nil {
196
-		return ocispec.Manifest{}, errors.Wrap(errdefs.ErrNotFound, "manifest not found")
196
+		return ocispec.Manifest{}, errors.Wrapf(errdefs.ErrNotFound, "manifest %v", image.Digest)
197 197
 	}
198 198
 
199 199
 	return *m, nil
... ...
@@ -257,7 +257,7 @@ func Check(ctx context.Context, provider content.Provider, image ocispec.Descrip
257 257
 			return false, []ocispec.Descriptor{image}, nil, []ocispec.Descriptor{image}, nil
258 258
 		}
259 259
 
260
-		return false, nil, nil, nil, errors.Wrap(err, "image check failed")
260
+		return false, nil, nil, nil, errors.Wrapf(err, "failed to check image %v", image.Digest)
261 261
 	}
262 262
 
263 263
 	// TODO(stevvooe): It is possible that referenced conponents could have
... ...
@@ -272,7 +272,7 @@ func Check(ctx context.Context, provider content.Provider, image ocispec.Descrip
272 272
 				missing = append(missing, desc)
273 273
 				continue
274 274
 			} else {
275
-				return false, nil, nil, nil, err
275
+				return false, nil, nil, nil, errors.Wrapf(err, "failed to check image %v", desc.Digest)
276 276
 			}
277 277
 		}
278 278
 		ra.Close()
... ...
@@ -75,10 +75,10 @@ type bundle struct {
75 75
 type ShimOpt func(*bundle, string, *runctypes.RuncOptions) (shim.Config, client.Opt)
76 76
 
77 77
 // ShimRemote is a ShimOpt for connecting and starting a remote shim
78
-func ShimRemote(shimBinary, daemonAddress, cgroup string, nonewns, debug bool, exitHandler func()) ShimOpt {
78
+func ShimRemote(shimBinary, daemonAddress, cgroup string, debug bool, exitHandler func()) ShimOpt {
79 79
 	return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
80 80
 		return b.shimConfig(ns, ropts),
81
-			client.WithStart(shimBinary, b.shimAddress(ns), daemonAddress, cgroup, nonewns, debug, exitHandler)
81
+			client.WithStart(shimBinary, b.shimAddress(ns), daemonAddress, cgroup, debug, exitHandler)
82 82
 	}
83 83
 }
84 84
 
... ...
@@ -22,6 +22,7 @@ import (
22 22
 	shim "github.com/containerd/containerd/linux/shim/v1"
23 23
 	"github.com/containerd/containerd/log"
24 24
 	"github.com/containerd/containerd/metadata"
25
+	"github.com/containerd/containerd/mount"
25 26
 	"github.com/containerd/containerd/namespaces"
26 27
 	"github.com/containerd/containerd/platforms"
27 28
 	"github.com/containerd/containerd/plugin"
... ...
@@ -78,17 +79,6 @@ type Config struct {
78 78
 	NoShim bool `toml:"no_shim"`
79 79
 	// Debug enable debug on the shim
80 80
 	ShimDebug bool `toml:"shim_debug"`
81
-	// ShimNoMountNS prevents the runtime from putting shims into their own mount namespace.
82
-	//
83
-	// Putting the shim in its own mount namespace ensure that any mounts made
84
-	// by it in order to get the task rootfs ready will be undone regardless
85
-	// on how the shim dies.
86
-	//
87
-	// NOTE: This should only be used in kernel older than 3.18 to avoid shims
88
-	// from causing a DoS in their parent namespace due to having a copy of
89
-	// mounts previously there which would prevent unlink, rename and remove
90
-	// operations on those mountpoints.
91
-	ShimNoMountNS bool `toml:"shim_no_newns"`
92 81
 }
93 82
 
94 83
 // New returns a configured runtime
... ...
@@ -226,8 +216,7 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
226 226
 				}).Warn("failed to clen up after killed shim")
227 227
 			}
228 228
 		}
229
-		shimopt = ShimRemote(r.config.Shim, r.address, cgroup,
230
-			r.config.ShimNoMountNS, r.config.ShimDebug, exitHandler)
229
+		shimopt = ShimRemote(r.config.Shim, r.address, cgroup, r.config.ShimDebug, exitHandler)
231 230
 	}
232 231
 
233 232
 	s, err := bundle.NewShimClient(ctx, namespace, shimopt, ropts)
... ...
@@ -486,7 +475,7 @@ func (r *Runtime) terminate(ctx context.Context, bundle *bundle, ns, id string)
486 486
 	}); err != nil {
487 487
 		log.G(ctx).WithError(err).Warnf("delete runtime state %s", id)
488 488
 	}
489
-	if err := unix.Unmount(filepath.Join(bundle.path, "rootfs"), 0); err != nil {
489
+	if err := mount.Unmount(filepath.Join(bundle.path, "rootfs"), 0); err != nil {
490 490
 		log.G(ctx).WithError(err).WithFields(logrus.Fields{
491 491
 			"path": bundle.path,
492 492
 			"id":   id,
... ...
@@ -34,7 +34,7 @@ var empty = &ptypes.Empty{}
34 34
 type Opt func(context.Context, shim.Config) (shimapi.ShimService, io.Closer, error)
35 35
 
36 36
 // WithStart executes a new shim process
37
-func WithStart(binary, address, daemonAddress, cgroup string, nonewns, debug bool, exitHandler func()) Opt {
37
+func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHandler func()) Opt {
38 38
 	return func(ctx context.Context, config shim.Config) (_ shimapi.ShimService, _ io.Closer, err error) {
39 39
 		socket, err := newSocket(address)
40 40
 		if err != nil {
... ...
@@ -47,7 +47,7 @@ func WithStart(binary, address, daemonAddress, cgroup string, nonewns, debug boo
47 47
 		}
48 48
 		defer f.Close()
49 49
 
50
-		cmd := newCommand(binary, daemonAddress, nonewns, debug, config, f)
50
+		cmd := newCommand(binary, daemonAddress, debug, config, f)
51 51
 		ec, err := reaper.Default.Start(cmd)
52 52
 		if err != nil {
53 53
 			return nil, nil, errors.Wrapf(err, "failed to start shim")
... ...
@@ -87,7 +87,7 @@ func WithStart(binary, address, daemonAddress, cgroup string, nonewns, debug boo
87 87
 	}
88 88
 }
89 89
 
90
-func newCommand(binary, daemonAddress string, nonewns, debug bool, config shim.Config, socket *os.File) *exec.Cmd {
90
+func newCommand(binary, daemonAddress string, debug bool, config shim.Config, socket *os.File) *exec.Cmd {
91 91
 	selfExe, err := os.Executable()
92 92
 	if err != nil {
93 93
 		panic(err)
... ...
@@ -117,7 +117,7 @@ func newCommand(binary, daemonAddress string, nonewns, debug bool, config shim.C
117 117
 	// make sure the shim can be re-parented to system init
118 118
 	// and is cloned in a new mount namespace because the overlay/filesystems
119 119
 	// will be mounted by the shim
120
-	cmd.SysProcAttr = getSysProcAttr(nonewns)
120
+	cmd.SysProcAttr = getSysProcAttr()
121 121
 	cmd.ExtraFiles = append(cmd.ExtraFiles, socket)
122 122
 	if debug {
123 123
 		cmd.Stdout = os.Stdout
... ...
@@ -10,14 +10,10 @@ import (
10 10
 	"github.com/pkg/errors"
11 11
 )
12 12
 
13
-func getSysProcAttr(nonewns bool) *syscall.SysProcAttr {
14
-	attr := syscall.SysProcAttr{
13
+func getSysProcAttr() *syscall.SysProcAttr {
14
+	return &syscall.SysProcAttr{
15 15
 		Setpgid: true,
16 16
 	}
17
-	if !nonewns {
18
-		attr.Cloneflags = syscall.CLONE_NEWNS
19
-	}
20
-	return &attr
21 17
 }
22 18
 
23 19
 func setCgroup(cgroupPath string, cmd *exec.Cmd) error {
... ...
@@ -7,7 +7,7 @@ import (
7 7
 	"syscall"
8 8
 )
9 9
 
10
-func getSysProcAttr(nonewns bool) *syscall.SysProcAttr {
10
+func getSysProcAttr() *syscall.SysProcAttr {
11 11
 	return &syscall.SysProcAttr{
12 12
 		Setpgid: true,
13 13
 	}
... ...
@@ -7,8 +7,8 @@ import (
7 7
 	"path/filepath"
8 8
 
9 9
 	shimapi "github.com/containerd/containerd/linux/shim/v1"
10
+	"github.com/containerd/containerd/mount"
10 11
 	ptypes "github.com/gogo/protobuf/types"
11
-	"golang.org/x/sys/unix"
12 12
 )
13 13
 
14 14
 // NewLocal returns a shim client implementation for issue commands to a shim
... ...
@@ -32,7 +32,7 @@ func (c *local) Start(ctx context.Context, in *shimapi.StartRequest) (*shimapi.S
32 32
 
33 33
 func (c *local) Delete(ctx context.Context, in *ptypes.Empty) (*shimapi.DeleteResponse, error) {
34 34
 	// make sure we unmount the containers rootfs for this local
35
-	if err := unix.Unmount(filepath.Join(c.s.config.Path, "rootfs"), 0); err != nil {
35
+	if err := mount.Unmount(filepath.Join(c.s.config.Path, "rootfs"), 0); err != nil {
36 36
 		return nil, err
37 37
 	}
38 38
 	return c.s.Delete(ctx, in)
... ...
@@ -37,12 +37,12 @@ func (s *containerStore) Get(ctx context.Context, id string) (containers.Contain
37 37
 
38 38
 	bkt := getContainerBucket(s.tx, namespace, id)
39 39
 	if bkt == nil {
40
-		return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "bucket name %q:%q", namespace, id)
40
+		return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "container %q in namespace %q", id, namespace)
41 41
 	}
42 42
 
43 43
 	container := containers.Container{ID: id}
44 44
 	if err := readContainer(&container, bkt); err != nil {
45
-		return containers.Container{}, errors.Wrapf(err, "failed to read container %v", id)
45
+		return containers.Container{}, errors.Wrapf(err, "failed to read container %q", id)
46 46
 	}
47 47
 
48 48
 	return container, nil
... ...
@@ -61,7 +61,7 @@ func (s *containerStore) List(ctx context.Context, fs ...string) ([]containers.C
61 61
 
62 62
 	bkt := getContainersBucket(s.tx, namespace)
63 63
 	if bkt == nil {
64
-		return nil, nil
64
+		return nil, nil // empty store
65 65
 	}
66 66
 
67 67
 	var m []containers.Container
... ...
@@ -73,7 +73,7 @@ func (s *containerStore) List(ctx context.Context, fs ...string) ([]containers.C
73 73
 		container := containers.Container{ID: string(k)}
74 74
 
75 75
 		if err := readContainer(&container, cbkt); err != nil {
76
-			return errors.Wrap(err, "failed to read container")
76
+			return errors.Wrapf(err, "failed to read container %q", string(k))
77 77
 		}
78 78
 
79 79
 		if filter.Match(adaptContainer(container)) {
... ...
@@ -113,7 +113,7 @@ func (s *containerStore) Create(ctx context.Context, container containers.Contai
113 113
 	container.CreatedAt = time.Now().UTC()
114 114
 	container.UpdatedAt = container.CreatedAt
115 115
 	if err := writeContainer(cbkt, &container); err != nil {
116
-		return containers.Container{}, errors.Wrap(err, "failed to write container")
116
+		return containers.Container{}, errors.Wrapf(err, "failed to write container %q", container.ID)
117 117
 	}
118 118
 
119 119
 	return container, nil
... ...
@@ -131,7 +131,7 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai
131 131
 
132 132
 	bkt := getContainersBucket(s.tx, namespace)
133 133
 	if bkt == nil {
134
-		return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "container %q", container.ID)
134
+		return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "cannot update container %q in namespace %q", container.ID, namespace)
135 135
 	}
136 136
 
137 137
 	cbkt := bkt.Bucket([]byte(container.ID))
... ...
@@ -141,7 +141,7 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai
141 141
 
142 142
 	var updated containers.Container
143 143
 	if err := readContainer(&updated, cbkt); err != nil {
144
-		return updated, errors.Wrapf(err, "failed to read container from bucket")
144
+		return updated, errors.Wrapf(err, "failed to read container %q", container.ID)
145 145
 	}
146 146
 	createdat := updated.CreatedAt
147 147
 	updated.ID = container.ID
... ...
@@ -211,7 +211,7 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai
211 211
 	updated.CreatedAt = createdat
212 212
 	updated.UpdatedAt = time.Now().UTC()
213 213
 	if err := writeContainer(cbkt, &updated); err != nil {
214
-		return containers.Container{}, errors.Wrap(err, "failed to write container")
214
+		return containers.Container{}, errors.Wrapf(err, "failed to write container %q", container.ID)
215 215
 	}
216 216
 
217 217
 	return updated, nil
... ...
@@ -225,7 +225,7 @@ func (s *containerStore) Delete(ctx context.Context, id string) error {
225 225
 
226 226
 	bkt := getContainersBucket(s.tx, namespace)
227 227
 	if bkt == nil {
228
-		return errors.Wrapf(errdefs.ErrNotFound, "cannot delete container %v, bucket not present", id)
228
+		return errors.Wrapf(errdefs.ErrNotFound, "cannot delete container %q in namespace %q", id, namespace)
229 229
 	}
230 230
 
231 231
 	if err := bkt.DeleteBucket([]byte(id)); err == bolt.ErrBucketNotFound {
... ...
@@ -236,7 +236,7 @@ func (s *containerStore) Delete(ctx context.Context, id string) error {
236 236
 
237 237
 func validateContainer(container *containers.Container) error {
238 238
 	if err := identifiers.Validate(container.ID); err != nil {
239
-		return errors.Wrapf(err, "container.ID validation error")
239
+		return errors.Wrap(err, "container.ID")
240 240
 	}
241 241
 
242 242
 	for k := range container.Extensions {
... ...
@@ -138,7 +138,7 @@ func (m *DB) Init(ctx context.Context) error {
138 138
 				if err := m.migrate(tx); err != nil {
139 139
 					return errors.Wrapf(err, "failed to migrate to %s.%d", m.schema, m.version)
140 140
 				}
141
-				log.G(ctx).WithField("d", time.Now().Sub(t0)).Debugf("database migration to %s.%d finished", m.schema, m.version)
141
+				log.G(ctx).WithField("d", time.Now().Sub(t0)).Debugf("finished database migration to %s.%d", m.schema, m.version)
142 142
 			}
143 143
 		}
144 144
 
... ...
@@ -269,7 +269,7 @@ func (m *DB) GarbageCollect(ctx context.Context) (stats GCStats, err error) {
269 269
 		stats.SnapshotD = map[string]time.Duration{}
270 270
 		wg.Add(len(m.dirtySS))
271 271
 		for snapshotterName := range m.dirtySS {
272
-			log.G(ctx).WithField("snapshotter", snapshotterName).Debug("scheduling snapshotter cleanup")
272
+			log.G(ctx).WithField("snapshotter", snapshotterName).Debug("schedule snapshotter cleanup")
273 273
 			go func(snapshotterName string) {
274 274
 				st1 := time.Now()
275 275
 				m.cleanupSnapshotter(snapshotterName)
... ...
@@ -286,7 +286,7 @@ func (m *DB) GarbageCollect(ctx context.Context) (stats GCStats, err error) {
286 286
 
287 287
 	if m.dirtyCS {
288 288
 		wg.Add(1)
289
-		log.G(ctx).Debug("scheduling content cleanup")
289
+		log.G(ctx).Debug("schedule content cleanup")
290 290
 		go func() {
291 291
 			ct1 := time.Now()
292 292
 			m.cleanupContent()
... ...
@@ -301,7 +301,7 @@ func remove(ctx context.Context, tx *bolt.Tx, node gc.Node) error {
301 301
 			cbkt = cbkt.Bucket(bucketKeyObjectBlob)
302 302
 		}
303 303
 		if cbkt != nil {
304
-			log.G(ctx).WithField("key", node.Key).Debug("delete content")
304
+			log.G(ctx).WithField("key", node.Key).Debug("remove content")
305 305
 			return cbkt.DeleteBucket([]byte(node.Key))
306 306
 		}
307 307
 	case ResourceSnapshot:
... ...
@@ -313,7 +313,7 @@ func remove(ctx context.Context, tx *bolt.Tx, node gc.Node) error {
313 313
 			}
314 314
 			ssbkt := sbkt.Bucket([]byte(parts[0]))
315 315
 			if ssbkt != nil {
316
-				log.G(ctx).WithField("key", parts[1]).WithField("snapshotter", parts[0]).Debug("delete snapshot")
316
+				log.G(ctx).WithField("key", parts[1]).WithField("snapshotter", parts[0]).Debug("remove snapshot")
317 317
 				return ssbkt.DeleteBucket([]byte(parts[1]))
318 318
 			}
319 319
 		}
... ...
@@ -359,7 +359,8 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
359 359
 	return update(ctx, s.db, func(tx *bolt.Tx) error {
360 360
 		bkt := getSnapshotterBucket(tx, ns, s.name)
361 361
 		if bkt == nil {
362
-			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key)
362
+			return errors.Wrapf(errdefs.ErrNotFound,
363
+				"can not find snapshotter %q", s.name)
363 364
 		}
364 365
 
365 366
 		bbkt, err := bkt.CreateBucket([]byte(name))
... ...
@@ -722,7 +723,7 @@ func (s *snapshotter) pruneBranch(ctx context.Context, node *treeNode) error {
722 722
 			if !errdefs.IsFailedPrecondition(err) {
723 723
 				return err
724 724
 			}
725
-			logger.WithError(err).WithField("key", node.info.Name).Warnf("snapshot removal failed")
725
+			logger.WithError(err).WithField("key", node.info.Name).Warnf("failed to remove snapshot")
726 726
 		} else {
727 727
 			logger.WithField("key", node.info.Name).Debug("removed snapshot")
728 728
 		}
... ...
@@ -2,7 +2,9 @@ package mount
2 2
 
3 3
 import (
4 4
 	"strings"
5
+	"time"
5 6
 
7
+	"github.com/pkg/errors"
6 8
 	"golang.org/x/sys/unix"
7 9
 )
8 10
 
... ...
@@ -42,8 +44,27 @@ func (m *Mount) Mount(target string) error {
42 42
 }
43 43
 
44 44
 // Unmount the provided mount path with the flags
45
-func Unmount(mount string, flags int) error {
46
-	return unix.Unmount(mount, flags)
45
+func Unmount(target string, flags int) error {
46
+	if err := unmount(target, flags); err != nil && err != unix.EINVAL {
47
+		return err
48
+	}
49
+	return nil
50
+}
51
+
52
+func unmount(target string, flags int) error {
53
+	for i := 0; i < 50; i++ {
54
+		if err := unix.Unmount(target, flags); err != nil {
55
+			switch err {
56
+			case unix.EBUSY:
57
+				time.Sleep(50 * time.Millisecond)
58
+				continue
59
+			default:
60
+				return err
61
+			}
62
+		}
63
+		return nil
64
+	}
65
+	return errors.Wrapf(unix.EBUSY, "failed to unmount target %s", target)
47 66
 }
48 67
 
49 68
 // UnmountAll repeatedly unmounts the given mount point until there
... ...
@@ -51,7 +72,7 @@ func Unmount(mount string, flags int) error {
51 51
 // useful for undoing a stack of mounts on the same mount point.
52 52
 func UnmountAll(mount string, flags int) error {
53 53
 	for {
54
-		if err := Unmount(mount, flags); err != nil {
54
+		if err := unmount(mount, flags); err != nil {
55 55
 			// EINVAL is returned if the target is not a
56 56
 			// mount point, indicating that we are
57 57
 			// done. It can also indicate a few other
... ...
@@ -12,12 +12,11 @@ import (
12 12
 	"strconv"
13 13
 	"strings"
14 14
 
15
-	"golang.org/x/sys/unix"
16
-
17 15
 	"github.com/containerd/containerd/containers"
18 16
 	"github.com/containerd/containerd/content"
19 17
 	"github.com/containerd/containerd/fs"
20 18
 	"github.com/containerd/containerd/images"
19
+	"github.com/containerd/containerd/mount"
21 20
 	"github.com/containerd/containerd/namespaces"
22 21
 	"github.com/opencontainers/image-spec/specs-go/v1"
23 22
 	"github.com/opencontainers/runc/libcontainer/user"
... ...
@@ -101,7 +100,7 @@ func WithImageConfig(image Image) SpecOpts {
101 101
 			parts := strings.Split(config.User, ":")
102 102
 			switch len(parts) {
103 103
 			case 1:
104
-				v, err := strconv.ParseUint(parts[0], 0, 10)
104
+				v, err := strconv.Atoi(parts[0])
105 105
 				if err != nil {
106 106
 					// if we cannot parse as a uint they try to see if it is a username
107 107
 					if err := WithUsername(config.User)(ctx, client, c, s); err != nil {
... ...
@@ -113,13 +112,13 @@ func WithImageConfig(image Image) SpecOpts {
113 113
 					return err
114 114
 				}
115 115
 			case 2:
116
-				v, err := strconv.ParseUint(parts[0], 0, 10)
116
+				v, err := strconv.Atoi(parts[0])
117 117
 				if err != nil {
118
-					return err
118
+					return errors.Wrapf(err, "parse uid %s", parts[0])
119 119
 				}
120 120
 				uid := uint32(v)
121
-				if v, err = strconv.ParseUint(parts[1], 0, 10); err != nil {
122
-					return err
121
+				if v, err = strconv.Atoi(parts[1]); err != nil {
122
+					return errors.Wrapf(err, "parse gid %s", parts[1])
123 123
 				}
124 124
 				gid := uint32(v)
125 125
 				s.Process.User.UID, s.Process.User.GID = uid, gid
... ...
@@ -260,7 +259,7 @@ func WithUIDGID(uid, gid uint32) SpecOpts {
260 260
 // or uid is not found in /etc/passwd, it sets gid to be the same with
261 261
 // uid, and not returns error.
262 262
 func WithUserID(uid uint32) SpecOpts {
263
-	return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) error {
263
+	return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) (err error) {
264 264
 		if c.Snapshotter == "" {
265 265
 			return errors.Errorf("no snapshotter set for container")
266 266
 		}
... ...
@@ -276,13 +275,19 @@ func WithUserID(uid uint32) SpecOpts {
276 276
 		if err != nil {
277 277
 			return err
278 278
 		}
279
-		defer os.RemoveAll(root)
279
+		defer os.Remove(root)
280 280
 		for _, m := range mounts {
281 281
 			if err := m.Mount(root); err != nil {
282 282
 				return err
283 283
 			}
284 284
 		}
285
-		defer unix.Unmount(root, 0)
285
+		defer func() {
286
+			if uerr := mount.Unmount(root, 0); uerr != nil {
287
+				if err == nil {
288
+					err = uerr
289
+				}
290
+			}
291
+		}()
286 292
 		ppath, err := fs.RootPath(root, "/etc/passwd")
287 293
 		if err != nil {
288 294
 			return err
... ...
@@ -317,7 +322,7 @@ func WithUserID(uid uint32) SpecOpts {
317 317
 // does not exist, or the username is not found in /etc/passwd,
318 318
 // it returns error.
319 319
 func WithUsername(username string) SpecOpts {
320
-	return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) error {
320
+	return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) (err error) {
321 321
 		if c.Snapshotter == "" {
322 322
 			return errors.Errorf("no snapshotter set for container")
323 323
 		}
... ...
@@ -333,13 +338,19 @@ func WithUsername(username string) SpecOpts {
333 333
 		if err != nil {
334 334
 			return err
335 335
 		}
336
-		defer os.RemoveAll(root)
336
+		defer os.Remove(root)
337 337
 		for _, m := range mounts {
338 338
 			if err := m.Mount(root); err != nil {
339 339
 				return err
340 340
 			}
341 341
 		}
342
-		defer unix.Unmount(root, 0)
342
+		defer func() {
343
+			if uerr := mount.Unmount(root, 0); uerr != nil {
344
+				if err == nil {
345
+					err = uerr
346
+				}
347
+			}
348
+		}()
343 349
 		ppath, err := fs.RootPath(root, "/etc/passwd")
344 350
 		if err != nil {
345 351
 			return err
... ...
@@ -60,3 +60,11 @@ func WithTTY(width, height int) SpecOpts {
60 60
 		return nil
61 61
 	}
62 62
 }
63
+
64
+// WithUsername sets the username on the process
65
+func WithUsername(username string) SpecOpts {
66
+	return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) error {
67
+		s.Process.User.Username = username
68
+		return nil
69
+	}
70
+}
... ...
@@ -55,10 +55,10 @@ func ApplyLayer(ctx context.Context, layer Layer, chain []digest.Digest, sn snap
55 55
 
56 56
 	_, err := sn.Stat(ctx, chainID.String())
57 57
 	if err == nil {
58
-		log.G(ctx).Debugf("Extraction not needed, layer snapshot exists")
58
+		log.G(ctx).Debugf("Extraction not needed, layer snapshot %s exists", chainID)
59 59
 		return false, nil
60 60
 	} else if !errdefs.IsNotFound(err) {
61
-		return false, errors.Wrap(err, "failed to stat snapshot")
61
+		return false, errors.Wrapf(err, "failed to stat snapshot %s", chainID)
62 62
 	}
63 63
 
64 64
 	key := fmt.Sprintf("extract-%s %s", uniquePart(), chainID)
... ...
@@ -67,7 +67,7 @@ func ApplyLayer(ctx context.Context, layer Layer, chain []digest.Digest, sn snap
67 67
 	mounts, err := sn.Prepare(ctx, key, parent.String(), opts...)
68 68
 	if err != nil {
69 69
 		//TODO: If is snapshot exists error, retry
70
-		return false, errors.Wrap(err, "failed to prepare extraction layer")
70
+		return false, errors.Wrapf(err, "failed to prepare extraction snapshot %q", key)
71 71
 	}
72 72
 	defer func() {
73 73
 		if err != nil {
... ...
@@ -89,7 +89,7 @@ func ApplyLayer(ctx context.Context, layer Layer, chain []digest.Digest, sn snap
89 89
 
90 90
 	if err = sn.Commit(ctx, chainID.String(), key, opts...); err != nil {
91 91
 		if !errdefs.IsAlreadyExists(err) {
92
-			return false, errors.Wrapf(err, "failed to commit snapshot %s", parent)
92
+			return false, errors.Wrapf(err, "failed to commit snapshot %s", key)
93 93
 		}
94 94
 
95 95
 		// Destination already exists, cleanup key and return without error
... ...
@@ -49,6 +49,8 @@ func (l *TaskList) Get(ctx context.Context, id string) (Task, error) {
49 49
 
50 50
 // GetAll tasks under a namespace
51 51
 func (l *TaskList) GetAll(ctx context.Context) ([]Task, error) {
52
+	l.mu.Lock()
53
+	defer l.mu.Unlock()
52 54
 	namespace, err := namespaces.NamespaceRequired(ctx)
53 55
 	if err != nil {
54 56
 		return nil, err
... ...
@@ -277,7 +277,7 @@ func (t *task) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (*ExitStat
277 277
 	return &ExitStatus{code: r.ExitStatus, exitedAt: r.ExitedAt}, nil
278 278
 }
279 279
 
280
-func (t *task) Exec(ctx context.Context, id string, spec *specs.Process, ioCreate cio.Creation) (Process, error) {
280
+func (t *task) Exec(ctx context.Context, id string, spec *specs.Process, ioCreate cio.Creation) (_ Process, err error) {
281 281
 	if id == "" {
282 282
 		return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "exec id must not be empty")
283 283
 	}
... ...
@@ -285,6 +285,12 @@ func (t *task) Exec(ctx context.Context, id string, spec *specs.Process, ioCreat
285 285
 	if err != nil {
286 286
 		return nil, err
287 287
 	}
288
+	defer func() {
289
+		if err != nil && i != nil {
290
+			i.Cancel()
291
+			i.Close()
292
+		}
293
+	}()
288 294
 	any, err := typeurl.MarshalAny(spec)
289 295
 	if err != nil {
290 296
 		return nil, err
... ...
@@ -41,4 +41,4 @@ github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
41 41
 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
42 42
 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
43 43
 github.com/dmcgowan/go-tar go1.10
44
-github.com/stevvooe/ttrpc 8c92e22ce0c492875ccaac3ab06143a77d8ed0c1
44
+github.com/stevvooe/ttrpc 76e68349ad9ab4d03d764c713826d31216715e4f
45 45
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+package ttrpc
1
+
2
+import "github.com/pkg/errors"
3
+
4
+type serverConfig struct {
5
+	handshaker Handshaker
6
+}
7
+
8
+type ServerOpt func(*serverConfig) error
9
+
10
+// WithServerHandshaker can be passed to NewServer to ensure that the
11
+// handshaker is called before every connection attempt.
12
+//
13
+// Only one handshaker is allowed per server.
14
+func WithServerHandshaker(handshaker Handshaker) ServerOpt {
15
+	return func(c *serverConfig) error {
16
+		if c.handshaker != nil {
17
+			return errors.New("only one handshaker allowed per server")
18
+		}
19
+		c.handshaker = handshaker
20
+		return nil
21
+	}
22
+}
0 23
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+package ttrpc
1
+
2
+import (
3
+	"context"
4
+	"net"
5
+)
6
+
7
+// Handshaker defines the interface for connection handshakes performed on the
8
+// server or client when first connecting.
9
+type Handshaker interface {
10
+	// Handshake should confirm or decorate a connection that may be incoming
11
+	// to a server or outgoing from a client.
12
+	//
13
+	// If this returns without an error, the caller should use the connection
14
+	// in place of the original connection.
15
+	//
16
+	// The second return value can contain credential specific data, such as
17
+	// unix socket credentials or TLS information.
18
+	//
19
+	// While we currently only have implementations on the server-side, this
20
+	// interface should be sufficient to implement similar handshakes on the
21
+	// client-side.
22
+	Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error)
23
+}
24
+
25
+type handshakerFunc func(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error)
26
+
27
+func (fn handshakerFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) {
28
+	return fn(ctx, conn)
29
+}
30
+
31
+func noopHandshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) {
32
+	return conn, nil, nil
33
+}
... ...
@@ -2,6 +2,7 @@ package ttrpc
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"io"
5 6
 	"math/rand"
6 7
 	"net"
7 8
 	"sync"
... ...
@@ -19,6 +20,7 @@ var (
19 19
 )
20 20
 
21 21
 type Server struct {
22
+	config   *serverConfig
22 23
 	services *serviceSet
23 24
 	codec    codec
24 25
 
... ...
@@ -28,13 +30,21 @@ type Server struct {
28 28
 	done        chan struct{}            // marks point at which we stop serving requests
29 29
 }
30 30
 
31
-func NewServer() *Server {
31
+func NewServer(opts ...ServerOpt) (*Server, error) {
32
+	config := &serverConfig{}
33
+	for _, opt := range opts {
34
+		if err := opt(config); err != nil {
35
+			return nil, err
36
+		}
37
+	}
38
+
32 39
 	return &Server{
40
+		config:      config,
33 41
 		services:    newServiceSet(),
34 42
 		done:        make(chan struct{}),
35 43
 		listeners:   make(map[net.Listener]struct{}),
36 44
 		connections: make(map[*serverConn]struct{}),
37
-	}
45
+	}, nil
38 46
 }
39 47
 
40 48
 func (s *Server) Register(name string, methods map[string]Method) {
... ...
@@ -46,10 +56,15 @@ func (s *Server) Serve(l net.Listener) error {
46 46
 	defer s.closeListener(l)
47 47
 
48 48
 	var (
49
-		ctx     = context.Background()
50
-		backoff time.Duration
49
+		ctx        = context.Background()
50
+		backoff    time.Duration
51
+		handshaker = s.config.handshaker
51 52
 	)
52 53
 
54
+	if handshaker == nil {
55
+		handshaker = handshakerFunc(noopHandshake)
56
+	}
57
+
53 58
 	for {
54 59
 		conn, err := l.Accept()
55 60
 		if err != nil {
... ...
@@ -82,7 +97,15 @@ func (s *Server) Serve(l net.Listener) error {
82 82
 		}
83 83
 
84 84
 		backoff = 0
85
-		sc := s.newConn(conn)
85
+
86
+		approved, handshake, err := handshaker.Handshake(ctx, conn)
87
+		if err != nil {
88
+			log.L.WithError(err).Errorf("ttrpc: refusing connection after handshake")
89
+			conn.Close()
90
+			continue
91
+		}
92
+
93
+		sc := s.newConn(approved, handshake)
86 94
 		go sc.run(ctx)
87 95
 	}
88 96
 }
... ...
@@ -205,11 +228,12 @@ func (cs connState) String() string {
205 205
 	}
206 206
 }
207 207
 
208
-func (s *Server) newConn(conn net.Conn) *serverConn {
208
+func (s *Server) newConn(conn net.Conn, handshake interface{}) *serverConn {
209 209
 	c := &serverConn{
210
-		server:   s,
211
-		conn:     conn,
212
-		shutdown: make(chan struct{}),
210
+		server:    s,
211
+		conn:      conn,
212
+		handshake: handshake,
213
+		shutdown:  make(chan struct{}),
213 214
 	}
214 215
 	c.setState(connStateIdle)
215 216
 	s.addConnection(c)
... ...
@@ -217,9 +241,10 @@ func (s *Server) newConn(conn net.Conn) *serverConn {
217 217
 }
218 218
 
219 219
 type serverConn struct {
220
-	server *Server
221
-	conn   net.Conn
222
-	state  atomic.Value
220
+	server    *Server
221
+	conn      net.Conn
222
+	handshake interface{} // data from handshake, not used for now
223
+	state     atomic.Value
223 224
 
224 225
 	shutdownOnce sync.Once
225 226
 	shutdown     chan struct{} // forced shutdown, used by close
... ...
@@ -406,7 +431,7 @@ func (c *serverConn) run(sctx context.Context) {
406 406
 			// branch. Basically, it means that we are no longer receiving
407 407
 			// requests due to a terminal error.
408 408
 			recvErr = nil // connection is now "closing"
409
-			if err != nil {
409
+			if err != nil && err != io.EOF {
410 410
 				log.L.WithError(err).Error("error receiving message")
411 411
 			}
412 412
 		case <-shutdown:
413 413
new file mode 100644
... ...
@@ -0,0 +1,92 @@
0
+package ttrpc
1
+
2
+import (
3
+	"context"
4
+	"net"
5
+	"os"
6
+	"syscall"
7
+
8
+	"github.com/pkg/errors"
9
+	"golang.org/x/sys/unix"
10
+)
11
+
12
+type UnixCredentialsFunc func(*unix.Ucred) error
13
+
14
+func (fn UnixCredentialsFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) {
15
+	uc, err := requireUnixSocket(conn)
16
+	if err != nil {
17
+		return nil, nil, errors.Wrap(err, "ttrpc.UnixCredentialsFunc: require unix socket")
18
+	}
19
+
20
+	rs, err := uc.SyscallConn()
21
+	if err != nil {
22
+		return nil, nil, errors.Wrap(err, "ttrpc.UnixCredentialsFunc: (net.UnixConn).SyscallConn failed")
23
+	}
24
+	var (
25
+		ucred    *unix.Ucred
26
+		ucredErr error
27
+	)
28
+	if err := rs.Control(func(fd uintptr) {
29
+		ucred, ucredErr = unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED)
30
+	}); err != nil {
31
+		return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: (*syscall.RawConn).Control failed")
32
+	}
33
+
34
+	if ucredErr != nil {
35
+		return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: failed to retrieve socket peer credentials")
36
+	}
37
+
38
+	if err := fn(ucred); err != nil {
39
+		return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: credential check failed")
40
+	}
41
+
42
+	return uc, ucred, nil
43
+}
44
+
45
+// UnixSocketRequireUidGid requires specific *effective* UID/GID, rather than the real UID/GID.
46
+//
47
+// For example, if a daemon binary is owned by the root (UID 0) with SUID bit but running as an
48
+// unprivileged user (UID 1001), the effective UID becomes 0, and the real UID becomes 1001.
49
+// So calling this function with uid=0 allows a connection from effective UID 0 but rejects
50
+// a connection from effective UID 1001.
51
+//
52
+// See socket(7), SO_PEERCRED: "The returned credentials are those that were in effect at the time of the call to connect(2) or socketpair(2)."
53
+func UnixSocketRequireUidGid(uid, gid int) UnixCredentialsFunc {
54
+	return func(ucred *unix.Ucred) error {
55
+		return requireUidGid(ucred, uid, gid)
56
+	}
57
+}
58
+
59
+func UnixSocketRequireRoot() UnixCredentialsFunc {
60
+	return UnixSocketRequireUidGid(0, 0)
61
+}
62
+
63
+// UnixSocketRequireSameUser resolves the current effective unix user and returns a
64
+// UnixCredentialsFunc that will validate incoming unix connections against the
65
+// current credentials.
66
+//
67
+// This is useful when using abstract sockets that are accessible by all users.
68
+func UnixSocketRequireSameUser() UnixCredentialsFunc {
69
+	euid, egid := os.Geteuid(), os.Getegid()
70
+	return UnixSocketRequireUidGid(euid, egid)
71
+}
72
+
73
+func requireRoot(ucred *unix.Ucred) error {
74
+	return requireUidGid(ucred, 0, 0)
75
+}
76
+
77
+func requireUidGid(ucred *unix.Ucred, uid, gid int) error {
78
+	if (uid != -1 && uint32(uid) != ucred.Uid) || (gid != -1 && uint32(gid) != ucred.Gid) {
79
+		return errors.Wrap(syscall.EPERM, "ttrpc: invalid credentials")
80
+	}
81
+	return nil
82
+}
83
+
84
+func requireUnixSocket(conn net.Conn) (*net.UnixConn, error) {
85
+	uc, ok := conn.(*net.UnixConn)
86
+	if !ok {
87
+		return nil, errors.New("a unix socket connection is required")
88
+	}
89
+
90
+	return uc, nil
91
+}