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