From b8dc00de71ae19bd693f49008f0eb73dd5ee8a0a Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 11 Jan 2018 17:49:09 -0800 Subject: [PATCH] vendor: update containerd to 1.0.1-rc0 Signed-off-by: Tonis Tiigi --- README.md | 2 +- examples/buildkit0/buildkit.go | 4 +- examples/buildkit1/buildkit.go | 4 +- examples/buildkit2/buildkit.go | 4 +- examples/buildkit3/buildkit.go | 4 +- executor/containerdexecutor/executor.go | 2 +- hack/dockerfiles/test.Dockerfile | 4 +- vendor.conf | 12 +- .../github.com/Microsoft/go-winio/backup.go | 14 +- vendor/github.com/Microsoft/go-winio/ea.go | 137 +++++++ vendor/github.com/Microsoft/go-winio/file.go | 8 + vendor/github.com/Microsoft/go-winio/pipe.go | 4 +- .../containerd/containerd/README.md | 3 +- .../containerd/containerd/archive/strconv.go | 52 +++ .../containerd/containerd/archive/tar.go | 360 ++++++++++-------- .../containerd/containerd/archive/tar_opts.go | 4 + .../containerd/archive/tar_opts_unix.go | 7 + .../containerd/archive/tar_opts_windows.go | 28 ++ .../containerd/containerd/archive/tar_unix.go | 14 +- .../containerd/archive/tar_windows.go | 323 ++++++++++++++++ .../containerd/containerd/cio/io.go | 284 +++++++------- .../containerd/containerd/cio/io_unix.go | 230 +++++------ .../containerd/containerd/cio/io_windows.go | 73 ++-- .../containerd/containerd/container.go | 32 +- .../containerd/container_opts_unix.go | 40 +- .../containerd/containerd/content/helpers.go | 2 +- .../containerd/content/local/store_unix.go | 3 +- .../containerd/content/local/writer.go | 4 + .../containerd/diff/walking/differ.go | 251 ++++++------ .../containerd/containerd/events/events.go | 4 +- .../containerd/containerd/fs/diff.go | 83 +--- .../containerd/containerd/fs/du_unix.go | 17 +- .../containerd/containerd/fs/hardlink_unix.go | 3 +- .../containerd/containerd/fs/path.go | 4 +- .../github.com/containerd/containerd/gc/gc.go | 6 + .../containerd/containerd/images/image.go | 10 +- .../containerd/containerd/metadata/content.go | 18 +- .../containerd/containerd/metadata/db.go | 16 +- .../containerd/containerd/metadata/images.go | 2 +- .../containerd/containerd/mount/mount.go | 48 +++ .../containerd/oci/spec_opts_unix.go | 110 ++---- .../containerd/platforms/cpuinfo.go | 85 +++++ .../containerd/platforms/database.go | 18 + .../containerd/platforms/defaults.go | 3 +- .../containerd/containerd/process.go | 1 + .../containerd/containerd/remotes/hints.go | 31 -- .../containerd/containerd/rootfs/diff.go | 2 +- .../containerd/containerd/sys/stat_bsd.go | 6 + .../containerd/containerd/sys/stat_unix.go | 6 + .../github.com/containerd/containerd/task.go | 5 +- .../containerd/containerd/vendor.conf | 13 +- .../opencontainers/image-spec/README.md | 2 +- .../image-spec/specs-go/version.go | 2 +- .../github.com/opencontainers/runc/README.md | 2 +- .../runc/libcontainer/system/sysconfig.go | 2 +- .../libcontainer/user/lookup_unsupported.go | 38 -- .../opencontainers/runc/vendor.conf | 2 +- .../opencontainers/runtime-spec/README.md | 11 +- .../runtime-spec/specs-go/config.go | 2 +- .../runtime-spec/specs-go/version.go | 2 +- .../grpc/balancer_conn_wrappers.go | 4 + vendor/google.golang.org/grpc/clientconn.go | 10 + .../grpc/credentials/credentials.go | 4 + vendor/google.golang.org/grpc/rpc_util.go | 2 +- vendor/google.golang.org/grpc/server.go | 39 +- .../grpc/transport/handler_server.go | 23 +- .../grpc/transport/http2_server.go | 64 ++-- 67 files changed, 1590 insertions(+), 1019 deletions(-) create mode 100644 vendor/github.com/Microsoft/go-winio/ea.go create mode 100644 vendor/github.com/containerd/containerd/archive/strconv.go create mode 100644 vendor/github.com/containerd/containerd/archive/tar_opts.go create mode 100644 vendor/github.com/containerd/containerd/archive/tar_opts_unix.go create mode 100644 vendor/github.com/containerd/containerd/archive/tar_opts_windows.go create mode 100644 vendor/github.com/containerd/containerd/platforms/cpuinfo.go delete mode 100644 vendor/github.com/containerd/containerd/remotes/hints.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go diff --git a/README.md b/README.md index af968abe2..fbe1f5f73 100644 --- a/README.md +++ b/README.md @@ -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.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-rc.0/RUNC.md) for more information. ### Contributing diff --git a/examples/buildkit0/buildkit.go b/examples/buildkit0/buildkit.go index 13e8cd9dc..186c03ea4 100644 --- a/examples/buildkit0/buildkit.go +++ b/examples/buildkit0/buildkit.go @@ -17,8 +17,8 @@ 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.0", "containerd version") - flag.StringVar(&opt.runc, "runc", "74a17296470088de3805e138d3d87c62e613dfc4", "runc version") + flag.StringVar(&opt.containerd, "containerd", "v1.0.1-rc.0", "containerd version") + flag.StringVar(&opt.runc, "runc", "7f24b40cc5423969b4554ef04ba0b00e2b4ba010", "runc version") flag.Parse() bk := buildkit(opt) diff --git a/examples/buildkit1/buildkit.go b/examples/buildkit1/buildkit.go index 292513dbc..841b366e0 100644 --- a/examples/buildkit1/buildkit.go +++ b/examples/buildkit1/buildkit.go @@ -17,8 +17,8 @@ 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.0", "containerd version") - flag.StringVar(&opt.runc, "runc", "74a17296470088de3805e138d3d87c62e613dfc4", "runc version") + flag.StringVar(&opt.containerd, "containerd", "v1.0.1-rc.0", "containerd version") + flag.StringVar(&opt.runc, "runc", "7f24b40cc5423969b4554ef04ba0b00e2b4ba010", "runc version") flag.Parse() bk := buildkit(opt) diff --git a/examples/buildkit2/buildkit.go b/examples/buildkit2/buildkit.go index b893979a8..524f3c6b9 100644 --- a/examples/buildkit2/buildkit.go +++ b/examples/buildkit2/buildkit.go @@ -17,8 +17,8 @@ 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.0", "containerd version") - flag.StringVar(&opt.runc, "runc", "74a17296470088de3805e138d3d87c62e613dfc4", "runc version") + flag.StringVar(&opt.containerd, "containerd", "v1.0.1-rc.0", "containerd version") + flag.StringVar(&opt.runc, "runc", "7f24b40cc5423969b4554ef04ba0b00e2b4ba010", "runc version") flag.Parse() bk := buildkit(opt) diff --git a/examples/buildkit3/buildkit.go b/examples/buildkit3/buildkit.go index f61ec0c6f..dc7b0f4da 100644 --- a/examples/buildkit3/buildkit.go +++ b/examples/buildkit3/buildkit.go @@ -18,8 +18,8 @@ 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.0", "containerd version") - flag.StringVar(&opt.runc, "runc", "74a17296470088de3805e138d3d87c62e613dfc4", "runc version") + flag.StringVar(&opt.containerd, "containerd", "v1.0.1-rc.0", "containerd version") + flag.StringVar(&opt.runc, "runc", "7f24b40cc5423969b4554ef04ba0b00e2b4ba010", "runc version") flag.StringVar(&opt.buildkit, "buildkit", "master", "buildkit version") flag.Parse() diff --git a/executor/containerdexecutor/executor.go b/executor/containerdexecutor/executor.go index 93fd6e98c..aaca583e9 100644 --- a/executor/containerdexecutor/executor.go +++ b/executor/containerdexecutor/executor.go @@ -85,7 +85,7 @@ func (w containerdExecutor) Exec(ctx context.Context, meta executor.Meta, root c stdin = &emptyReadCloser{} } - task, err := container.NewTask(ctx, cio.NewIO(stdin, stdout, stderr), containerd.WithRootFS(rootMounts)) + task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStreams(stdin, stdout, stderr)), containerd.WithRootFS(rootMounts)) if err != nil { return err } diff --git a/hack/dockerfiles/test.Dockerfile b/hack/dockerfiles/test.Dockerfile index 26d10cc6f..200b28af1 100644 --- a/hack/dockerfiles/test.Dockerfile +++ b/hack/dockerfiles/test.Dockerfile @@ -1,5 +1,5 @@ -ARG RUNC_VERSION=74a17296470088de3805e138d3d87c62e613dfc4 -ARG CONTAINERD_VERSION=v1.0.0 +ARG RUNC_VERSION=7f24b40cc5423969b4554ef04ba0b00e2b4ba010 +ARG CONTAINERD_VERSION=v1.0.1-rc.0 # available targets: buildkitd, buildkitd.oci_only, buildkitd.containerd_only ARG BUILDKIT_TARGET=buildkitd ARG REGISTRY_VERSION=2.6 diff --git a/vendor.conf b/vendor.conf index 400d9762a..c45f29a9c 100644 --- a/vendor.conf +++ b/vendor.conf @@ -6,21 +6,21 @@ 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 v1.0.0 +github.com/containerd/containerd 90553efdef0678b2609aed74926a487f8ff58d1a github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c github.com/sirupsen/logrus v1.0.0 -google.golang.org/grpc v1.7.2 +google.golang.org/grpc v1.7.4 github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448 golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 github.com/gogo/protobuf v0.5 github.com/golang/protobuf 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9 github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e -github.com/opencontainers/image-spec v1.0.0 -github.com/opencontainers/runc 74a17296470088de3805e138d3d87c62e613dfc4 -github.com/Microsoft/go-winio v0.4.4 +github.com/opencontainers/image-spec v1.0.1 +github.com/opencontainers/runc 7f24b40cc5423969b4554ef04ba0b00e2b4ba010 +github.com/Microsoft/go-winio v0.4.5 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 -github.com/opencontainers/runtime-spec v1.0.0 +github.com/opencontainers/runtime-spec v1.0.1 github.com/containerd/go-runc ed1cbe1fc31f5fb2359d3a54b6330d1a097858b7 github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 diff --git a/vendor/github.com/Microsoft/go-winio/backup.go b/vendor/github.com/Microsoft/go-winio/backup.go index 27d6ace0c..2be34af43 100644 --- a/vendor/github.com/Microsoft/go-winio/backup.go +++ b/vendor/github.com/Microsoft/go-winio/backup.go @@ -68,10 +68,20 @@ func NewBackupStreamReader(r io.Reader) *BackupStreamReader { return &BackupStreamReader{r, 0} } -// Next returns the next backup stream and prepares for calls to Write(). It skips the remainder of the current stream if +// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if // it was not completely read. func (r *BackupStreamReader) Next() (*BackupHeader, error) { if r.bytesLeft > 0 { + if s, ok := r.r.(io.Seeker); ok { + // Make sure Seek on io.SeekCurrent sometimes succeeds + // before trying the actual seek. + if _, err := s.Seek(0, io.SeekCurrent); err == nil { + if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil { + return nil, err + } + r.bytesLeft = 0 + } + } if _, err := io.Copy(ioutil.Discard, r); err != nil { return nil, err } @@ -220,7 +230,7 @@ type BackupFileWriter struct { ctx uintptr } -// NewBackupFileWrtier returns a new BackupFileWriter from a file handle. If includeSecurity is true, +// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true, // Write() will attempt to restore the security descriptor from the stream. func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter { w := &BackupFileWriter{f, includeSecurity, 0} diff --git a/vendor/github.com/Microsoft/go-winio/ea.go b/vendor/github.com/Microsoft/go-winio/ea.go new file mode 100644 index 000000000..b37e930d6 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/ea.go @@ -0,0 +1,137 @@ +package winio + +import ( + "bytes" + "encoding/binary" + "errors" +) + +type fileFullEaInformation struct { + NextEntryOffset uint32 + Flags uint8 + NameLength uint8 + ValueLength uint16 +} + +var ( + fileFullEaInformationSize = binary.Size(&fileFullEaInformation{}) + + errInvalidEaBuffer = errors.New("invalid extended attribute buffer") + errEaNameTooLarge = errors.New("extended attribute name too large") + errEaValueTooLarge = errors.New("extended attribute value too large") +) + +// ExtendedAttribute represents a single Windows EA. +type ExtendedAttribute struct { + Name string + Value []byte + Flags uint8 +} + +func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { + var info fileFullEaInformation + err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) + if err != nil { + err = errInvalidEaBuffer + return + } + + nameOffset := fileFullEaInformationSize + nameLen := int(info.NameLength) + valueOffset := nameOffset + int(info.NameLength) + 1 + valueLen := int(info.ValueLength) + nextOffset := int(info.NextEntryOffset) + if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { + err = errInvalidEaBuffer + return + } + + ea.Name = string(b[nameOffset : nameOffset+nameLen]) + ea.Value = b[valueOffset : valueOffset+valueLen] + ea.Flags = info.Flags + if info.NextEntryOffset != 0 { + nb = b[info.NextEntryOffset:] + } + return +} + +// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION +// buffer retrieved from BackupRead, ZwQueryEaFile, etc. +func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { + for len(b) != 0 { + ea, nb, err := parseEa(b) + if err != nil { + return nil, err + } + + eas = append(eas, ea) + b = nb + } + return +} + +func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { + if int(uint8(len(ea.Name))) != len(ea.Name) { + return errEaNameTooLarge + } + if int(uint16(len(ea.Value))) != len(ea.Value) { + return errEaValueTooLarge + } + entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value)) + withPadding := (entrySize + 3) &^ 3 + nextOffset := uint32(0) + if !last { + nextOffset = withPadding + } + info := fileFullEaInformation{ + NextEntryOffset: nextOffset, + Flags: ea.Flags, + NameLength: uint8(len(ea.Name)), + ValueLength: uint16(len(ea.Value)), + } + + err := binary.Write(buf, binary.LittleEndian, &info) + if err != nil { + return err + } + + _, err = buf.Write([]byte(ea.Name)) + if err != nil { + return err + } + + err = buf.WriteByte(0) + if err != nil { + return err + } + + _, err = buf.Write(ea.Value) + if err != nil { + return err + } + + _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize]) + if err != nil { + return err + } + + return nil +} + +// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION +// buffer for use with BackupWrite, ZwSetEaFile, etc. +func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) { + var buf bytes.Buffer + for i := range eas { + last := false + if i == len(eas)-1 { + last = true + } + + err := writeEa(&buf, &eas[i], last) + if err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go index 2a311d1f2..57ac3696a 100644 --- a/vendor/github.com/Microsoft/go-winio/file.go +++ b/vendor/github.com/Microsoft/go-winio/file.go @@ -78,6 +78,7 @@ func initIo() { type win32File struct { handle syscall.Handle wg sync.WaitGroup + wgLock sync.RWMutex closing atomicBool readDeadline deadlineHandler writeDeadline deadlineHandler @@ -114,14 +115,18 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { // closeHandle closes the resources associated with a Win32 handle func (f *win32File) closeHandle() { + f.wgLock.Lock() // Atomically set that we are closing, releasing the resources only once. if !f.closing.swap(true) { + f.wgLock.Unlock() // cancel all IO and wait for it to complete cancelIoEx(f.handle, nil) f.wg.Wait() // at this point, no new IO can start syscall.Close(f.handle) f.handle = 0 + } else { + f.wgLock.Unlock() } } @@ -134,10 +139,13 @@ func (f *win32File) Close() error { // prepareIo prepares for a new IO operation. // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. func (f *win32File) prepareIo() (*ioOperation, error) { + f.wgLock.RLock() if f.closing.isSet() { + f.wgLock.RUnlock() return nil, ErrFileClosed } f.wg.Add(1) + f.wgLock.RUnlock() c := &ioOperation{} c.ch = make(chan ioResult) return c, nil diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go index da706cc8a..44340b816 100644 --- a/vendor/github.com/Microsoft/go-winio/pipe.go +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -265,9 +265,9 @@ func (l *win32PipeListener) listenerRoutine() { if err == nil { // Wait for the client to connect. ch := make(chan error) - go func() { + go func(p *win32File) { ch <- connectPipe(p) - }() + }(p) select { case err = <-ch: if err != nil { diff --git a/vendor/github.com/containerd/containerd/README.md b/vendor/github.com/containerd/containerd/README.md index 84d1eec8a..d76ce1b2c 100644 --- a/vendor/github.com/containerd/containerd/README.md +++ b/vendor/github.com/containerd/containerd/README.md @@ -4,6 +4,7 @@ [![Build Status](https://travis-ci.org/containerd/containerd.svg?branch=master)](https://travis-ci.org/containerd/containerd) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd?ref=badge_shield) [![Go Report Card](https://goreportcard.com/badge/github.com/containerd/containerd)](https://goreportcard.com/report/github.com/containerd/containerd) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1271/badge)](https://bestpractices.coreinfrastructure.org/projects/1271) containerd is an industry-standard container runtime with an emphasis on simplicity, robustness and portability. It is available as a daemon for Linux and Windows, which can manage the complete container lifecycle of its host system: image transfer and storage, container execution and supervision, low-level storage and network attachments, etc. @@ -13,7 +14,7 @@ containerd is designed to be embedded into a larger system, rather than being us ## Getting Started -See our documentation on [containerd.io](containerd.io): +See our documentation on [containerd.io](https://containerd.io): * [for ops and admins](docs/ops.md) * [namespaces](docs/namespaces.md) * [client options](docs/client-opts.md) diff --git a/vendor/github.com/containerd/containerd/archive/strconv.go b/vendor/github.com/containerd/containerd/archive/strconv.go new file mode 100644 index 000000000..8f4f26ba8 --- /dev/null +++ b/vendor/github.com/containerd/containerd/archive/strconv.go @@ -0,0 +1,52 @@ +// +build windows + +package archive + +import ( + "strconv" + "strings" + "time" + + "github.com/dmcgowan/go-tar" +) + +// Forked from https://github.com/golang/go/blob/master/src/archive/tar/strconv.go +// as archive/tar doesn't support CreationTime, but does handle PAX time parsing, +// and there's no need to re-invent the wheel. + +// parsePAXTime takes a string of the form %d.%d as described in the PAX +// specification. Note that this implementation allows for negative timestamps, +// which is allowed for by the PAX specification, but not always portable. +func parsePAXTime(s string) (time.Time, error) { + const maxNanoSecondDigits = 9 + + // Split string into seconds and sub-seconds parts. + ss, sn := s, "" + if pos := strings.IndexByte(s, '.'); pos >= 0 { + ss, sn = s[:pos], s[pos+1:] + } + + // Parse the seconds. + secs, err := strconv.ParseInt(ss, 10, 64) + if err != nil { + return time.Time{}, tar.ErrHeader + } + if len(sn) == 0 { + return time.Unix(secs, 0), nil // No sub-second values + } + + // Parse the nanoseconds. + if strings.Trim(sn, "0123456789") != "" { + return time.Time{}, tar.ErrHeader + } + if len(sn) < maxNanoSecondDigits { + sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad + } else { + sn = sn[:maxNanoSecondDigits] // Right truncate + } + nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed + if len(ss) > 0 && ss[0] == '-' { + return time.Unix(secs, -nsecs), nil // Negative correction + } + return time.Unix(secs, nsecs), nil +} diff --git a/vendor/github.com/containerd/containerd/archive/tar.go b/vendor/github.com/containerd/containerd/archive/tar.go index c12d4f280..99cfc35fc 100644 --- a/vendor/github.com/containerd/containerd/archive/tar.go +++ b/vendor/github.com/containerd/containerd/archive/tar.go @@ -87,12 +87,23 @@ const ( // Apply applies a tar stream of an OCI style diff tar. // See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets -func Apply(ctx context.Context, root string, r io.Reader) (int64, error) { +func Apply(ctx context.Context, root string, r io.Reader, opts ...ApplyOpt) (int64, error) { root = filepath.Clean(root) + var options ApplyOptions + for _, opt := range opts { + if err := opt(&options); err != nil { + return 0, errors.Wrap(err, "failed to apply option") + } + } + + return apply(ctx, root, tar.NewReader(r), options) +} + +// applyNaive applies a tar stream of an OCI style diff tar. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets +func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { var ( - tr = tar.NewReader(r) - size int64 dirs []*tar.Header // Used for handling opaque directory markers which @@ -285,161 +296,6 @@ func Apply(ctx context.Context, root string, r io.Reader) (int64, error) { return size, nil } -type changeWriter struct { - tw *tar.Writer - source string - whiteoutT time.Time - inodeSrc map[uint64]string - inodeRefs map[uint64][]string -} - -func newChangeWriter(w io.Writer, source string) *changeWriter { - return &changeWriter{ - tw: tar.NewWriter(w), - source: source, - whiteoutT: time.Now(), - inodeSrc: map[uint64]string{}, - inodeRefs: map[uint64][]string{}, - } -} - -func (cw *changeWriter) HandleChange(k fs.ChangeKind, p string, f os.FileInfo, err error) error { - if err != nil { - return err - } - if k == fs.ChangeKindDelete { - whiteOutDir := filepath.Dir(p) - whiteOutBase := filepath.Base(p) - whiteOut := filepath.Join(whiteOutDir, whiteoutPrefix+whiteOutBase) - hdr := &tar.Header{ - Name: whiteOut[1:], - Size: 0, - ModTime: cw.whiteoutT, - AccessTime: cw.whiteoutT, - ChangeTime: cw.whiteoutT, - } - if err := cw.tw.WriteHeader(hdr); err != nil { - return errors.Wrap(err, "failed to write whiteout header") - } - } else { - var ( - link string - err error - source = filepath.Join(cw.source, p) - ) - - if f.Mode()&os.ModeSymlink != 0 { - if link, err = os.Readlink(source); err != nil { - return err - } - } - - hdr, err := tar.FileInfoHeader(f, link) - if err != nil { - return err - } - - hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) - - name := p - if strings.HasPrefix(name, string(filepath.Separator)) { - name, err = filepath.Rel(string(filepath.Separator), name) - if err != nil { - return errors.Wrap(err, "failed to make path relative") - } - } - name, err = tarName(name) - if err != nil { - return errors.Wrap(err, "cannot canonicalize path") - } - // suffix with '/' for directories - if f.IsDir() && !strings.HasSuffix(name, "/") { - name += "/" - } - hdr.Name = name - - if err := setHeaderForSpecialDevice(hdr, name, f); err != nil { - return errors.Wrap(err, "failed to set device headers") - } - - // additionalLinks stores file names which must be linked to - // this file when this file is added - var additionalLinks []string - inode, isHardlink := fs.GetLinkInfo(f) - if isHardlink { - // If the inode has a source, always link to it - if source, ok := cw.inodeSrc[inode]; ok { - hdr.Typeflag = tar.TypeLink - hdr.Linkname = source - hdr.Size = 0 - } else { - if k == fs.ChangeKindUnmodified { - cw.inodeRefs[inode] = append(cw.inodeRefs[inode], name) - return nil - } - cw.inodeSrc[inode] = name - additionalLinks = cw.inodeRefs[inode] - delete(cw.inodeRefs, inode) - } - } else if k == fs.ChangeKindUnmodified { - // Nothing to write to diff - return nil - } - - if capability, err := getxattr(source, "security.capability"); err != nil { - return errors.Wrap(err, "failed to get capabilities xattr") - } else if capability != nil { - if hdr.PAXRecords == nil { - hdr.PAXRecords = map[string]string{} - } - hdr.PAXRecords[paxSchilyXattr+"security.capability"] = string(capability) - } - - if err := cw.tw.WriteHeader(hdr); err != nil { - return errors.Wrap(err, "failed to write file header") - } - - if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 { - file, err := open(source) - if err != nil { - return errors.Wrapf(err, "failed to open path: %v", source) - } - defer file.Close() - - buf := bufferPool.Get().(*[]byte) - n, err := io.CopyBuffer(cw.tw, file, *buf) - bufferPool.Put(buf) - if err != nil { - return errors.Wrap(err, "failed to copy") - } - if n != hdr.Size { - return errors.New("short write copying file") - } - } - - if additionalLinks != nil { - source = hdr.Name - for _, extra := range additionalLinks { - hdr.Name = extra - hdr.Typeflag = tar.TypeLink - hdr.Linkname = source - hdr.Size = 0 - if err := cw.tw.WriteHeader(hdr); err != nil { - return errors.Wrap(err, "failed to write file header") - } - } - } - } - return nil -} - -func (cw *changeWriter) Close() error { - if err := cw.tw.Close(); err != nil { - return errors.Wrap(err, "failed to close tar writer") - } - return nil -} - func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header, reader io.Reader) error { // hdr.Mode is in linux format, which we can use for syscalls, // but for os.Foo() calls we need the mode converted to os.FileMode, @@ -533,6 +389,194 @@ func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header return chtimes(path, boundTime(latestTime(hdr.AccessTime, hdr.ModTime)), boundTime(hdr.ModTime)) } +type changeWriter struct { + tw *tar.Writer + source string + whiteoutT time.Time + inodeSrc map[uint64]string + inodeRefs map[uint64][]string + addedDirs map[string]struct{} +} + +func newChangeWriter(w io.Writer, source string) *changeWriter { + return &changeWriter{ + tw: tar.NewWriter(w), + source: source, + whiteoutT: time.Now(), + inodeSrc: map[uint64]string{}, + inodeRefs: map[uint64][]string{}, + addedDirs: map[string]struct{}{}, + } +} + +func (cw *changeWriter) HandleChange(k fs.ChangeKind, p string, f os.FileInfo, err error) error { + if err != nil { + return err + } + if k == fs.ChangeKindDelete { + whiteOutDir := filepath.Dir(p) + whiteOutBase := filepath.Base(p) + whiteOut := filepath.Join(whiteOutDir, whiteoutPrefix+whiteOutBase) + hdr := &tar.Header{ + Typeflag: tar.TypeReg, + Name: whiteOut[1:], + Size: 0, + ModTime: cw.whiteoutT, + AccessTime: cw.whiteoutT, + ChangeTime: cw.whiteoutT, + } + if err := cw.tw.WriteHeader(hdr); err != nil { + return errors.Wrap(err, "failed to write whiteout header") + } + } else { + var ( + link string + err error + source = filepath.Join(cw.source, p) + ) + + if f.Mode()&os.ModeSymlink != 0 { + if link, err = os.Readlink(source); err != nil { + return err + } + } + + hdr, err := tar.FileInfoHeader(f, link) + if err != nil { + return err + } + + hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) + + name := p + if strings.HasPrefix(name, string(filepath.Separator)) { + name, err = filepath.Rel(string(filepath.Separator), name) + if err != nil { + return errors.Wrap(err, "failed to make path relative") + } + } + name, err = tarName(name) + if err != nil { + return errors.Wrap(err, "cannot canonicalize path") + } + // suffix with '/' for directories + if f.IsDir() && !strings.HasSuffix(name, "/") { + name += "/" + } + hdr.Name = name + + if err := setHeaderForSpecialDevice(hdr, name, f); err != nil { + return errors.Wrap(err, "failed to set device headers") + } + + // additionalLinks stores file names which must be linked to + // this file when this file is added + var additionalLinks []string + inode, isHardlink := fs.GetLinkInfo(f) + if isHardlink { + // If the inode has a source, always link to it + if source, ok := cw.inodeSrc[inode]; ok { + hdr.Typeflag = tar.TypeLink + hdr.Linkname = source + hdr.Size = 0 + } else { + if k == fs.ChangeKindUnmodified { + cw.inodeRefs[inode] = append(cw.inodeRefs[inode], name) + return nil + } + cw.inodeSrc[inode] = name + additionalLinks = cw.inodeRefs[inode] + delete(cw.inodeRefs, inode) + } + } else if k == fs.ChangeKindUnmodified { + // Nothing to write to diff + return nil + } + + if capability, err := getxattr(source, "security.capability"); err != nil { + return errors.Wrap(err, "failed to get capabilities xattr") + } else if capability != nil { + if hdr.PAXRecords == nil { + hdr.PAXRecords = map[string]string{} + } + hdr.PAXRecords[paxSchilyXattr+"security.capability"] = string(capability) + } + + if err := cw.includeParents(hdr); err != nil { + return err + } + if err := cw.tw.WriteHeader(hdr); err != nil { + return errors.Wrap(err, "failed to write file header") + } + + if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 { + file, err := open(source) + if err != nil { + return errors.Wrapf(err, "failed to open path: %v", source) + } + defer file.Close() + + buf := bufferPool.Get().(*[]byte) + n, err := io.CopyBuffer(cw.tw, file, *buf) + bufferPool.Put(buf) + if err != nil { + return errors.Wrap(err, "failed to copy") + } + if n != hdr.Size { + return errors.New("short write copying file") + } + } + + if additionalLinks != nil { + source = hdr.Name + for _, extra := range additionalLinks { + hdr.Name = extra + hdr.Typeflag = tar.TypeLink + hdr.Linkname = source + hdr.Size = 0 + + if err := cw.includeParents(hdr); err != nil { + return err + } + if err := cw.tw.WriteHeader(hdr); err != nil { + return errors.Wrap(err, "failed to write file header") + } + } + } + } + return nil +} + +func (cw *changeWriter) Close() error { + if err := cw.tw.Close(); err != nil { + return errors.Wrap(err, "failed to close tar writer") + } + return nil +} + +func (cw *changeWriter) includeParents(hdr *tar.Header) error { + name := strings.TrimRight(hdr.Name, "/") + fname := filepath.Join(cw.source, name) + parent := filepath.Dir(name) + pname := filepath.Join(cw.source, parent) + + // Do not include root directory as parent + if fname != cw.source && pname != cw.source { + _, ok := cw.addedDirs[parent] + if !ok { + cw.addedDirs[parent] = struct{}{} + fi, err := os.Stat(pname) + if err != nil { + return err + } + if err := cw.HandleChange(fs.ChangeKindModify, parent, fi, nil); err != nil { + return err + } + } + } + return nil +} + func copyBuffered(ctx context.Context, dst io.Writer, src io.Reader) (written int64, err error) { buf := bufferPool.Get().(*[]byte) defer bufferPool.Put(buf) diff --git a/vendor/github.com/containerd/containerd/archive/tar_opts.go b/vendor/github.com/containerd/containerd/archive/tar_opts.go new file mode 100644 index 000000000..50a260595 --- /dev/null +++ b/vendor/github.com/containerd/containerd/archive/tar_opts.go @@ -0,0 +1,4 @@ +package archive + +// ApplyOpt allows setting mutable archive apply properties on creation +type ApplyOpt func(options *ApplyOptions) error diff --git a/vendor/github.com/containerd/containerd/archive/tar_opts_unix.go b/vendor/github.com/containerd/containerd/archive/tar_opts_unix.go new file mode 100644 index 000000000..717944701 --- /dev/null +++ b/vendor/github.com/containerd/containerd/archive/tar_opts_unix.go @@ -0,0 +1,7 @@ +// +build !windows + +package archive + +// ApplyOptions provides additional options for an Apply operation +type ApplyOptions struct { +} diff --git a/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go b/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go new file mode 100644 index 000000000..baba7eedd --- /dev/null +++ b/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go @@ -0,0 +1,28 @@ +// +build windows + +package archive + +// ApplyOptions provides additional options for an Apply operation +type ApplyOptions struct { + ParentLayerPaths []string // Parent layer paths used for Windows layer apply + IsWindowsContainerLayer bool // True if the tar stream to be applied is a Windows Container Layer +} + +// WithParentLayers adds parent layers to the apply process this is required +// for all Windows layers except the base layer. +func WithParentLayers(parentPaths []string) ApplyOpt { + return func(options *ApplyOptions) error { + options.ParentLayerPaths = parentPaths + return nil + } +} + +// AsWindowsContainerLayer indicates that the tar stream to apply is that of +// a Windows Container Layer. The caller must be holding SeBackupPrivilege and +// SeRestorePrivilege. +func AsWindowsContainerLayer() ApplyOpt { + return func(options *ApplyOptions) error { + options.IsWindowsContainerLayer = true + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/archive/tar_unix.go b/vendor/github.com/containerd/containerd/archive/tar_unix.go index 44b106943..631d08229 100644 --- a/vendor/github.com/containerd/containerd/archive/tar_unix.go +++ b/vendor/github.com/containerd/containerd/archive/tar_unix.go @@ -3,6 +3,7 @@ package archive import ( + "context" "os" "sync" "syscall" @@ -28,11 +29,14 @@ func setHeaderForSpecialDevice(hdr *tar.Header, name string, fi os.FileInfo) err return errors.New("unsupported stat type") } + // Rdev is int32 on darwin/bsd, int64 on linux/solaris + rdev := uint64(s.Rdev) // nolint: unconvert + // Currently go does not fill in the major/minors if s.Mode&syscall.S_IFBLK != 0 || s.Mode&syscall.S_IFCHR != 0 { - hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) - hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) + hdr.Devmajor = int64(unix.Major(rdev)) + hdr.Devminor = int64(unix.Minor(rdev)) } return nil @@ -128,3 +132,9 @@ func getxattr(path, attr string) ([]byte, error) { func setxattr(path, key, value string) error { return sysx.LSetxattr(path, key, []byte(value), 0) } + +// apply applies a tar stream of an OCI style diff tar. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets +func apply(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { + return applyNaive(ctx, root, tr, options) +} diff --git a/vendor/github.com/containerd/containerd/archive/tar_windows.go b/vendor/github.com/containerd/containerd/archive/tar_windows.go index cb3b6c59a..ea2403e4f 100644 --- a/vendor/github.com/containerd/containerd/archive/tar_windows.go +++ b/vendor/github.com/containerd/containerd/archive/tar_windows.go @@ -1,15 +1,55 @@ +// +build windows + package archive import ( + "bufio" + "context" + "encoding/base64" "errors" "fmt" + "io" "os" + "path" + "path/filepath" + "strconv" "strings" + "syscall" + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim" + "github.com/containerd/containerd/log" "github.com/containerd/containerd/sys" "github.com/dmcgowan/go-tar" ) +const ( + // MSWINDOWS pax vendor extensions + hdrMSWindowsPrefix = "MSWINDOWS." + + hdrFileAttributes = hdrMSWindowsPrefix + "fileattr" + hdrSecurityDescriptor = hdrMSWindowsPrefix + "sd" + hdrRawSecurityDescriptor = hdrMSWindowsPrefix + "rawsd" + hdrMountPoint = hdrMSWindowsPrefix + "mountpoint" + hdrEaPrefix = hdrMSWindowsPrefix + "xattr." + + // LIBARCHIVE pax vendor extensions + hdrLibArchivePrefix = "LIBARCHIVE." + + hdrCreateTime = hdrLibArchivePrefix + "creationtime" +) + +var ( + // mutatedFiles is a list of files that are mutated by the import process + // and must be backed up and restored. + mutatedFiles = map[string]string{ + "UtilityVM/Files/EFI/Microsoft/Boot/BCD": "bcd.bak", + "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG": "bcd.log.bak", + "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak", + "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak", + } +) + // tarName returns platform-specific filepath // to canonical posix-style path for tar archival. p is relative // path. @@ -101,3 +141,286 @@ func setxattr(path, key, value string) error { // since xattrs should not exist in windows diff archives return errors.New("xattrs not supported on Windows") } + +// apply applies a tar stream of an OCI style diff tar of a Windows layer. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets +func apply(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { + if options.IsWindowsContainerLayer { + return applyWindowsLayer(ctx, root, tr, options) + } + return applyNaive(ctx, root, tr, options) +} + +// applyWindowsLayer applies a tar stream of an OCI style diff tar of a Windows layer. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets +func applyWindowsLayer(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { + home, id := filepath.Split(root) + info := hcsshim.DriverInfo{ + HomeDir: home, + } + + w, err := hcsshim.NewLayerWriter(info, id, options.ParentLayerPaths) + if err != nil { + return 0, err + } + defer func() { + if err := w.Close(); err != nil { + log.G(ctx).Errorf("failed to close layer writer: %v", err) + } + }() + + buf := bufio.NewWriter(nil) + hdr, nextErr := tr.Next() + // Iterate through the files in the archive. + for { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + + if nextErr == io.EOF { + // end of tar archive + break + } + if nextErr != nil { + return 0, nextErr + } + + // Note: path is used instead of filepath to prevent OS specific handling + // of the tar path + base := path.Base(hdr.Name) + if strings.HasPrefix(base, whiteoutPrefix) { + dir := path.Dir(hdr.Name) + originalBase := base[len(whiteoutPrefix):] + originalPath := path.Join(dir, originalBase) + if err := w.Remove(filepath.FromSlash(originalPath)); err != nil { + return 0, err + } + hdr, nextErr = tr.Next() + } else if hdr.Typeflag == tar.TypeLink { + err := w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)) + if err != nil { + return 0, err + } + hdr, nextErr = tr.Next() + } else { + name, fileSize, fileInfo, err := fileInfoFromHeader(hdr) + if err != nil { + return 0, err + } + if err := w.Add(filepath.FromSlash(name), fileInfo); err != nil { + return 0, err + } + size += fileSize + hdr, nextErr = tarToBackupStreamWithMutatedFiles(buf, w, tr, hdr, root) + } + } + + return +} + +// fileInfoFromHeader retrieves basic Win32 file information from a tar header, using the additional metadata written by +// WriteTarFileFromBackupStream. +func fileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *winio.FileBasicInfo, err error) { + name = hdr.Name + if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA { + size = hdr.Size + } + fileInfo = &winio.FileBasicInfo{ + LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()), + LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()), + ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()), + + // Default CreationTime to ModTime, updated below if MSWINDOWS.createtime exists + CreationTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()), + } + if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok { + attr, err := strconv.ParseUint(attrStr, 10, 32) + if err != nil { + return "", 0, nil, err + } + fileInfo.FileAttributes = uintptr(attr) + } else { + if hdr.Typeflag == tar.TypeDir { + fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY + } + } + if createStr, ok := hdr.PAXRecords[hdrCreateTime]; ok { + createTime, err := parsePAXTime(createStr) + if err != nil { + return "", 0, nil, err + } + fileInfo.CreationTime = syscall.NsecToFiletime(createTime.UnixNano()) + } + return +} + +// tarToBackupStreamWithMutatedFiles reads data from a tar stream and +// writes it to a backup stream, and also saves any files that will be mutated +// by the import layer process to a backup location. +func tarToBackupStreamWithMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) { + var ( + bcdBackup *os.File + bcdBackupWriter *winio.BackupFileWriter + ) + if backupPath, ok := mutatedFiles[hdr.Name]; ok { + bcdBackup, err = os.Create(filepath.Join(root, backupPath)) + if err != nil { + return nil, err + } + defer func() { + cerr := bcdBackup.Close() + if err == nil { + err = cerr + } + }() + + bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false) + defer func() { + cerr := bcdBackupWriter.Close() + if err == nil { + err = cerr + } + }() + + buf.Reset(io.MultiWriter(w, bcdBackupWriter)) + } else { + buf.Reset(w) + } + + defer func() { + ferr := buf.Flush() + if err == nil { + err = ferr + } + }() + + return writeBackupStreamFromTarFile(buf, t, hdr) +} + +// writeBackupStreamFromTarFile writes a Win32 backup stream from the current tar file. Since this function may process multiple +// tar file entries in order to collect all the alternate data streams for the file, it returns the next +// tar file that was not processed, or io.EOF is there are no more. +func writeBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) { + bw := winio.NewBackupStreamWriter(w) + var sd []byte + var err error + // Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written + // by this library will have raw binary for the security descriptor. + if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { + sd, err = winio.SddlToSecurityDescriptor(sddl) + if err != nil { + return nil, err + } + } + if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok { + sd, err = base64.StdEncoding.DecodeString(sdraw) + if err != nil { + return nil, err + } + } + if len(sd) != 0 { + bhdr := winio.BackupHeader{ + Id: winio.BackupSecurity, + Size: int64(len(sd)), + } + err := bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = bw.Write(sd) + if err != nil { + return nil, err + } + } + var eas []winio.ExtendedAttribute + for k, v := range hdr.PAXRecords { + if !strings.HasPrefix(k, hdrEaPrefix) { + continue + } + data, err := base64.StdEncoding.DecodeString(v) + if err != nil { + return nil, err + } + eas = append(eas, winio.ExtendedAttribute{ + Name: k[len(hdrEaPrefix):], + Value: data, + }) + } + if len(eas) != 0 { + eadata, err := winio.EncodeExtendedAttributes(eas) + if err != nil { + return nil, err + } + bhdr := winio.BackupHeader{ + Id: winio.BackupEaData, + Size: int64(len(eadata)), + } + err = bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = bw.Write(eadata) + if err != nil { + return nil, err + } + } + if hdr.Typeflag == tar.TypeSymlink { + _, isMountPoint := hdr.PAXRecords[hdrMountPoint] + rp := winio.ReparsePoint{ + Target: filepath.FromSlash(hdr.Linkname), + IsMountPoint: isMountPoint, + } + reparse := winio.EncodeReparsePoint(&rp) + bhdr := winio.BackupHeader{ + Id: winio.BackupReparseData, + Size: int64(len(reparse)), + } + err := bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = bw.Write(reparse) + if err != nil { + return nil, err + } + } + if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA { + bhdr := winio.BackupHeader{ + Id: winio.BackupData, + Size: hdr.Size, + } + err := bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = io.Copy(bw, t) + if err != nil { + return nil, err + } + } + // Copy all the alternate data streams and return the next non-ADS header. + for { + ahdr, err := t.Next() + if err != nil { + return nil, err + } + if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") { + return ahdr, nil + } + bhdr := winio.BackupHeader{ + Id: winio.BackupAlternateData, + Size: ahdr.Size, + Name: ahdr.Name[len(hdr.Name):] + ":$DATA", + } + err = bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = io.Copy(bw, t) + if err != nil { + return nil, err + } + } +} diff --git a/vendor/github.com/containerd/containerd/cio/io.go b/vendor/github.com/containerd/containerd/cio/io.go index 25e398147..1b4a4dc25 100644 --- a/vendor/github.com/containerd/containerd/cio/io.go +++ b/vendor/github.com/containerd/containerd/cio/io.go @@ -8,7 +8,7 @@ import ( "sync" ) -// Config holds the io configurations. +// Config holds the IO configurations. type Config struct { // Terminal is true if one has been allocated Terminal bool @@ -24,48 +24,17 @@ type Config struct { type IO interface { // Config returns the IO configuration. Config() Config - // Cancel aborts all current io operations + // Cancel aborts all current io operations. Cancel() - // Wait blocks until all io copy operations have completed + // Wait blocks until all io copy operations have completed. Wait() - // Close cleans up all open io resources + // Close cleans up all open io resources. Cancel() is always called before + // Close() Close() error } -// cio is a basic container IO implementation. -type cio struct { - config Config - - closer *wgCloser -} - -func (c *cio) Config() Config { - return c.config -} - -func (c *cio) Cancel() { - if c.closer == nil { - return - } - c.closer.Cancel() -} - -func (c *cio) Wait() { - if c.closer == nil { - return - } - c.closer.Wait() -} - -func (c *cio) Close() error { - if c.closer == nil { - return nil - } - return c.closer.Close() -} - -// Creation creates new IO sets for a task -type Creation func(id string) (IO, error) +// Creator creates new IO sets for a task +type Creator func(id string) (IO, error) // Attach allows callers to reattach to running tasks // @@ -74,123 +43,138 @@ type Creation func(id string) (IO, error) // will be sent only to the first reads type Attach func(*FIFOSet) (IO, error) -// NewIO returns an Creation that will provide IO sets without a terminal -func NewIO(stdin io.Reader, stdout, stderr io.Writer) Creation { - return NewIOWithTerminal(stdin, stdout, stderr, false) -} - -// NewIOWithTerminal creates a new io set with the provied io.Reader/Writers for use with a terminal -func NewIOWithTerminal(stdin io.Reader, stdout, stderr io.Writer, terminal bool) Creation { - return func(id string) (_ IO, err error) { - paths, err := NewFifos(id) - if err != nil { - return nil, err - } - defer func() { - if err != nil && paths.Dir != "" { - os.RemoveAll(paths.Dir) - } - }() - cfg := Config{ - Terminal: terminal, - Stdout: paths.Out, - Stderr: paths.Err, - Stdin: paths.In, - } - i := &cio{config: cfg} - set := &ioSet{ - in: stdin, - out: stdout, - err: stderr, - } - closer, err := copyIO(paths, set, cfg.Terminal) - if err != nil { - return nil, err - } - i.closer = closer - return i, nil - } -} - -// WithAttach attaches the existing io for a task to the provided io.Reader/Writers -func WithAttach(stdin io.Reader, stdout, stderr io.Writer) Attach { - return func(paths *FIFOSet) (IO, error) { - if paths == nil { - return nil, fmt.Errorf("cannot attach to existing fifos") - } - cfg := Config{ - Terminal: paths.Terminal, - Stdout: paths.Out, - Stderr: paths.Err, - Stdin: paths.In, - } - i := &cio{config: cfg} - set := &ioSet{ - in: stdin, - out: stdout, - err: stderr, - } - closer, err := copyIO(paths, set, cfg.Terminal) - if err != nil { - return nil, err - } - i.closer = closer - return i, nil - } -} - -// Stdio returns an IO set to be used for a task -// that outputs the container's IO as the current processes Stdio -func Stdio(id string) (IO, error) { - return NewIO(os.Stdin, os.Stdout, os.Stderr)(id) -} - -// StdioTerminal will setup the IO for the task to use a terminal -func StdioTerminal(id string) (IO, error) { - return NewIOWithTerminal(os.Stdin, os.Stdout, os.Stderr, true)(id) -} - -// NullIO redirects the container's IO into /dev/null -func NullIO(id string) (IO, error) { - return &cio{}, nil -} - -// FIFOSet is a set of fifos for use with tasks +// FIFOSet is a set of file paths to FIFOs for a task's standard IO streams type FIFOSet struct { - // Dir is the directory holding the task fifos - Dir string - // In, Out, and Err fifo paths - In, Out, Err string - // Terminal returns true if a terminal is being used for the task - Terminal bool + Config + close func() error } -type ioSet struct { - in io.Reader - out, err io.Writer -} - -type wgCloser struct { - wg *sync.WaitGroup - dir string - set []io.Closer - cancel context.CancelFunc -} - -func (g *wgCloser) Wait() { - g.wg.Wait() -} - -func (g *wgCloser) Close() error { - for _, f := range g.set { - f.Close() - } - if g.dir != "" { - return os.RemoveAll(g.dir) +// Close the FIFOSet +func (f *FIFOSet) Close() error { + if f.close != nil { + return f.close() } return nil } -func (g *wgCloser) Cancel() { - g.cancel() +// NewFIFOSet returns a new FIFOSet from a Config and a close function +func NewFIFOSet(config Config, close func() error) *FIFOSet { + return &FIFOSet{Config: config, close: close} } + +// Streams used to configure a Creator or Attach +type Streams struct { + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer + Terminal bool +} + +// Opt customize options for creating a Creator or Attach +type Opt func(*Streams) + +// WithStdio sets stream options to the standard input/output streams +func WithStdio(opt *Streams) { + WithStreams(os.Stdin, os.Stdout, os.Stderr)(opt) +} + +// WithTerminal sets the terminal option +func WithTerminal(opt *Streams) { + opt.Terminal = true +} + +// WithStreams sets the stream options to the specified Reader and Writers +func WithStreams(stdin io.Reader, stdout, stderr io.Writer) Opt { + return func(opt *Streams) { + opt.Stdin = stdin + opt.Stdout = stdout + opt.Stderr = stderr + } +} + +// NewCreator returns an IO creator from the options +func NewCreator(opts ...Opt) Creator { + streams := &Streams{} + for _, opt := range opts { + opt(streams) + } + return func(id string) (IO, error) { + // TODO: accept root as a param + root := "/run/containerd/fifo" + fifos, err := NewFIFOSetInDir(root, id, streams.Terminal) + if err != nil { + return nil, err + } + return copyIO(fifos, streams) + } +} + +// NewAttach attaches the existing io for a task to the provided io.Reader/Writers +func NewAttach(opts ...Opt) Attach { + streams := &Streams{} + for _, opt := range opts { + opt(streams) + } + return func(fifos *FIFOSet) (IO, error) { + if fifos == nil { + return nil, fmt.Errorf("cannot attach, missing fifos") + } + return copyIO(fifos, streams) + } +} + +// NullIO redirects the container's IO into /dev/null +func NullIO(_ string) (IO, error) { + return &cio{}, nil +} + +// cio is a basic container IO implementation. +type cio struct { + config Config + wg *sync.WaitGroup + closers []io.Closer + cancel context.CancelFunc +} + +func (c *cio) Config() Config { + return c.config +} + +func (c *cio) Wait() { + if c.wg != nil { + c.wg.Wait() + } +} + +func (c *cio) Close() error { + var lastErr error + for _, closer := range c.closers { + if closer == nil { + continue + } + if err := closer.Close(); err != nil { + lastErr = err + } + } + return lastErr +} + +func (c *cio) Cancel() { + if c.cancel != nil { + c.cancel() + } +} + +type pipes struct { + Stdin io.WriteCloser + Stdout io.ReadCloser + Stderr io.ReadCloser +} + +// DirectIO allows task IO to be handled externally by the caller +type DirectIO struct { + pipes + cio +} + +var _ IO = &DirectIO{} diff --git a/vendor/github.com/containerd/containerd/cio/io_unix.go b/vendor/github.com/containerd/containerd/cio/io_unix.go index c18f7ecf9..005fb0ce9 100644 --- a/vendor/github.com/containerd/containerd/cio/io_unix.go +++ b/vendor/github.com/containerd/containerd/cio/io_unix.go @@ -12,173 +12,115 @@ import ( "syscall" "github.com/containerd/fifo" + "github.com/pkg/errors" ) -// NewFifos returns a new set of fifos for the task -func NewFifos(id string) (*FIFOSet, error) { - root := "/run/containerd/fifo" - if err := os.MkdirAll(root, 0700); err != nil { - return nil, err +// NewFIFOSetInDir returns a new FIFOSet with paths in a temporary directory under root +func NewFIFOSetInDir(root, id string, terminal bool) (*FIFOSet, error) { + if root != "" { + if err := os.MkdirAll(root, 0700); err != nil { + return nil, err + } } dir, err := ioutil.TempDir(root, "") if err != nil { return nil, err } - return &FIFOSet{ - Dir: dir, - In: filepath.Join(dir, id+"-stdin"), - Out: filepath.Join(dir, id+"-stdout"), - Err: filepath.Join(dir, id+"-stderr"), - }, nil + closer := func() error { + return os.RemoveAll(dir) + } + return NewFIFOSet(Config{ + Stdin: filepath.Join(dir, id+"-stdin"), + Stdout: filepath.Join(dir, id+"-stdout"), + Stderr: filepath.Join(dir, id+"-stderr"), + Terminal: terminal, + }, closer), nil } -func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) { - var ( - f io.ReadWriteCloser - set []io.Closer - ctx, cancel = context.WithCancel(context.Background()) - wg = &sync.WaitGroup{} - ) - defer func() { - if err != nil { - for _, f := range set { - f.Close() - } - cancel() - } - }() - - if f, err = fifo.OpenFifo(ctx, fifos.In, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - return nil, err - } - set = append(set, f) - go func(w io.WriteCloser) { - io.Copy(w, ioset.in) - w.Close() - }(f) - - if f, err = fifo.OpenFifo(ctx, fifos.Out, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - return nil, err - } - set = append(set, f) - wg.Add(1) - go func(r io.ReadCloser) { - io.Copy(ioset.out, r) - r.Close() - wg.Done() - }(f) - - if f, err = fifo.OpenFifo(ctx, fifos.Err, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - return nil, err - } - set = append(set, f) - - if !tty { - wg.Add(1) - go func(r io.ReadCloser) { - io.Copy(ioset.err, r) - r.Close() - wg.Done() - }(f) - } - return &wgCloser{ - wg: wg, - dir: fifos.Dir, - set: set, - cancel: cancel, - }, nil -} - -// NewDirectIO returns an IO implementation that exposes the pipes directly -func NewDirectIO(ctx context.Context, terminal bool) (*DirectIO, error) { - set, err := NewFifos("") +func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { + var ctx, cancel = context.WithCancel(context.Background()) + pipes, err := openFifos(ctx, fifos) if err != nil { + cancel() return nil, err } - f := &DirectIO{ - set: set, - terminal: terminal, + + if fifos.Stdin != "" { + go func() { + io.Copy(pipes.Stdin, ioset.Stdin) + pipes.Stdin.Close() + }() } + + var wg = &sync.WaitGroup{} + wg.Add(1) + go func() { + io.Copy(ioset.Stdout, pipes.Stdout) + pipes.Stdout.Close() + wg.Done() + }() + + if !fifos.Terminal { + wg.Add(1) + go func() { + io.Copy(ioset.Stderr, pipes.Stderr) + pipes.Stderr.Close() + wg.Done() + }() + } + return &cio{ + config: fifos.Config, + wg: wg, + closers: append(pipes.closers(), fifos), + cancel: cancel, + }, nil +} + +func openFifos(ctx context.Context, fifos *FIFOSet) (pipes, error) { + var err error defer func() { if err != nil { - f.Delete() + fifos.Close() } }() - if f.Stdin, err = fifo.OpenFifo(ctx, set.In, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - return nil, err + + var f pipes + if fifos.Stdin != "" { + if f.Stdin, err = fifo.OpenFifo(ctx, fifos.Stdin, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { + return f, errors.Wrapf(err, "failed to open stdin fifo") + } } - if f.Stdout, err = fifo.OpenFifo(ctx, set.Out, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - f.Stdin.Close() - return nil, err + if fifos.Stdout != "" { + if f.Stdout, err = fifo.OpenFifo(ctx, fifos.Stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { + f.Stdin.Close() + return f, errors.Wrapf(err, "failed to open stdout fifo") + } } - if f.Stderr, err = fifo.OpenFifo(ctx, set.Err, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - f.Stdin.Close() - f.Stdout.Close() - return nil, err + if fifos.Stderr != "" { + if f.Stderr, err = fifo.OpenFifo(ctx, fifos.Stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { + f.Stdin.Close() + f.Stdout.Close() + return f, errors.Wrapf(err, "failed to open stderr fifo") + } } return f, nil } -// DirectIO allows task IO to be handled externally by the caller -type DirectIO struct { - Stdin io.WriteCloser - Stdout io.ReadCloser - Stderr io.ReadCloser - - set *FIFOSet - terminal bool +// NewDirectIO returns an IO implementation that exposes the IO streams as io.ReadCloser +// and io.WriteCloser. +func NewDirectIO(ctx context.Context, fifos *FIFOSet) (*DirectIO, error) { + ctx, cancel := context.WithCancel(ctx) + pipes, err := openFifos(ctx, fifos) + return &DirectIO{ + pipes: pipes, + cio: cio{ + config: fifos.Config, + closers: append(pipes.closers(), fifos), + cancel: cancel, + }, + }, err } -// IOCreate returns IO avaliable for use with task creation -func (f *DirectIO) IOCreate(id string) (IO, error) { - return f, nil -} - -// IOAttach returns IO avaliable for use with task attachment -func (f *DirectIO) IOAttach(set *FIFOSet) (IO, error) { - return f, nil -} - -// Config returns the Config -func (f *DirectIO) Config() Config { - return Config{ - Terminal: f.terminal, - Stdin: f.set.In, - Stdout: f.set.Out, - Stderr: f.set.Err, - } -} - -// Cancel stops any IO copy operations -// -// Not applicable for DirectIO -func (f *DirectIO) Cancel() { - // nothing to cancel as all operations are handled externally -} - -// Wait on any IO copy operations -// -// Not applicable for DirectIO -func (f *DirectIO) Wait() { - // nothing to wait on as all operations are handled externally -} - -// Close closes all open fds -func (f *DirectIO) Close() error { - err := f.Stdin.Close() - if err2 := f.Stdout.Close(); err == nil { - err = err2 - } - if err2 := f.Stderr.Close(); err == nil { - err = err2 - } - return err -} - -// Delete removes the underlying directory containing fifos -func (f *DirectIO) Delete() error { - if f.set.Dir == "" { - return nil - } - return os.RemoveAll(f.set.Dir) +func (p *pipes) closers() []io.Closer { + return []io.Closer{p.Stdin, p.Stdout, p.Stderr} } diff --git a/vendor/github.com/containerd/containerd/cio/io_windows.go b/vendor/github.com/containerd/containerd/cio/io_windows.go index 1458c3173..017c9a11f 100644 --- a/vendor/github.com/containerd/containerd/cio/io_windows.go +++ b/vendor/github.com/containerd/containerd/cio/io_windows.go @@ -13,25 +13,26 @@ import ( const pipeRoot = `\\.\pipe` -// NewFifos returns a new set of fifos for the task -func NewFifos(id string) (*FIFOSet, error) { - return &FIFOSet{ - In: fmt.Sprintf(`%s\ctr-%s-stdin`, pipeRoot, id), - Out: fmt.Sprintf(`%s\ctr-%s-stdout`, pipeRoot, id), - Err: fmt.Sprintf(`%s\ctr-%s-stderr`, pipeRoot, id), - }, nil +// NewFIFOSetInDir returns a new set of fifos for the task +func NewFIFOSetInDir(_, id string, terminal bool) (*FIFOSet, error) { + return NewFIFOSet(Config{ + Terminal: terminal, + Stdin: fmt.Sprintf(`%s\ctr-%s-stdin`, pipeRoot, id), + Stdout: fmt.Sprintf(`%s\ctr-%s-stdout`, pipeRoot, id), + Stderr: fmt.Sprintf(`%s\ctr-%s-stderr`, pipeRoot, id), + }, nil), nil } -func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) { +func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { var ( wg sync.WaitGroup set []io.Closer ) - if fifos.In != "" { - l, err := winio.ListenPipe(fifos.In, nil) + if fifos.Stdin != "" { + l, err := winio.ListenPipe(fifos.Stdin, nil) if err != nil { - return nil, errors.Wrapf(err, "failed to create stdin pipe %s", fifos.In) + return nil, errors.Wrapf(err, "failed to create stdin pipe %s", fifos.Stdin) } defer func(l net.Listener) { if err != nil { @@ -43,19 +44,19 @@ func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) { go func() { c, err := l.Accept() if err != nil { - log.L.WithError(err).Errorf("failed to accept stdin connection on %s", fifos.In) + log.L.WithError(err).Errorf("failed to accept stdin connection on %s", fifos.Stdin) return } - io.Copy(c, ioset.in) + io.Copy(c, ioset.Stdin) c.Close() l.Close() }() } - if fifos.Out != "" { - l, err := winio.ListenPipe(fifos.Out, nil) + if fifos.Stdout != "" { + l, err := winio.ListenPipe(fifos.Stdout, nil) if err != nil { - return nil, errors.Wrapf(err, "failed to create stdin pipe %s", fifos.Out) + return nil, errors.Wrapf(err, "failed to create stdin pipe %s", fifos.Stdout) } defer func(l net.Listener) { if err != nil { @@ -69,19 +70,19 @@ func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) { defer wg.Done() c, err := l.Accept() if err != nil { - log.L.WithError(err).Errorf("failed to accept stdout connection on %s", fifos.Out) + log.L.WithError(err).Errorf("failed to accept stdout connection on %s", fifos.Stdout) return } - io.Copy(ioset.out, c) + io.Copy(ioset.Stdout, c) c.Close() l.Close() }() } - if !tty && fifos.Err != "" { - l, err := winio.ListenPipe(fifos.Err, nil) + if !fifos.Terminal && fifos.Stderr != "" { + l, err := winio.ListenPipe(fifos.Stderr, nil) if err != nil { - return nil, errors.Wrapf(err, "failed to create stderr pipe %s", fifos.Err) + return nil, errors.Wrapf(err, "failed to create stderr pipe %s", fifos.Stderr) } defer func(l net.Listener) { if err != nil { @@ -95,23 +96,29 @@ func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) { defer wg.Done() c, err := l.Accept() if err != nil { - log.L.WithError(err).Errorf("failed to accept stderr connection on %s", fifos.Err) + log.L.WithError(err).Errorf("failed to accept stderr connection on %s", fifos.Stderr) return } - io.Copy(ioset.err, c) + io.Copy(ioset.Stderr, c) c.Close() l.Close() }() } - return &wgCloser{ - wg: &wg, - dir: fifos.Dir, - set: set, - cancel: func() { - for _, l := range set { - l.Close() - } - }, - }, nil + return &cio{config: fifos.Config, closers: set}, nil +} + +// NewDirectIO returns an IO implementation that exposes the IO streams as io.ReadCloser +// and io.WriteCloser. +func NewDirectIO(stdin io.WriteCloser, stdout, stderr io.ReadCloser, terminal bool) *DirectIO { + return &DirectIO{ + pipes: pipes{ + Stdin: stdin, + Stdout: stdout, + Stderr: stderr, + }, + cio: cio{ + config: Config{Terminal: terminal}, + }, + } } diff --git a/vendor/github.com/containerd/containerd/container.go b/vendor/github.com/containerd/containerd/container.go index 716e0456c..ad60c69ea 100644 --- a/vendor/github.com/containerd/containerd/container.go +++ b/vendor/github.com/containerd/containerd/container.go @@ -3,6 +3,7 @@ package containerd import ( "context" "encoding/json" + "os" "path/filepath" "strings" @@ -26,7 +27,7 @@ type Container interface { // Delete removes the container Delete(context.Context, ...DeleteOpts) error // NewTask creates a new task based on the container metadata - NewTask(context.Context, cio.Creation, ...NewTaskOpts) (Task, error) + NewTask(context.Context, cio.Creator, ...NewTaskOpts) (Task, error) // Spec returns the OCI runtime specification Spec(context.Context) (*specs.Spec, error) // Task returns the current task for the container @@ -162,7 +163,7 @@ func (c *container) Image(ctx context.Context) (Image, error) { }, nil } -func (c *container) NewTask(ctx context.Context, ioCreate cio.Creation, opts ...NewTaskOpts) (_ Task, err error) { +func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...NewTaskOpts) (_ Task, err error) { i, err := ioCreate(c.id) if err != nil { return nil, err @@ -288,20 +289,23 @@ func (c *container) get(ctx context.Context) (containers.Container, error) { return c.client.ContainerService().Get(ctx, c.id) } +// get the existing fifo paths from the task information stored by the daemon func attachExistingIO(response *tasks.GetResponse, ioAttach cio.Attach) (cio.IO, error) { - // get the existing fifo paths from the task information stored by the daemon - paths := &cio.FIFOSet{ - Dir: getFifoDir([]string{ - response.Process.Stdin, - response.Process.Stdout, - response.Process.Stderr, - }), - In: response.Process.Stdin, - Out: response.Process.Stdout, - Err: response.Process.Stderr, - Terminal: response.Process.Terminal, + path := getFifoDir([]string{ + response.Process.Stdin, + response.Process.Stdout, + response.Process.Stderr, + }) + closer := func() error { + return os.RemoveAll(path) } - return ioAttach(paths) + fifoSet := cio.NewFIFOSet(cio.Config{ + Stdin: response.Process.Stdin, + Stdout: response.Process.Stdout, + Stderr: response.Process.Stderr, + Terminal: response.Process.Terminal, + }, closer) + return ioAttach(fifoSet) } // getFifoDir looks for any non-empty path for a stdio fifo diff --git a/vendor/github.com/containerd/containerd/container_opts_unix.go b/vendor/github.com/containerd/containerd/container_opts_unix.go index b678033b7..deda0f70f 100644 --- a/vendor/github.com/containerd/containerd/container_opts_unix.go +++ b/vendor/github.com/containerd/containerd/container_opts_unix.go @@ -6,7 +6,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" "syscall" @@ -16,6 +15,7 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" + "github.com/containerd/containerd/linux/runctypes" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/platforms" "github.com/gogo/protobuf/proto" @@ -166,7 +166,7 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool if err != nil { return err } - if err := remapRootFS(mounts, uid, gid); err != nil { + if err := remapRootFS(ctx, mounts, uid, gid); err != nil { snapshotter.Remove(ctx, usernsID) return err } @@ -187,22 +187,10 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool } } -func remapRootFS(mounts []mount.Mount, uid, gid uint32) error { - root, err := ioutil.TempDir("", "ctd-remap") - if err != nil { - return err - } - defer os.Remove(root) - for _, m := range mounts { - if err := m.Mount(root); err != nil { - return err - } - } - err = filepath.Walk(root, incrementFS(root, uid, gid)) - if uerr := mount.Unmount(root, 0); err == nil { - err = uerr - } - return err +func remapRootFS(ctx context.Context, mounts []mount.Mount, uid, gid uint32) error { + return mount.WithTempMount(ctx, mounts, func(root string) error { + return filepath.Walk(root, incrementFS(root, uid, gid)) + }) } func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc { @@ -218,3 +206,19 @@ func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc { return os.Lchown(path, u, g) } } + +// WithNoPivotRoot instructs the runtime not to you pivot_root +func WithNoPivotRoot(_ context.Context, _ *Client, info *TaskInfo) error { + if info.Options == nil { + info.Options = &runctypes.CreateOptions{ + NoPivotRoot: true, + } + return nil + } + copts, ok := info.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("invalid options type, expected runctypes.CreateOptions") + } + copts.NoPivotRoot = true + return nil +} diff --git a/vendor/github.com/containerd/containerd/content/helpers.go b/vendor/github.com/containerd/containerd/content/helpers.go index 86b853685..83c31d917 100644 --- a/vendor/github.com/containerd/containerd/content/helpers.go +++ b/vendor/github.com/containerd/containerd/content/helpers.go @@ -77,7 +77,7 @@ func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected dige r, err = seekReader(r, ws.Offset, size) if err != nil { if !isUnseekable(err) { - return errors.Wrapf(err, "unabled to resume write to %v", ws.Ref) + return errors.Wrapf(err, "unable to resume write to %v", ws.Ref) } // reader is unseekable, try to move the writer back to the start. diff --git a/vendor/github.com/containerd/containerd/content/local/store_unix.go b/vendor/github.com/containerd/containerd/content/local/store_unix.go index c0587e1b2..464970611 100644 --- a/vendor/github.com/containerd/containerd/content/local/store_unix.go +++ b/vendor/github.com/containerd/containerd/content/local/store_unix.go @@ -12,8 +12,7 @@ import ( func getATime(fi os.FileInfo) time.Time { if st, ok := fi.Sys().(*syscall.Stat_t); ok { - return time.Unix(int64(sys.StatAtime(st).Sec), - int64(sys.StatAtime(st).Nsec)) + return sys.StatATimeAsTime(st) } return fi.ModTime() diff --git a/vendor/github.com/containerd/containerd/content/local/writer.go b/vendor/github.com/containerd/containerd/content/local/writer.go index fbf39f755..e6b4276b4 100644 --- a/vendor/github.com/containerd/containerd/content/local/writer.go +++ b/vendor/github.com/containerd/containerd/content/local/writer.go @@ -2,6 +2,7 @@ package local import ( "context" + "io" "os" "path/filepath" "runtime" @@ -167,5 +168,8 @@ func (w *writer) Truncate(size int64) error { } w.offset = 0 w.digester.Hash().Reset() + if _, err := w.fp.Seek(0, io.SeekStart); err != nil { + return err + } return w.fp.Truncate(0) } diff --git a/vendor/github.com/containerd/containerd/diff/walking/differ.go b/vendor/github.com/containerd/containerd/diff/walking/differ.go index b1afbe930..eb6c2df70 100644 --- a/vendor/github.com/containerd/containerd/diff/walking/differ.go +++ b/vendor/github.com/containerd/containerd/diff/walking/differ.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "io/ioutil" - "os" "strings" "time" @@ -90,60 +89,49 @@ func (s *walkingDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts } } - dir, err := ioutil.TempDir("", "extract-") - if err != nil { - return emptyDesc, errors.Wrap(err, "failed to create temporary directory") - } - // We change RemoveAll to Remove so that we either leak a temp dir - // if it fails but not RM snapshot data. refer to #1868 #1785 - defer os.Remove(dir) - - if err := mount.All(mounts, dir); err != nil { - return emptyDesc, errors.Wrap(err, "failed to mount") - } - defer func() { - if uerr := mount.Unmount(dir, 0); uerr != nil { - if err == nil { - err = uerr - } - } - }() - - ra, err := s.store.ReaderAt(ctx, desc.Digest) - if err != nil { - return emptyDesc, errors.Wrap(err, "failed to get reader from content store") - } - defer ra.Close() - - r := content.NewReader(ra) - if isCompressed { - ds, err := compression.DecompressStream(r) + var ocidesc ocispec.Descriptor + if err := mount.WithTempMount(ctx, mounts, func(root string) error { + ra, err := s.store.ReaderAt(ctx, desc.Digest) if err != nil { - return emptyDesc, err + return errors.Wrap(err, "failed to get reader from content store") } - defer ds.Close() - r = ds - } + defer ra.Close() - digester := digest.Canonical.Digester() - rc := &readCounter{ - r: io.TeeReader(r, digester.Hash()), - } + r := content.NewReader(ra) + if isCompressed { + ds, err := compression.DecompressStream(r) + if err != nil { + return err + } + defer ds.Close() + r = ds + } - if _, err := archive.Apply(ctx, dir, rc); err != nil { + digester := digest.Canonical.Digester() + rc := &readCounter{ + r: io.TeeReader(r, digester.Hash()), + } + + if _, err := archive.Apply(ctx, root, rc); err != nil { + return err + } + + // Read any trailing data + if _, err := io.Copy(ioutil.Discard, rc); err != nil { + return err + } + + ocidesc = ocispec.Descriptor{ + MediaType: ocispec.MediaTypeImageLayer, + Size: rc.c, + Digest: digester.Digest(), + } + return nil + + }); err != nil { return emptyDesc, err } - - // Read any trailing data - if _, err := io.Copy(ioutil.Discard, rc); err != nil { - return emptyDesc, err - } - - return ocispec.Descriptor{ - MediaType: ocispec.MediaTypeImageLayer, - Size: rc.c, - Digest: digester.Digest(), - }, nil + return ocidesc, nil } // DiffMounts creates a diff between the given mounts and uploads the result @@ -168,108 +156,85 @@ func (s *walkingDiff) DiffMounts(ctx context.Context, lower, upper []mount.Mount default: return emptyDesc, errors.Wrapf(errdefs.ErrNotImplemented, "unsupported diff media type: %v", config.MediaType) } - aDir, err := ioutil.TempDir("", "left-") - if err != nil { - return emptyDesc, errors.Wrap(err, "failed to create temporary directory") - } - defer os.Remove(aDir) - bDir, err := ioutil.TempDir("", "right-") - if err != nil { - return emptyDesc, errors.Wrap(err, "failed to create temporary directory") - } - defer os.Remove(bDir) - - if err := mount.All(lower, aDir); err != nil { - return emptyDesc, errors.Wrap(err, "failed to mount") - } - defer func() { - if uerr := mount.Unmount(aDir, 0); uerr != nil { - if err == nil { - err = uerr + var ocidesc ocispec.Descriptor + if err := mount.WithTempMount(ctx, lower, func(lowerRoot string) error { + return mount.WithTempMount(ctx, upper, func(upperRoot string) error { + var newReference bool + if config.Reference == "" { + newReference = true + config.Reference = uniqueRef() } - } - }() - if err := mount.All(upper, bDir); err != nil { - return emptyDesc, errors.Wrap(err, "failed to mount") - } - defer func() { - if uerr := mount.Unmount(bDir, 0); uerr != nil { - if err == nil { - err = uerr + cw, err := s.store.Writer(ctx, config.Reference, 0, "") + if err != nil { + return errors.Wrap(err, "failed to open writer") } - } - }() - - var newReference bool - if config.Reference == "" { - newReference = true - config.Reference = uniqueRef() - } - - cw, err := s.store.Writer(ctx, config.Reference, 0, "") - if err != nil { - return emptyDesc, errors.Wrap(err, "failed to open writer") - } - defer func() { - if err != nil { - cw.Close() - if newReference { - if err := s.store.Abort(ctx, config.Reference); err != nil { - log.G(ctx).WithField("ref", config.Reference).Warnf("failed to delete diff upload") + defer func() { + if err != nil { + cw.Close() + if newReference { + if err := s.store.Abort(ctx, config.Reference); err != nil { + log.G(ctx).WithField("ref", config.Reference).Warnf("failed to delete diff upload") + } + } + } + }() + if !newReference { + if err := cw.Truncate(0); err != nil { + return err } } - } - }() - if !newReference { - if err := cw.Truncate(0); err != nil { - return emptyDesc, err - } + + if isCompressed { + dgstr := digest.SHA256.Digester() + compressed, err := compression.CompressStream(cw, compression.Gzip) + if err != nil { + return errors.Wrap(err, "failed to get compressed stream") + } + err = archive.WriteDiff(ctx, io.MultiWriter(compressed, dgstr.Hash()), lowerRoot, upperRoot) + compressed.Close() + if err != nil { + return errors.Wrap(err, "failed to write compressed diff") + } + + if config.Labels == nil { + config.Labels = map[string]string{} + } + config.Labels["containerd.io/uncompressed"] = dgstr.Digest().String() + } else { + if err = archive.WriteDiff(ctx, cw, lowerRoot, upperRoot); err != nil { + return errors.Wrap(err, "failed to write diff") + } + } + + var commitopts []content.Opt + if config.Labels != nil { + commitopts = append(commitopts, content.WithLabels(config.Labels)) + } + + dgst := cw.Digest() + if err := cw.Commit(ctx, 0, dgst, commitopts...); err != nil { + return errors.Wrap(err, "failed to commit") + } + + info, err := s.store.Info(ctx, dgst) + if err != nil { + return errors.Wrap(err, "failed to get info from content store") + } + + ocidesc = ocispec.Descriptor{ + MediaType: config.MediaType, + Size: info.Size, + Digest: info.Digest, + } + return nil + }) + }); err != nil { + return emptyDesc, err } - if isCompressed { - dgstr := digest.SHA256.Digester() - compressed, err := compression.CompressStream(cw, compression.Gzip) - if err != nil { - return emptyDesc, errors.Wrap(err, "failed to get compressed stream") - } - err = archive.WriteDiff(ctx, io.MultiWriter(compressed, dgstr.Hash()), aDir, bDir) - compressed.Close() - if err != nil { - return emptyDesc, errors.Wrap(err, "failed to write compressed diff") - } - - if config.Labels == nil { - config.Labels = map[string]string{} - } - config.Labels["containerd.io/uncompressed"] = dgstr.Digest().String() - } else { - if err = archive.WriteDiff(ctx, cw, aDir, bDir); err != nil { - return emptyDesc, errors.Wrap(err, "failed to write diff") - } - } - - var commitopts []content.Opt - if config.Labels != nil { - commitopts = append(commitopts, content.WithLabels(config.Labels)) - } - - dgst := cw.Digest() - if err := cw.Commit(ctx, 0, dgst, commitopts...); err != nil { - return emptyDesc, errors.Wrap(err, "failed to commit") - } - - info, err := s.store.Info(ctx, dgst) - if err != nil { - return emptyDesc, errors.Wrap(err, "failed to get info from content store") - } - - return ocispec.Descriptor{ - MediaType: config.MediaType, - Size: info.Size, - Digest: info.Digest, - }, nil + return ocidesc, nil } type readCounter struct { diff --git a/vendor/github.com/containerd/containerd/events/events.go b/vendor/github.com/containerd/containerd/events/events.go index 2bfedb1e8..6f4c8b3af 100644 --- a/vendor/github.com/containerd/containerd/events/events.go +++ b/vendor/github.com/containerd/containerd/events/events.go @@ -26,9 +26,9 @@ func (e *Envelope) Field(fieldpath []string) (string, bool) { switch fieldpath[0] { // unhandled: timestamp case "namespace": - return string(e.Namespace), len(e.Namespace) > 0 + return e.Namespace, len(e.Namespace) > 0 case "topic": - return string(e.Topic), len(e.Topic) > 0 + return e.Topic, len(e.Topic) > 0 case "event": decoded, err := typeurl.UnmarshalAny(e.Event) if err != nil { diff --git a/vendor/github.com/containerd/containerd/fs/diff.go b/vendor/github.com/containerd/containerd/fs/diff.go index 3a53f4215..9073d0d92 100644 --- a/vendor/github.com/containerd/containerd/fs/diff.go +++ b/vendor/github.com/containerd/containerd/fs/diff.go @@ -222,10 +222,8 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err c1 = make(chan *currentPath) c2 = make(chan *currentPath) - f1, f2 *currentPath - rmdir string - lastEmittedDir = string(filepath.Separator) - parents []os.FileInfo + f1, f2 *currentPath + rmdir string ) g.Go(func() error { defer close(c1) @@ -260,10 +258,7 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err continue } - var ( - f os.FileInfo - emit = true - ) + var f os.FileInfo k, p := pathChange(f1, f2) switch k { case ChangeKindAdd: @@ -299,35 +294,13 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err f2 = nil if same { if !isLinked(f) { - emit = false + continue } k = ChangeKindUnmodified } } - if emit { - emittedDir, emitParents := commonParents(lastEmittedDir, p, parents) - for _, pf := range emitParents { - p := filepath.Join(emittedDir, pf.Name()) - if err := changeFn(ChangeKindUnmodified, p, pf, nil); err != nil { - return err - } - emittedDir = p - } - - if err := changeFn(k, p, f, nil); err != nil { - return err - } - - if f != nil && f.IsDir() { - lastEmittedDir = p - } else { - lastEmittedDir = emittedDir - } - - parents = parents[:0] - } else if f.IsDir() { - lastEmittedDir, parents = commonParents(lastEmittedDir, p, parents) - parents = append(parents, f) + if err := changeFn(k, p, f, nil); err != nil { + return err } } return nil @@ -335,47 +308,3 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err return g.Wait() } - -func commonParents(base, updated string, dirs []os.FileInfo) (string, []os.FileInfo) { - if basePrefix := makePrefix(base); strings.HasPrefix(updated, basePrefix) { - var ( - parents []os.FileInfo - last = base - ) - for _, d := range dirs { - next := filepath.Join(last, d.Name()) - if strings.HasPrefix(updated, makePrefix(last)) { - parents = append(parents, d) - last = next - } else { - break - } - } - return base, parents - } - - baseS := strings.Split(base, string(filepath.Separator)) - updatedS := strings.Split(updated, string(filepath.Separator)) - commonS := []string{string(filepath.Separator)} - - min := len(baseS) - if len(updatedS) < min { - min = len(updatedS) - } - for i := 0; i < min; i++ { - if baseS[i] == updatedS[i] { - commonS = append(commonS, baseS[i]) - } else { - break - } - } - - return filepath.Join(commonS...), []os.FileInfo{} -} - -func makePrefix(d string) string { - if d == "" || d[len(d)-1] != filepath.Separator { - return d + string(filepath.Separator) - } - return d -} diff --git a/vendor/github.com/containerd/containerd/fs/du_unix.go b/vendor/github.com/containerd/containerd/fs/du_unix.go index 6328e80f3..fe3426d27 100644 --- a/vendor/github.com/containerd/containerd/fs/du_unix.go +++ b/vendor/github.com/containerd/containerd/fs/du_unix.go @@ -15,6 +15,15 @@ type inode struct { dev, ino uint64 } +func newInode(stat *syscall.Stat_t) inode { + return inode{ + // Dev is uint32 on darwin/bsd, uint64 on linux/solaris + dev: uint64(stat.Dev), // nolint: unconvert + // Ino is uint32 on bsd, uint64 on darwin/linux/solaris + ino: uint64(stat.Ino), // nolint: unconvert + } +} + func diskUsage(roots ...string) (Usage, error) { var ( @@ -28,9 +37,7 @@ func diskUsage(roots ...string) (Usage, error) { return err } - stat := fi.Sys().(*syscall.Stat_t) - - inoKey := inode{dev: uint64(stat.Dev), ino: uint64(stat.Ino)} + inoKey := newInode(fi.Sys().(*syscall.Stat_t)) if _, ok := inodes[inoKey]; !ok { inodes[inoKey] = struct{}{} size += fi.Size() @@ -60,9 +67,7 @@ func diffUsage(ctx context.Context, a, b string) (Usage, error) { } if kind == ChangeKindAdd || kind == ChangeKindModify { - stat := fi.Sys().(*syscall.Stat_t) - - inoKey := inode{dev: uint64(stat.Dev), ino: uint64(stat.Ino)} + inoKey := newInode(fi.Sys().(*syscall.Stat_t)) if _, ok := inodes[inoKey]; !ok { inodes[inoKey] = struct{}{} size += fi.Size() diff --git a/vendor/github.com/containerd/containerd/fs/hardlink_unix.go b/vendor/github.com/containerd/containerd/fs/hardlink_unix.go index 3b825c940..a6f99778d 100644 --- a/vendor/github.com/containerd/containerd/fs/hardlink_unix.go +++ b/vendor/github.com/containerd/containerd/fs/hardlink_unix.go @@ -13,5 +13,6 @@ func getLinkInfo(fi os.FileInfo) (uint64, bool) { return 0, false } - return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 + // Ino is uint32 on bsd, uint64 on darwin/linux/solaris + return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 // nolint: unconvert } diff --git a/vendor/github.com/containerd/containerd/fs/path.go b/vendor/github.com/containerd/containerd/fs/path.go index 412da6711..13fb82638 100644 --- a/vendor/github.com/containerd/containerd/fs/path.go +++ b/vendor/github.com/containerd/containerd/fs/path.go @@ -71,9 +71,9 @@ func sameFile(f1, f2 *currentPath) (bool, error) { return false, nil } - // If the timestamp may have been truncated in one of the + // If the timestamp may have been truncated in both of the // files, check content of file to determine difference - if t1.Nanosecond() == 0 || t2.Nanosecond() == 0 { + if t1.Nanosecond() == 0 && t2.Nanosecond() == 0 { var eq bool if (f1.f.Mode() & os.ModeSymlink) == os.ModeSymlink { eq, err = compareSymlinkTarget(f1.fullPath, f2.fullPath) diff --git a/vendor/github.com/containerd/containerd/gc/gc.go b/vendor/github.com/containerd/containerd/gc/gc.go index 66898c5de..b5aad39e6 100644 --- a/vendor/github.com/containerd/containerd/gc/gc.go +++ b/vendor/github.com/containerd/containerd/gc/gc.go @@ -8,6 +8,7 @@ package gc import ( "context" "sync" + "time" ) // ResourceType represents type of resource at a node @@ -21,6 +22,11 @@ type Node struct { Key string } +// Stats about a garbage collection run +type Stats interface { + Elapsed() time.Duration +} + // Tricolor implements basic, single-thread tri-color GC. Given the roots, the // complete set and a refs function, this function returns a map of all // reachable objects. diff --git a/vendor/github.com/containerd/containerd/images/image.go b/vendor/github.com/containerd/containerd/images/image.go index 7b4215faf..3f58e0620 100644 --- a/vendor/github.com/containerd/containerd/images/image.go +++ b/vendor/github.com/containerd/containerd/images/image.go @@ -357,13 +357,5 @@ func RootFS(ctx context.Context, provider content.Provider, configDesc ocispec.D if err := json.Unmarshal(p, &config); err != nil { return nil, err } - - // TODO(stevvooe): Remove this bit when OCI structure uses correct type for - // rootfs.DiffIDs. - var diffIDs []digest.Digest - for _, diffID := range config.RootFS.DiffIDs { - diffIDs = append(diffIDs, digest.Digest(diffID)) - } - - return diffIDs, nil + return config.RootFS.DiffIDs, nil } diff --git a/vendor/github.com/containerd/containerd/metadata/content.go b/vendor/github.com/containerd/containerd/metadata/content.go index c13f7867e..57eabf307 100644 --- a/vendor/github.com/containerd/containerd/metadata/content.go +++ b/vendor/github.com/containerd/containerd/metadata/content.go @@ -318,12 +318,23 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe cs.l.RLock() defer cs.l.RUnlock() - var w content.Writer + var ( + w content.Writer + exists bool + ) if err := update(ctx, cs.db, func(tx *bolt.Tx) error { if expected != "" { cbkt := getBlobBucket(tx, ns, expected) if cbkt != nil { - return errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", expected) + // Add content to lease to prevent other reference removals + // from effecting this object during a provided lease + if err := addContentLease(ctx, tx, expected); err != nil { + return errors.Wrap(err, "unable to lease content") + } + // Return error outside of transaction to ensure + // commit succeeds with the lease. + exists = true + return nil } } @@ -363,6 +374,9 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe }); err != nil { return nil, err } + if exists { + return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", expected) + } // TODO: keep the expected in the writer to use on commit // when no expected is provided there. diff --git a/vendor/github.com/containerd/containerd/metadata/db.go b/vendor/github.com/containerd/containerd/metadata/db.go index 8be62a95c..5e830ad82 100644 --- a/vendor/github.com/containerd/containerd/metadata/db.go +++ b/vendor/github.com/containerd/containerd/metadata/db.go @@ -204,7 +204,7 @@ func (m *DB) Update(fn func(*bolt.Tx) error) error { // RegisterMutationCallback registers a function to be called after a metadata // mutations has been performed. // -// The callback function in an argument for whether a deletion has occurred +// The callback function is an argument for whether a deletion has occurred // since the last garbage collection. func (m *DB) RegisterMutationCallback(fn func(bool)) { m.dirtyL.Lock() @@ -219,15 +219,20 @@ type GCStats struct { SnapshotD map[string]time.Duration } +// Elapsed returns the duration which elapsed during a collection +func (s GCStats) Elapsed() time.Duration { + return s.MetaD +} + // GarbageCollect starts garbage collection -func (m *DB) GarbageCollect(ctx context.Context) (stats GCStats, err error) { +func (m *DB) GarbageCollect(ctx context.Context) (gc.Stats, error) { m.wlock.Lock() t1 := time.Now() marked, err := m.getMarked(ctx) if err != nil { m.wlock.Unlock() - return GCStats{}, err + return nil, err } m.dirtyL.Lock() @@ -259,9 +264,10 @@ func (m *DB) GarbageCollect(ctx context.Context) (stats GCStats, err error) { }); err != nil { m.dirtyL.Unlock() m.wlock.Unlock() - return GCStats{}, err + return nil, err } + var stats GCStats var wg sync.WaitGroup if len(m.dirtySS) > 0 { @@ -303,7 +309,7 @@ func (m *DB) GarbageCollect(ctx context.Context) (stats GCStats, err error) { wg.Wait() - return + return stats, err } func (m *DB) getMarked(ctx context.Context) (map[gc.Node]struct{}, error) { diff --git a/vendor/github.com/containerd/containerd/metadata/images.go b/vendor/github.com/containerd/containerd/metadata/images.go index 070439a8c..8bf0c6173 100644 --- a/vendor/github.com/containerd/containerd/metadata/images.go +++ b/vendor/github.com/containerd/containerd/metadata/images.go @@ -275,7 +275,7 @@ func writeImage(bkt *bolt.Bucket, image *images.Image) error { } // write the target bucket - tbkt, err := bkt.CreateBucketIfNotExists([]byte(bucketKeyTarget)) + tbkt, err := bkt.CreateBucketIfNotExists(bucketKeyTarget) if err != nil { return err } diff --git a/vendor/github.com/containerd/containerd/mount/mount.go b/vendor/github.com/containerd/containerd/mount/mount.go index 94c2c9f92..323e6e2fb 100644 --- a/vendor/github.com/containerd/containerd/mount/mount.go +++ b/vendor/github.com/containerd/containerd/mount/mount.go @@ -1,5 +1,14 @@ package mount +import ( + "context" + "io/ioutil" + "os" + + "github.com/containerd/containerd/log" + "github.com/pkg/errors" +) + // Mount is the lingua franca of containerd. A mount represents a // serialized mount syscall. Components either emit or consume mounts. type Mount struct { @@ -22,3 +31,42 @@ func All(mounts []Mount, target string) error { } return nil } + +// WithTempMount mounts the provided mounts to a temp dir, and pass the temp dir to f. +// The mounts are valid during the call to the f. +// Finally we will unmount and remove the temp dir regardless of the result of f. +func WithTempMount(ctx context.Context, mounts []Mount, f func(root string) error) (err error) { + root, uerr := ioutil.TempDir("", "containerd-WithTempMount") + if uerr != nil { + return errors.Wrapf(uerr, "failed to create temp dir") + } + // We use Remove here instead of RemoveAll. + // The RemoveAll will delete the temp dir and all children it contains. + // When the Unmount fails, RemoveAll will incorrectly delete data from + // the mounted dir. However, if we use Remove, even though we won't + // successfully delete the temp dir and it may leak, we won't loss data + // from the mounted dir. + // For details, please refer to #1868 #1785. + defer func() { + if uerr = os.Remove(root); uerr != nil { + log.G(ctx).WithError(uerr).WithField("dir", root).Errorf("failed to remove mount temp dir") + } + }() + + // We should do defer first, if not we will not do Unmount when only a part of Mounts are failed. + defer func() { + if uerr = UnmountAll(root, 0); uerr != nil { + uerr = errors.Wrapf(uerr, "failed to unmount %s", root) + if err == nil { + err = uerr + } else { + err = errors.Wrap(err, uerr.Error()) + } + } + }() + if uerr = All(mounts, root); uerr != nil { + return errors.Wrapf(uerr, "failed to mount %s", root) + } + + return errors.Wrapf(f(root), "mount callback failed on %s", root) +} diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go b/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go index 865aff29a..ebc423d3e 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go +++ b/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go @@ -6,7 +6,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" "strconv" @@ -271,49 +270,35 @@ func WithUserID(uid uint32) SpecOpts { if err != nil { return err } - root, err := ioutil.TempDir("", "ctd-username") - if err != nil { - return err - } - defer os.Remove(root) - for _, m := range mounts { - if err := m.Mount(root); err != nil { + + return mount.WithTempMount(ctx, mounts, func(root string) error { + ppath, err := fs.RootPath(root, "/etc/passwd") + if err != nil { return err } - } - defer func() { - if uerr := mount.Unmount(root, 0); uerr != nil { - if err == nil { - err = uerr + f, err := os.Open(ppath) + if err != nil { + if os.IsNotExist(err) { + s.Process.User.UID, s.Process.User.GID = uid, uid + return nil } + return err } - }() - ppath, err := fs.RootPath(root, "/etc/passwd") - if err != nil { - return err - } - f, err := os.Open(ppath) - if err != nil { - if os.IsNotExist(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 } - 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 + u := users[0] + s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid) return nil - } - u := users[0] - s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid) - return nil + }) } } @@ -334,43 +319,28 @@ func WithUsername(username string) SpecOpts { if err != nil { return err } - root, err := ioutil.TempDir("", "ctd-username") - if err != nil { - return err - } - defer os.Remove(root) - for _, m := range mounts { - if err := m.Mount(root); err != nil { + return mount.WithTempMount(ctx, mounts, func(root string) error { + ppath, err := fs.RootPath(root, "/etc/passwd") + if err != nil { return err } - } - defer func() { - if uerr := mount.Unmount(root, 0); uerr != nil { - if err == nil { - err = uerr - } + f, err := os.Open(ppath) + if err != nil { + return err } - }() - 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 { - return u.Name == username + defer f.Close() + users, err := user.ParsePasswdFilter(f, 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) + return nil }) - 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) - return nil } } diff --git a/vendor/github.com/containerd/containerd/platforms/cpuinfo.go b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go new file mode 100644 index 000000000..b7c23cc19 --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go @@ -0,0 +1,85 @@ +package platforms + +import ( + "bufio" + "os" + "runtime" + "strings" + + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" + "github.com/pkg/errors" +) + +// Present the ARM instruction set architecture, eg: v7, v8 +var cpuVariant string + +func init() { + if isArmArch(runtime.GOARCH) { + cpuVariant = getCPUVariant() + } else { + cpuVariant = "" + } +} + +// For Linux, the kernel has already detected the ABI, ISA and Features. +// So we don't need to access the ARM registers to detect platform information +// by ourselves. We can just parse these information from /proc/cpuinfo +func getCPUInfo(pattern string) (info string, err error) { + if !isLinuxOS(runtime.GOOS) { + return "", errors.Wrapf(errdefs.ErrNotImplemented, "getCPUInfo for OS %s", runtime.GOOS) + } + + cpuinfo, err := os.Open("/proc/cpuinfo") + if err != nil { + return "", err + } + defer cpuinfo.Close() + + // Start to Parse the Cpuinfo line by line. For SMP SoC, we parse + // the first core is enough. + scanner := bufio.NewScanner(cpuinfo) + for scanner.Scan() { + newline := scanner.Text() + list := strings.Split(newline, ":") + + if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) { + return strings.TrimSpace(list[1]), nil + } + } + + // Check whether the scanner encountered errors + err = scanner.Err() + if err != nil { + return "", err + } + + return "", errors.Wrapf(errdefs.ErrNotFound, "getCPUInfo for pattern: %s", pattern) +} + +func getCPUVariant() string { + variant, err := getCPUInfo("Cpu architecture") + if err != nil { + log.L.WithError(err).Error("failure getting variant") + return "" + } + + switch variant { + case "8": + variant = "v8" + case "7", "7M", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)": + variant = "v7" + case "6", "6TEJ": + variant = "v6" + case "5", "5T", "5TE", "5TEJ": + variant = "v5" + case "4", "4T": + variant = "v4" + case "3": + variant = "v3" + default: + variant = "unknown" + } + + return variant +} diff --git a/vendor/github.com/containerd/containerd/platforms/database.go b/vendor/github.com/containerd/containerd/platforms/database.go index bd66e2517..362b005a6 100644 --- a/vendor/github.com/containerd/containerd/platforms/database.go +++ b/vendor/github.com/containerd/containerd/platforms/database.go @@ -5,6 +5,13 @@ import ( "strings" ) +// isLinuxOS returns true if the operating system is Linux. +// +// The OS value should be normalized before calling this function. +func isLinuxOS(os string) bool { + return os == "linux" +} + // These function are generated from from https://golang.org/src/go/build/syslist.go. // // We use switch statements because they are slightly faster than map lookups @@ -21,6 +28,17 @@ func isKnownOS(os string) bool { return false } +// isArmArch returns true if the architecture is ARM. +// +// The arch value should be normalized before being passed to this function. +func isArmArch(arch string) bool { + switch arch { + case "arm", "arm64": + return true + } + return false +} + // isKnownArch returns true if we know about the architecture. // // The arch value should be normalized before being passed to this function. diff --git a/vendor/github.com/containerd/containerd/platforms/defaults.go b/vendor/github.com/containerd/containerd/platforms/defaults.go index 2b57b4979..ed493ab2c 100644 --- a/vendor/github.com/containerd/containerd/platforms/defaults.go +++ b/vendor/github.com/containerd/containerd/platforms/defaults.go @@ -16,6 +16,7 @@ func DefaultSpec() specs.Platform { return specs.Platform{ OS: runtime.GOOS, Architecture: runtime.GOARCH, - // TODO(stevvooe): Need to resolve GOARM for arm hosts. + // The Variant field will be empty if arch != ARM. + Variant: cpuVariant, } } diff --git a/vendor/github.com/containerd/containerd/process.go b/vendor/github.com/containerd/containerd/process.go index ad1a2a1f4..32049cf07 100644 --- a/vendor/github.com/containerd/containerd/process.go +++ b/vendor/github.com/containerd/containerd/process.go @@ -191,6 +191,7 @@ func (p *process) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (*ExitS return nil, errdefs.FromGRPC(err) } if p.io != nil { + p.io.Cancel() p.io.Wait() p.io.Close() } diff --git a/vendor/github.com/containerd/containerd/remotes/hints.go b/vendor/github.com/containerd/containerd/remotes/hints.go deleted file mode 100644 index 1694477a5..000000000 --- a/vendor/github.com/containerd/containerd/remotes/hints.go +++ /dev/null @@ -1,31 +0,0 @@ -package remotes - -import "strings" - -// HintExists returns true if a hint of the provided kind and values exists in -// the set of provided hints. -func HintExists(kind, value string, hints ...string) bool { - for _, hint := range hints { - if strings.HasPrefix(hint, kind) && strings.HasSuffix(hint, value) { - return true - } - } - - return false -} - -// HintValues returns a slice of the values of the hints that match kind. -func HintValues(kind string, hints ...string) []string { - var values []string - for _, hint := range hints { - if strings.HasPrefix(hint, kind) { - parts := strings.SplitN(hint, ":", 2) - if len(parts) < 2 { - continue - } - values = append(values, parts[1]) - } - } - - return values -} diff --git a/vendor/github.com/containerd/containerd/rootfs/diff.go b/vendor/github.com/containerd/containerd/rootfs/diff.go index bab7a3cca..89ed71d03 100644 --- a/vendor/github.com/containerd/containerd/rootfs/diff.go +++ b/vendor/github.com/containerd/containerd/rootfs/diff.go @@ -39,7 +39,7 @@ func Diff(ctx context.Context, snapshotID string, sn snapshots.Snapshotter, d di if err != nil { return ocispec.Descriptor{}, err } - defer sn.Remove(ctx, lowerKey) + defer sn.Remove(ctx, upperKey) } return d.DiffMounts(ctx, lower, upper, opts...) diff --git a/vendor/github.com/containerd/containerd/sys/stat_bsd.go b/vendor/github.com/containerd/containerd/sys/stat_bsd.go index e043ae52b..84c59ab6f 100644 --- a/vendor/github.com/containerd/containerd/sys/stat_bsd.go +++ b/vendor/github.com/containerd/containerd/sys/stat_bsd.go @@ -4,6 +4,7 @@ package sys import ( "syscall" + "time" ) // StatAtime returns the access time from a stat struct @@ -20,3 +21,8 @@ func StatCtime(st *syscall.Stat_t) syscall.Timespec { func StatMtime(st *syscall.Stat_t) syscall.Timespec { return st.Mtimespec } + +// StatATimeAsTime returns the access time as a time.Time +func StatATimeAsTime(st *syscall.Stat_t) time.Time { + return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) // nolint: unconvert +} diff --git a/vendor/github.com/containerd/containerd/sys/stat_unix.go b/vendor/github.com/containerd/containerd/sys/stat_unix.go index 1f983a98d..799b9b997 100644 --- a/vendor/github.com/containerd/containerd/sys/stat_unix.go +++ b/vendor/github.com/containerd/containerd/sys/stat_unix.go @@ -4,6 +4,7 @@ package sys import ( "syscall" + "time" ) // StatAtime returns the Atim @@ -20,3 +21,8 @@ func StatCtime(st *syscall.Stat_t) syscall.Timespec { func StatMtime(st *syscall.Stat_t) syscall.Timespec { return st.Mtim } + +// StatATimeAsTime returns st.Atim as a time.Time +func StatATimeAsTime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Atim.Sec, st.Atim.Nsec) +} diff --git a/vendor/github.com/containerd/containerd/task.go b/vendor/github.com/containerd/containerd/task.go index 8d256834f..121da9af5 100644 --- a/vendor/github.com/containerd/containerd/task.go +++ b/vendor/github.com/containerd/containerd/task.go @@ -123,7 +123,7 @@ type Task interface { // Resume the execution of the task Resume(context.Context) error // Exec creates a new process inside the task - Exec(context.Context, string, *specs.Process, cio.Creation) (Process, error) + Exec(context.Context, string, *specs.Process, cio.Creator) (Process, error) // Pids returns a list of system specific process ids inside the task Pids(context.Context) ([]ProcessInfo, error) // Checkpoint serializes the runtime and memory information of a task into an @@ -163,6 +163,7 @@ func (t *task) Start(ctx context.Context) error { ContainerID: t.id, }) if err != nil { + t.io.Cancel() t.io.Close() return errdefs.FromGRPC(err) } @@ -277,7 +278,7 @@ func (t *task) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (*ExitStat return &ExitStatus{code: r.ExitStatus, exitedAt: r.ExitedAt}, nil } -func (t *task) Exec(ctx context.Context, id string, spec *specs.Process, ioCreate cio.Creation) (_ Process, err error) { +func (t *task) Exec(ctx context.Context, id string, spec *specs.Process, ioCreate cio.Creator) (_ Process, err error) { if id == "" { return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "exec id must not be empty") } diff --git a/vendor/github.com/containerd/containerd/vendor.conf b/vendor/github.com/containerd/containerd/vendor.conf index fea47e480..a185cd7b7 100644 --- a/vendor/github.com/containerd/containerd/vendor.conf +++ b/vendor/github.com/containerd/containerd/vendor.conf @@ -15,8 +15,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.0 github.com/docker/go-units v0.3.1 github.com/gogo/protobuf v0.5 github.com/golang/protobuf 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9 -github.com/opencontainers/runtime-spec v1.0.0 -github.com/opencontainers/runc 74a17296470088de3805e138d3d87c62e613dfc4 +github.com/opencontainers/runtime-spec v1.0.1 +github.com/opencontainers/runc 7f24b40cc5423969b4554ef04ba0b00e2b4ba010 github.com/sirupsen/logrus v1.0.0 github.com/containerd/btrfs cc52c4dea2ce11a44e6639e561bb5c2af9ada9e3 github.com/stretchr/testify v1.1.4 @@ -25,20 +25,19 @@ github.com/pmezard/go-difflib v1.0.0 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 -google.golang.org/grpc v1.7.2 +google.golang.org/grpc v1.7.4 github.com/pkg/errors v0.8.0 github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448 golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys -github.com/opencontainers/image-spec v1.0.0 +github.com/opencontainers/image-spec v1.0.1 github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c github.com/BurntSushi/toml v0.2.0-21-g9906417 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 -github.com/Microsoft/go-winio v0.4.4 +github.com/Microsoft/go-winio v0.4.5 github.com/Microsoft/hcsshim v0.6.7 -github.com/Microsoft/opengcs v0.3.2 github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 github.com/dmcgowan/go-tar go1.10 -github.com/stevvooe/ttrpc 76e68349ad9ab4d03d764c713826d31216715e4f +github.com/stevvooe/ttrpc d2710463e497617f16f26d1e715a3308609e7982 diff --git a/vendor/github.com/opencontainers/image-spec/README.md b/vendor/github.com/opencontainers/image-spec/README.md index 5ab5554e4..31e827865 100644 --- a/vendor/github.com/opencontainers/image-spec/README.md +++ b/vendor/github.com/opencontainers/image-spec/README.md @@ -51,7 +51,7 @@ Find more [FAQ on the OCI site](https://www.opencontainers.org/faq). ## Roadmap -The [GitHub milestones](https://github.com/opencontainers/image-spec/milestones) lay out the path to the OCI v1.0.0 release in late 2016. +The [GitHub milestones](https://github.com/opencontainers/image-spec/milestones) lay out the path to the future improvements. # Contributing diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/version.go b/vendor/github.com/opencontainers/image-spec/specs-go/version.go index e3eee29b4..5d493df23 100644 --- a/vendor/github.com/opencontainers/image-spec/specs-go/version.go +++ b/vendor/github.com/opencontainers/image-spec/specs-go/version.go @@ -22,7 +22,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 0 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 0 + VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "" diff --git a/vendor/github.com/opencontainers/runc/README.md b/vendor/github.com/opencontainers/runc/README.md index eabfb982b..3ca7a1a22 100644 --- a/vendor/github.com/opencontainers/runc/README.md +++ b/vendor/github.com/opencontainers/runc/README.md @@ -56,7 +56,7 @@ make BUILDTAGS='seccomp apparmor' |-----------|------------------------------------|-------------| | seccomp | Syscall filtering | libseccomp | | selinux | selinux process and mount labeling | | -| apparmor | apparmor profile support | libapparmor | +| apparmor | apparmor profile support | | | ambient | ambient capability support | kernel 4.3 | diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/sysconfig.go b/vendor/github.com/opencontainers/runc/libcontainer/system/sysconfig.go index b3a07cba3..b8434f105 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/system/sysconfig.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/system/sysconfig.go @@ -1,4 +1,4 @@ -// +build cgo,linux cgo,freebsd +// +build cgo,linux package system diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go deleted file mode 100644 index 4a8d00acb..000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go +++ /dev/null @@ -1,38 +0,0 @@ -// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris - -package user - -import ( - "io" - "syscall" -) - -func GetPasswdPath() (string, error) { - return "", ErrUnsupported -} - -func GetPasswd() (io.ReadCloser, error) { - return nil, ErrUnsupported -} - -func GetGroupPath() (string, error) { - return "", ErrUnsupported -} - -func GetGroup() (io.ReadCloser, error) { - return nil, ErrUnsupported -} - -// CurrentUser looks up the current user by their user id in /etc/passwd. If the -// user cannot be found (or there is no /etc/passwd file on the filesystem), -// then CurrentUser returns an error. -func CurrentUser() (User, error) { - return LookupUid(syscall.Getuid()) -} - -// CurrentGroup looks up the current user's group by their primary group id's -// entry in /etc/passwd. If the group cannot be found (or there is no -// /etc/group file on the filesystem), then CurrentGroup returns an error. -func CurrentGroup() (Group, error) { - return LookupGid(syscall.Getgid()) -} diff --git a/vendor/github.com/opencontainers/runc/vendor.conf b/vendor/github.com/opencontainers/runc/vendor.conf index 8139d6962..0ab4685fd 100644 --- a/vendor/github.com/opencontainers/runc/vendor.conf +++ b/vendor/github.com/opencontainers/runc/vendor.conf @@ -15,7 +15,7 @@ github.com/coreos/pkg v3 github.com/godbus/dbus v3 github.com/golang/protobuf 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8 # Command-line interface. -github.com/docker/docker 0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d +github.com/cyphar/filepath-securejoin v0.2.1 github.com/docker/go-units v0.2.0 github.com/urfave/cli d53eb991652b1d438abdd34ce4bfa3ef1539108e golang.org/x/sys 7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce https://github.com/golang/sys diff --git a/vendor/github.com/opencontainers/runtime-spec/README.md b/vendor/github.com/opencontainers/runtime-spec/README.md index 2f7eb6086..f0ad86e67 100644 --- a/vendor/github.com/opencontainers/runtime-spec/README.md +++ b/vendor/github.com/opencontainers/runtime-spec/README.md @@ -52,17 +52,12 @@ It also guarantees that the design is sound before code is written; a GitHub pul Typos and grammatical errors can go straight to a pull-request. When in doubt, start on the [mailing-list](#mailing-list). -### Weekly Call - -The contributors and maintainers of all OCI projects have a weekly meeting on Wednesdays at: - -* 8:00 AM (USA Pacific), during [odd weeks][iso-week]. -* 2:00 PM (USA Pacific), during [even weeks][iso-week]. +### Meetings +The contributors and maintainers of all OCI projects have monthly meetings at 2:00 PM (USA Pacific) on the first Wednesday of every month. There is an [iCalendar][rfc5545] format for the meetings [here](meeting.ics). - Everyone is welcome to participate via [UberConference web][uberconference] or audio-only: +1 415 968 0849 (no PIN needed). -An initial agenda will be posted to the [mailing list](#mailing-list) earlier in the week, and everyone is welcome to propose additional topics or suggest other agenda alterations there. +An initial agenda will be posted to the [mailing list](#mailing-list) in the week before each meeting, and everyone is welcome to propose additional topics or suggest other agenda alterations there. Minutes are posted to the [mailing list](#mailing-list) and minutes from past calls are archived [here][minutes], with minutes from especially old meetings (September 2015 and earlier) archived [here][runtime-wiki]. ### Mailing List diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go index f3f37d42d..71c9fa773 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go @@ -4,7 +4,7 @@ import "os" // Spec is the base configuration for the container. type Spec struct { - // Version of the Open Container Runtime Specification with which the bundle complies. + // Version of the Open Container Initiative Runtime Specification with which the bundle complies. Version string `json:"ociVersion"` // Process configures the container process. Process *Process `json:"process,omitempty"` diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go index 926ce6650..ff0cb6a80 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go @@ -8,7 +8,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 0 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 0 + VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "" diff --git a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go index e4a95fd5c..f5dbc4ba2 100644 --- a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go +++ b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go @@ -171,7 +171,9 @@ func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer return nil, err } acbw := &acBalancerWrapper{ac: ac} + ac.mu.Lock() ac.acbw = acbw + ac.mu.Unlock() return acbw, nil } @@ -228,7 +230,9 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) { return } acbw.ac = ac + ac.mu.Lock() ac.acbw = acbw + ac.mu.Unlock() if acState != connectivity.Idle { ac.connect(false) } diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index 5462062be..71de2e50d 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -929,6 +929,16 @@ func (ac *addrConn) resetTransport() error { newTransport, err := transport.NewClientTransport(ac.cc.ctx, sinfo, copts, timeout) if err != nil { if e, ok := err.(transport.ConnectionError); ok && !e.Temporary() { + ac.mu.Lock() + if ac.state != connectivity.Shutdown { + ac.state = connectivity.TransientFailure + if ac.cc.balancerWrapper != nil { + ac.cc.balancerWrapper.handleSubConnStateChange(ac.acbw, ac.state) + } else { + ac.cc.csMgr.updateState(ac.state) + } + } + ac.mu.Unlock() return err } grpclog.Warningf("grpc: addrConn.resetTransport failed to create client transport: %v; Reconnecting to %v", err, addr) diff --git a/vendor/google.golang.org/grpc/credentials/credentials.go b/vendor/google.golang.org/grpc/credentials/credentials.go index 2475fe832..946aa1f2b 100644 --- a/vendor/google.golang.org/grpc/credentials/credentials.go +++ b/vendor/google.golang.org/grpc/credentials/credentials.go @@ -91,10 +91,14 @@ type TransportCredentials interface { // (io.EOF, context.DeadlineExceeded or err.Temporary() == true). // If the returned error is a wrapper error, implementations should make sure that // the error implements Temporary() to have the correct retry behaviors. + // + // If the returned net.Conn is closed, it MUST close the net.Conn provided. ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error) // ServerHandshake does the authentication handshake for servers. It returns // the authenticated connection and the corresponding auth information about // the connection. + // + // If the returned net.Conn is closed, it MUST close the net.Conn provided. ServerHandshake(net.Conn) (net.Conn, AuthInfo, error) // Info provides the ProtocolInfo of this TransportCredentials. Info() ProtocolInfo diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go index e71c77bb6..54fc3ecee 100644 --- a/vendor/google.golang.org/grpc/rpc_util.go +++ b/vendor/google.golang.org/grpc/rpc_util.go @@ -567,6 +567,6 @@ const SupportPackageIsVersion3 = true const SupportPackageIsVersion4 = true // Version is the current grpc version. -const Version = "1.7.2" +const Version = "1.7.4" const grpcUA = "grpc-go/" + Version diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go index 7c882dbe6..787665dfe 100644 --- a/vendor/google.golang.org/grpc/server.go +++ b/vendor/google.golang.org/grpc/server.go @@ -118,11 +118,13 @@ type options struct { initialConnWindowSize int32 writeBufferSize int readBufferSize int + connectionTimeout time.Duration } var defaultServerOptions = options{ maxReceiveMessageSize: defaultServerMaxReceiveMessageSize, maxSendMessageSize: defaultServerMaxSendMessageSize, + connectionTimeout: 120 * time.Second, } // A ServerOption sets options such as credentials, codec and keepalive parameters, etc. @@ -291,6 +293,16 @@ func UnknownServiceHandler(streamHandler StreamHandler) ServerOption { } } +// ConnectionTimeout returns a ServerOption that sets the timeout for +// connection establishment (up to and including HTTP/2 handshaking) for all +// new connections. If this is not set, the default is 120 seconds. A zero or +// negative value will result in an immediate timeout. +func ConnectionTimeout(d time.Duration) ServerOption { + return func(o *options) { + o.connectionTimeout = d + } +} + // NewServer creates a gRPC server which has no service registered and has not // started to accept requests yet. func NewServer(opt ...ServerOption) *Server { @@ -499,16 +511,18 @@ func (s *Server) Serve(lis net.Listener) error { // handleRawConn is run in its own goroutine and handles a just-accepted // connection that has not had any I/O performed on it yet. func (s *Server) handleRawConn(rawConn net.Conn) { + rawConn.SetDeadline(time.Now().Add(s.opts.connectionTimeout)) conn, authInfo, err := s.useTransportAuthenticator(rawConn) if err != nil { s.mu.Lock() s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err) s.mu.Unlock() grpclog.Warningf("grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err) - // If serverHandShake returns ErrConnDispatched, keep rawConn open. + // If serverHandshake returns ErrConnDispatched, keep rawConn open. if err != credentials.ErrConnDispatched { rawConn.Close() } + rawConn.SetDeadline(time.Time{}) return } @@ -521,18 +535,21 @@ func (s *Server) handleRawConn(rawConn net.Conn) { s.mu.Unlock() if s.opts.useHandlerImpl { + rawConn.SetDeadline(time.Time{}) s.serveUsingHandler(conn) } else { - s.serveHTTP2Transport(conn, authInfo) + st := s.newHTTP2Transport(conn, authInfo) + if st == nil { + return + } + rawConn.SetDeadline(time.Time{}) + s.serveStreams(st) } } -// serveHTTP2Transport sets up a http/2 transport (using the -// gRPC http2 server transport in transport/http2_server.go) and -// serves streams on it. -// This is run in its own goroutine (it does network I/O in -// transport.NewServerTransport). -func (s *Server) serveHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) { +// newHTTP2Transport sets up a http/2 transport (using the +// gRPC http2 server transport in transport/http2_server.go). +func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) transport.ServerTransport { config := &transport.ServerConfig{ MaxStreams: s.opts.maxConcurrentStreams, AuthInfo: authInfo, @@ -552,13 +569,13 @@ func (s *Server) serveHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) s.mu.Unlock() c.Close() grpclog.Warningln("grpc: Server.Serve failed to create ServerTransport: ", err) - return + return nil } if !s.addConn(st) { st.Close() - return + return nil } - s.serveStreams(st) + return st } func (s *Server) serveStreams(st transport.ServerTransport) { diff --git a/vendor/google.golang.org/grpc/transport/handler_server.go b/vendor/google.golang.org/grpc/transport/handler_server.go index f1f6caf89..7e0fdb359 100644 --- a/vendor/google.golang.org/grpc/transport/handler_server.go +++ b/vendor/google.golang.org/grpc/transport/handler_server.go @@ -123,10 +123,9 @@ type serverHandlerTransport struct { // when WriteStatus is called. writes chan func() - mu sync.Mutex - // streamDone indicates whether WriteStatus has been called and writes channel - // has been closed. - streamDone bool + // block concurrent WriteStatus calls + // e.g. grpc/(*serverStream).SendMsg/RecvMsg + writeStatusMu sync.Mutex } func (ht *serverHandlerTransport) Close() error { @@ -177,13 +176,9 @@ func (ht *serverHandlerTransport) do(fn func()) error { } func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) error { - ht.mu.Lock() - if ht.streamDone { - ht.mu.Unlock() - return nil - } - ht.streamDone = true - ht.mu.Unlock() + ht.writeStatusMu.Lock() + defer ht.writeStatusMu.Unlock() + err := ht.do(func() { ht.writeCommonHeaders(s) @@ -222,7 +217,11 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro } } }) - close(ht.writes) + + if err == nil { // transport has not been closed + ht.Close() + close(ht.writes) + } return err } diff --git a/vendor/google.golang.org/grpc/transport/http2_server.go b/vendor/google.golang.org/grpc/transport/http2_server.go index bad29b88a..00df8eed0 100644 --- a/vendor/google.golang.org/grpc/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/transport/http2_server.go @@ -152,12 +152,12 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err Val: uint32(iwz)}) } if err := framer.fr.WriteSettings(isettings...); err != nil { - return nil, connectionErrorf(true, err, "transport: %v", err) + return nil, connectionErrorf(false, err, "transport: %v", err) } // Adjust the connection flow control window if needed. if delta := uint32(icwz - defaultWindowSize); delta > 0 { if err := framer.fr.WriteWindowUpdate(0, delta); err != nil { - return nil, connectionErrorf(true, err, "transport: %v", err) + return nil, connectionErrorf(false, err, "transport: %v", err) } } kp := config.KeepaliveParams @@ -223,6 +223,31 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err t.stats.HandleConn(t.ctx, connBegin) } t.framer.writer.Flush() + + // Check the validity of client preface. + preface := make([]byte, len(clientPreface)) + if _, err := io.ReadFull(t.conn, preface); err != nil { + return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to receive the preface from client: %v", err) + } + if !bytes.Equal(preface, clientPreface) { + return nil, connectionErrorf(false, nil, "transport: http2Server.HandleStreams received bogus greeting from client: %q", preface) + } + + frame, err := t.framer.fr.ReadFrame() + if err == io.EOF || err == io.ErrUnexpectedEOF { + t.Close() + return + } + if err != nil { + return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to read initial settings frame: %v", err) + } + atomic.StoreUint32(&t.activity, 1) + sf, ok := frame.(*http2.SettingsFrame) + if !ok { + return nil, connectionErrorf(false, nil, "transport: http2Server.HandleStreams saw invalid preface type %T from client", frame) + } + t.handleSettings(sf) + go func() { loopyWriter(t.ctx, t.controlBuf, t.itemHandler) t.Close() @@ -354,41 +379,6 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( // typically run in a separate goroutine. // traceCtx attaches trace to ctx and returns the new context. func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.Context) { - // Check the validity of client preface. - preface := make([]byte, len(clientPreface)) - if _, err := io.ReadFull(t.conn, preface); err != nil { - // Only log if it isn't a simple tcp accept check (ie: tcp balancer doing open/close socket) - if err != io.EOF { - errorf("transport: http2Server.HandleStreams failed to receive the preface from client: %v", err) - } - t.Close() - return - } - if !bytes.Equal(preface, clientPreface) { - errorf("transport: http2Server.HandleStreams received bogus greeting from client: %q", preface) - t.Close() - return - } - - frame, err := t.framer.fr.ReadFrame() - if err == io.EOF || err == io.ErrUnexpectedEOF { - t.Close() - return - } - if err != nil { - errorf("transport: http2Server.HandleStreams failed to read initial settings frame: %v", err) - t.Close() - return - } - atomic.StoreUint32(&t.activity, 1) - sf, ok := frame.(*http2.SettingsFrame) - if !ok { - errorf("transport: http2Server.HandleStreams saw invalid preface type %T from client", frame) - t.Close() - return - } - t.handleSettings(sf) - for { frame, err := t.framer.fr.ReadFrame() atomic.StoreUint32(&t.activity, 1)