github.com/shishir-a412ed/docker@v1.3.2-0.20180103180333-fda904911d87/libcontainerd/client_daemon.go (about)

     1  // +build !windows
     2  
     3  package libcontainerd
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"runtime"
    14  	"strings"
    15  	"sync"
    16  	"syscall"
    17  	"time"
    18  
    19  	"google.golang.org/grpc"
    20  	"google.golang.org/grpc/codes"
    21  	"google.golang.org/grpc/status"
    22  
    23  	"github.com/containerd/containerd"
    24  	"github.com/containerd/containerd/api/events"
    25  	eventsapi "github.com/containerd/containerd/api/services/events/v1"
    26  	"github.com/containerd/containerd/api/types"
    27  	"github.com/containerd/containerd/archive"
    28  	"github.com/containerd/containerd/cio"
    29  	"github.com/containerd/containerd/content"
    30  	"github.com/containerd/containerd/errdefs"
    31  	"github.com/containerd/containerd/images"
    32  	"github.com/containerd/containerd/linux/runctypes"
    33  	"github.com/containerd/typeurl"
    34  	"github.com/docker/docker/pkg/ioutils"
    35  	"github.com/opencontainers/image-spec/specs-go/v1"
    36  	specs "github.com/opencontainers/runtime-spec/specs-go"
    37  	"github.com/pkg/errors"
    38  	"github.com/sirupsen/logrus"
    39  )
    40  
    41  // InitProcessName is the name given to the first process of a
    42  // container
    43  const InitProcessName = "init"
    44  
    45  type container struct {
    46  	mu sync.Mutex
    47  
    48  	bundleDir string
    49  	ctr       containerd.Container
    50  	task      containerd.Task
    51  	execs     map[string]containerd.Process
    52  	oomKilled bool
    53  }
    54  
    55  func (c *container) setTask(t containerd.Task) {
    56  	c.mu.Lock()
    57  	c.task = t
    58  	c.mu.Unlock()
    59  }
    60  
    61  func (c *container) getTask() containerd.Task {
    62  	c.mu.Lock()
    63  	t := c.task
    64  	c.mu.Unlock()
    65  	return t
    66  }
    67  
    68  func (c *container) addProcess(id string, p containerd.Process) {
    69  	c.mu.Lock()
    70  	if c.execs == nil {
    71  		c.execs = make(map[string]containerd.Process)
    72  	}
    73  	c.execs[id] = p
    74  	c.mu.Unlock()
    75  }
    76  
    77  func (c *container) deleteProcess(id string) {
    78  	c.mu.Lock()
    79  	delete(c.execs, id)
    80  	c.mu.Unlock()
    81  }
    82  
    83  func (c *container) getProcess(id string) containerd.Process {
    84  	c.mu.Lock()
    85  	p := c.execs[id]
    86  	c.mu.Unlock()
    87  	return p
    88  }
    89  
    90  func (c *container) setOOMKilled(killed bool) {
    91  	c.mu.Lock()
    92  	c.oomKilled = killed
    93  	c.mu.Unlock()
    94  }
    95  
    96  func (c *container) getOOMKilled() bool {
    97  	c.mu.Lock()
    98  	killed := c.oomKilled
    99  	c.mu.Unlock()
   100  	return killed
   101  }
   102  
   103  type client struct {
   104  	sync.RWMutex // protects containers map
   105  
   106  	remote   *containerd.Client
   107  	stateDir string
   108  	logger   *logrus.Entry
   109  
   110  	namespace  string
   111  	backend    Backend
   112  	eventQ     queue
   113  	containers map[string]*container
   114  }
   115  
   116  func (c *client) Version(ctx context.Context) (containerd.Version, error) {
   117  	return c.remote.Version(ctx)
   118  }
   119  
   120  func (c *client) Restore(ctx context.Context, id string, attachStdio StdioCallback) (alive bool, pid int, err error) {
   121  	c.Lock()
   122  	defer c.Unlock()
   123  
   124  	var rio cio.IO
   125  	defer func() {
   126  		err = wrapError(err)
   127  	}()
   128  
   129  	ctr, err := c.remote.LoadContainer(ctx, id)
   130  	if err != nil {
   131  		return false, -1, errors.WithStack(err)
   132  	}
   133  
   134  	defer func() {
   135  		if err != nil && rio != nil {
   136  			rio.Cancel()
   137  			rio.Close()
   138  		}
   139  	}()
   140  
   141  	t, err := ctr.Task(ctx, func(fifos *cio.FIFOSet) (cio.IO, error) {
   142  		io, err := newIOPipe(fifos)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  
   147  		rio, err = attachStdio(io)
   148  		return rio, err
   149  	})
   150  	if err != nil && !strings.Contains(err.Error(), "no running task found") {
   151  		return false, -1, err
   152  	}
   153  
   154  	if t != nil {
   155  		s, err := t.Status(ctx)
   156  		if err != nil {
   157  			return false, -1, err
   158  		}
   159  
   160  		alive = s.Status != containerd.Stopped
   161  		pid = int(t.Pid())
   162  	}
   163  	c.containers[id] = &container{
   164  		bundleDir: filepath.Join(c.stateDir, id),
   165  		ctr:       ctr,
   166  		task:      t,
   167  		// TODO(mlaventure): load execs
   168  	}
   169  
   170  	c.logger.WithFields(logrus.Fields{
   171  		"container": id,
   172  		"alive":     alive,
   173  		"pid":       pid,
   174  	}).Debug("restored container")
   175  
   176  	return alive, pid, nil
   177  }
   178  
   179  func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, runtimeOptions interface{}) error {
   180  	if ctr := c.getContainer(id); ctr != nil {
   181  		return errors.WithStack(newConflictError("id already in use"))
   182  	}
   183  
   184  	bdir, err := prepareBundleDir(filepath.Join(c.stateDir, id), ociSpec)
   185  	if err != nil {
   186  		return wrapSystemError(errors.Wrap(err, "prepare bundle dir failed"))
   187  	}
   188  
   189  	c.logger.WithField("bundle", bdir).WithField("root", ociSpec.Root.Path).Debug("bundle dir created")
   190  
   191  	cdCtr, err := c.remote.NewContainer(ctx, id,
   192  		containerd.WithSpec(ociSpec),
   193  		// TODO(mlaventure): when containerd support lcow, revisit runtime value
   194  		containerd.WithRuntime(fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS), runtimeOptions))
   195  	if err != nil {
   196  		return err
   197  	}
   198  
   199  	c.Lock()
   200  	c.containers[id] = &container{
   201  		bundleDir: bdir,
   202  		ctr:       cdCtr,
   203  	}
   204  	c.Unlock()
   205  
   206  	return nil
   207  }
   208  
   209  // Start create and start a task for the specified containerd id
   210  func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin bool, attachStdio StdioCallback) (int, error) {
   211  	ctr := c.getContainer(id)
   212  	if ctr == nil {
   213  		return -1, errors.WithStack(newNotFoundError("no such container"))
   214  	}
   215  	if t := ctr.getTask(); t != nil {
   216  		return -1, errors.WithStack(newConflictError("container already started"))
   217  	}
   218  
   219  	var (
   220  		cp             *types.Descriptor
   221  		t              containerd.Task
   222  		rio            cio.IO
   223  		err            error
   224  		stdinCloseSync = make(chan struct{})
   225  	)
   226  
   227  	if checkpointDir != "" {
   228  		// write checkpoint to the content store
   229  		tar := archive.Diff(ctx, "", checkpointDir)
   230  		cp, err = c.writeContent(ctx, images.MediaTypeContainerd1Checkpoint, checkpointDir, tar)
   231  		// remove the checkpoint when we're done
   232  		defer func() {
   233  			if cp != nil {
   234  				err := c.remote.ContentStore().Delete(context.Background(), cp.Digest)
   235  				if err != nil {
   236  					c.logger.WithError(err).WithFields(logrus.Fields{
   237  						"ref":    checkpointDir,
   238  						"digest": cp.Digest,
   239  					}).Warnf("failed to delete temporary checkpoint entry")
   240  				}
   241  			}
   242  		}()
   243  		if err := tar.Close(); err != nil {
   244  			return -1, errors.Wrap(err, "failed to close checkpoint tar stream")
   245  		}
   246  		if err != nil {
   247  			return -1, errors.Wrapf(err, "failed to upload checkpoint to containerd")
   248  		}
   249  	}
   250  
   251  	spec, err := ctr.ctr.Spec(ctx)
   252  	if err != nil {
   253  		return -1, errors.Wrap(err, "failed to retrieve spec")
   254  	}
   255  	uid, gid := getSpecUser(spec)
   256  	t, err = ctr.ctr.NewTask(ctx,
   257  		func(id string) (cio.IO, error) {
   258  			fifos := newFIFOSet(ctr.bundleDir, id, InitProcessName, withStdin, spec.Process.Terminal)
   259  			rio, err = c.createIO(fifos, id, InitProcessName, stdinCloseSync, attachStdio)
   260  			return rio, err
   261  		},
   262  		func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error {
   263  			info.Checkpoint = cp
   264  			info.Options = &runctypes.CreateOptions{
   265  				IoUid: uint32(uid),
   266  				IoGid: uint32(gid),
   267  			}
   268  			return nil
   269  		})
   270  	if err != nil {
   271  		close(stdinCloseSync)
   272  		if rio != nil {
   273  			rio.Cancel()
   274  			rio.Close()
   275  		}
   276  		return -1, err
   277  	}
   278  
   279  	ctr.setTask(t)
   280  
   281  	// Signal c.createIO that it can call CloseIO
   282  	close(stdinCloseSync)
   283  
   284  	if err := t.Start(ctx); err != nil {
   285  		if _, err := t.Delete(ctx); err != nil {
   286  			c.logger.WithError(err).WithField("container", id).
   287  				Error("failed to delete task after fail start")
   288  		}
   289  		ctr.setTask(nil)
   290  		return -1, err
   291  	}
   292  
   293  	return int(t.Pid()), nil
   294  }
   295  
   296  func (c *client) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error) {
   297  	ctr := c.getContainer(containerID)
   298  	if ctr == nil {
   299  		return -1, errors.WithStack(newNotFoundError("no such container"))
   300  	}
   301  	t := ctr.getTask()
   302  	if t == nil {
   303  		return -1, errors.WithStack(newInvalidParameterError("container is not running"))
   304  	}
   305  
   306  	if p := ctr.getProcess(processID); p != nil {
   307  		return -1, errors.WithStack(newConflictError("id already in use"))
   308  	}
   309  
   310  	var (
   311  		p              containerd.Process
   312  		rio            cio.IO
   313  		err            error
   314  		stdinCloseSync = make(chan struct{})
   315  	)
   316  
   317  	fifos := newFIFOSet(ctr.bundleDir, containerID, processID, withStdin, spec.Terminal)
   318  
   319  	defer func() {
   320  		if err != nil {
   321  			if rio != nil {
   322  				rio.Cancel()
   323  				rio.Close()
   324  			}
   325  			rmFIFOSet(fifos)
   326  		}
   327  	}()
   328  
   329  	p, err = t.Exec(ctx, processID, spec, func(id string) (cio.IO, error) {
   330  		rio, err = c.createIO(fifos, containerID, processID, stdinCloseSync, attachStdio)
   331  		return rio, err
   332  	})
   333  	if err != nil {
   334  		close(stdinCloseSync)
   335  		if rio != nil {
   336  			rio.Cancel()
   337  			rio.Close()
   338  		}
   339  		return -1, err
   340  	}
   341  
   342  	ctr.addProcess(processID, p)
   343  
   344  	// Signal c.createIO that it can call CloseIO
   345  	close(stdinCloseSync)
   346  
   347  	if err = p.Start(ctx); err != nil {
   348  		p.Delete(context.Background())
   349  		ctr.deleteProcess(processID)
   350  		return -1, err
   351  	}
   352  
   353  	return int(p.Pid()), nil
   354  }
   355  
   356  func (c *client) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
   357  	p, err := c.getProcess(containerID, processID)
   358  	if err != nil {
   359  		return err
   360  	}
   361  	return wrapError(p.Kill(ctx, syscall.Signal(signal)))
   362  }
   363  
   364  func (c *client) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error {
   365  	p, err := c.getProcess(containerID, processID)
   366  	if err != nil {
   367  		return err
   368  	}
   369  
   370  	return p.Resize(ctx, uint32(width), uint32(height))
   371  }
   372  
   373  func (c *client) CloseStdin(ctx context.Context, containerID, processID string) error {
   374  	p, err := c.getProcess(containerID, processID)
   375  	if err != nil {
   376  		return err
   377  	}
   378  
   379  	return p.CloseIO(ctx, containerd.WithStdinCloser)
   380  }
   381  
   382  func (c *client) Pause(ctx context.Context, containerID string) error {
   383  	p, err := c.getProcess(containerID, InitProcessName)
   384  	if err != nil {
   385  		return err
   386  	}
   387  
   388  	return p.(containerd.Task).Pause(ctx)
   389  }
   390  
   391  func (c *client) Resume(ctx context.Context, containerID string) error {
   392  	p, err := c.getProcess(containerID, InitProcessName)
   393  	if err != nil {
   394  		return err
   395  	}
   396  
   397  	return p.(containerd.Task).Resume(ctx)
   398  }
   399  
   400  func (c *client) Stats(ctx context.Context, containerID string) (*Stats, error) {
   401  	p, err := c.getProcess(containerID, InitProcessName)
   402  	if err != nil {
   403  		return nil, err
   404  	}
   405  
   406  	m, err := p.(containerd.Task).Metrics(ctx)
   407  	if err != nil {
   408  		return nil, err
   409  	}
   410  
   411  	v, err := typeurl.UnmarshalAny(m.Data)
   412  	if err != nil {
   413  		return nil, err
   414  	}
   415  	return interfaceToStats(m.Timestamp, v), nil
   416  }
   417  
   418  func (c *client) ListPids(ctx context.Context, containerID string) ([]uint32, error) {
   419  	p, err := c.getProcess(containerID, InitProcessName)
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  
   424  	pis, err := p.(containerd.Task).Pids(ctx)
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  
   429  	var pids []uint32
   430  	for _, i := range pis {
   431  		pids = append(pids, i.Pid)
   432  	}
   433  
   434  	return pids, nil
   435  }
   436  
   437  func (c *client) Summary(ctx context.Context, containerID string) ([]Summary, error) {
   438  	p, err := c.getProcess(containerID, InitProcessName)
   439  	if err != nil {
   440  		return nil, err
   441  	}
   442  
   443  	pis, err := p.(containerd.Task).Pids(ctx)
   444  	if err != nil {
   445  		return nil, err
   446  	}
   447  
   448  	var infos []Summary
   449  	for _, pi := range pis {
   450  		i, err := typeurl.UnmarshalAny(pi.Info)
   451  		if err != nil {
   452  			return nil, errors.Wrap(err, "unable to decode process details")
   453  		}
   454  		s, err := summaryFromInterface(i)
   455  		if err != nil {
   456  			return nil, err
   457  		}
   458  		infos = append(infos, *s)
   459  	}
   460  
   461  	return infos, nil
   462  }
   463  
   464  func (c *client) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) {
   465  	p, err := c.getProcess(containerID, InitProcessName)
   466  	if err != nil {
   467  		return 255, time.Now(), nil
   468  	}
   469  
   470  	status, err := p.(containerd.Task).Delete(ctx)
   471  	if err != nil {
   472  		return 255, time.Now(), nil
   473  	}
   474  
   475  	if ctr := c.getContainer(containerID); ctr != nil {
   476  		ctr.setTask(nil)
   477  	}
   478  	return status.ExitCode(), status.ExitTime(), nil
   479  }
   480  
   481  func (c *client) Delete(ctx context.Context, containerID string) error {
   482  	ctr := c.getContainer(containerID)
   483  	if ctr == nil {
   484  		return errors.WithStack(newNotFoundError("no such container"))
   485  	}
   486  
   487  	if err := ctr.ctr.Delete(ctx); err != nil {
   488  		return err
   489  	}
   490  
   491  	if os.Getenv("LIBCONTAINERD_NOCLEAN") != "1" {
   492  		if err := os.RemoveAll(ctr.bundleDir); err != nil {
   493  			c.logger.WithError(err).WithFields(logrus.Fields{
   494  				"container": containerID,
   495  				"bundle":    ctr.bundleDir,
   496  			}).Error("failed to remove state dir")
   497  		}
   498  	}
   499  
   500  	c.removeContainer(containerID)
   501  
   502  	return nil
   503  }
   504  
   505  func (c *client) Status(ctx context.Context, containerID string) (Status, error) {
   506  	ctr := c.getContainer(containerID)
   507  	if ctr == nil {
   508  		return StatusUnknown, errors.WithStack(newNotFoundError("no such container"))
   509  	}
   510  
   511  	t := ctr.getTask()
   512  	if t == nil {
   513  		return StatusUnknown, errors.WithStack(newNotFoundError("no such task"))
   514  	}
   515  
   516  	s, err := t.Status(ctx)
   517  	if err != nil {
   518  		return StatusUnknown, err
   519  	}
   520  
   521  	return Status(s.Status), nil
   522  }
   523  
   524  func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error {
   525  	p, err := c.getProcess(containerID, InitProcessName)
   526  	if err != nil {
   527  		return err
   528  	}
   529  
   530  	img, err := p.(containerd.Task).Checkpoint(ctx)
   531  	if err != nil {
   532  		return err
   533  	}
   534  	// Whatever happens, delete the checkpoint from containerd
   535  	defer func() {
   536  		err := c.remote.ImageService().Delete(context.Background(), img.Name())
   537  		if err != nil {
   538  			c.logger.WithError(err).WithField("digest", img.Target().Digest).
   539  				Warnf("failed to delete checkpoint image")
   540  		}
   541  	}()
   542  
   543  	b, err := content.ReadBlob(ctx, c.remote.ContentStore(), img.Target().Digest)
   544  	if err != nil {
   545  		return wrapSystemError(errors.Wrapf(err, "failed to retrieve checkpoint data"))
   546  	}
   547  	var index v1.Index
   548  	if err := json.Unmarshal(b, &index); err != nil {
   549  		return wrapSystemError(errors.Wrapf(err, "failed to decode checkpoint data"))
   550  	}
   551  
   552  	var cpDesc *v1.Descriptor
   553  	for _, m := range index.Manifests {
   554  		if m.MediaType == images.MediaTypeContainerd1Checkpoint {
   555  			cpDesc = &m
   556  			break
   557  		}
   558  	}
   559  	if cpDesc == nil {
   560  		return wrapSystemError(errors.Wrapf(err, "invalid checkpoint"))
   561  	}
   562  
   563  	rat, err := c.remote.ContentStore().ReaderAt(ctx, cpDesc.Digest)
   564  	if err != nil {
   565  		return wrapSystemError(errors.Wrapf(err, "failed to get checkpoint reader"))
   566  	}
   567  	defer rat.Close()
   568  	_, err = archive.Apply(ctx, checkpointDir, content.NewReader(rat))
   569  	if err != nil {
   570  		return wrapSystemError(errors.Wrapf(err, "failed to read checkpoint reader"))
   571  	}
   572  
   573  	return err
   574  }
   575  
   576  func (c *client) getContainer(id string) *container {
   577  	c.RLock()
   578  	ctr := c.containers[id]
   579  	c.RUnlock()
   580  
   581  	return ctr
   582  }
   583  
   584  func (c *client) removeContainer(id string) {
   585  	c.Lock()
   586  	delete(c.containers, id)
   587  	c.Unlock()
   588  }
   589  
   590  func (c *client) getProcess(containerID, processID string) (containerd.Process, error) {
   591  	ctr := c.getContainer(containerID)
   592  	if ctr == nil {
   593  		return nil, errors.WithStack(newNotFoundError("no such container"))
   594  	}
   595  
   596  	t := ctr.getTask()
   597  	if t == nil {
   598  		return nil, errors.WithStack(newNotFoundError("container is not running"))
   599  	}
   600  	if processID == InitProcessName {
   601  		return t, nil
   602  	}
   603  
   604  	p := ctr.getProcess(processID)
   605  	if p == nil {
   606  		return nil, errors.WithStack(newNotFoundError("no such exec"))
   607  	}
   608  	return p, nil
   609  }
   610  
   611  // createIO creates the io to be used by a process
   612  // This needs to get a pointer to interface as upon closure the process may not have yet been registered
   613  func (c *client) createIO(fifos *cio.FIFOSet, containerID, processID string, stdinCloseSync chan struct{}, attachStdio StdioCallback) (cio.IO, error) {
   614  	io, err := newIOPipe(fifos)
   615  	if err != nil {
   616  		return nil, err
   617  	}
   618  
   619  	if io.Stdin != nil {
   620  		var (
   621  			err       error
   622  			stdinOnce sync.Once
   623  		)
   624  		pipe := io.Stdin
   625  		io.Stdin = ioutils.NewWriteCloserWrapper(pipe, func() error {
   626  			stdinOnce.Do(func() {
   627  				err = pipe.Close()
   628  				// Do the rest in a new routine to avoid a deadlock if the
   629  				// Exec/Start call failed.
   630  				go func() {
   631  					<-stdinCloseSync
   632  					p, err := c.getProcess(containerID, processID)
   633  					if err == nil {
   634  						err = p.CloseIO(context.Background(), containerd.WithStdinCloser)
   635  						if err != nil && strings.Contains(err.Error(), "transport is closing") {
   636  							err = nil
   637  						}
   638  					}
   639  				}()
   640  			})
   641  			return err
   642  		})
   643  	}
   644  
   645  	rio, err := attachStdio(io)
   646  	if err != nil {
   647  		io.Cancel()
   648  		io.Close()
   649  	}
   650  	return rio, err
   651  }
   652  
   653  func (c *client) processEvent(ctr *container, et EventType, ei EventInfo) {
   654  	c.eventQ.append(ei.ContainerID, func() {
   655  		err := c.backend.ProcessEvent(ei.ContainerID, et, ei)
   656  		if err != nil {
   657  			c.logger.WithError(err).WithFields(logrus.Fields{
   658  				"container":  ei.ContainerID,
   659  				"event":      et,
   660  				"event-info": ei,
   661  			}).Error("failed to process event")
   662  		}
   663  
   664  		if et == EventExit && ei.ProcessID != ei.ContainerID {
   665  			p := ctr.getProcess(ei.ProcessID)
   666  			if p == nil {
   667  				c.logger.WithError(errors.New("no such process")).
   668  					WithFields(logrus.Fields{
   669  						"container": ei.ContainerID,
   670  						"process":   ei.ProcessID,
   671  					}).Error("exit event")
   672  				return
   673  			}
   674  			_, err = p.Delete(context.Background())
   675  			if err != nil {
   676  				c.logger.WithError(err).WithFields(logrus.Fields{
   677  					"container": ei.ContainerID,
   678  					"process":   ei.ProcessID,
   679  				}).Warn("failed to delete process")
   680  			}
   681  			ctr.deleteProcess(ei.ProcessID)
   682  
   683  			ctr := c.getContainer(ei.ContainerID)
   684  			if ctr == nil {
   685  				c.logger.WithFields(logrus.Fields{
   686  					"container": ei.ContainerID,
   687  				}).Error("failed to find container")
   688  			} else {
   689  				rmFIFOSet(newFIFOSet(ctr.bundleDir, ei.ContainerID, ei.ProcessID, true, false))
   690  			}
   691  		}
   692  	})
   693  }
   694  
   695  func (c *client) processEventStream(ctx context.Context) {
   696  	var (
   697  		err         error
   698  		eventStream eventsapi.Events_SubscribeClient
   699  		ev          *eventsapi.Envelope
   700  		et          EventType
   701  		ei          EventInfo
   702  		ctr         *container
   703  	)
   704  	defer func() {
   705  		if err != nil {
   706  			select {
   707  			case <-ctx.Done():
   708  				c.logger.WithError(ctx.Err()).
   709  					Info("stopping event stream following graceful shutdown")
   710  			default:
   711  				go c.processEventStream(ctx)
   712  			}
   713  		}
   714  	}()
   715  
   716  	eventStream, err = c.remote.EventService().Subscribe(ctx, &eventsapi.SubscribeRequest{
   717  		Filters: []string{
   718  			// Filter on both namespace *and* topic. To create an "and" filter,
   719  			// this must be a single, comma-separated string
   720  			"namespace==" + c.namespace + ",topic~=|^/tasks/|",
   721  		},
   722  	}, grpc.FailFast(false))
   723  	if err != nil {
   724  		return
   725  	}
   726  
   727  	var oomKilled bool
   728  	for {
   729  		ev, err = eventStream.Recv()
   730  		if err != nil {
   731  			errStatus, ok := status.FromError(err)
   732  			if !ok || errStatus.Code() != codes.Canceled {
   733  				c.logger.WithError(err).Error("failed to get event")
   734  			}
   735  			return
   736  		}
   737  
   738  		if ev.Event == nil {
   739  			c.logger.WithField("event", ev).Warn("invalid event")
   740  			continue
   741  		}
   742  
   743  		v, err := typeurl.UnmarshalAny(ev.Event)
   744  		if err != nil {
   745  			c.logger.WithError(err).WithField("event", ev).Warn("failed to unmarshal event")
   746  			continue
   747  		}
   748  
   749  		c.logger.WithField("topic", ev.Topic).Debug("event")
   750  
   751  		switch t := v.(type) {
   752  		case *events.TaskCreate:
   753  			et = EventCreate
   754  			ei = EventInfo{
   755  				ContainerID: t.ContainerID,
   756  				ProcessID:   t.ContainerID,
   757  				Pid:         t.Pid,
   758  			}
   759  		case *events.TaskStart:
   760  			et = EventStart
   761  			ei = EventInfo{
   762  				ContainerID: t.ContainerID,
   763  				ProcessID:   t.ContainerID,
   764  				Pid:         t.Pid,
   765  			}
   766  		case *events.TaskExit:
   767  			et = EventExit
   768  			ei = EventInfo{
   769  				ContainerID: t.ContainerID,
   770  				ProcessID:   t.ID,
   771  				Pid:         t.Pid,
   772  				ExitCode:    t.ExitStatus,
   773  				ExitedAt:    t.ExitedAt,
   774  			}
   775  		case *events.TaskOOM:
   776  			et = EventOOM
   777  			ei = EventInfo{
   778  				ContainerID: t.ContainerID,
   779  				OOMKilled:   true,
   780  			}
   781  			oomKilled = true
   782  		case *events.TaskExecAdded:
   783  			et = EventExecAdded
   784  			ei = EventInfo{
   785  				ContainerID: t.ContainerID,
   786  				ProcessID:   t.ExecID,
   787  			}
   788  		case *events.TaskExecStarted:
   789  			et = EventExecStarted
   790  			ei = EventInfo{
   791  				ContainerID: t.ContainerID,
   792  				ProcessID:   t.ExecID,
   793  				Pid:         t.Pid,
   794  			}
   795  		case *events.TaskPaused:
   796  			et = EventPaused
   797  			ei = EventInfo{
   798  				ContainerID: t.ContainerID,
   799  			}
   800  		case *events.TaskResumed:
   801  			et = EventResumed
   802  			ei = EventInfo{
   803  				ContainerID: t.ContainerID,
   804  			}
   805  		default:
   806  			c.logger.WithFields(logrus.Fields{
   807  				"topic": ev.Topic,
   808  				"type":  reflect.TypeOf(t)},
   809  			).Info("ignoring event")
   810  			continue
   811  		}
   812  
   813  		ctr = c.getContainer(ei.ContainerID)
   814  		if ctr == nil {
   815  			c.logger.WithField("container", ei.ContainerID).Warn("unknown container")
   816  			continue
   817  		}
   818  
   819  		if oomKilled {
   820  			ctr.setOOMKilled(true)
   821  			oomKilled = false
   822  		}
   823  		ei.OOMKilled = ctr.getOOMKilled()
   824  
   825  		c.processEvent(ctr, et, ei)
   826  	}
   827  }
   828  
   829  func (c *client) writeContent(ctx context.Context, mediaType, ref string, r io.Reader) (*types.Descriptor, error) {
   830  	writer, err := c.remote.ContentStore().Writer(ctx, ref, 0, "")
   831  	if err != nil {
   832  		return nil, err
   833  	}
   834  	defer writer.Close()
   835  	size, err := io.Copy(writer, r)
   836  	if err != nil {
   837  		return nil, err
   838  	}
   839  	labels := map[string]string{
   840  		"containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339),
   841  	}
   842  	if err := writer.Commit(ctx, 0, "", content.WithLabels(labels)); err != nil {
   843  		return nil, err
   844  	}
   845  	return &types.Descriptor{
   846  		MediaType: mediaType,
   847  		Digest:    writer.Digest(),
   848  		Size_:     size,
   849  	}, nil
   850  }
   851  
   852  func wrapError(err error) error {
   853  	if err == nil {
   854  		return nil
   855  	}
   856  
   857  	switch {
   858  	case errdefs.IsNotFound(err):
   859  		return wrapNotFoundError(err)
   860  	}
   861  
   862  	msg := err.Error()
   863  	for _, s := range []string{"container does not exist", "not found", "no such container"} {
   864  		if strings.Contains(msg, s) {
   865  			return wrapNotFoundError(err)
   866  		}
   867  	}
   868  	return err
   869  }