update containerd (binary: v1.0.1, library: Jan 23, 2018)

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
This commit is contained in:
Akihiro Suda
2018-01-23 11:21:15 +09:00
parent ff49ce87a6
commit a39256e9bf
23 changed files with 365 additions and 177 deletions

View File

@@ -211,7 +211,7 @@ export JAEGER_TRACE=0.0.0.0:6831
### Supported runc version
During development, BuildKit is tested with the version of runc that is being used by the containerd repository. Please refer to [runc.md](https://github.com/containerd/containerd/blob/v1.0.1-rc.0/RUNC.md) for more information.
During development, BuildKit is tested with the version of runc that is being used by the containerd repository. Please refer to [runc.md](https://github.com/containerd/containerd/blob/v1.0.1/RUNC.md) for more information.
### Contributing

View File

@@ -17,7 +17,7 @@ type buildOpt struct {
func main() {
var opt buildOpt
flag.BoolVar(&opt.withContainerd, "with-containerd", true, "enable containerd worker")
flag.StringVar(&opt.containerd, "containerd", "v1.0.1-rc.0", "containerd version")
flag.StringVar(&opt.containerd, "containerd", "v1.0.1", "containerd version")
flag.StringVar(&opt.runc, "runc", "7f24b40cc5423969b4554ef04ba0b00e2b4ba010", "runc version")
flag.Parse()

View File

@@ -17,7 +17,7 @@ type buildOpt struct {
func main() {
var opt buildOpt
flag.BoolVar(&opt.withContainerd, "with-containerd", true, "enable containerd worker")
flag.StringVar(&opt.containerd, "containerd", "v1.0.1-rc.0", "containerd version")
flag.StringVar(&opt.containerd, "containerd", "v1.0.1", "containerd version")
flag.StringVar(&opt.runc, "runc", "7f24b40cc5423969b4554ef04ba0b00e2b4ba010", "runc version")
flag.Parse()

View File

@@ -17,7 +17,7 @@ type buildOpt struct {
func main() {
var opt buildOpt
flag.BoolVar(&opt.withContainerd, "with-containerd", true, "enable containerd worker")
flag.StringVar(&opt.containerd, "containerd", "v1.0.1-rc.0", "containerd version")
flag.StringVar(&opt.containerd, "containerd", "v1.0.1", "containerd version")
flag.StringVar(&opt.runc, "runc", "7f24b40cc5423969b4554ef04ba0b00e2b4ba010", "runc version")
flag.Parse()

View File

@@ -18,7 +18,7 @@ type buildOpt struct {
func main() {
var opt buildOpt
flag.BoolVar(&opt.withContainerd, "with-containerd", true, "enable containerd worker")
flag.StringVar(&opt.containerd, "containerd", "v1.0.1-rc.0", "containerd version")
flag.StringVar(&opt.containerd, "containerd", "v1.0.1", "containerd version")
flag.StringVar(&opt.runc, "runc", "7f24b40cc5423969b4554ef04ba0b00e2b4ba010", "runc version")
flag.StringVar(&opt.buildkit, "buildkit", "master", "buildkit version")
flag.Parse()

View File

@@ -1,5 +1,5 @@
ARG RUNC_VERSION=7f24b40cc5423969b4554ef04ba0b00e2b4ba010
ARG CONTAINERD_VERSION=v1.0.1-rc.0
ARG CONTAINERD_VERSION=v1.0.1
# available targets: buildkitd, buildkitd.oci_only, buildkitd.containerd_only
ARG BUILDKIT_TARGET=buildkitd
ARG REGISTRY_VERSION=2.6

View File

@@ -6,7 +6,7 @@ github.com/davecgh/go-spew v1.1.0
github.com/pmezard/go-difflib v1.0.0
golang.org/x/sys 9aade4d3a3b7e6d876cd3823ad20ec45fc035402 # update to 7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce wiht pkg/signal change
github.com/containerd/containerd 90553efdef0678b2609aed74926a487f8ff58d1a
github.com/containerd/containerd dc5964ccc2a3c5dddeef806efaab0d905aec96ed
github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/sirupsen/logrus v1.0.0
@@ -57,4 +57,4 @@ github.com/apache/thrift b2a4d4ae21c789b689dd162deb819665567f481c
github.com/uber/jaeger-lib c48167d9cae5887393dd5e61efd06a4a48b7fbb3
github.com/codahale/hdrhistogram f8ad88b59a584afeee9d334eff879b104439117b
github.com/opentracing-contrib/go-stdlib b1a47cfbdd7543e70e9ef3e73d0802ad306cc1cc
github.com/opentracing-contrib/go-stdlib b1a47cfbdd7543e70e9ef3e73d0802ad306cc1cc

View File

@@ -26,6 +26,8 @@ var bufferPool = &sync.Pool{
},
}
var errInvalidArchive = errors.New("invalid archive")
// Diff returns a tar stream of the computed filesystem
// difference between the provided directories.
//
@@ -231,6 +233,15 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO
originalBase := base[len(whiteoutPrefix):]
originalPath := filepath.Join(dir, originalBase)
// Ensure originalPath is under dir
if dir[len(dir)-1] != filepath.Separator {
dir += string(filepath.Separator)
}
if !strings.HasPrefix(originalPath, dir) {
return 0, errors.Wrapf(errInvalidArchive, "invalid whiteout name: %v", base)
}
if err := os.RemoveAll(originalPath); err != nil {
return 0, err
}

View File

@@ -6,6 +6,8 @@ import (
"io"
"os"
"sync"
"github.com/containerd/containerd/defaults"
)
// Config holds the IO configurations.
@@ -68,6 +70,7 @@ type Streams struct {
Stdout io.Writer
Stderr io.Writer
Terminal bool
FIFODir string
}
// Opt customize options for creating a Creator or Attach
@@ -92,16 +95,25 @@ func WithStreams(stdin io.Reader, stdout, stderr io.Writer) Opt {
}
}
// WithFIFODir sets the fifo directory.
// e.g. "/run/containerd/fifo", "/run/users/1001/containerd/fifo"
func WithFIFODir(dir string) Opt {
return func(opt *Streams) {
opt.FIFODir = dir
}
}
// NewCreator returns an IO creator from the options
func NewCreator(opts ...Opt) Creator {
streams := &Streams{}
for _, opt := range opts {
opt(streams)
}
if streams.FIFODir == "" {
streams.FIFODir = defaults.DefaultFIFODir
}
return func(id string) (IO, error) {
// TODO: accept root as a param
root := "/run/containerd/fifo"
fifos, err := NewFIFOSetInDir(root, id, streams.Terminal)
fifos, err := NewFIFOSetInDir(streams.FIFODir, id, streams.Terminal)
if err != nil {
return nil, err
}

View File

@@ -222,11 +222,11 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
name, desc, err := pullCtx.Resolver.Resolve(ctx, ref)
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "failed to resolve reference %q", ref)
}
fetcher, err := pullCtx.Resolver.Fetcher(ctx, name)
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "failed to get fetcher for %q", name)
}
var (
@@ -281,7 +281,7 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
}
if pullCtx.Unpack {
if err := img.Unpack(ctx, pullCtx.Snapshotter); err != nil {
return nil, err
errors.Wrapf(err, "failed to unpack image on snapshotter %s", pullCtx.Snapshotter)
}
}
return img, nil

View File

@@ -0,0 +1,19 @@
// +build !windows
package defaults
const (
// DefaultRootDir is the default location used by containerd to store
// persistent data
DefaultRootDir = "/var/lib/containerd"
// DefaultStateDir is the default location used by containerd to store
// transient data
DefaultStateDir = "/run/containerd"
// DefaultAddress is the default unix socket address
DefaultAddress = "/run/containerd/containerd.sock"
// DefaultDebugAddress is the default unix socket address for pprof data
DefaultDebugAddress = "/run/containerd/debug.sock"
// DefaultFIFODir is the default location used by client-side cio library
// to store FIFOs.
DefaultFIFODir = "/run/containerd/fifo"
)

View File

@@ -0,0 +1,27 @@
// +build windows
package defaults
import (
"os"
"path/filepath"
)
var (
// DefaultRootDir is the default location used by containerd to store
// persistent data
DefaultRootDir = filepath.Join(os.Getenv("programfiles"), "containerd", "root")
// DefaultStateDir is the default location used by containerd to store
// transient data
DefaultStateDir = filepath.Join(os.Getenv("programfiles"), "containerd", "state")
)
const (
// DefaultAddress is the default winpipe address
DefaultAddress = `\\.\pipe\containerd-containerd`
// DefaultDebugAddress is the default winpipe address for pprof data
DefaultDebugAddress = `\\.\pipe\containerd-debug`
// DefaultFIFODir is the default location used by client-side cio library
// to store FIFOs. Unused on Windows.
DefaultFIFODir = ""
)

View File

@@ -0,0 +1,3 @@
// Package defaults provides several common defaults for interacting wtih
// containerd. These can be used on the client-side or server-side.
package defaults

View File

@@ -42,7 +42,7 @@ func Dialer(address string, timeout time.Duration) (net.Conn, error) {
close(stopC)
go func() {
dr := <-synC
if dr != nil {
if dr != nil && dr.c != nil {
dr.c.Close()
}
}()

View File

@@ -14,7 +14,20 @@ import (
func copyFileInfo(fi os.FileInfo, name string) error {
st := fi.Sys().(*syscall.Stat_t)
if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
return errors.Wrapf(err, "failed to chown %s", name)
if os.IsPermission(err) {
// Normally if uid/gid are the same this would be a no-op, but some
// filesystems may still return EPERM... for instance NFS does this.
// In such a case, this is not an error.
if dstStat, err2 := os.Lstat(name); err2 == nil {
st2 := dstStat.Sys().(*syscall.Stat_t)
if st.Uid == st2.Uid && st.Gid == st2.Gid {
err = nil
}
}
}
if err != nil {
return errors.Wrapf(err, "failed to chown %s", name)
}
}
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {

View File

@@ -16,7 +16,20 @@ import (
func copyFileInfo(fi os.FileInfo, name string) error {
st := fi.Sys().(*syscall.Stat_t)
if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
return errors.Wrapf(err, "failed to chown %s", name)
if os.IsPermission(err) {
// Normally if uid/gid are the same this would be a no-op, but some
// filesystems may still return EPERM... for instance NFS does this.
// In such a case, this is not an error.
if dstStat, err2 := os.Lstat(name); err2 == nil {
st2 := dstStat.Sys().(*syscall.Stat_t)
if st.Uid == st2.Uid && st.Gid == st2.Gid {
err = nil
}
}
}
if err != nil {
return errors.Wrapf(err, "failed to chown %s", name)
}
}
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {

View File

@@ -2,7 +2,6 @@ package log
import (
"context"
"path"
"github.com/sirupsen/logrus"
)
@@ -20,7 +19,6 @@ var (
type (
loggerKey struct{}
moduleKey struct{}
)
// WithLogger returns a new context with the provided logger. Use in
@@ -40,42 +38,3 @@ func GetLogger(ctx context.Context) *logrus.Entry {
return logger.(*logrus.Entry)
}
// WithModule adds the module to the context, appending it with a slash if a
// module already exists. A module is just an roughly correlated defined by the
// call tree for a given context.
//
// As an example, we might have a "node" module already part of a context. If
// this function is called with "tls", the new value of module will be
// "node/tls".
//
// Modules represent the call path. If the new module and last module are the
// same, a new module entry will not be created. If the new module and old
// older module are the same but separated by other modules, the cycle will be
// represented by the module path.
func WithModule(ctx context.Context, module string) context.Context {
parent := GetModulePath(ctx)
if parent != "" {
// don't re-append module when module is the same.
if path.Base(parent) == module {
return ctx
}
module = path.Join(parent, module)
}
ctx = WithLogger(ctx, GetLogger(ctx).WithField("module", module))
return context.WithValue(ctx, moduleKey{}, module)
}
// GetModulePath returns the module path for the provided context. If no module
// is set, an empty string is returned.
func GetModulePath(ctx context.Context) string {
module := ctx.Value(moduleKey{})
if module == nil {
return ""
}
return module.(string)
}

View File

@@ -106,13 +106,8 @@ func imagesBucketPath(namespace string) [][]byte {
return [][]byte{bucketKeyVersion, []byte(namespace), bucketKeyObjectImages}
}
func withImagesBucket(tx *bolt.Tx, namespace string, fn func(bkt *bolt.Bucket) error) error {
bkt, err := createBucketIfNotExists(tx, imagesBucketPath(namespace)...)
if err != nil {
return err
}
return fn(bkt)
func createImagesBucket(tx *bolt.Tx, namespace string) (*bolt.Bucket, error) {
return createBucketIfNotExists(tx, imagesBucketPath(namespace)...)
}
func getImagesBucket(tx *bolt.Tx, namespace string) *bolt.Bucket {
@@ -143,6 +138,10 @@ func createSnapshotterBucket(tx *bolt.Tx, namespace, snapshotter string) (*bolt.
return bkt, nil
}
func getSnapshottersBucket(tx *bolt.Tx, namespace string) *bolt.Bucket {
return getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectSnapshots)
}
func getSnapshotterBucket(tx *bolt.Tx, namespace, snapshotter string) *bolt.Bucket {
return getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectSnapshots, []byte(snapshotter))
}

View File

@@ -20,12 +20,12 @@ import (
)
type imageStore struct {
tx *bolt.Tx
db *DB
}
// NewImageStore returns a store backed by a bolt DB
func NewImageStore(tx *bolt.Tx) images.Store {
return &imageStore{tx: tx}
func NewImageStore(db *DB) images.Store {
return &imageStore{db: db}
}
func (s *imageStore) Get(ctx context.Context, name string) (images.Image, error) {
@@ -36,19 +36,25 @@ func (s *imageStore) Get(ctx context.Context, name string) (images.Image, error)
return images.Image{}, err
}
bkt := getImagesBucket(s.tx, namespace)
if bkt == nil {
return images.Image{}, errors.Wrapf(errdefs.ErrNotFound, "image %q", name)
}
if err := view(ctx, s.db, func(tx *bolt.Tx) error {
bkt := getImagesBucket(tx, namespace)
if bkt == nil {
return errors.Wrapf(errdefs.ErrNotFound, "image %q", name)
}
ibkt := bkt.Bucket([]byte(name))
if ibkt == nil {
return images.Image{}, errors.Wrapf(errdefs.ErrNotFound, "image %q", name)
}
ibkt := bkt.Bucket([]byte(name))
if ibkt == nil {
return errors.Wrapf(errdefs.ErrNotFound, "image %q", name)
}
image.Name = name
if err := readImage(&image, ibkt); err != nil {
return images.Image{}, errors.Wrapf(err, "image %q", name)
image.Name = name
if err := readImage(&image, ibkt); err != nil {
return errors.Wrapf(err, "image %q", name)
}
return nil
}); err != nil {
return images.Image{}, err
}
return image, nil
@@ -65,28 +71,30 @@ func (s *imageStore) List(ctx context.Context, fs ...string) ([]images.Image, er
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error())
}
bkt := getImagesBucket(s.tx, namespace)
if bkt == nil {
return nil, nil // empty store
}
var m []images.Image
if err := bkt.ForEach(func(k, v []byte) error {
var (
image = images.Image{
Name: string(k),
if err := view(ctx, s.db, func(tx *bolt.Tx) error {
bkt := getImagesBucket(tx, namespace)
if bkt == nil {
return nil // empty store
}
return bkt.ForEach(func(k, v []byte) error {
var (
image = images.Image{
Name: string(k),
}
kbkt = bkt.Bucket(k)
)
if err := readImage(&image, kbkt); err != nil {
return err
}
kbkt = bkt.Bucket(k)
)
if err := readImage(&image, kbkt); err != nil {
return err
}
if filter.Match(adaptImage(image)) {
m = append(m, image)
}
return nil
if filter.Match(adaptImage(image)) {
m = append(m, image)
}
return nil
})
}); err != nil {
return nil, err
}
@@ -100,11 +108,16 @@ func (s *imageStore) Create(ctx context.Context, image images.Image) (images.Ima
return images.Image{}, err
}
if err := validateImage(&image); err != nil {
return images.Image{}, err
}
if err := update(ctx, s.db, func(tx *bolt.Tx) error {
if err := validateImage(&image); err != nil {
return err
}
bkt, err := createImagesBucket(tx, namespace)
if err != nil {
return err
}
return image, withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error {
ibkt, err := bkt.CreateBucket([]byte(image.Name))
if err != nil {
if err != bolt.ErrBucketExists {
@@ -117,7 +130,11 @@ func (s *imageStore) Create(ctx context.Context, image images.Image) (images.Ima
image.CreatedAt = time.Now().UTC()
image.UpdatedAt = image.CreatedAt
return writeImage(ibkt, &image)
})
}); err != nil {
return images.Image{}, err
}
return image, nil
}
func (s *imageStore) Update(ctx context.Context, image images.Image, fieldpaths ...string) (images.Image, error) {
@@ -131,7 +148,13 @@ func (s *imageStore) Update(ctx context.Context, image images.Image, fieldpaths
}
var updated images.Image
return updated, withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error {
if err := update(ctx, s.db, func(tx *bolt.Tx) error {
bkt, err := createImagesBucket(tx, namespace)
if err != nil {
return err
}
ibkt := bkt.Bucket([]byte(image.Name))
if ibkt == nil {
return errors.Wrapf(errdefs.ErrNotFound, "image %q", image.Name)
@@ -180,7 +203,12 @@ func (s *imageStore) Update(ctx context.Context, image images.Image, fieldpaths
updated.CreatedAt = createdat
updated.UpdatedAt = time.Now().UTC()
return writeImage(ibkt, &updated)
})
}); err != nil {
return images.Image{}, err
}
return updated, nil
}
func (s *imageStore) Delete(ctx context.Context, name string, opts ...images.DeleteOpt) error {
@@ -189,11 +217,24 @@ func (s *imageStore) Delete(ctx context.Context, name string, opts ...images.Del
return err
}
return withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error {
err := bkt.DeleteBucket([]byte(name))
return update(ctx, s.db, func(tx *bolt.Tx) error {
bkt := getImagesBucket(tx, namespace)
if bkt == nil {
return errors.Wrapf(errdefs.ErrNotFound, "image %q", name)
}
err = bkt.DeleteBucket([]byte(name))
if err == bolt.ErrBucketNotFound {
return errors.Wrapf(errdefs.ErrNotFound, "image %q", name)
}
// A reference to a piece of content has been removed,
// mark content store as dirty for triggering garbage
// collection
s.db.dirtyL.Lock()
s.db.dirtyCS = true
s.db.dirtyL.Unlock()
return err
})
}

View File

@@ -133,31 +133,38 @@ func (s *namespaceStore) Delete(ctx context.Context, namespace string) error {
}
func (s *namespaceStore) namespaceEmpty(ctx context.Context, namespace string) (bool, error) {
ctx = namespaces.WithNamespace(ctx, namespace)
// need to check the various object stores.
imageStore := NewImageStore(s.tx)
images, err := imageStore.List(ctx)
if err != nil {
return false, err
// Get all data buckets
buckets := []*bolt.Bucket{
getImagesBucket(s.tx, namespace),
getBlobsBucket(s.tx, namespace),
getContainersBucket(s.tx, namespace),
}
if len(images) > 0 {
return false, nil
if snbkt := getSnapshottersBucket(s.tx, namespace); snbkt != nil {
if err := snbkt.ForEach(func(k, v []byte) error {
if v == nil {
buckets = append(buckets, snbkt.Bucket(k))
}
return nil
}); err != nil {
return false, err
}
}
containerStore := NewContainerStore(s.tx)
containers, err := containerStore.List(ctx)
if err != nil {
return false, err
// Ensure data buckets are empty
for _, bkt := range buckets {
if !isBucketEmpty(bkt) {
return false, nil
}
}
if len(containers) > 0 {
return false, nil
}
// TODO(stevvooe): Need to add check for content store, as well. Still need
// to make content store namespace aware.
return true, nil
}
func isBucketEmpty(bkt *bolt.Bucket) bool {
if bkt == nil {
return true
}
k, _ := bkt.Cursor().First()
return k == nil
}

View File

@@ -2,6 +2,7 @@ package oci
import (
"context"
"strings"
"github.com/containerd/containerd/containers"
specs "github.com/opencontainers/runtime-spec/specs-go"
@@ -33,3 +34,59 @@ func WithHostname(name string) SpecOpts {
return nil
}
}
// WithEnv appends environment variables
func WithEnv(environmnetVariables []string) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
if len(environmnetVariables) > 0 {
s.Process.Env = replaceOrAppendEnvValues(s.Process.Env, environmnetVariables)
}
return nil
}
}
// WithMounts appends mounts
func WithMounts(mounts []specs.Mount) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error {
s.Mounts = append(s.Mounts, mounts...)
return nil
}
}
// replaceOrAppendEnvValues returns the defaults with the overrides either
// replaced by env key or appended to the list
func replaceOrAppendEnvValues(defaults, overrides []string) []string {
cache := make(map[string]int, len(defaults))
for i, e := range defaults {
parts := strings.SplitN(e, "=", 2)
cache[parts[0]] = i
}
for _, value := range overrides {
// Values w/o = means they want this env to be removed/unset.
if !strings.Contains(value, "=") {
if i, exists := cache[value]; exists {
defaults[i] = "" // Used to indicate it should be removed
}
continue
}
// Just do a normal set/update
parts := strings.SplitN(value, "=", 2)
if i, exists := cache[parts[0]]; exists {
defaults[i] = value
} else {
defaults = append(defaults, value)
}
}
// Now remove all entries that we want to "unset"
for i := 0; i < len(defaults); i++ {
if defaults[i] == "" {
defaults = append(defaults[:i], defaults[i+1:]...)
i--
}
}
return defaults
}

View File

@@ -95,22 +95,25 @@ func WithImageConfig(image Image) SpecOpts {
s.Process.Env = append(s.Process.Env, config.Env...)
cmd := config.Cmd
s.Process.Args = append(config.Entrypoint, cmd...)
cwd := config.WorkingDir
if cwd == "" {
cwd = "/"
}
s.Process.Cwd = cwd
if config.User != "" {
// According to OCI Image Spec v1.0.0, the following are valid for Linux:
// user, uid, user:group, uid:gid, uid:group, user:gid
parts := strings.Split(config.User, ":")
switch len(parts) {
case 1:
v, err := strconv.Atoi(parts[0])
if err != nil {
// if we cannot parse as a uint they try to see if it is a username
if err := WithUsername(config.User)(ctx, client, c, s); err != nil {
return err
}
return err
}
if err := WithUserID(uint32(v))(ctx, client, c, s); err != nil {
return err
return WithUsername(config.User)(ctx, client, c, s)
}
return WithUserID(uint32(v))(ctx, client, c, s)
case 2:
// TODO: support username and groupname
v, err := strconv.Atoi(parts[0])
if err != nil {
return errors.Wrapf(err, "parse uid %s", parts[0])
@@ -125,11 +128,6 @@ func WithImageConfig(image Image) SpecOpts {
return fmt.Errorf("invalid USER value %s", config.User)
}
}
cwd := config.WorkingDir
if cwd == "" {
cwd = "/"
}
s.Process.Cwd = cwd
return nil
}
}
@@ -259,6 +257,24 @@ func WithUIDGID(uid, gid uint32) SpecOpts {
// uid, and not returns error.
func WithUserID(uid uint32) SpecOpts {
return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) (err error) {
if c.Snapshotter == "" && c.SnapshotKey == "" {
if !isRootfsAbs(s.Root.Path) {
return errors.Errorf("rootfs absolute path is required")
}
uuid, ugid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool {
return u.Uid == int(uid)
})
if err != nil {
if os.IsNotExist(err) || err == errNoUsersFound {
s.Process.User.UID, s.Process.User.GID = uid, uid
return nil
}
return err
}
s.Process.User.UID, s.Process.User.GID = uuid, ugid
return nil
}
if c.Snapshotter == "" {
return errors.Errorf("no snapshotter set for container")
}
@@ -270,33 +286,18 @@ func WithUserID(uid uint32) SpecOpts {
if err != nil {
return err
}
return mount.WithTempMount(ctx, mounts, func(root string) error {
ppath, err := fs.RootPath(root, "/etc/passwd")
uuid, ugid, err := getUIDGIDFromPath(root, func(u user.User) bool {
return u.Uid == int(uid)
})
if err != nil {
return err
}
f, err := os.Open(ppath)
if err != nil {
if os.IsNotExist(err) {
if os.IsNotExist(err) || err == errNoUsersFound {
s.Process.User.UID, s.Process.User.GID = uid, uid
return nil
}
return err
}
defer f.Close()
users, err := user.ParsePasswdFilter(f, func(u user.User) bool {
return u.Uid == int(uid)
})
if err != nil {
return err
}
if len(users) == 0 {
s.Process.User.UID, s.Process.User.GID = uid, uid
return nil
}
u := users[0]
s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
s.Process.User.UID, s.Process.User.GID = uuid, ugid
return nil
})
}
@@ -308,6 +309,19 @@ func WithUserID(uid uint32) SpecOpts {
// it returns error.
func WithUsername(username string) SpecOpts {
return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) (err error) {
if c.Snapshotter == "" && c.SnapshotKey == "" {
if !isRootfsAbs(s.Root.Path) {
return errors.Errorf("rootfs absolute path is required")
}
uid, gid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool {
return u.Name == username
})
if err != nil {
return err
}
s.Process.User.UID, s.Process.User.GID = uid, gid
return nil
}
if c.Snapshotter == "" {
return errors.Errorf("no snapshotter set for container")
}
@@ -320,27 +334,41 @@ func WithUsername(username string) SpecOpts {
return err
}
return mount.WithTempMount(ctx, mounts, func(root string) error {
ppath, err := fs.RootPath(root, "/etc/passwd")
if err != nil {
return err
}
f, err := os.Open(ppath)
if err != nil {
return err
}
defer f.Close()
users, err := user.ParsePasswdFilter(f, func(u user.User) bool {
uid, gid, err := getUIDGIDFromPath(root, func(u user.User) bool {
return u.Name == username
})
if err != nil {
return err
}
if len(users) == 0 {
return errors.Errorf("no users found for %s", username)
}
u := users[0]
s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
s.Process.User.UID, s.Process.User.GID = uid, gid
return nil
})
}
}
var errNoUsersFound = errors.New("no users found")
func getUIDGIDFromPath(root string, filter func(user.User) bool) (uid, gid uint32, err error) {
ppath, err := fs.RootPath(root, "/etc/passwd")
if err != nil {
return 0, 0, err
}
f, err := os.Open(ppath)
if err != nil {
return 0, 0, err
}
defer f.Close()
users, err := user.ParsePasswdFilter(f, filter)
if err != nil {
return 0, 0, err
}
if len(users) == 0 {
return 0, 0, errNoUsersFound
}
u := users[0]
return uint32(u.Uid), uint32(u.Gid), nil
}
func isRootfsAbs(root string) bool {
return filepath.IsAbs(root)
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events/exchange"
"github.com/containerd/containerd/log"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
@@ -28,7 +27,7 @@ type InitContext struct {
// NewContext returns a new plugin InitContext
func NewContext(ctx context.Context, r *Registration, plugins *Set, root, state string) *InitContext {
return &InitContext{
Context: log.WithModule(ctx, r.URI()),
Context: ctx,
Root: filepath.Join(root, r.URI()),
State: filepath.Join(state, r.URI()),
Meta: &Meta{