mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Remove inmemory container map
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
		
							parent
							
								
									adb15c2899
								
							
						
					
					
						commit
						b9b5dc37e3
					
				
					 9 changed files with 213 additions and 412 deletions
				
			
		| 
						 | 
				
			
			@ -325,16 +325,17 @@ func (daemon *Daemon) restore() error {
 | 
			
		|||
				alive    bool
 | 
			
		||||
				ec       uint32
 | 
			
		||||
				exitedAt time.Time
 | 
			
		||||
				process  libcontainerdtypes.Process
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			alive, _, err = daemon.containerd.Restore(context.Background(), c.ID, c.InitializeStdio)
 | 
			
		||||
			alive, _, process, err = daemon.containerd.Restore(context.Background(), c.ID, c.InitializeStdio)
 | 
			
		||||
			if err != nil && !errdefs.IsNotFound(err) {
 | 
			
		||||
				logrus.Errorf("Failed to restore container %s with containerd: %s", c.ID, err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !alive {
 | 
			
		||||
				ec, exitedAt, err = daemon.containerd.DeleteTask(context.Background(), c.ID)
 | 
			
		||||
				if err != nil && !errdefs.IsNotFound(err) {
 | 
			
		||||
			if !alive && process != nil {
 | 
			
		||||
				ec, exitedAt, err = process.Delete(context.Background())
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					logrus.WithError(err).Errorf("Failed to delete container %s from containerd", c.ID)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,13 @@ import (
 | 
			
		|||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type mockProcess struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *mockProcess) Delete(_ context.Context) (uint32, time.Time, error) {
 | 
			
		||||
	return 0, time.Time{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mock containerd client implementation, for unit tests.
 | 
			
		||||
type MockContainerdClient struct {
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -18,8 +25,8 @@ type MockContainerdClient struct {
 | 
			
		|||
func (c *MockContainerdClient) Version(ctx context.Context) (containerd.Version, error) {
 | 
			
		||||
	return containerd.Version{}, nil
 | 
			
		||||
}
 | 
			
		||||
func (c *MockContainerdClient) Restore(ctx context.Context, containerID string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, err error) {
 | 
			
		||||
	return false, 0, nil
 | 
			
		||||
func (c *MockContainerdClient) Restore(ctx context.Context, containerID string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, p libcontainerdtypes.Process, err error) {
 | 
			
		||||
	return false, 0, &mockProcess{}, nil
 | 
			
		||||
}
 | 
			
		||||
func (c *MockContainerdClient) Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}) error {
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1085,7 +1085,7 @@ func (c *client) Stats(_ context.Context, containerID string) (*libcontainerdtyp
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Restore is the handler for restoring a container
 | 
			
		||||
func (c *client) Restore(ctx context.Context, id string, attachStdio libcontainerdtypes.StdioCallback) (bool, int, error) {
 | 
			
		||||
func (c *client) Restore(ctx context.Context, id string, attachStdio libcontainerdtypes.StdioCallback) (bool, int, libcontainerdtypes.Process, error) {
 | 
			
		||||
	c.logger.WithField("container", id).Debug("restore()")
 | 
			
		||||
 | 
			
		||||
	// TODO Windows: On RS1, a re-attach isn't possible.
 | 
			
		||||
| 
						 | 
				
			
			@ -1107,10 +1107,13 @@ func (c *client) Restore(ctx context.Context, id string, attachStdio libcontaine
 | 
			
		|||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.logger.WithField("container", id).WithError(err).Debug("terminate failed on restore")
 | 
			
		||||
			return false, -1, err
 | 
			
		||||
			return false, -1, nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false, -1, nil
 | 
			
		||||
	return false, -1, &restoredProcess{
 | 
			
		||||
		c:  c,
 | 
			
		||||
		id: id,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPidsForContainer returns a list of process IDs running in a container.
 | 
			
		||||
| 
						 | 
				
			
			@ -1153,6 +1156,15 @@ func (c *client) Summary(_ context.Context, containerID string) ([]libcontainerd
 | 
			
		|||
	return pl, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type restoredProcess struct {
 | 
			
		||||
	id string
 | 
			
		||||
	c  *client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *restoredProcess) Delete(ctx context.Context) (uint32, time.Time, error) {
 | 
			
		||||
	return p.c.DeleteTask(ctx, p.id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) {
 | 
			
		||||
	ec := -1
 | 
			
		||||
	ctr := c.getContainer(containerID)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,86 +38,30 @@ import (
 | 
			
		|||
	"google.golang.org/grpc/status"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type container struct {
 | 
			
		||||
	mu sync.Mutex
 | 
			
		||||
 | 
			
		||||
	bundleDir string
 | 
			
		||||
	ctr       containerd.Container
 | 
			
		||||
	task      containerd.Task
 | 
			
		||||
	execs     map[string]containerd.Process
 | 
			
		||||
	oomKilled bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) setTask(t containerd.Task) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	c.task = t
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) getTask() containerd.Task {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	t := c.task
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) addProcess(id string, p containerd.Process) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	if c.execs == nil {
 | 
			
		||||
		c.execs = make(map[string]containerd.Process)
 | 
			
		||||
	}
 | 
			
		||||
	c.execs[id] = p
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) deleteProcess(id string) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	delete(c.execs, id)
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) getProcess(id string) containerd.Process {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	p := c.execs[id]
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
	return p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) setOOMKilled(killed bool) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	c.oomKilled = killed
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) getOOMKilled() bool {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	killed := c.oomKilled
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
	return killed
 | 
			
		||||
}
 | 
			
		||||
// DockerContainerBundlePath is the label key pointing to the container's bundle path
 | 
			
		||||
const DockerContainerBundlePath = "com.docker/engine.bundle.path"
 | 
			
		||||
 | 
			
		||||
type client struct {
 | 
			
		||||
	sync.RWMutex // protects containers map
 | 
			
		||||
 | 
			
		||||
	client   *containerd.Client
 | 
			
		||||
	stateDir string
 | 
			
		||||
	logger   *logrus.Entry
 | 
			
		||||
	ns       string
 | 
			
		||||
 | 
			
		||||
	backend    libcontainerdtypes.Backend
 | 
			
		||||
	eventQ     queue.Queue
 | 
			
		||||
	containers map[string]*container
 | 
			
		||||
	backend libcontainerdtypes.Backend
 | 
			
		||||
	eventQ  queue.Queue
 | 
			
		||||
	oomMu   sync.Mutex
 | 
			
		||||
	oom     map[string]bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient creates a new libcontainerd client from a containerd client
 | 
			
		||||
func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) {
 | 
			
		||||
	c := &client{
 | 
			
		||||
		client:     cli,
 | 
			
		||||
		stateDir:   stateDir,
 | 
			
		||||
		logger:     logrus.WithField("module", "libcontainerd").WithField("namespace", ns),
 | 
			
		||||
		ns:         ns,
 | 
			
		||||
		backend:    b,
 | 
			
		||||
		containers: make(map[string]*container),
 | 
			
		||||
		client:   cli,
 | 
			
		||||
		stateDir: stateDir,
 | 
			
		||||
		logger:   logrus.WithField("module", "libcontainerd").WithField("namespace", ns),
 | 
			
		||||
		ns:       ns,
 | 
			
		||||
		backend:  b,
 | 
			
		||||
		oom:      make(map[string]bool),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go c.processEventStream(ctx, ns)
 | 
			
		||||
| 
						 | 
				
			
			@ -131,29 +75,7 @@ func (c *client) Version(ctx context.Context) (containerd.Version, error) {
 | 
			
		|||
 | 
			
		||||
// Restore loads the containerd container.
 | 
			
		||||
// It should not be called concurrently with any other operation for the given ID.
 | 
			
		||||
func (c *client) Restore(ctx context.Context, id string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, err error) {
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	_, ok := c.containers[id]
 | 
			
		||||
	if ok {
 | 
			
		||||
		c.Unlock()
 | 
			
		||||
		return false, 0, errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cntr := &container{}
 | 
			
		||||
	c.containers[id] = cntr
 | 
			
		||||
	cntr.mu.Lock()
 | 
			
		||||
	defer cntr.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	c.Unlock()
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Lock()
 | 
			
		||||
			delete(c.containers, id)
 | 
			
		||||
			c.Unlock()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
func (c *client) Restore(ctx context.Context, id string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, p libcontainerdtypes.Process, err error) {
 | 
			
		||||
	var dio *cio.DirectIO
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err != nil && dio != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -165,13 +87,12 @@ func (c *client) Restore(ctx context.Context, id string, attachStdio libcontaine
 | 
			
		|||
 | 
			
		||||
	ctr, err := c.client.LoadContainer(ctx, id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, -1, errors.WithStack(wrapError(err))
 | 
			
		||||
		return false, -1, nil, errors.WithStack(wrapError(err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	attachIO := func(fifos *cio.FIFOSet) (cio.IO, error) {
 | 
			
		||||
		// dio must be assigned to the previously defined dio for the defer above
 | 
			
		||||
		// to handle cleanup
 | 
			
		||||
 | 
			
		||||
		dio, err = c.newDirectIO(ctx, fifos)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
| 
						 | 
				
			
			@ -180,75 +101,57 @@ func (c *client) Restore(ctx context.Context, id string, attachStdio libcontaine
 | 
			
		|||
	}
 | 
			
		||||
	t, err := ctr.Task(ctx, attachIO)
 | 
			
		||||
	if err != nil && !containerderrors.IsNotFound(err) {
 | 
			
		||||
		return false, -1, errors.Wrap(wrapError(err), "error getting containerd task for container")
 | 
			
		||||
		return false, -1, nil, errors.Wrap(wrapError(err), "error getting containerd task for container")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t != nil {
 | 
			
		||||
		s, err := t.Status(ctx)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, -1, errors.Wrap(wrapError(err), "error getting task status")
 | 
			
		||||
			return false, -1, nil, errors.Wrap(wrapError(err), "error getting task status")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		alive = s.Status != containerd.Stopped
 | 
			
		||||
		pid = int(t.Pid())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cntr.bundleDir = filepath.Join(c.stateDir, id)
 | 
			
		||||
	cntr.ctr = ctr
 | 
			
		||||
	cntr.task = t
 | 
			
		||||
	// TODO(mlaventure): load execs
 | 
			
		||||
 | 
			
		||||
	c.logger.WithFields(logrus.Fields{
 | 
			
		||||
		"container": id,
 | 
			
		||||
		"alive":     alive,
 | 
			
		||||
		"pid":       pid,
 | 
			
		||||
	}).Debug("restored container")
 | 
			
		||||
 | 
			
		||||
	return alive, pid, nil
 | 
			
		||||
	return alive, pid, &restoredProcess{
 | 
			
		||||
		p: t,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, runtimeOptions interface{}) error {
 | 
			
		||||
	if ctr := c.getContainer(id); ctr != nil {
 | 
			
		||||
		return errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bdir := filepath.Join(c.stateDir, id)
 | 
			
		||||
	bdir := c.bundleDir(id)
 | 
			
		||||
	c.logger.WithField("bundle", bdir).WithField("root", ociSpec.Root.Path).Debug("bundle dir created")
 | 
			
		||||
 | 
			
		||||
	cdCtr, err := c.client.NewContainer(ctx, id,
 | 
			
		||||
	_, err := c.client.NewContainer(ctx, id,
 | 
			
		||||
		containerd.WithSpec(ociSpec),
 | 
			
		||||
		containerd.WithRuntime(runtimeName, runtimeOptions),
 | 
			
		||||
		WithBundle(bdir, ociSpec),
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if containerderrors.IsAlreadyExists(err) {
 | 
			
		||||
			return errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
 | 
			
		||||
		}
 | 
			
		||||
		return wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	c.containers[id] = &container{
 | 
			
		||||
		bundleDir: bdir,
 | 
			
		||||
		ctr:       cdCtr,
 | 
			
		||||
	}
 | 
			
		||||
	c.Unlock()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start create and start a task for the specified containerd id
 | 
			
		||||
func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {
 | 
			
		||||
	ctr := c.getContainer(id)
 | 
			
		||||
	if ctr == nil {
 | 
			
		||||
		return -1, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
 | 
			
		||||
	ctr, err := c.getContainer(ctx, id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
	if t := ctr.getTask(); t != nil {
 | 
			
		||||
		return -1, errors.WithStack(errdefs.Conflict(errors.New("container already started")))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		cp             *types.Descriptor
 | 
			
		||||
		t              containerd.Task
 | 
			
		||||
		rio            cio.IO
 | 
			
		||||
		err            error
 | 
			
		||||
		stdinCloseSync = make(chan struct{})
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -276,14 +179,19 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spec, err := ctr.ctr.Spec(ctx)
 | 
			
		||||
	spec, err := ctr.Spec(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return -1, errors.Wrap(err, "failed to retrieve spec")
 | 
			
		||||
	}
 | 
			
		||||
	labels, err := ctr.Labels(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return -1, errors.Wrap(err, "failed to retreive labels")
 | 
			
		||||
	}
 | 
			
		||||
	bundle := labels[DockerContainerBundlePath]
 | 
			
		||||
	uid, gid := getSpecUser(spec)
 | 
			
		||||
	t, err = ctr.ctr.NewTask(ctx,
 | 
			
		||||
	t, err = ctr.NewTask(ctx,
 | 
			
		||||
		func(id string) (cio.IO, error) {
 | 
			
		||||
			fifos := newFIFOSet(ctr.bundleDir, libcontainerdtypes.InitProcessName, withStdin, spec.Process.Terminal)
 | 
			
		||||
			fifos := newFIFOSet(bundle, libcontainerdtypes.InitProcessName, withStdin, spec.Process.Terminal)
 | 
			
		||||
 | 
			
		||||
			rio, err = c.createIO(fifos, id, libcontainerdtypes.InitProcessName, stdinCloseSync, attachStdio)
 | 
			
		||||
			return rio, err
 | 
			
		||||
| 
						 | 
				
			
			@ -313,8 +221,6 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
 | 
			
		|||
		return -1, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctr.setTask(t)
 | 
			
		||||
 | 
			
		||||
	// Signal c.createIO that it can call CloseIO
 | 
			
		||||
	close(stdinCloseSync)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -323,7 +229,6 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
 | 
			
		|||
			c.logger.WithError(err).WithField("container", id).
 | 
			
		||||
				Error("failed to delete task after fail start")
 | 
			
		||||
		}
 | 
			
		||||
		ctr.setTask(nil)
 | 
			
		||||
		return -1, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -338,27 +243,30 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
 | 
			
		|||
// the Start call. stdinCloseSync channel should be closed after Start exec
 | 
			
		||||
// process.
 | 
			
		||||
func (c *client) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {
 | 
			
		||||
	ctr := c.getContainer(containerID)
 | 
			
		||||
	if ctr == nil {
 | 
			
		||||
		return -1, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
 | 
			
		||||
	ctr, err := c.getContainer(ctx, containerID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
	t := ctr.getTask()
 | 
			
		||||
	if t == nil {
 | 
			
		||||
		return -1, errors.WithStack(errdefs.InvalidParameter(errors.New("container is not running")))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p := ctr.getProcess(processID); p != nil {
 | 
			
		||||
		return -1, errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
 | 
			
		||||
	t, err := ctr.Task(ctx, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if containerderrors.IsNotFound(err) {
 | 
			
		||||
			return -1, errors.WithStack(errdefs.InvalidParameter(errors.New("container is not running")))
 | 
			
		||||
		}
 | 
			
		||||
		return -1, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		p              containerd.Process
 | 
			
		||||
		rio            cio.IO
 | 
			
		||||
		err            error
 | 
			
		||||
		stdinCloseSync = make(chan struct{})
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	fifos := newFIFOSet(ctr.bundleDir, processID, withStdin, spec.Terminal)
 | 
			
		||||
	labels, err := ctr.Labels(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return -1, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fifos := newFIFOSet(labels[DockerContainerBundlePath], processID, withStdin, spec.Terminal)
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -375,11 +283,12 @@ func (c *client) Exec(ctx context.Context, containerID, processID string, spec *
 | 
			
		|||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		close(stdinCloseSync)
 | 
			
		||||
		if containerderrors.IsAlreadyExists(err) {
 | 
			
		||||
			return -1, errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
 | 
			
		||||
		}
 | 
			
		||||
		return -1, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctr.addProcess(processID, p)
 | 
			
		||||
 | 
			
		||||
	// Signal c.createIO that it can call CloseIO
 | 
			
		||||
	//
 | 
			
		||||
	// the stdin of exec process will be created after p.Start in containerd
 | 
			
		||||
| 
						 | 
				
			
			@ -392,15 +301,13 @@ func (c *client) Exec(ctx context.Context, containerID, processID string, spec *
 | 
			
		|||
		ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
		p.Delete(ctx)
 | 
			
		||||
		ctr.deleteProcess(processID)
 | 
			
		||||
		return -1, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return int(p.Pid()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
 | 
			
		||||
	p, err := c.getProcess(containerID, processID)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, processID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -408,7 +315,7 @@ func (c *client) SignalProcess(ctx context.Context, containerID, processID strin
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error {
 | 
			
		||||
	p, err := c.getProcess(containerID, processID)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, processID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -417,7 +324,7 @@ func (c *client) ResizeTerminal(ctx context.Context, containerID, processID stri
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) CloseStdin(ctx context.Context, containerID, processID string) error {
 | 
			
		||||
	p, err := c.getProcess(containerID, processID)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, processID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -426,7 +333,7 @@ func (c *client) CloseStdin(ctx context.Context, containerID, processID string)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Pause(ctx context.Context, containerID string) error {
 | 
			
		||||
	p, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -435,7 +342,7 @@ func (c *client) Pause(ctx context.Context, containerID string) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Resume(ctx context.Context, containerID string) error {
 | 
			
		||||
	p, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -444,7 +351,7 @@ func (c *client) Resume(ctx context.Context, containerID string) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Stats(ctx context.Context, containerID string) (*libcontainerdtypes.Stats, error) {
 | 
			
		||||
	p, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -462,7 +369,7 @@ func (c *client) Stats(ctx context.Context, containerID string) (*libcontainerdt
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) ListPids(ctx context.Context, containerID string) ([]uint32, error) {
 | 
			
		||||
	p, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -481,7 +388,7 @@ func (c *client) ListPids(ctx context.Context, containerID string) ([]uint32, er
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Summary(ctx context.Context, containerID string) ([]libcontainerdtypes.Summary, error) {
 | 
			
		||||
	p, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -507,68 +414,75 @@ func (c *client) Summary(ctx context.Context, containerID string) ([]libcontaine
 | 
			
		|||
	return infos, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type restoredProcess struct {
 | 
			
		||||
	p containerd.Process
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *restoredProcess) Delete(ctx context.Context) (uint32, time.Time, error) {
 | 
			
		||||
	if p.p == nil {
 | 
			
		||||
		return 255, time.Now(), nil
 | 
			
		||||
	}
 | 
			
		||||
	status, err := p.p.Delete(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 255, time.Now(), nil
 | 
			
		||||
	}
 | 
			
		||||
	return status.ExitCode(), status.ExitTime(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) {
 | 
			
		||||
	p, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 255, time.Now(), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status, err := p.(containerd.Task).Delete(ctx)
 | 
			
		||||
	status, err := p.Delete(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 255, time.Now(), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctr := c.getContainer(containerID); ctr != nil {
 | 
			
		||||
		ctr.setTask(nil)
 | 
			
		||||
	}
 | 
			
		||||
	return status.ExitCode(), status.ExitTime(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Delete(ctx context.Context, containerID string) error {
 | 
			
		||||
	ctr := c.getContainer(containerID)
 | 
			
		||||
	if ctr == nil {
 | 
			
		||||
		return errors.WithStack(errdefs.NotFound(errors.New("no such container")))
 | 
			
		||||
	ctr, err := c.getContainer(ctx, containerID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := ctr.ctr.Delete(ctx); err != nil {
 | 
			
		||||
	labels, err := ctr.Labels(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	bundle := labels[DockerContainerBundlePath]
 | 
			
		||||
	if err := ctr.Delete(ctx); err != nil {
 | 
			
		||||
		return wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.oomMu.Lock()
 | 
			
		||||
	delete(c.oom, containerID)
 | 
			
		||||
	c.oomMu.Unlock()
 | 
			
		||||
	if os.Getenv("LIBCONTAINERD_NOCLEAN") != "1" {
 | 
			
		||||
		if err := os.RemoveAll(ctr.bundleDir); err != nil {
 | 
			
		||||
		if err := os.RemoveAll(bundle); err != nil {
 | 
			
		||||
			c.logger.WithError(err).WithFields(logrus.Fields{
 | 
			
		||||
				"container": containerID,
 | 
			
		||||
				"bundle":    ctr.bundleDir,
 | 
			
		||||
				"bundle":    bundle,
 | 
			
		||||
			}).Error("failed to remove state dir")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.removeContainer(containerID)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Status(ctx context.Context, containerID string) (containerd.ProcessStatus, error) {
 | 
			
		||||
	ctr := c.getContainer(containerID)
 | 
			
		||||
	if ctr == nil {
 | 
			
		||||
		return containerd.Unknown, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
 | 
			
		||||
	t, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return containerd.Unknown, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t := ctr.getTask()
 | 
			
		||||
	if t == nil {
 | 
			
		||||
		return containerd.Unknown, errors.WithStack(errdefs.NotFound(errors.New("no such task")))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, err := t.Status(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return containerd.Unknown, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s.Status, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error {
 | 
			
		||||
	p, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -633,37 +547,38 @@ func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDi
 | 
			
		|||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) getContainer(id string) *container {
 | 
			
		||||
	c.RLock()
 | 
			
		||||
	ctr := c.containers[id]
 | 
			
		||||
	c.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return ctr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) removeContainer(id string) {
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	delete(c.containers, id)
 | 
			
		||||
	c.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) getProcess(containerID, processID string) (containerd.Process, error) {
 | 
			
		||||
	ctr := c.getContainer(containerID)
 | 
			
		||||
	if ctr == nil {
 | 
			
		||||
		return nil, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
 | 
			
		||||
func (c *client) getContainer(ctx context.Context, id string) (containerd.Container, error) {
 | 
			
		||||
	ctr, err := c.client.LoadContainer(ctx, id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if containerderrors.IsNotFound(err) {
 | 
			
		||||
			return nil, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
 | 
			
		||||
		}
 | 
			
		||||
		return nil, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
	return ctr, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	t := ctr.getTask()
 | 
			
		||||
	if t == nil {
 | 
			
		||||
		return nil, errors.WithStack(errdefs.NotFound(errors.New("container is not running")))
 | 
			
		||||
func (c *client) getProcess(ctx context.Context, containerID, processID string) (containerd.Process, error) {
 | 
			
		||||
	ctr, err := c.getContainer(ctx, containerID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	t, err := ctr.Task(ctx, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if containerderrors.IsNotFound(err) {
 | 
			
		||||
			return nil, errors.WithStack(errdefs.NotFound(errors.New("container is not running")))
 | 
			
		||||
		}
 | 
			
		||||
		return nil, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
	if processID == libcontainerdtypes.InitProcessName {
 | 
			
		||||
		return t, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := ctr.getProcess(processID)
 | 
			
		||||
	if p == nil {
 | 
			
		||||
		return nil, errors.WithStack(errdefs.NotFound(errors.New("no such exec")))
 | 
			
		||||
	p, err := t.LoadProcess(ctx, processID, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if containerderrors.IsNotFound(err) {
 | 
			
		||||
			return nil, errors.WithStack(errdefs.NotFound(errors.New("no such exec")))
 | 
			
		||||
		}
 | 
			
		||||
		return nil, wrapError(err)
 | 
			
		||||
	}
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -693,7 +608,7 @@ func (c *client) createIO(fifos *cio.FIFOSet, containerID, processID string, std
 | 
			
		|||
				// Exec/Start call failed.
 | 
			
		||||
				go func() {
 | 
			
		||||
					<-stdinCloseSync
 | 
			
		||||
					p, err := c.getProcess(containerID, processID)
 | 
			
		||||
					p, err := c.getProcess(context.Background(), containerID, processID)
 | 
			
		||||
					if err == nil {
 | 
			
		||||
						err = p.CloseIO(context.Background(), containerd.WithStdinCloser)
 | 
			
		||||
						if err != nil && strings.Contains(err.Error(), "transport is closing") {
 | 
			
		||||
| 
						 | 
				
			
			@ -714,7 +629,7 @@ func (c *client) createIO(fifos *cio.FIFOSet, containerID, processID string, std
 | 
			
		|||
	return rio, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) processEvent(ctr *container, et libcontainerdtypes.EventType, ei libcontainerdtypes.EventInfo) {
 | 
			
		||||
func (c *client) processEvent(ctx context.Context, et libcontainerdtypes.EventType, ei libcontainerdtypes.EventInfo) {
 | 
			
		||||
	c.eventQ.Append(ei.ContainerID, func() {
 | 
			
		||||
		err := c.backend.ProcessEvent(ei.ContainerID, et, ei)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -726,10 +641,12 @@ func (c *client) processEvent(ctr *container, et libcontainerdtypes.EventType, e
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		if et == libcontainerdtypes.EventExit && ei.ProcessID != ei.ContainerID {
 | 
			
		||||
			p := ctr.getProcess(ei.ProcessID)
 | 
			
		||||
			if p == nil {
 | 
			
		||||
			p, err := c.getProcess(ctx, ei.ContainerID, ei.ProcessID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
 | 
			
		||||
				c.logger.WithError(errors.New("no such process")).
 | 
			
		||||
					WithFields(logrus.Fields{
 | 
			
		||||
						"error":     err,
 | 
			
		||||
						"container": ei.ContainerID,
 | 
			
		||||
						"process":   ei.ProcessID,
 | 
			
		||||
					}).Error("exit event")
 | 
			
		||||
| 
						 | 
				
			
			@ -742,15 +659,23 @@ func (c *client) processEvent(ctr *container, et libcontainerdtypes.EventType, e
 | 
			
		|||
					"process":   ei.ProcessID,
 | 
			
		||||
				}).Warn("failed to delete process")
 | 
			
		||||
			}
 | 
			
		||||
			ctr.deleteProcess(ei.ProcessID)
 | 
			
		||||
 | 
			
		||||
			ctr := c.getContainer(ei.ContainerID)
 | 
			
		||||
			if ctr == nil {
 | 
			
		||||
			ctr, err := c.getContainer(ctx, ei.ContainerID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				c.logger.WithFields(logrus.Fields{
 | 
			
		||||
					"container": ei.ContainerID,
 | 
			
		||||
					"error":     err,
 | 
			
		||||
				}).Error("failed to find container")
 | 
			
		||||
			} else {
 | 
			
		||||
				newFIFOSet(ctr.bundleDir, ei.ProcessID, true, false).Close()
 | 
			
		||||
				labels, err := ctr.Labels(ctx)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					c.logger.WithFields(logrus.Fields{
 | 
			
		||||
						"container": ei.ContainerID,
 | 
			
		||||
						"error":     err,
 | 
			
		||||
					}).Error("failed to find container")
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				newFIFOSet(labels[DockerContainerBundlePath], ei.ProcessID, true, false).Close()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
| 
						 | 
				
			
			@ -762,7 +687,6 @@ func (c *client) processEventStream(ctx context.Context, ns string) {
 | 
			
		|||
		ev  *events.Envelope
 | 
			
		||||
		et  libcontainerdtypes.EventType
 | 
			
		||||
		ei  libcontainerdtypes.EventInfo
 | 
			
		||||
		ctr *container
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Filter on both namespace *and* topic. To create an "and" filter,
 | 
			
		||||
| 
						 | 
				
			
			@ -771,8 +695,8 @@ func (c *client) processEventStream(ctx context.Context, ns string) {
 | 
			
		|||
 | 
			
		||||
	c.logger.Debug("processing event stream")
 | 
			
		||||
 | 
			
		||||
	var oomKilled bool
 | 
			
		||||
	for {
 | 
			
		||||
		var oomKilled bool
 | 
			
		||||
		select {
 | 
			
		||||
		case err = <-errC:
 | 
			
		||||
			if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -861,19 +785,14 @@ func (c *client) processEventStream(ctx context.Context, ns string) {
 | 
			
		|||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ctr = c.getContainer(ei.ContainerID)
 | 
			
		||||
			if ctr == nil {
 | 
			
		||||
				c.logger.WithField("container", ei.ContainerID).Warn("unknown container")
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			c.oomMu.Lock()
 | 
			
		||||
			if oomKilled {
 | 
			
		||||
				ctr.setOOMKilled(true)
 | 
			
		||||
				oomKilled = false
 | 
			
		||||
				c.oom[ei.ContainerID] = true
 | 
			
		||||
			}
 | 
			
		||||
			ei.OOMKilled = ctr.getOOMKilled()
 | 
			
		||||
			ei.OOMKilled = c.oom[ei.ContainerID]
 | 
			
		||||
			c.oomMu.Unlock()
 | 
			
		||||
 | 
			
		||||
			c.processEvent(ctr, et, ei)
 | 
			
		||||
			c.processEvent(ctx, et, ei)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -901,6 +820,10 @@ func (c *client) writeContent(ctx context.Context, mediaType, ref string, r io.R
 | 
			
		|||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) bundleDir(id string) string {
 | 
			
		||||
	return filepath.Join(c.stateDir, id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func wrapError(err error) error {
 | 
			
		||||
	switch {
 | 
			
		||||
	case err == nil:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ func summaryFromInterface(i interface{}) (*libcontainerdtypes.Summary, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) UpdateResources(ctx context.Context, containerID string, resources *libcontainerdtypes.Resources) error {
 | 
			
		||||
	p, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -62,8 +62,12 @@ func getSpecUser(ociSpec *specs.Spec) (int, int) {
 | 
			
		|||
// WithBundle creates the bundle for the container
 | 
			
		||||
func WithBundle(bundleDir string, ociSpec *specs.Spec) containerd.NewContainerOpts {
 | 
			
		||||
	return func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
 | 
			
		||||
		if c.Labels == nil {
 | 
			
		||||
			c.Labels = make(map[string]string)
 | 
			
		||||
		}
 | 
			
		||||
		uid, gid := getSpecUser(ociSpec)
 | 
			
		||||
		if uid == 0 && gid == 0 {
 | 
			
		||||
			c.Labels[DockerContainerBundlePath] = bundleDir
 | 
			
		||||
			return idtools.MkdirAllAndChownNew(bundleDir, 0755, idtools.Identity{UID: 0, GID: 0})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +86,10 @@ func WithBundle(bundleDir string, ociSpec *specs.Spec) containerd.NewContainerOp
 | 
			
		|||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if c.Labels == nil {
 | 
			
		||||
			c.Labels = make(map[string]string)
 | 
			
		||||
		}
 | 
			
		||||
		c.Labels[DockerContainerBundlePath] = p
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,10 @@ func summaryFromInterface(i interface{}) (*libcontainerdtypes.Summary, error) {
 | 
			
		|||
func WithBundle(bundleDir string, ociSpec *specs.Spec) containerd.NewContainerOpts {
 | 
			
		||||
	return func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
 | 
			
		||||
		// TODO: (containerd) Determine if we need to use system.MkdirAllWithACL here
 | 
			
		||||
		if c.Labels == nil {
 | 
			
		||||
			c.Labels = make(map[string]string)
 | 
			
		||||
		}
 | 
			
		||||
		c.Labels[DockerContainerBundlePath] = bundleDir
 | 
			
		||||
		return os.MkdirAll(bundleDir, 0755)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,11 +41,16 @@ type Backend interface {
 | 
			
		|||
	ProcessEvent(containerID string, event EventType, ei EventInfo) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Process of a container
 | 
			
		||||
type Process interface {
 | 
			
		||||
	Delete(context.Context) (uint32, time.Time, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client provides access to containerd features.
 | 
			
		||||
type Client interface {
 | 
			
		||||
	Version(ctx context.Context) (containerd.Version, error)
 | 
			
		||||
 | 
			
		||||
	Restore(ctx context.Context, containerID string, attachStdio StdioCallback) (alive bool, pid int, err error)
 | 
			
		||||
	Restore(ctx context.Context, containerID string, attachStdio StdioCallback) (alive bool, pid int, p Process, err error)
 | 
			
		||||
 | 
			
		||||
	Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}) error
 | 
			
		||||
	Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio StdioCallback) (pid int, err error)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@ import (
 | 
			
		|||
	"io"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	"github.com/containerd/containerd/cio"
 | 
			
		||||
| 
						 | 
				
			
			@ -26,19 +25,6 @@ type ExitHandler interface {
 | 
			
		|||
	HandleExitEvent(id string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client is used by the exector to perform operations.
 | 
			
		||||
// TODO(@cpuguy83): This should really just be based off the containerd client interface.
 | 
			
		||||
// However right now this whole package is tied to github.com/docker/docker/libcontainerd
 | 
			
		||||
type Client interface {
 | 
			
		||||
	Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}) error
 | 
			
		||||
	Restore(ctx context.Context, containerID string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, err error)
 | 
			
		||||
	Status(ctx context.Context, containerID string) (containerd.ProcessStatus, error)
 | 
			
		||||
	Delete(ctx context.Context, containerID string) error
 | 
			
		||||
	DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error)
 | 
			
		||||
	Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (pid int, err error)
 | 
			
		||||
	SignalProcess(ctx context.Context, containerID, processID string, signal int) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New creates a new containerd plugin executor
 | 
			
		||||
func New(ctx context.Context, rootDir string, cli *containerd.Client, exitHandler ExitHandler) (*Executor, error) {
 | 
			
		||||
	e := &Executor{
 | 
			
		||||
| 
						 | 
				
			
			@ -57,19 +43,23 @@ func New(ctx context.Context, rootDir string, cli *containerd.Client, exitHandle
 | 
			
		|||
// Executor is the containerd client implementation of a plugin executor
 | 
			
		||||
type Executor struct {
 | 
			
		||||
	rootDir     string
 | 
			
		||||
	client      Client
 | 
			
		||||
	client      libcontainerdtypes.Client
 | 
			
		||||
	exitHandler ExitHandler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// deleteTaskAndContainer deletes plugin task and then plugin container from containerd
 | 
			
		||||
func deleteTaskAndContainer(ctx context.Context, cli Client, id string) {
 | 
			
		||||
	_, _, err := cli.DeleteTask(ctx, id)
 | 
			
		||||
	if err != nil && !errdefs.IsNotFound(err) {
 | 
			
		||||
		logrus.WithError(err).WithField("id", id).Error("failed to delete plugin task from containerd")
 | 
			
		||||
func deleteTaskAndContainer(ctx context.Context, cli libcontainerdtypes.Client, id string, p libcontainerdtypes.Process) {
 | 
			
		||||
	if p != nil {
 | 
			
		||||
		if _, _, err := p.Delete(ctx); err != nil && !errdefs.IsNotFound(err) {
 | 
			
		||||
			logrus.WithError(err).WithField("id", id).Error("failed to delete plugin task from containerd")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if _, _, err := cli.DeleteTask(ctx, id); err != nil && !errdefs.IsNotFound(err) {
 | 
			
		||||
			logrus.WithError(err).WithField("id", id).Error("failed to delete plugin task from containerd")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cli.Delete(ctx, id)
 | 
			
		||||
	if err != nil && !errdefs.IsNotFound(err) {
 | 
			
		||||
	if err := cli.Delete(ctx, id); err != nil && !errdefs.IsNotFound(err) {
 | 
			
		||||
		logrus.WithError(err).WithField("id", id).Error("failed to delete plugin container from containerd")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -103,19 +93,19 @@ func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteClo
 | 
			
		|||
 | 
			
		||||
	_, err = e.client.Start(ctx, id, "", false, attachStreamsFunc(stdout, stderr))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		deleteTaskAndContainer(ctx, e.client, id)
 | 
			
		||||
		deleteTaskAndContainer(ctx, e.client, id, nil)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restore restores a container
 | 
			
		||||
func (e *Executor) Restore(id string, stdout, stderr io.WriteCloser) (bool, error) {
 | 
			
		||||
	alive, _, err := e.client.Restore(context.Background(), id, attachStreamsFunc(stdout, stderr))
 | 
			
		||||
	alive, _, p, err := e.client.Restore(context.Background(), id, attachStreamsFunc(stdout, stderr))
 | 
			
		||||
	if err != nil && !errdefs.IsNotFound(err) {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	if !alive {
 | 
			
		||||
		deleteTaskAndContainer(context.Background(), e.client, id)
 | 
			
		||||
		deleteTaskAndContainer(context.Background(), e.client, id, p)
 | 
			
		||||
	}
 | 
			
		||||
	return alive, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +126,7 @@ func (e *Executor) Signal(id string, signal int) error {
 | 
			
		|||
func (e *Executor) ProcessEvent(id string, et libcontainerdtypes.EventType, ei libcontainerdtypes.EventInfo) error {
 | 
			
		||||
	switch et {
 | 
			
		||||
	case libcontainerdtypes.EventExit:
 | 
			
		||||
		deleteTaskAndContainer(context.Background(), e.client, id)
 | 
			
		||||
		deleteTaskAndContainer(context.Background(), e.client, id, nil)
 | 
			
		||||
		return e.exitHandler.HandleExitEvent(ei.ContainerID)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,149 +0,0 @@
 | 
			
		|||
package containerd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
 | 
			
		||||
	"github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"gotest.tools/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestLifeCycle(t *testing.T) {
 | 
			
		||||
	t.Parallel()
 | 
			
		||||
 | 
			
		||||
	mock := newMockClient()
 | 
			
		||||
	exec, cleanup := setupTest(t, mock, mock)
 | 
			
		||||
	defer cleanup()
 | 
			
		||||
 | 
			
		||||
	id := "test-create"
 | 
			
		||||
	mock.simulateStartError(true, id)
 | 
			
		||||
	err := exec.Create(id, specs.Spec{}, nil, nil)
 | 
			
		||||
	assert.Assert(t, err != nil)
 | 
			
		||||
	mock.simulateStartError(false, id)
 | 
			
		||||
 | 
			
		||||
	err = exec.Create(id, specs.Spec{}, nil, nil)
 | 
			
		||||
	assert.NilError(t, err)
 | 
			
		||||
	running, _ := exec.IsRunning(id)
 | 
			
		||||
	assert.Assert(t, running)
 | 
			
		||||
 | 
			
		||||
	// create with the same ID
 | 
			
		||||
	err = exec.Create(id, specs.Spec{}, nil, nil)
 | 
			
		||||
	assert.Assert(t, err != nil)
 | 
			
		||||
 | 
			
		||||
	mock.HandleExitEvent(id) // simulate a plugin that exits
 | 
			
		||||
 | 
			
		||||
	err = exec.Create(id, specs.Spec{}, nil, nil)
 | 
			
		||||
	assert.NilError(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setupTest(t *testing.T, client Client, eh ExitHandler) (*Executor, func()) {
 | 
			
		||||
	rootDir, err := ioutil.TempDir("", "test-daemon")
 | 
			
		||||
	assert.NilError(t, err)
 | 
			
		||||
	assert.Assert(t, client != nil)
 | 
			
		||||
	assert.Assert(t, eh != nil)
 | 
			
		||||
 | 
			
		||||
	return &Executor{
 | 
			
		||||
			rootDir:     rootDir,
 | 
			
		||||
			client:      client,
 | 
			
		||||
			exitHandler: eh,
 | 
			
		||||
		}, func() {
 | 
			
		||||
			assert.Assert(t, os.RemoveAll(rootDir))
 | 
			
		||||
		}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type mockClient struct {
 | 
			
		||||
	mu           sync.Mutex
 | 
			
		||||
	containers   map[string]bool
 | 
			
		||||
	errorOnStart map[string]bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newMockClient() *mockClient {
 | 
			
		||||
	return &mockClient{
 | 
			
		||||
		containers:   make(map[string]bool),
 | 
			
		||||
		errorOnStart: make(map[string]bool),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *mockClient) Create(ctx context.Context, id string, _ *specs.Spec, _ interface{}) error {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if _, ok := c.containers[id]; ok {
 | 
			
		||||
		return errors.New("exists")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.containers[id] = false
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *mockClient) Restore(ctx context.Context, id string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, err error) {
 | 
			
		||||
	return false, 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *mockClient) Status(ctx context.Context, id string) (containerd.ProcessStatus, error) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	running, ok := c.containers[id]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return containerd.Unknown, errors.New("not found")
 | 
			
		||||
	}
 | 
			
		||||
	if running {
 | 
			
		||||
		return containerd.Running, nil
 | 
			
		||||
	}
 | 
			
		||||
	return containerd.Stopped, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *mockClient) Delete(ctx context.Context, id string) error {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
	delete(c.containers, id)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *mockClient) DeleteTask(ctx context.Context, id string) (uint32, time.Time, error) {
 | 
			
		||||
	return 0, time.Time{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *mockClient) Start(ctx context.Context, id, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (pid int, err error) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if _, ok := c.containers[id]; !ok {
 | 
			
		||||
		return 0, errors.New("not found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.errorOnStart[id] {
 | 
			
		||||
		return 0, errors.New("some startup error")
 | 
			
		||||
	}
 | 
			
		||||
	c.containers[id] = true
 | 
			
		||||
	return 1, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *mockClient) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *mockClient) simulateStartError(sim bool, id string) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
	if sim {
 | 
			
		||||
		c.errorOnStart[id] = sim
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	delete(c.errorOnStart, id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *mockClient) HandleExitEvent(id string) error {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
	delete(c.containers, id)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue