Update containerd version to 90553efdef.

Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
Lantao Liu 2018-01-12 22:34:21 +00:00
parent d23853e60e
commit b07017b93e
51 changed files with 831 additions and 716 deletions

View File

@ -1,6 +1,6 @@
RUNC_VERSION=74a17296470088de3805e138d3d87c62e613dfc4 RUNC_VERSION=7f24b40cc5423969b4554ef04ba0b00e2b4ba010
CNI_VERSION=v0.6.0 CNI_VERSION=v0.6.0
CONTAINERD_VERSION=6c7abf7c76c1973d4fb4b0bad51691de84869a51 CONTAINERD_VERSION=90553efdef0678b2609aed74926a487f8ff58d1a
CONTAINERD_REPO= CONTAINERD_REPO=
CRITOOL_VERSION=v1.0.0-alpha.0 CRITOOL_VERSION=v1.0.0-alpha.0
KUBERNETES_VERSION=v1.9.0 KUBERNETES_VERSION=v1.9.0

View File

@ -1,7 +1,7 @@
github.com/blang/semver v3.1.0 github.com/blang/semver v3.1.0
github.com/BurntSushi/toml v0.2.0-21-g9906417 github.com/BurntSushi/toml v0.2.0-21-g9906417
github.com/containerd/cgroups 29da22c6171a4316169f9205ab6c49f59b5b852f github.com/containerd/cgroups 29da22c6171a4316169f9205ab6c49f59b5b852f
github.com/containerd/containerd 6c7abf7c76c1973d4fb4b0bad51691de84869a51 github.com/containerd/containerd 90553efdef0678b2609aed74926a487f8ff58d1a
github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e
github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788
@ -35,11 +35,11 @@ github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
github.com/json-iterator/go 13f86432b882000a51c6e610c620974462691a97 github.com/json-iterator/go 13f86432b882000a51c6e610c620974462691a97
github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342 github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342
github.com/mailru/easyjson d5b7844b561a7bc640052f1b935f7b800330d7e0 github.com/mailru/easyjson d5b7844b561a7bc640052f1b935f7b800330d7e0
github.com/Microsoft/go-winio v0.4.4 github.com/Microsoft/go-winio v0.4.5
github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448 github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448
github.com/opencontainers/image-spec v1.0.0 github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc 74a17296470088de3805e138d3d87c62e613dfc4 github.com/opencontainers/runc 7f24b40cc5423969b4554ef04ba0b00e2b4ba010
github.com/opencontainers/runtime-spec v1.0.0 github.com/opencontainers/runtime-spec v1.0.1
github.com/opencontainers/runtime-tools 6073aff4ac61897f75895123f7e24135204a404d github.com/opencontainers/runtime-tools 6073aff4ac61897f75895123f7e24135204a404d
github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206 github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206
github.com/peterbourgon/diskv v2.0.1 github.com/peterbourgon/diskv v2.0.1
@ -60,7 +60,7 @@ golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
google.golang.org/grpc v1.7.2 google.golang.org/grpc v1.7.4
gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77 gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77
k8s.io/api 9382f5a87a364c195477134986b578d103c7c24d k8s.io/api 9382f5a87a364c195477134986b578d103c7c24d

View File

@ -68,10 +68,20 @@ func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
return &BackupStreamReader{r, 0} 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. // it was not completely read.
func (r *BackupStreamReader) Next() (*BackupHeader, error) { func (r *BackupStreamReader) Next() (*BackupHeader, error) {
if r.bytesLeft > 0 { 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 { if _, err := io.Copy(ioutil.Discard, r); err != nil {
return nil, err return nil, err
} }
@ -220,7 +230,7 @@ type BackupFileWriter struct {
ctx uintptr 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. // Write() will attempt to restore the security descriptor from the stream.
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter { func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
w := &BackupFileWriter{f, includeSecurity, 0} w := &BackupFileWriter{f, includeSecurity, 0}

137
vendor/github.com/Microsoft/go-winio/ea.go generated vendored Normal file
View File

@ -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
}

View File

@ -78,6 +78,7 @@ func initIo() {
type win32File struct { type win32File struct {
handle syscall.Handle handle syscall.Handle
wg sync.WaitGroup wg sync.WaitGroup
wgLock sync.RWMutex
closing atomicBool closing atomicBool
readDeadline deadlineHandler readDeadline deadlineHandler
writeDeadline deadlineHandler writeDeadline deadlineHandler
@ -114,14 +115,18 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
// closeHandle closes the resources associated with a Win32 handle // closeHandle closes the resources associated with a Win32 handle
func (f *win32File) closeHandle() { func (f *win32File) closeHandle() {
f.wgLock.Lock()
// Atomically set that we are closing, releasing the resources only once. // Atomically set that we are closing, releasing the resources only once.
if !f.closing.swap(true) { if !f.closing.swap(true) {
f.wgLock.Unlock()
// cancel all IO and wait for it to complete // cancel all IO and wait for it to complete
cancelIoEx(f.handle, nil) cancelIoEx(f.handle, nil)
f.wg.Wait() f.wg.Wait()
// at this point, no new IO can start // at this point, no new IO can start
syscall.Close(f.handle) syscall.Close(f.handle)
f.handle = 0 f.handle = 0
} else {
f.wgLock.Unlock()
} }
} }
@ -134,10 +139,13 @@ func (f *win32File) Close() error {
// prepareIo prepares for a new IO operation. // prepareIo prepares for a new IO operation.
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
func (f *win32File) prepareIo() (*ioOperation, error) { func (f *win32File) prepareIo() (*ioOperation, error) {
f.wgLock.RLock()
if f.closing.isSet() { if f.closing.isSet() {
f.wgLock.RUnlock()
return nil, ErrFileClosed return nil, ErrFileClosed
} }
f.wg.Add(1) f.wg.Add(1)
f.wgLock.RUnlock()
c := &ioOperation{} c := &ioOperation{}
c.ch = make(chan ioResult) c.ch = make(chan ioResult)
return c, nil return c, nil

View File

@ -265,9 +265,9 @@ func (l *win32PipeListener) listenerRoutine() {
if err == nil { if err == nil {
// Wait for the client to connect. // Wait for the client to connect.
ch := make(chan error) ch := make(chan error)
go func() { go func(p *win32File) {
ch <- connectPipe(p) ch <- connectPipe(p)
}() }(p)
select { select {
case err = <-ch: case err = <-ch:
if err != nil { if err != nil {

View File

@ -4,6 +4,7 @@
[![Build Status](https://travis-ci.org/containerd/containerd.svg?branch=master)](https://travis-ci.org/containerd/containerd) [![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) [![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) [![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. 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 ## 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) * [for ops and admins](docs/ops.md)
* [namespaces](docs/namespaces.md) * [namespaces](docs/namespaces.md)
* [client options](docs/client-opts.md) * [client options](docs/client-opts.md)

View File

@ -8,7 +8,7 @@ import (
"sync" "sync"
) )
// Config holds the io configurations. // Config holds the IO configurations.
type Config struct { type Config struct {
// Terminal is true if one has been allocated // Terminal is true if one has been allocated
Terminal bool Terminal bool
@ -24,48 +24,17 @@ type Config struct {
type IO interface { type IO interface {
// Config returns the IO configuration. // Config returns the IO configuration.
Config() Config Config() Config
// Cancel aborts all current io operations // Cancel aborts all current io operations.
Cancel() Cancel()
// Wait blocks until all io copy operations have completed // Wait blocks until all io copy operations have completed.
Wait() Wait()
// Close cleans up all open io resources // Close cleans up all open io resources. Cancel() is always called before
// Close()
Close() error Close() error
} }
// cio is a basic container IO implementation. // Creator creates new IO sets for a task
type cio struct { type Creator func(id string) (IO, error)
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)
// Attach allows callers to reattach to running tasks // 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 // will be sent only to the first reads
type Attach func(*FIFOSet) (IO, error) type Attach func(*FIFOSet) (IO, error)
// NewIO returns an Creation that will provide IO sets without a terminal // FIFOSet is a set of file paths to FIFOs for a task's standard IO streams
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
type FIFOSet struct { type FIFOSet struct {
// Dir is the directory holding the task fifos Config
Dir string close func() error
// In, Out, and Err fifo paths
In, Out, Err string
// Terminal returns true if a terminal is being used for the task
Terminal bool
} }
type ioSet struct { // Close the FIFOSet
in io.Reader func (f *FIFOSet) Close() error {
out, err io.Writer if f.close != nil {
} return f.close()
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)
} }
return nil return nil
} }
func (g *wgCloser) Cancel() { // NewFIFOSet returns a new FIFOSet from a Config and a close function
g.cancel() 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{}

View File

@ -12,173 +12,115 @@ import (
"syscall" "syscall"
"github.com/containerd/fifo" "github.com/containerd/fifo"
"github.com/pkg/errors"
) )
// NewFifos returns a new set of fifos for the task // NewFIFOSetInDir returns a new FIFOSet with paths in a temporary directory under root
func NewFifos(id string) (*FIFOSet, error) { func NewFIFOSetInDir(root, id string, terminal bool) (*FIFOSet, error) {
root := "/run/containerd/fifo" if root != "" {
if err := os.MkdirAll(root, 0700); err != nil { if err := os.MkdirAll(root, 0700); err != nil {
return nil, err return nil, err
} }
}
dir, err := ioutil.TempDir(root, "") dir, err := ioutil.TempDir(root, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &FIFOSet{ closer := func() error {
Dir: dir, return os.RemoveAll(dir)
In: filepath.Join(dir, id+"-stdin"), }
Out: filepath.Join(dir, id+"-stdout"), return NewFIFOSet(Config{
Err: filepath.Join(dir, id+"-stderr"), Stdin: filepath.Join(dir, id+"-stdin"),
}, nil 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) { func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) {
var ( var ctx, cancel = context.WithCancel(context.Background())
f io.ReadWriteCloser pipes, err := openFifos(ctx, fifos)
set []io.Closer
ctx, cancel = context.WithCancel(context.Background())
wg = &sync.WaitGroup{}
)
defer func() {
if err != nil { if err != nil {
for _, f := range set {
f.Close()
}
cancel() cancel()
return nil, err
} }
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 f, err = fifo.OpenFifo(ctx, fifos.In, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { if !fifos.Terminal {
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) wg.Add(1)
go func(r io.ReadCloser) { go func() {
io.Copy(ioset.out, r) io.Copy(ioset.Stderr, pipes.Stderr)
r.Close() pipes.Stderr.Close()
wg.Done() 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) return &cio{
config: fifos.Config,
if !tty {
wg.Add(1)
go func(r io.ReadCloser) {
io.Copy(ioset.err, r)
r.Close()
wg.Done()
}(f)
}
return &wgCloser{
wg: wg, wg: wg,
dir: fifos.Dir, closers: append(pipes.closers(), fifos),
set: set,
cancel: cancel, cancel: cancel,
}, nil }, nil
} }
// NewDirectIO returns an IO implementation that exposes the pipes directly func openFifos(ctx context.Context, fifos *FIFOSet) (pipes, error) {
func NewDirectIO(ctx context.Context, terminal bool) (*DirectIO, error) { var err error
set, err := NewFifos("")
if err != nil {
return nil, err
}
f := &DirectIO{
set: set,
terminal: terminal,
}
defer func() { defer func() {
if err != nil { 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 { }
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() f.Stdin.Close()
return nil, err 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 { }
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.Stdin.Close()
f.Stdout.Close() f.Stdout.Close()
return nil, err return f, errors.Wrapf(err, "failed to open stderr fifo")
}
} }
return f, nil return f, nil
} }
// DirectIO allows task IO to be handled externally by the caller // NewDirectIO returns an IO implementation that exposes the IO streams as io.ReadCloser
type DirectIO struct { // and io.WriteCloser.
Stdin io.WriteCloser func NewDirectIO(ctx context.Context, fifos *FIFOSet) (*DirectIO, error) {
Stdout io.ReadCloser ctx, cancel := context.WithCancel(ctx)
Stderr io.ReadCloser pipes, err := openFifos(ctx, fifos)
return &DirectIO{
set *FIFOSet pipes: pipes,
terminal bool cio: cio{
config: fifos.Config,
closers: append(pipes.closers(), fifos),
cancel: cancel,
},
}, err
} }
// IOCreate returns IO avaliable for use with task creation func (p *pipes) closers() []io.Closer {
func (f *DirectIO) IOCreate(id string) (IO, error) { return []io.Closer{p.Stdin, p.Stdout, p.Stderr}
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)
} }

View File

@ -13,25 +13,26 @@ import (
const pipeRoot = `\\.\pipe` const pipeRoot = `\\.\pipe`
// NewFifos returns a new set of fifos for the task // NewFIFOSetInDir returns a new set of fifos for the task
func NewFifos(id string) (*FIFOSet, error) { func NewFIFOSetInDir(_, id string, terminal bool) (*FIFOSet, error) {
return &FIFOSet{ return NewFIFOSet(Config{
In: fmt.Sprintf(`%s\ctr-%s-stdin`, pipeRoot, id), Terminal: terminal,
Out: fmt.Sprintf(`%s\ctr-%s-stdout`, pipeRoot, id), Stdin: fmt.Sprintf(`%s\ctr-%s-stdin`, pipeRoot, id),
Err: fmt.Sprintf(`%s\ctr-%s-stderr`, pipeRoot, id), Stdout: fmt.Sprintf(`%s\ctr-%s-stdout`, pipeRoot, id),
}, nil 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 ( var (
wg sync.WaitGroup wg sync.WaitGroup
set []io.Closer set []io.Closer
) )
if fifos.In != "" { if fifos.Stdin != "" {
l, err := winio.ListenPipe(fifos.In, nil) l, err := winio.ListenPipe(fifos.Stdin, nil)
if err != 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) { defer func(l net.Listener) {
if err != nil { if err != nil {
@ -43,19 +44,19 @@ func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) {
go func() { go func() {
c, err := l.Accept() c, err := l.Accept()
if err != nil { 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 return
} }
io.Copy(c, ioset.in) io.Copy(c, ioset.Stdin)
c.Close() c.Close()
l.Close() l.Close()
}() }()
} }
if fifos.Out != "" { if fifos.Stdout != "" {
l, err := winio.ListenPipe(fifos.Out, nil) l, err := winio.ListenPipe(fifos.Stdout, nil)
if err != 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) { defer func(l net.Listener) {
if err != nil { if err != nil {
@ -69,19 +70,19 @@ func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) {
defer wg.Done() defer wg.Done()
c, err := l.Accept() c, err := l.Accept()
if err != nil { 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 return
} }
io.Copy(ioset.out, c) io.Copy(ioset.Stdout, c)
c.Close() c.Close()
l.Close() l.Close()
}() }()
} }
if !tty && fifos.Err != "" { if !fifos.Terminal && fifos.Stderr != "" {
l, err := winio.ListenPipe(fifos.Err, nil) l, err := winio.ListenPipe(fifos.Stderr, nil)
if err != 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) { defer func(l net.Listener) {
if err != nil { if err != nil {
@ -95,23 +96,29 @@ func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) {
defer wg.Done() defer wg.Done()
c, err := l.Accept() c, err := l.Accept()
if err != nil { 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 return
} }
io.Copy(ioset.err, c) io.Copy(ioset.Stderr, c)
c.Close() c.Close()
l.Close() l.Close()
}() }()
} }
return &wgCloser{ return &cio{config: fifos.Config, closers: set}, nil
wg: &wg, }
dir: fifos.Dir,
set: set, // NewDirectIO returns an IO implementation that exposes the IO streams as io.ReadCloser
cancel: func() { // and io.WriteCloser.
for _, l := range set { func NewDirectIO(stdin io.WriteCloser, stdout, stderr io.ReadCloser, terminal bool) *DirectIO {
l.Close() return &DirectIO{
} pipes: pipes{
}, Stdin: stdin,
}, nil Stdout: stdout,
Stderr: stderr,
},
cio: cio{
config: Config{Terminal: terminal},
},
}
} }

View File

@ -3,6 +3,7 @@ package containerd
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -26,7 +27,7 @@ type Container interface {
// Delete removes the container // Delete removes the container
Delete(context.Context, ...DeleteOpts) error Delete(context.Context, ...DeleteOpts) error
// NewTask creates a new task based on the container metadata // 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 returns the OCI runtime specification
Spec(context.Context) (*specs.Spec, error) Spec(context.Context) (*specs.Spec, error)
// Task returns the current task for the container // Task returns the current task for the container
@ -162,7 +163,7 @@ func (c *container) Image(ctx context.Context) (Image, error) {
}, nil }, 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) i, err := ioCreate(c.id)
if err != nil { if err != nil {
return nil, err 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) 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) { func attachExistingIO(response *tasks.GetResponse, ioAttach cio.Attach) (cio.IO, error) {
// get the existing fifo paths from the task information stored by the daemon path := getFifoDir([]string{
paths := &cio.FIFOSet{
Dir: getFifoDir([]string{
response.Process.Stdin, response.Process.Stdin,
response.Process.Stdout, response.Process.Stdout,
response.Process.Stderr, response.Process.Stderr,
}), })
In: response.Process.Stdin, closer := func() error {
Out: response.Process.Stdout, return os.RemoveAll(path)
Err: response.Process.Stderr,
Terminal: response.Process.Terminal,
} }
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 // getFifoDir looks for any non-empty path for a stdio fifo

View File

@ -6,7 +6,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"syscall" "syscall"
@ -16,6 +15,7 @@ import (
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/linux/runctypes"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
@ -166,7 +166,7 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool
if err != nil { if err != nil {
return err return err
} }
if err := remapRootFS(mounts, uid, gid); err != nil { if err := remapRootFS(ctx, mounts, uid, gid); err != nil {
snapshotter.Remove(ctx, usernsID) snapshotter.Remove(ctx, usernsID)
return err 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 { func remapRootFS(ctx context.Context, mounts []mount.Mount, uid, gid uint32) error {
root, err := ioutil.TempDir("", "ctd-remap") return mount.WithTempMount(ctx, mounts, func(root string) error {
if err != nil { return filepath.Walk(root, incrementFS(root, uid, gid))
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 incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc { 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) 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
}

View File

@ -26,9 +26,9 @@ func (e *Envelope) Field(fieldpath []string) (string, bool) {
switch fieldpath[0] { switch fieldpath[0] {
// unhandled: timestamp // unhandled: timestamp
case "namespace": case "namespace":
return string(e.Namespace), len(e.Namespace) > 0 return e.Namespace, len(e.Namespace) > 0
case "topic": case "topic":
return string(e.Topic), len(e.Topic) > 0 return e.Topic, len(e.Topic) > 0
case "event": case "event":
decoded, err := typeurl.UnmarshalAny(e.Event) decoded, err := typeurl.UnmarshalAny(e.Event)
if err != nil { if err != nil {

View File

@ -224,8 +224,6 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err
f1, f2 *currentPath f1, f2 *currentPath
rmdir string rmdir string
lastEmittedDir = string(filepath.Separator)
parents []os.FileInfo
) )
g.Go(func() error { g.Go(func() error {
defer close(c1) defer close(c1)
@ -260,10 +258,7 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err
continue continue
} }
var ( var f os.FileInfo
f os.FileInfo
emit = true
)
k, p := pathChange(f1, f2) k, p := pathChange(f1, f2)
switch k { switch k {
case ChangeKindAdd: case ChangeKindAdd:
@ -299,83 +294,17 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err
f2 = nil f2 = nil
if same { if same {
if !isLinked(f) { if !isLinked(f) {
emit = false continue
} }
k = ChangeKindUnmodified 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 { if err := changeFn(k, p, f, nil); err != nil {
return err 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)
}
} }
return nil return nil
}) })
return g.Wait() 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
}

View File

@ -15,6 +15,15 @@ type inode struct {
dev, ino uint64 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) { func diskUsage(roots ...string) (Usage, error) {
var ( var (
@ -28,9 +37,7 @@ func diskUsage(roots ...string) (Usage, error) {
return err return err
} }
stat := fi.Sys().(*syscall.Stat_t) inoKey := newInode(fi.Sys().(*syscall.Stat_t))
inoKey := inode{dev: uint64(stat.Dev), ino: uint64(stat.Ino)}
if _, ok := inodes[inoKey]; !ok { if _, ok := inodes[inoKey]; !ok {
inodes[inoKey] = struct{}{} inodes[inoKey] = struct{}{}
size += fi.Size() size += fi.Size()
@ -60,9 +67,7 @@ func diffUsage(ctx context.Context, a, b string) (Usage, error) {
} }
if kind == ChangeKindAdd || kind == ChangeKindModify { if kind == ChangeKindAdd || kind == ChangeKindModify {
stat := fi.Sys().(*syscall.Stat_t) inoKey := newInode(fi.Sys().(*syscall.Stat_t))
inoKey := inode{dev: uint64(stat.Dev), ino: uint64(stat.Ino)}
if _, ok := inodes[inoKey]; !ok { if _, ok := inodes[inoKey]; !ok {
inodes[inoKey] = struct{}{} inodes[inoKey] = struct{}{}
size += fi.Size() size += fi.Size()

View File

@ -13,5 +13,6 @@ func getLinkInfo(fi os.FileInfo) (uint64, bool) {
return 0, false 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
} }

View File

@ -71,9 +71,9 @@ func sameFile(f1, f2 *currentPath) (bool, error) {
return false, nil 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 // 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 var eq bool
if (f1.f.Mode() & os.ModeSymlink) == os.ModeSymlink { if (f1.f.Mode() & os.ModeSymlink) == os.ModeSymlink {
eq, err = compareSymlinkTarget(f1.fullPath, f2.fullPath) eq, err = compareSymlinkTarget(f1.fullPath, f2.fullPath)

View File

@ -357,13 +357,5 @@ func RootFS(ctx context.Context, provider content.Provider, configDesc ocispec.D
if err := json.Unmarshal(p, &config); err != nil { if err := json.Unmarshal(p, &config); err != nil {
return nil, err return nil, err
} }
return config.RootFS.DiffIDs, nil
// 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
} }

View File

@ -1,5 +1,14 @@
package mount 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 // Mount is the lingua franca of containerd. A mount represents a
// serialized mount syscall. Components either emit or consume mounts. // serialized mount syscall. Components either emit or consume mounts.
type Mount struct { type Mount struct {
@ -22,3 +31,42 @@ func All(mounts []Mount, target string) error {
} }
return nil 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)
}

View File

@ -6,7 +6,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -271,23 +270,8 @@ func WithUserID(uid uint32) SpecOpts {
if err != nil { if err != nil {
return err return err
} }
root, err := ioutil.TempDir("", "ctd-username")
if err != nil { return mount.WithTempMount(ctx, mounts, func(root string) error {
return err
}
defer os.Remove(root)
for _, m := range mounts {
if err := m.Mount(root); err != nil {
return err
}
}
defer func() {
if uerr := mount.Unmount(root, 0); uerr != nil {
if err == nil {
err = uerr
}
}
}()
ppath, err := fs.RootPath(root, "/etc/passwd") ppath, err := fs.RootPath(root, "/etc/passwd")
if err != nil { if err != nil {
return err return err
@ -314,6 +298,7 @@ func WithUserID(uid uint32) SpecOpts {
u := users[0] u := users[0]
s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid) s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
return nil return nil
})
} }
} }
@ -334,23 +319,7 @@ func WithUsername(username string) SpecOpts {
if err != nil { if err != nil {
return err return err
} }
root, err := ioutil.TempDir("", "ctd-username") return mount.WithTempMount(ctx, mounts, func(root string) error {
if err != nil {
return err
}
defer os.Remove(root)
for _, m := range mounts {
if err := m.Mount(root); err != nil {
return err
}
}
defer func() {
if uerr := mount.Unmount(root, 0); uerr != nil {
if err == nil {
err = uerr
}
}
}()
ppath, err := fs.RootPath(root, "/etc/passwd") ppath, err := fs.RootPath(root, "/etc/passwd")
if err != nil { if err != nil {
return err return err
@ -372,5 +341,6 @@ func WithUsername(username string) SpecOpts {
u := users[0] u := users[0]
s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid) s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
return nil return nil
})
} }
} }

View File

@ -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
}

View File

@ -5,6 +5,13 @@ import (
"strings" "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. // 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 // We use switch statements because they are slightly faster than map lookups
@ -21,6 +28,17 @@ func isKnownOS(os string) bool {
return false 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. // isKnownArch returns true if we know about the architecture.
// //
// The arch value should be normalized before being passed to this function. // The arch value should be normalized before being passed to this function.

View File

@ -16,6 +16,7 @@ func DefaultSpec() specs.Platform {
return specs.Platform{ return specs.Platform{
OS: runtime.GOOS, OS: runtime.GOOS,
Architecture: runtime.GOARCH, Architecture: runtime.GOARCH,
// TODO(stevvooe): Need to resolve GOARM for arm hosts. // The Variant field will be empty if arch != ARM.
Variant: cpuVariant,
} }
} }

View File

@ -191,6 +191,7 @@ func (p *process) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (*ExitS
return nil, errdefs.FromGRPC(err) return nil, errdefs.FromGRPC(err)
} }
if p.io != nil { if p.io != nil {
p.io.Cancel()
p.io.Wait() p.io.Wait()
p.io.Close() p.io.Close()
} }

View File

@ -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
}

View File

@ -39,7 +39,7 @@ func Diff(ctx context.Context, snapshotID string, sn snapshots.Snapshotter, d di
if err != nil { if err != nil {
return ocispec.Descriptor{}, err return ocispec.Descriptor{}, err
} }
defer sn.Remove(ctx, lowerKey) defer sn.Remove(ctx, upperKey)
} }
return d.DiffMounts(ctx, lower, upper, opts...) return d.DiffMounts(ctx, lower, upper, opts...)

View File

@ -4,6 +4,7 @@ package sys
import ( import (
"syscall" "syscall"
"time"
) )
// StatAtime returns the access time from a stat struct // 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 { func StatMtime(st *syscall.Stat_t) syscall.Timespec {
return st.Mtimespec 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
}

View File

@ -4,6 +4,7 @@ package sys
import ( import (
"syscall" "syscall"
"time"
) )
// StatAtime returns the Atim // StatAtime returns the Atim
@ -20,3 +21,8 @@ func StatCtime(st *syscall.Stat_t) syscall.Timespec {
func StatMtime(st *syscall.Stat_t) syscall.Timespec { func StatMtime(st *syscall.Stat_t) syscall.Timespec {
return st.Mtim 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)
}

View File

@ -123,7 +123,7 @@ type Task interface {
// Resume the execution of the task // Resume the execution of the task
Resume(context.Context) error Resume(context.Context) error
// Exec creates a new process inside the task // 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 returns a list of system specific process ids inside the task
Pids(context.Context) ([]ProcessInfo, error) Pids(context.Context) ([]ProcessInfo, error)
// Checkpoint serializes the runtime and memory information of a task into an // 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, ContainerID: t.id,
}) })
if err != nil { if err != nil {
t.io.Cancel()
t.io.Close() t.io.Close()
return errdefs.FromGRPC(err) 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 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 == "" { if id == "" {
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "exec id must not be empty") return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "exec id must not be empty")
} }

View File

@ -15,8 +15,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.0
github.com/docker/go-units v0.3.1 github.com/docker/go-units v0.3.1
github.com/gogo/protobuf v0.5 github.com/gogo/protobuf v0.5
github.com/golang/protobuf 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9 github.com/golang/protobuf 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9
github.com/opencontainers/runtime-spec v1.0.0 github.com/opencontainers/runtime-spec v1.0.1
github.com/opencontainers/runc 74a17296470088de3805e138d3d87c62e613dfc4 github.com/opencontainers/runc 7f24b40cc5423969b4554ef04ba0b00e2b4ba010
github.com/sirupsen/logrus v1.0.0 github.com/sirupsen/logrus v1.0.0
github.com/containerd/btrfs cc52c4dea2ce11a44e6639e561bb5c2af9ada9e3 github.com/containerd/btrfs cc52c4dea2ce11a44e6639e561bb5c2af9ada9e3
github.com/stretchr/testify v1.1.4 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/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 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/pkg/errors v0.8.0
github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448 github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448
golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys 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 github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/BurntSushi/toml v0.2.0-21-g9906417 github.com/BurntSushi/toml v0.2.0-21-g9906417
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 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/hcsshim v0.6.7
github.com/Microsoft/opengcs v0.3.2
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
github.com/dmcgowan/go-tar go1.10 github.com/dmcgowan/go-tar go1.10
github.com/stevvooe/ttrpc 76e68349ad9ab4d03d764c713826d31216715e4f github.com/stevvooe/ttrpc d2710463e497617f16f26d1e715a3308609e7982

View File

@ -51,7 +51,7 @@ Find more [FAQ on the OCI site](https://www.opencontainers.org/faq).
## Roadmap ## 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 # Contributing

View File

@ -22,7 +22,7 @@ const (
// VersionMinor is for functionality in a backwards-compatible manner // VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 0 VersionMinor = 0
// VersionPatch is for backwards-compatible bug fixes // VersionPatch is for backwards-compatible bug fixes
VersionPatch = 0 VersionPatch = 1
// VersionDev indicates development branch. Releases will be empty string. // VersionDev indicates development branch. Releases will be empty string.
VersionDev = "" VersionDev = ""

View File

@ -56,7 +56,7 @@ make BUILDTAGS='seccomp apparmor'
|-----------|------------------------------------|-------------| |-----------|------------------------------------|-------------|
| seccomp | Syscall filtering | libseccomp | | seccomp | Syscall filtering | libseccomp |
| selinux | selinux process and mount labeling | <none> | | selinux | selinux process and mount labeling | <none> |
| apparmor | apparmor profile support | libapparmor | | apparmor | apparmor profile support | <none> |
| ambient | ambient capability support | kernel 4.3 | | ambient | ambient capability support | kernel 4.3 |

View File

@ -2,15 +2,10 @@
package apparmor package apparmor
// #cgo LDFLAGS: -lapparmor
// #include <sys/apparmor.h>
// #include <stdlib.h>
import "C"
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"unsafe"
) )
// IsEnabled returns true if apparmor is enabled for the host. // IsEnabled returns true if apparmor is enabled for the host.
@ -24,16 +19,36 @@ func IsEnabled() bool {
return false return false
} }
func setprocattr(attr, value string) error {
// Under AppArmor you can only change your own attr, so use /proc/self/
// instead of /proc/<tid>/ like libapparmor does
path := fmt.Sprintf("/proc/self/attr/%s", attr)
f, err := os.OpenFile(path, os.O_WRONLY, 0)
if err != nil {
return err
}
defer f.Close()
_, err = fmt.Fprintf(f, "%s", value)
return err
}
// changeOnExec reimplements aa_change_onexec from libapparmor in Go
func changeOnExec(name string) error {
value := "exec " + name
if err := setprocattr("exec", value); err != nil {
return fmt.Errorf("apparmor failed to apply profile: %s", err)
}
return nil
}
// ApplyProfile will apply the profile with the specified name to the process after // ApplyProfile will apply the profile with the specified name to the process after
// the next exec. // the next exec.
func ApplyProfile(name string) error { func ApplyProfile(name string) error {
if name == "" { if name == "" {
return nil return nil
} }
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName)) return changeOnExec(name)
if _, err := C.aa_change_onexec(cName); err != nil {
return fmt.Errorf("apparmor failed to apply profile: %s", err)
}
return nil
} }

View File

@ -1,6 +0,0 @@
// +build !windows,!linux,!freebsd
package configs
type Cgroup struct {
}

View File

@ -1,4 +1,4 @@
// +build linux freebsd // +build linux
package configs package configs

View File

@ -1,3 +0,0 @@
// +build !linux
package devices

View File

@ -1,4 +1,4 @@
// +build cgo,linux cgo,freebsd // +build cgo,linux
package system package system

View File

@ -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())
}

View File

@ -15,7 +15,7 @@ github.com/coreos/pkg v3
github.com/godbus/dbus v3 github.com/godbus/dbus v3
github.com/golang/protobuf 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8 github.com/golang/protobuf 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8
# Command-line interface. # 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/docker/go-units v0.2.0
github.com/urfave/cli d53eb991652b1d438abdd34ce4bfa3ef1539108e github.com/urfave/cli d53eb991652b1d438abdd34ce4bfa3ef1539108e
golang.org/x/sys 7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce https://github.com/golang/sys golang.org/x/sys 7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce https://github.com/golang/sys

View File

@ -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. Typos and grammatical errors can go straight to a pull-request.
When in doubt, start on the [mailing-list](#mailing-list). When in doubt, start on the [mailing-list](#mailing-list).
### Weekly Call ### Meetings
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].
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). 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). 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]. 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 ### Mailing List

View File

@ -4,7 +4,7 @@ import "os"
// Spec is the base configuration for the container. // Spec is the base configuration for the container.
type Spec struct { 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"` Version string `json:"ociVersion"`
// Process configures the container process. // Process configures the container process.
Process *Process `json:"process,omitempty"` Process *Process `json:"process,omitempty"`

View File

@ -8,7 +8,7 @@ const (
// VersionMinor is for functionality in a backwards-compatible manner // VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 0 VersionMinor = 0
// VersionPatch is for backwards-compatible bug fixes // VersionPatch is for backwards-compatible bug fixes
VersionPatch = 0 VersionPatch = 1
// VersionDev indicates development branch. Releases will be empty string. // VersionDev indicates development branch. Releases will be empty string.
VersionDev = "" VersionDev = ""

View File

@ -171,7 +171,9 @@ func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer
return nil, err return nil, err
} }
acbw := &acBalancerWrapper{ac: ac} acbw := &acBalancerWrapper{ac: ac}
ac.mu.Lock()
ac.acbw = acbw ac.acbw = acbw
ac.mu.Unlock()
return acbw, nil return acbw, nil
} }
@ -228,7 +230,9 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
return return
} }
acbw.ac = ac acbw.ac = ac
ac.mu.Lock()
ac.acbw = acbw ac.acbw = acbw
ac.mu.Unlock()
if acState != connectivity.Idle { if acState != connectivity.Idle {
ac.connect(false) ac.connect(false)
} }

View File

@ -929,6 +929,16 @@ func (ac *addrConn) resetTransport() error {
newTransport, err := transport.NewClientTransport(ac.cc.ctx, sinfo, copts, timeout) newTransport, err := transport.NewClientTransport(ac.cc.ctx, sinfo, copts, timeout)
if err != nil { if err != nil {
if e, ok := err.(transport.ConnectionError); ok && !e.Temporary() { 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 return err
} }
grpclog.Warningf("grpc: addrConn.resetTransport failed to create client transport: %v; Reconnecting to %v", err, addr) grpclog.Warningf("grpc: addrConn.resetTransport failed to create client transport: %v; Reconnecting to %v", err, addr)

View File

@ -91,10 +91,14 @@ type TransportCredentials interface {
// (io.EOF, context.DeadlineExceeded or err.Temporary() == true). // (io.EOF, context.DeadlineExceeded or err.Temporary() == true).
// If the returned error is a wrapper error, implementations should make sure that // If the returned error is a wrapper error, implementations should make sure that
// the error implements Temporary() to have the correct retry behaviors. // 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) ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
// ServerHandshake does the authentication handshake for servers. It returns // ServerHandshake does the authentication handshake for servers. It returns
// the authenticated connection and the corresponding auth information about // the authenticated connection and the corresponding auth information about
// the connection. // the connection.
//
// If the returned net.Conn is closed, it MUST close the net.Conn provided.
ServerHandshake(net.Conn) (net.Conn, AuthInfo, error) ServerHandshake(net.Conn) (net.Conn, AuthInfo, error)
// Info provides the ProtocolInfo of this TransportCredentials. // Info provides the ProtocolInfo of this TransportCredentials.
Info() ProtocolInfo Info() ProtocolInfo

View File

@ -567,6 +567,6 @@ const SupportPackageIsVersion3 = true
const SupportPackageIsVersion4 = true const SupportPackageIsVersion4 = true
// Version is the current grpc version. // Version is the current grpc version.
const Version = "1.7.2" const Version = "1.7.4"
const grpcUA = "grpc-go/" + Version const grpcUA = "grpc-go/" + Version

View File

@ -118,11 +118,13 @@ type options struct {
initialConnWindowSize int32 initialConnWindowSize int32
writeBufferSize int writeBufferSize int
readBufferSize int readBufferSize int
connectionTimeout time.Duration
} }
var defaultServerOptions = options{ var defaultServerOptions = options{
maxReceiveMessageSize: defaultServerMaxReceiveMessageSize, maxReceiveMessageSize: defaultServerMaxReceiveMessageSize,
maxSendMessageSize: defaultServerMaxSendMessageSize, maxSendMessageSize: defaultServerMaxSendMessageSize,
connectionTimeout: 120 * time.Second,
} }
// A ServerOption sets options such as credentials, codec and keepalive parameters, etc. // 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 // NewServer creates a gRPC server which has no service registered and has not
// started to accept requests yet. // started to accept requests yet.
func NewServer(opt ...ServerOption) *Server { 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 // handleRawConn is run in its own goroutine and handles a just-accepted
// connection that has not had any I/O performed on it yet. // connection that has not had any I/O performed on it yet.
func (s *Server) handleRawConn(rawConn net.Conn) { func (s *Server) handleRawConn(rawConn net.Conn) {
rawConn.SetDeadline(time.Now().Add(s.opts.connectionTimeout))
conn, authInfo, err := s.useTransportAuthenticator(rawConn) conn, authInfo, err := s.useTransportAuthenticator(rawConn)
if err != nil { if err != nil {
s.mu.Lock() s.mu.Lock()
s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err) s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
s.mu.Unlock() s.mu.Unlock()
grpclog.Warningf("grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err) 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 { if err != credentials.ErrConnDispatched {
rawConn.Close() rawConn.Close()
} }
rawConn.SetDeadline(time.Time{})
return return
} }
@ -521,18 +535,21 @@ func (s *Server) handleRawConn(rawConn net.Conn) {
s.mu.Unlock() s.mu.Unlock()
if s.opts.useHandlerImpl { if s.opts.useHandlerImpl {
rawConn.SetDeadline(time.Time{})
s.serveUsingHandler(conn) s.serveUsingHandler(conn)
} else { } 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 // newHTTP2Transport sets up a http/2 transport (using the
// gRPC http2 server transport in transport/http2_server.go) and // gRPC http2 server transport in transport/http2_server.go).
// serves streams on it. func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) transport.ServerTransport {
// 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) {
config := &transport.ServerConfig{ config := &transport.ServerConfig{
MaxStreams: s.opts.maxConcurrentStreams, MaxStreams: s.opts.maxConcurrentStreams,
AuthInfo: authInfo, AuthInfo: authInfo,
@ -552,13 +569,13 @@ func (s *Server) serveHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo)
s.mu.Unlock() s.mu.Unlock()
c.Close() c.Close()
grpclog.Warningln("grpc: Server.Serve failed to create ServerTransport: ", err) grpclog.Warningln("grpc: Server.Serve failed to create ServerTransport: ", err)
return return nil
} }
if !s.addConn(st) { if !s.addConn(st) {
st.Close() st.Close()
return return nil
} }
s.serveStreams(st) return st
} }
func (s *Server) serveStreams(st transport.ServerTransport) { func (s *Server) serveStreams(st transport.ServerTransport) {

View File

@ -123,10 +123,9 @@ type serverHandlerTransport struct {
// when WriteStatus is called. // when WriteStatus is called.
writes chan func() writes chan func()
mu sync.Mutex // block concurrent WriteStatus calls
// streamDone indicates whether WriteStatus has been called and writes channel // e.g. grpc/(*serverStream).SendMsg/RecvMsg
// has been closed. writeStatusMu sync.Mutex
streamDone bool
} }
func (ht *serverHandlerTransport) Close() error { 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 { func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) error {
ht.mu.Lock() ht.writeStatusMu.Lock()
if ht.streamDone { defer ht.writeStatusMu.Unlock()
ht.mu.Unlock()
return nil
}
ht.streamDone = true
ht.mu.Unlock()
err := ht.do(func() { err := ht.do(func() {
ht.writeCommonHeaders(s) ht.writeCommonHeaders(s)
@ -222,7 +217,11 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro
} }
} }
}) })
if err == nil { // transport has not been closed
ht.Close()
close(ht.writes) close(ht.writes)
}
return err return err
} }

View File

@ -152,12 +152,12 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
Val: uint32(iwz)}) Val: uint32(iwz)})
} }
if err := framer.fr.WriteSettings(isettings...); err != nil { 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. // Adjust the connection flow control window if needed.
if delta := uint32(icwz - defaultWindowSize); delta > 0 { if delta := uint32(icwz - defaultWindowSize); delta > 0 {
if err := framer.fr.WriteWindowUpdate(0, delta); err != nil { 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 kp := config.KeepaliveParams
@ -223,6 +223,31 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
t.stats.HandleConn(t.ctx, connBegin) t.stats.HandleConn(t.ctx, connBegin)
} }
t.framer.writer.Flush() 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() { go func() {
loopyWriter(t.ctx, t.controlBuf, t.itemHandler) loopyWriter(t.ctx, t.controlBuf, t.itemHandler)
t.Close() t.Close()
@ -354,41 +379,6 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
// typically run in a separate goroutine. // typically run in a separate goroutine.
// traceCtx attaches trace to ctx and returns the new context. // traceCtx attaches trace to ctx and returns the new context.
func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.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 { for {
frame, err := t.framer.fr.ReadFrame() frame, err := t.framer.fr.ReadFrame()
atomic.StoreUint32(&t.activity, 1) atomic.StoreUint32(&t.activity, 1)