Port ctr to use client
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
		
							
								
								
									
										22
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								client.go
									
									
									
									
									
								
							@@ -17,6 +17,7 @@ import (
 | 
			
		||||
	imagesapi "github.com/containerd/containerd/api/services/images"
 | 
			
		||||
	namespacesapi "github.com/containerd/containerd/api/services/namespaces"
 | 
			
		||||
	snapshotapi "github.com/containerd/containerd/api/services/snapshot"
 | 
			
		||||
	versionservice "github.com/containerd/containerd/api/services/version"
 | 
			
		||||
	"github.com/containerd/containerd/content"
 | 
			
		||||
	"github.com/containerd/containerd/images"
 | 
			
		||||
	"github.com/containerd/containerd/remotes"
 | 
			
		||||
@@ -28,6 +29,7 @@ import (
 | 
			
		||||
	imagesservice "github.com/containerd/containerd/services/images"
 | 
			
		||||
	snapshotservice "github.com/containerd/containerd/services/snapshot"
 | 
			
		||||
	"github.com/containerd/containerd/snapshot"
 | 
			
		||||
	pempty "github.com/golang/protobuf/ptypes/empty"
 | 
			
		||||
	"github.com/opencontainers/image-spec/identity"
 | 
			
		||||
	"github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
@@ -462,3 +464,23 @@ func (c *Client) DiffService() diff.DiffService {
 | 
			
		||||
func (c *Client) HealthService() grpc_health_v1.HealthClient {
 | 
			
		||||
	return grpc_health_v1.NewHealthClient(c.conn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) VersionService() versionservice.VersionClient {
 | 
			
		||||
	return versionservice.NewVersionClient(c.conn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Version struct {
 | 
			
		||||
	Version  string
 | 
			
		||||
	Revision string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) Version(ctx context.Context) (Version, error) {
 | 
			
		||||
	response, err := c.VersionService().Version(ctx, &pempty.Empty{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Version{}, err
 | 
			
		||||
	}
 | 
			
		||||
	return Version{
 | 
			
		||||
		Version:  response.Version,
 | 
			
		||||
		Revision: response.Revision,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,9 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	gocontext "context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/containerd/containerd/api/types/descriptor"
 | 
			
		||||
	"github.com/containerd/containerd/archive"
 | 
			
		||||
	"github.com/containerd/containerd/content"
 | 
			
		||||
	"github.com/containerd/containerd/images"
 | 
			
		||||
	"github.com/containerd/containerd/rootfs"
 | 
			
		||||
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
@@ -25,169 +12,42 @@ var checkpointCommand = cli.Command{
 | 
			
		||||
	Name:  "checkpoint",
 | 
			
		||||
	Usage: "checkpoint a container",
 | 
			
		||||
	Flags: []cli.Flag{
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "id",
 | 
			
		||||
			Usage: "id of the container",
 | 
			
		||||
		},
 | 
			
		||||
		cli.BoolFlag{
 | 
			
		||||
			Name:  "exit",
 | 
			
		||||
			Usage: "stop the container after the checkpoint",
 | 
			
		||||
		},
 | 
			
		||||
		cli.BoolFlag{
 | 
			
		||||
			Name:  "binds",
 | 
			
		||||
			Usage: "checkpoint bind mounts with the checkpoint",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	Action: func(context *cli.Context) error {
 | 
			
		||||
		var (
 | 
			
		||||
			id          = context.String("id")
 | 
			
		||||
			ctx, cancel = appContext(context)
 | 
			
		||||
			id          = context.Args().First()
 | 
			
		||||
		)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			return errors.New("container id must be provided")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		content, err := getContentStore(context)
 | 
			
		||||
		container, err := client.LoadContainer(ctx, id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		imageStore, err := getImageStore(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return errors.Wrap(err, "failed resolving image store")
 | 
			
		||||
		}
 | 
			
		||||
		var spec specs.Spec
 | 
			
		||||
		info, err := tasks.Info(ctx, &execution.InfoRequest{
 | 
			
		||||
			ContainerID: id,
 | 
			
		||||
		})
 | 
			
		||||
		task, err := container.Task(ctx, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err := json.Unmarshal(info.Task.Spec.Value, &spec); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		var opts []containerd.CheckpointOpts
 | 
			
		||||
		if context.Bool("exit") {
 | 
			
		||||
			opts = append(opts, containerd.WithExit)
 | 
			
		||||
		}
 | 
			
		||||
		stopped := context.Bool("exit")
 | 
			
		||||
		// if the container will still be running after the checkpoint make sure that
 | 
			
		||||
		// we pause the container and give us time to checkpoint the filesystem before
 | 
			
		||||
		// it resumes execution
 | 
			
		||||
		if !stopped {
 | 
			
		||||
			if _, err := tasks.Pause(ctx, &execution.PauseRequest{
 | 
			
		||||
				ContainerID: id,
 | 
			
		||||
			}); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if _, err := tasks.Resume(ctx, &execution.ResumeRequest{
 | 
			
		||||
					ContainerID: id,
 | 
			
		||||
				}); err != nil {
 | 
			
		||||
					logrus.WithError(err).Error("ctr: unable to resume container")
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		checkpoint, err := tasks.Checkpoint(ctx, &execution.CheckpointRequest{
 | 
			
		||||
			ContainerID: id,
 | 
			
		||||
			Exit:        context.Bool("exit"),
 | 
			
		||||
		})
 | 
			
		||||
		checkpoint, err := task.Checkpoint(ctx, opts...)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		image, err := imageStore.Get(ctx, spec.Annotations["image"])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		var additionalDescriptors []*descriptor.Descriptor
 | 
			
		||||
		if context.Bool("binds") {
 | 
			
		||||
			if additionalDescriptors, err = checkpointBinds(ctx, &spec, content); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		var index ocispec.Index
 | 
			
		||||
		for _, d := range append(checkpoint.Descriptors, additionalDescriptors...) {
 | 
			
		||||
			index.Manifests = append(index.Manifests, ocispec.Descriptor{
 | 
			
		||||
				MediaType: d.MediaType,
 | 
			
		||||
				Size:      d.Size_,
 | 
			
		||||
				Digest:    d.Digest,
 | 
			
		||||
				Platform: &ocispec.Platform{
 | 
			
		||||
					OS:           runtime.GOOS,
 | 
			
		||||
					Architecture: runtime.GOARCH,
 | 
			
		||||
				},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		// add image to the index
 | 
			
		||||
		index.Manifests = append(index.Manifests, image.Target)
 | 
			
		||||
		// checkpoint rw layer
 | 
			
		||||
		snapshotter, err := getSnapshotter(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		differ, err := getDiffService(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		rw, err := rootfs.Diff(ctx, id, fmt.Sprintf("checkpoint-rw-%s", id), snapshotter, differ)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		rw.Platform = &ocispec.Platform{
 | 
			
		||||
			OS:           runtime.GOOS,
 | 
			
		||||
			Architecture: runtime.GOARCH,
 | 
			
		||||
		}
 | 
			
		||||
		index.Manifests = append(index.Manifests, rw)
 | 
			
		||||
		data, err := json.Marshal(index)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		// write the index to the content store
 | 
			
		||||
		buf := bytes.NewReader(data)
 | 
			
		||||
		desc, err := writeContent(ctx, content, ocispec.MediaTypeImageIndex, id, buf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println(desc.Digest.String())
 | 
			
		||||
		fmt.Println(checkpoint.Digest.String())
 | 
			
		||||
		return nil
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkpointBinds(ctx gocontext.Context, s *specs.Spec, store content.Store) ([]*descriptor.Descriptor, error) {
 | 
			
		||||
	var out []*descriptor.Descriptor
 | 
			
		||||
	for _, m := range s.Mounts {
 | 
			
		||||
		if m.Type != "bind" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		tar := archive.Diff(ctx, "", m.Source)
 | 
			
		||||
		d, err := writeContent(ctx, store, images.MediaTypeContainerd1Resource, m.Source, tar)
 | 
			
		||||
		if err := tar.Close(); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		out = append(out, d)
 | 
			
		||||
	}
 | 
			
		||||
	return out, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeContent(ctx gocontext.Context, store content.Store, mediaType, ref string, r io.Reader) (*descriptor.Descriptor, error) {
 | 
			
		||||
	writer, err := store.Writer(ctx, ref, 0, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer writer.Close()
 | 
			
		||||
	size, err := io.Copy(writer, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := writer.Commit(0, ""); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &descriptor.Descriptor{
 | 
			
		||||
		MediaType: mediaType,
 | 
			
		||||
		Digest:    writer.Digest(),
 | 
			
		||||
		Size_:     size,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,8 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"google.golang.org/grpc"
 | 
			
		||||
	"google.golang.org/grpc/codes"
 | 
			
		||||
 | 
			
		||||
	containersapi "github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -19,49 +13,17 @@ var deleteCommand = cli.Command{
 | 
			
		||||
	Action: func(context *cli.Context) error {
 | 
			
		||||
		ctx, cancel := appContext(context)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		containers, err := getContainersService(context)
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		container, err := client.LoadContainer(ctx, context.Args().First())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		snapshotter, err := getSnapshotter(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		if _, err := container.Task(ctx, nil); err == nil {
 | 
			
		||||
			return fmt.Errorf("cannot delete a container with a running task")
 | 
			
		||||
		}
 | 
			
		||||
		id := context.Args().First()
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			return errors.New("container id must be provided")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		_, err = containers.Delete(ctx, &containersapi.DeleteContainerRequest{
 | 
			
		||||
			ID: id,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return errors.Wrap(err, "failed to delete container")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		_, err = tasks.Delete(ctx, &execution.DeleteRequest{
 | 
			
		||||
			ContainerID: id,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Ignore error if task has already been removed, task is
 | 
			
		||||
			// removed by default after run
 | 
			
		||||
			if grpc.Code(errors.Cause(err)) != codes.NotFound {
 | 
			
		||||
				return errors.Wrap(err, "failed to task container")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if runtime.GOOS != "windows" {
 | 
			
		||||
			if err := snapshotter.Remove(ctx, id); err != nil {
 | 
			
		||||
				return errors.Wrapf(err, "failed to remove snapshot %q", id)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
		return container.Delete(ctx)
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var eventsCommand = cli.Command{
 | 
			
		||||
	Name:  "events",
 | 
			
		||||
	Usage: "display containerd events",
 | 
			
		||||
	Action: func(context *cli.Context) error {
 | 
			
		||||
		ctx, cancel := appContext(context)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		events, err := tasks.Events(ctx, &execution.EventsRequest{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
 | 
			
		||||
		fmt.Fprintln(w, "TYPE\tID\tPID\tEXIT_STATUS")
 | 
			
		||||
		for {
 | 
			
		||||
			e, err := events.Recv()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if _, err := fmt.Fprintf(w,
 | 
			
		||||
				"%s\t%s\t%d\t%d\n",
 | 
			
		||||
				e.Type.String(),
 | 
			
		||||
				e.ID,
 | 
			
		||||
				e.Pid,
 | 
			
		||||
				e.ExitStatus,
 | 
			
		||||
			); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if err := w.Flush(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +1,11 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/containerd/console"
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -14,10 +13,6 @@ var execCommand = cli.Command{
 | 
			
		||||
	Name:  "exec",
 | 
			
		||||
	Usage: "execute additional processes in an existing container",
 | 
			
		||||
	Flags: []cli.Flag{
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "id",
 | 
			
		||||
			Usage: "id of the container",
 | 
			
		||||
		},
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "cwd",
 | 
			
		||||
			Usage: "working directory of the new process",
 | 
			
		||||
@@ -29,61 +24,75 @@ var execCommand = cli.Command{
 | 
			
		||||
	},
 | 
			
		||||
	Action: func(context *cli.Context) error {
 | 
			
		||||
		var (
 | 
			
		||||
			id          = context.String("id")
 | 
			
		||||
			ctx, cancel = appContext(context)
 | 
			
		||||
			id          = context.Args().First()
 | 
			
		||||
			args        = context.Args().Tail()
 | 
			
		||||
			tty         = context.Bool("tty")
 | 
			
		||||
		)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			return errors.New("container id must be provided")
 | 
			
		||||
		}
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		container, err := client.LoadContainer(ctx, id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		spec, err := container.Spec()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		task, err := container.Task(ctx, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		events, err := tasks.Events(ctx, &execution.EventsRequest{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		tmpDir, err := getTempDir(id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		defer os.RemoveAll(tmpDir)
 | 
			
		||||
		request, err := newExecRequest(context, tmpDir, id)
 | 
			
		||||
		pspec := &spec.Process
 | 
			
		||||
		pspec.Terminal = tty
 | 
			
		||||
		pspec.Args = args
 | 
			
		||||
 | 
			
		||||
		io := containerd.Stdio
 | 
			
		||||
		if tty {
 | 
			
		||||
			io = containerd.StdioTerminal
 | 
			
		||||
		}
 | 
			
		||||
		process, err := task.Exec(ctx, pspec, io)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		defer process.Delete()
 | 
			
		||||
 | 
			
		||||
		statusC := make(chan uint32, 1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			status, err := process.Wait(ctx)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logrus.WithError(err).Error("wait process")
 | 
			
		||||
			}
 | 
			
		||||
			statusC <- status
 | 
			
		||||
		}()
 | 
			
		||||
		var con console.Console
 | 
			
		||||
		if request.Terminal {
 | 
			
		||||
		if tty {
 | 
			
		||||
			con = console.Current()
 | 
			
		||||
			defer con.Reset()
 | 
			
		||||
			if err := con.SetRaw(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		fwg, err := prepareStdio(request.Stdin, request.Stdout, request.Stderr, request.Terminal)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
		if err := process.Start(ctx); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		response, err := tasks.Exec(ctx, request)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if request.Terminal {
 | 
			
		||||
			if err := handleConsoleResize(ctx, tasks, id, response.Pid, con); err != nil {
 | 
			
		||||
		if tty {
 | 
			
		||||
			if err := handleConsoleResize(ctx, process, con); err != nil {
 | 
			
		||||
				logrus.WithError(err).Error("console resize")
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			sigc := forwardAllSignals(ctx, process)
 | 
			
		||||
			defer stopCatch(sigc)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Ensure we read all io only if container started successfully.
 | 
			
		||||
		defer fwg.Wait()
 | 
			
		||||
 | 
			
		||||
		status, err := waitContainer(events, id, response.Pid)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		status := <-statusC
 | 
			
		||||
		if status != 0 {
 | 
			
		||||
			return cli.NewExitError("", int(status))
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,69 +0,0 @@
 | 
			
		||||
// +build !windows
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	protobuf "github.com/gogo/protobuf/types"
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func createProcessSpec(args []string, cwd string, tty bool) specs.Process {
 | 
			
		||||
	env := []string{
 | 
			
		||||
		"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
 | 
			
		||||
	}
 | 
			
		||||
	if tty {
 | 
			
		||||
		env = append(env, "TERM=xterm")
 | 
			
		||||
	}
 | 
			
		||||
	if cwd == "" {
 | 
			
		||||
		cwd = "/"
 | 
			
		||||
	}
 | 
			
		||||
	return specs.Process{
 | 
			
		||||
		Args:            args,
 | 
			
		||||
		Env:             env,
 | 
			
		||||
		Terminal:        tty,
 | 
			
		||||
		Cwd:             cwd,
 | 
			
		||||
		NoNewPrivileges: true,
 | 
			
		||||
		User: specs.User{
 | 
			
		||||
			UID: 0,
 | 
			
		||||
			GID: 0,
 | 
			
		||||
		},
 | 
			
		||||
		Capabilities: &specs.LinuxCapabilities{
 | 
			
		||||
			Bounding:    capabilities,
 | 
			
		||||
			Permitted:   capabilities,
 | 
			
		||||
			Inheritable: capabilities,
 | 
			
		||||
			Effective:   capabilities,
 | 
			
		||||
			Ambient:     capabilities,
 | 
			
		||||
		},
 | 
			
		||||
		Rlimits: []specs.LinuxRlimit{
 | 
			
		||||
			{
 | 
			
		||||
				Type: "RLIMIT_NOFILE",
 | 
			
		||||
				Hard: uint64(1024),
 | 
			
		||||
				Soft: uint64(1024),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newExecRequest(context *cli.Context, tmpDir, id string) (*execution.ExecRequest, error) {
 | 
			
		||||
	process := createProcessSpec(context.Args(), context.String("cwd"), context.Bool("tty"))
 | 
			
		||||
	data, err := json.Marshal(process)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &execution.ExecRequest{
 | 
			
		||||
		ContainerID: id,
 | 
			
		||||
		Spec: &protobuf.Any{
 | 
			
		||||
			TypeUrl: specs.Version,
 | 
			
		||||
			Value:   data,
 | 
			
		||||
		},
 | 
			
		||||
		Terminal: context.Bool("tty"),
 | 
			
		||||
		Stdin:    filepath.Join(tmpDir, "stdin"),
 | 
			
		||||
		Stdout:   filepath.Join(tmpDir, "stdout"),
 | 
			
		||||
		Stderr:   filepath.Join(tmpDir, "stderr"),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	protobuf "github.com/gogo/protobuf/types"
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newExecRequest(context *cli.Context, tmpDir, id string) (*execution.ExecRequest, error) {
 | 
			
		||||
	process := specs.Process{
 | 
			
		||||
		Args:     context.Args(),
 | 
			
		||||
		Terminal: context.Bool("tty"),
 | 
			
		||||
		Cwd:      context.String("cwd"),
 | 
			
		||||
	}
 | 
			
		||||
	data, err := json.Marshal(process)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	now := time.Now().UnixNano()
 | 
			
		||||
	request := &execution.ExecRequest{
 | 
			
		||||
		ContainerID: id,
 | 
			
		||||
		Spec: &protobuf.Any{
 | 
			
		||||
			TypeUrl: specs.Version,
 | 
			
		||||
			Value:   data,
 | 
			
		||||
		},
 | 
			
		||||
		Terminal: context.Bool("tty"),
 | 
			
		||||
		Stdin:    fmt.Sprintf(`%s\ctr-%s-stdin-%d`, pipeRoot, id, now),
 | 
			
		||||
		Stdout:   fmt.Sprintf(`%s\ctr-%s-stdout-%d`, pipeRoot, id, now),
 | 
			
		||||
	}
 | 
			
		||||
	if !request.Terminal {
 | 
			
		||||
		request.Stderr = fmt.Sprintf(`%s\ctr-%s-stderr-%d`, pipeRoot, id, now)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return request, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -3,11 +3,8 @@ package main
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	gocontext "context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	containersapi "github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
@@ -15,55 +12,29 @@ import (
 | 
			
		||||
var infoCommand = cli.Command{
 | 
			
		||||
	Name:  "info",
 | 
			
		||||
	Usage: "get info about a container",
 | 
			
		||||
	Flags: []cli.Flag{
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "id",
 | 
			
		||||
			Usage: "id of the container",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	Action: func(context *cli.Context) error {
 | 
			
		||||
		var (
 | 
			
		||||
			id          = context.String("id")
 | 
			
		||||
			ctx, cancel = appContext(context)
 | 
			
		||||
			id          = context.Args().First()
 | 
			
		||||
		)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			return errors.New("container id must be provided")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		containers, err := getContainersService(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		containerResponse, err := containers.Get(ctx, &containersapi.GetContainerRequest{ID: id})
 | 
			
		||||
		container, err := client.LoadContainer(ctx, id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO(stevvooe): Just dumping the container and the task, for now. We
 | 
			
		||||
		// should split this into two separate commands.
 | 
			
		||||
		cjson, err := json.MarshalIndent(containerResponse, "", "    ")
 | 
			
		||||
		cjson, err := json.MarshalIndent(container, "", "    ")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fmt.Println(string(cjson))
 | 
			
		||||
 | 
			
		||||
		response, err := tasks.Info(gocontext.Background(), &execution.InfoRequest{ContainerID: id})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		json, err := json.MarshalIndent(response, "", "    ")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println(string(json))
 | 
			
		||||
		return nil
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
@@ -11,8 +10,9 @@ var killCommand = cli.Command{
 | 
			
		||||
	Usage: "signal a container (default: SIGTERM)",
 | 
			
		||||
	Flags: []cli.Flag{
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "id",
 | 
			
		||||
			Usage: "id of the container",
 | 
			
		||||
			Name:  "signal, s",
 | 
			
		||||
			Value: "SIGTERM",
 | 
			
		||||
			Usage: "signal to send to the container",
 | 
			
		||||
		},
 | 
			
		||||
		cli.IntFlag{
 | 
			
		||||
			Name:  "pid",
 | 
			
		||||
@@ -26,53 +26,36 @@ var killCommand = cli.Command{
 | 
			
		||||
	},
 | 
			
		||||
	Action: func(context *cli.Context) error {
 | 
			
		||||
		var (
 | 
			
		||||
			id          = context.String("id")
 | 
			
		||||
			id          = context.Args().First()
 | 
			
		||||
			ctx, cancel = appContext(context)
 | 
			
		||||
		)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			return errors.New("container id must be provided")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sigstr := context.Args().First()
 | 
			
		||||
		if sigstr == "" {
 | 
			
		||||
			sigstr = "SIGTERM"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		signal, err := parseSignal(sigstr)
 | 
			
		||||
		signal, err := parseSignal(context.String("signal"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pid := context.Int("pid")
 | 
			
		||||
		all := context.Bool("all")
 | 
			
		||||
		var (
 | 
			
		||||
			pid = context.Int("pid")
 | 
			
		||||
			all = context.Bool("all")
 | 
			
		||||
		)
 | 
			
		||||
		if pid > 0 && all {
 | 
			
		||||
			return errors.New("enter a pid or all; not both")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		killRequest := &execution.KillRequest{
 | 
			
		||||
			ContainerID: id,
 | 
			
		||||
			Signal:      uint32(signal),
 | 
			
		||||
			PidOrAll: &execution.KillRequest_Pid{
 | 
			
		||||
				Pid: uint32(pid),
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if all {
 | 
			
		||||
			killRequest.PidOrAll = &execution.KillRequest_All{
 | 
			
		||||
				All: true,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		_, err = tasks.Kill(ctx, killRequest)
 | 
			
		||||
		container, err := client.LoadContainer(ctx, id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
		task, err := container.Task(ctx, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return task.Kill(ctx, signal)
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,11 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	gocontext "context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	containersapi "github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	tasktypes "github.com/containerd/containerd/api/types/task"
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -33,69 +30,59 @@ var listCommand = cli.Command{
 | 
			
		||||
		)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		containers, err := getContainersService(context)
 | 
			
		||||
		containers, err := client.Containers(ctx)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		response, err := containers.List(gocontext.Background(), &containersapi.ListContainersRequest{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if quiet {
 | 
			
		||||
			for _, c := range response.Containers {
 | 
			
		||||
				fmt.Println(c.ID)
 | 
			
		||||
			for _, c := range containers {
 | 
			
		||||
				fmt.Println(c.ID())
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
			tasksResponse, err := tasks.List(ctx, &execution.ListRequest{})
 | 
			
		||||
		w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
 | 
			
		||||
		fmt.Fprintln(w, "ID\tIMAGE\tPID\tSTATUS")
 | 
			
		||||
		for _, c := range containers {
 | 
			
		||||
			image, err := c.Image(ctx)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Join with tasks to get status.
 | 
			
		||||
			tasksByContainerID := map[string]*tasktypes.Task{}
 | 
			
		||||
			for _, task := range tasksResponse.Tasks {
 | 
			
		||||
				task.Descriptor()
 | 
			
		||||
				tasksByContainerID[task.ContainerID] = task
 | 
			
		||||
			var (
 | 
			
		||||
				status string
 | 
			
		||||
				pid    uint32
 | 
			
		||||
			)
 | 
			
		||||
			task, err := c.Task(ctx, nil)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				s, err := task.Status(ctx)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				status = string(s)
 | 
			
		||||
				pid = task.Pid()
 | 
			
		||||
			} else {
 | 
			
		||||
				if err != containerd.ErrNoRunningTask {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				status = string(containerd.Stopped)
 | 
			
		||||
				pid = 0
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
 | 
			
		||||
			fmt.Fprintln(w, "ID\tIMAGE\tPID\tSTATUS")
 | 
			
		||||
			for _, c := range response.Containers {
 | 
			
		||||
				var (
 | 
			
		||||
					status string
 | 
			
		||||
					pid    uint32
 | 
			
		||||
				)
 | 
			
		||||
				task, ok := tasksByContainerID[c.ID]
 | 
			
		||||
				if ok {
 | 
			
		||||
					status = task.Status.String()
 | 
			
		||||
					pid = task.Pid
 | 
			
		||||
				} else {
 | 
			
		||||
					status = "STOPPED" // TODO(stevvooe): Is this assumption correct?
 | 
			
		||||
					pid = 0
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if _, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\n",
 | 
			
		||||
					c.ID,
 | 
			
		||||
					c.Image,
 | 
			
		||||
					pid,
 | 
			
		||||
					status,
 | 
			
		||||
				); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				if err := w.Flush(); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			if _, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\n",
 | 
			
		||||
				c.ID(),
 | 
			
		||||
				image.Name(),
 | 
			
		||||
				pid,
 | 
			
		||||
				status,
 | 
			
		||||
			); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if err := w.Flush(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,7 @@ import (
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	extraCmds = []cli.Command{}
 | 
			
		||||
)
 | 
			
		||||
var extraCmds = []cli.Command{}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	cli.VersionPrinter = func(c *cli.Context) {
 | 
			
		||||
@@ -30,7 +28,7 @@ func main() {
 | 
			
		||||
/ /__/ /_/ /
 | 
			
		||||
\___/\__/_/
 | 
			
		||||
 | 
			
		||||
containerd client
 | 
			
		||||
containerd CLI
 | 
			
		||||
`
 | 
			
		||||
	app.Flags = []cli.Flag{
 | 
			
		||||
		cli.BoolFlag{
 | 
			
		||||
@@ -53,10 +51,9 @@ containerd client
 | 
			
		||||
			EnvVar: "CONTAINERD_NAMESPACE",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	app.Commands = []cli.Command{
 | 
			
		||||
	app.Commands = append([]cli.Command{
 | 
			
		||||
		checkpointCommand,
 | 
			
		||||
		runCommand,
 | 
			
		||||
		eventsCommand,
 | 
			
		||||
		deleteCommand,
 | 
			
		||||
		namespacesCommand,
 | 
			
		||||
		listCommand,
 | 
			
		||||
@@ -69,8 +66,7 @@ containerd client
 | 
			
		||||
		snapshotCommand,
 | 
			
		||||
		versionCommand,
 | 
			
		||||
		psCommand,
 | 
			
		||||
	}
 | 
			
		||||
	app.Commands = append(app.Commands, extraCmds...)
 | 
			
		||||
	}, extraCmds...)
 | 
			
		||||
	app.Before = func(context *cli.Context) error {
 | 
			
		||||
		if context.GlobalBool("debug") {
 | 
			
		||||
			logrus.SetLevel(logrus.DebugLevel)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,6 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
import "github.com/urfave/cli"
 | 
			
		||||
 | 
			
		||||
var pauseCommand = cli.Command{
 | 
			
		||||
	Name:      "pause",
 | 
			
		||||
@@ -15,17 +10,18 @@ var pauseCommand = cli.Command{
 | 
			
		||||
		ctx, cancel := appContext(context)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		id := context.Args().First()
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			return errors.New("container id must be provided")
 | 
			
		||||
		container, err := client.LoadContainer(ctx, context.Args().First())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		_, err = tasks.Pause(ctx, &execution.PauseRequest{
 | 
			
		||||
			ContainerID: id,
 | 
			
		||||
		})
 | 
			
		||||
		return err
 | 
			
		||||
		task, err := container.Task(ctx, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return task.Pause(ctx)
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
@@ -13,15 +12,9 @@ import (
 | 
			
		||||
var psCommand = cli.Command{
 | 
			
		||||
	Name:  "ps",
 | 
			
		||||
	Usage: "list processes for container",
 | 
			
		||||
	Flags: []cli.Flag{
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "id",
 | 
			
		||||
			Usage: "id of the container",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	Action: func(context *cli.Context) error {
 | 
			
		||||
		var (
 | 
			
		||||
			id          = context.String("id")
 | 
			
		||||
			id          = context.Args().First()
 | 
			
		||||
			ctx, cancel = appContext(context)
 | 
			
		||||
		)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
@@ -29,34 +22,33 @@ var psCommand = cli.Command{
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			return errors.New("container id must be provided")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pr := &execution.ProcessesRequest{
 | 
			
		||||
			ContainerID: id,
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		container, err := client.LoadContainer(ctx, id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		resp, err := tasks.Processes(ctx, pr)
 | 
			
		||||
		task, err := container.Task(ctx, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		processes, err := task.Processes(ctx)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
 | 
			
		||||
		fmt.Fprintln(w, "PID")
 | 
			
		||||
		for _, ps := range resp.Processes {
 | 
			
		||||
			if _, err := fmt.Fprintf(w, "%d\n",
 | 
			
		||||
				ps.Pid,
 | 
			
		||||
			); err != nil {
 | 
			
		||||
		for _, ps := range processes {
 | 
			
		||||
			if _, err := fmt.Fprintf(w, "%d\n", ps); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err := w.Flush(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,6 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
import "github.com/urfave/cli"
 | 
			
		||||
 | 
			
		||||
var resumeCommand = cli.Command{
 | 
			
		||||
	Name:      "resume",
 | 
			
		||||
@@ -15,17 +10,18 @@ var resumeCommand = cli.Command{
 | 
			
		||||
		ctx, cancel := appContext(context)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		id := context.Args().First()
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			return errors.New("container id must be provided")
 | 
			
		||||
		container, err := client.LoadContainer(ctx, context.Args().First())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		_, err = tasks.Resume(ctx, &execution.ResumeRequest{
 | 
			
		||||
			ContainerID: id,
 | 
			
		||||
		})
 | 
			
		||||
		return err
 | 
			
		||||
		task, err := container.Task(ctx, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return task.Resume(ctx)
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										294
									
								
								cmd/ctr/run.go
									
									
									
									
									
								
							
							
						
						
									
										294
									
								
								cmd/ctr/run.go
									
									
									
									
									
								
							@@ -1,52 +1,63 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	gocontext "context"
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/containerd/console"
 | 
			
		||||
	containersapi "github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/containerd/containerd/images"
 | 
			
		||||
	"github.com/containerd/containerd/mount"
 | 
			
		||||
	"github.com/containerd/containerd/snapshot"
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	digest "github.com/opencontainers/go-digest"
 | 
			
		||||
	"github.com/opencontainers/image-spec/identity"
 | 
			
		||||
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type resizer interface {
 | 
			
		||||
	Resize(ctx gocontext.Context, w, h uint32) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type killer interface {
 | 
			
		||||
	Kill(gocontext.Context, syscall.Signal) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func withEnv(context *cli.Context) containerd.SpecOpts {
 | 
			
		||||
	return func(s *specs.Spec) error {
 | 
			
		||||
		env := context.StringSlice("env")
 | 
			
		||||
		if len(env) > 0 {
 | 
			
		||||
			s.Process.Env = append(s.Process.Env, env...)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func withMounts(context *cli.Context) containerd.SpecOpts {
 | 
			
		||||
	return func(s *specs.Spec) error {
 | 
			
		||||
		for _, mount := range context.StringSlice("mount") {
 | 
			
		||||
			m, err := parseMountFlag(mount)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			s.Mounts = append(s.Mounts, m)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var runCommand = cli.Command{
 | 
			
		||||
	Name:      "run",
 | 
			
		||||
	Usage:     "run a container",
 | 
			
		||||
	ArgsUsage: "IMAGE [COMMAND] [ARG...]",
 | 
			
		||||
	ArgsUsage: "IMAGE ID [COMMAND] [ARG...]",
 | 
			
		||||
	Flags: []cli.Flag{
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "id",
 | 
			
		||||
			Usage: "id of the container",
 | 
			
		||||
		},
 | 
			
		||||
		cli.BoolFlag{
 | 
			
		||||
			Name:  "tty,t",
 | 
			
		||||
			Usage: "allocate a TTY for the container",
 | 
			
		||||
		},
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "rootfs",
 | 
			
		||||
			Usage: "path to rootfs",
 | 
			
		||||
		},
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "runtime",
 | 
			
		||||
			Usage: "runtime name (linux, windows, vmware-linux)",
 | 
			
		||||
			Value: "linux",
 | 
			
		||||
		},
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "runtime-config",
 | 
			
		||||
			Usage: "set the OCI config file for the container",
 | 
			
		||||
		},
 | 
			
		||||
		cli.BoolFlag{
 | 
			
		||||
			Name:  "readonly",
 | 
			
		||||
			Usage: "set the containers filesystem as readonly",
 | 
			
		||||
@@ -74,237 +85,72 @@ var runCommand = cli.Command{
 | 
			
		||||
	},
 | 
			
		||||
	Action: func(context *cli.Context) error {
 | 
			
		||||
		var (
 | 
			
		||||
			err         error
 | 
			
		||||
			mounts      []mount.Mount
 | 
			
		||||
			imageConfig ocispec.Image
 | 
			
		||||
			err             error
 | 
			
		||||
			checkpointIndex digest.Digest
 | 
			
		||||
 | 
			
		||||
			ctx, cancel = appContext(context)
 | 
			
		||||
			id          = context.String("id")
 | 
			
		||||
			id          = context.Args().Get(1)
 | 
			
		||||
			tty         = context.Bool("tty")
 | 
			
		||||
		)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			return errors.New("container id must be provided")
 | 
			
		||||
		}
 | 
			
		||||
		containers, err := getContainersService(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		tasks, err := getTasksService(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		tmpDir, err := getTempDir(id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		defer os.RemoveAll(tmpDir)
 | 
			
		||||
		events, err := tasks.Events(ctx, &execution.EventsRequest{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		content, err := getContentStore(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		snapshotter, err := getSnapshotter(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		imageStore, err := getImageStore(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return errors.Wrap(err, "failed resolving image store")
 | 
			
		||||
		}
 | 
			
		||||
		differ, err := getDiffService(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		var (
 | 
			
		||||
			checkpoint      *ocispec.Descriptor
 | 
			
		||||
			checkpointIndex digest.Digest
 | 
			
		||||
			ref             = context.Args().First()
 | 
			
		||||
		)
 | 
			
		||||
		if raw := context.String("checkpoint"); raw != "" {
 | 
			
		||||
			if checkpointIndex, err = digest.Parse(raw); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		var spec []byte
 | 
			
		||||
		if checkpointIndex != "" {
 | 
			
		||||
			var index ocispec.Index
 | 
			
		||||
			r, err := content.Reader(ctx, checkpointIndex)
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		container, err := newContainer(ctx, client, context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if context.Bool("rm") {
 | 
			
		||||
			defer container.Delete(ctx)
 | 
			
		||||
		}
 | 
			
		||||
		task, err := newTask(ctx, container, checkpointIndex, tty)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		defer task.Delete(ctx)
 | 
			
		||||
 | 
			
		||||
		statusC := make(chan uint32, 1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			status, err := task.Wait(ctx)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
				logrus.WithError(err).Error("wait process")
 | 
			
		||||
			}
 | 
			
		||||
			err = json.NewDecoder(r).Decode(&index)
 | 
			
		||||
			r.Close()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			var rw ocispec.Descriptor
 | 
			
		||||
			for _, m := range index.Manifests {
 | 
			
		||||
				switch m.MediaType {
 | 
			
		||||
				case images.MediaTypeContainerd1Checkpoint:
 | 
			
		||||
					fkingo := m
 | 
			
		||||
					checkpoint = &fkingo
 | 
			
		||||
				case images.MediaTypeContainerd1CheckpointConfig:
 | 
			
		||||
					if r, err = content.Reader(ctx, m.Digest); err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					spec, err = ioutil.ReadAll(r)
 | 
			
		||||
					r.Close()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				case images.MediaTypeDockerSchema2Manifest:
 | 
			
		||||
					// make sure we have the original image that was used during checkpoint
 | 
			
		||||
					diffIDs, err := images.RootFS(ctx, content, m)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					if _, err := snapshotter.Prepare(ctx, id, identity.ChainID(diffIDs).String()); err != nil {
 | 
			
		||||
						if !snapshot.IsExist(err) {
 | 
			
		||||
							return err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				case ocispec.MediaTypeImageLayer:
 | 
			
		||||
					rw = m
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if mounts, err = snapshotter.Mounts(ctx, id); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if _, err := differ.Apply(ctx, rw, mounts); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if runtime.GOOS != "windows" && context.String("rootfs") == "" {
 | 
			
		||||
				image, err := imageStore.Get(ctx, ref)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return errors.Wrapf(err, "could not resolve %q", ref)
 | 
			
		||||
				}
 | 
			
		||||
				// let's close out our db and tx so we don't hold the lock whilst running.
 | 
			
		||||
				diffIDs, err := image.RootFS(ctx, content)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				if context.Bool("readonly") {
 | 
			
		||||
					mounts, err = snapshotter.View(ctx, id, identity.ChainID(diffIDs).String())
 | 
			
		||||
				} else {
 | 
			
		||||
					mounts, err = snapshotter.Prepare(ctx, id, identity.ChainID(diffIDs).String())
 | 
			
		||||
				}
 | 
			
		||||
				defer func() {
 | 
			
		||||
					if err != nil || context.Bool("rm") {
 | 
			
		||||
						if err := snapshotter.Remove(ctx, id); err != nil {
 | 
			
		||||
							logrus.WithError(err).Errorf("failed to remove snapshot %q", id)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					if !snapshot.IsExist(err) {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					mounts, err = snapshotter.Mounts(ctx, id)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				ic, err := image.Config(ctx, content)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				switch ic.MediaType {
 | 
			
		||||
				case ocispec.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
 | 
			
		||||
					r, err := content.Reader(ctx, ic.Digest)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					if err := json.NewDecoder(r).Decode(&imageConfig); err != nil {
 | 
			
		||||
						r.Close()
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					r.Close()
 | 
			
		||||
				default:
 | 
			
		||||
					return fmt.Errorf("unknown image config media type %s", ic.MediaType)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				// TODO: get the image / rootfs through the API once windows has a snapshotter
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if len(spec) == 0 {
 | 
			
		||||
			if spec, err = newContainerSpec(context, &imageConfig.Config, ref); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		createContainer, err := newCreateContainerRequest(context, id, id, ref, spec)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		_, err = containers.Create(ctx, createContainer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		create, err := newCreateTaskRequest(context, id, tmpDir, checkpoint, mounts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
			statusC <- status
 | 
			
		||||
		}()
 | 
			
		||||
		var con console.Console
 | 
			
		||||
		if create.Terminal {
 | 
			
		||||
		if tty {
 | 
			
		||||
			con = console.Current()
 | 
			
		||||
			defer con.Reset()
 | 
			
		||||
			if err := con.SetRaw(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		fwg, err := prepareStdio(create.Stdin, create.Stdout, create.Stderr, create.Terminal)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
		if err := task.Start(ctx); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		response, err := tasks.Create(ctx, create)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		pid := response.Pid
 | 
			
		||||
		if create.Terminal {
 | 
			
		||||
			if err := handleConsoleResize(ctx, tasks, id, pid, con); err != nil {
 | 
			
		||||
		if tty {
 | 
			
		||||
			if err := handleConsoleResize(ctx, task, con); err != nil {
 | 
			
		||||
				logrus.WithError(err).Error("console resize")
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			sigc := forwardAllSignals(tasks, id)
 | 
			
		||||
			sigc := forwardAllSignals(ctx, task)
 | 
			
		||||
			defer stopCatch(sigc)
 | 
			
		||||
		}
 | 
			
		||||
		if checkpoint == nil {
 | 
			
		||||
			if _, err := tasks.Start(ctx, &execution.StartRequest{
 | 
			
		||||
				ContainerID: id,
 | 
			
		||||
			}); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Ensure we read all io only if container started successfully.
 | 
			
		||||
		defer fwg.Wait()
 | 
			
		||||
 | 
			
		||||
		status, err := waitContainer(events, id, pid)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
		status := <-statusC
 | 
			
		||||
		if _, err := task.Delete(ctx); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := tasks.Delete(ctx, &execution.DeleteRequest{
 | 
			
		||||
			ContainerID: response.ContainerID,
 | 
			
		||||
		}); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if context.Bool("rm") {
 | 
			
		||||
			if _, err := containers.Delete(ctx, &containersapi.DeleteContainerRequest{ID: id}); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if status != 0 {
 | 
			
		||||
			return cli.NewExitError("", int(status))
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,343 +3,29 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	gocontext "context"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/signal"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/containerd/console"
 | 
			
		||||
	containersapi "github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/containerd/containerd/api/types/descriptor"
 | 
			
		||||
	"github.com/containerd/containerd/api/types/mount"
 | 
			
		||||
	mountt "github.com/containerd/containerd/mount"
 | 
			
		||||
	protobuf "github.com/gogo/protobuf/types"
 | 
			
		||||
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	digest "github.com/opencontainers/go-digest"
 | 
			
		||||
	"github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rwm               = "rwm"
 | 
			
		||||
	defaultRootfsPath = "rootfs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var capabilities = []string{
 | 
			
		||||
	"CAP_CHOWN",
 | 
			
		||||
	"CAP_DAC_OVERRIDE",
 | 
			
		||||
	"CAP_FSETID",
 | 
			
		||||
	"CAP_FOWNER",
 | 
			
		||||
	"CAP_MKNOD",
 | 
			
		||||
	"CAP_NET_RAW",
 | 
			
		||||
	"CAP_SETGID",
 | 
			
		||||
	"CAP_SETUID",
 | 
			
		||||
	"CAP_SETFCAP",
 | 
			
		||||
	"CAP_SETPCAP",
 | 
			
		||||
	"CAP_NET_BIND_SERVICE",
 | 
			
		||||
	"CAP_SYS_CHROOT",
 | 
			
		||||
	"CAP_KILL",
 | 
			
		||||
	"CAP_AUDIT_WRITE",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func spec(id string, config *ocispec.ImageConfig, context *cli.Context, rootfs string) (*specs.Spec, error) {
 | 
			
		||||
	defaultEnv := []string{
 | 
			
		||||
		"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
 | 
			
		||||
	}
 | 
			
		||||
	defaultEnv = append(defaultEnv, config.Env...)
 | 
			
		||||
	cmd := config.Cmd
 | 
			
		||||
	if v := context.Args().Tail(); len(v) > 0 {
 | 
			
		||||
		cmd = v
 | 
			
		||||
	}
 | 
			
		||||
	var (
 | 
			
		||||
		// TODO: support overriding entrypoint
 | 
			
		||||
		args     = append(config.Entrypoint, cmd...)
 | 
			
		||||
		tty      = context.Bool("tty")
 | 
			
		||||
		uid, gid uint32
 | 
			
		||||
	)
 | 
			
		||||
	if config.User != "" {
 | 
			
		||||
		parts := strings.Split(config.User, ":")
 | 
			
		||||
		switch len(parts) {
 | 
			
		||||
		case 1:
 | 
			
		||||
			v, err := strconv.ParseUint(parts[0], 0, 10)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			uid, gid = uint32(v), uint32(v)
 | 
			
		||||
		case 2:
 | 
			
		||||
			v, err := strconv.ParseUint(parts[0], 0, 10)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			uid = uint32(v)
 | 
			
		||||
			if v, err = strconv.ParseUint(parts[1], 0, 10); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			gid = uint32(v)
 | 
			
		||||
		default:
 | 
			
		||||
			return nil, fmt.Errorf("invalid USER value %s", config.User)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if tty {
 | 
			
		||||
		defaultEnv = append(defaultEnv, "TERM=xterm")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// additional environment vars
 | 
			
		||||
	env := replaceOrAppendEnvValues(defaultEnv, context.StringSlice("env"))
 | 
			
		||||
 | 
			
		||||
	cwd := config.WorkingDir
 | 
			
		||||
	if cwd == "" {
 | 
			
		||||
		cwd = "/"
 | 
			
		||||
	}
 | 
			
		||||
	if rootfs == "" {
 | 
			
		||||
		rootfs = defaultRootfsPath
 | 
			
		||||
	}
 | 
			
		||||
	s := &specs.Spec{
 | 
			
		||||
		Version: specs.Version,
 | 
			
		||||
		Platform: specs.Platform{
 | 
			
		||||
			OS:   runtime.GOOS,
 | 
			
		||||
			Arch: runtime.GOARCH,
 | 
			
		||||
		},
 | 
			
		||||
		Root: specs.Root{
 | 
			
		||||
			Path:     rootfs,
 | 
			
		||||
			Readonly: context.Bool("readonly"),
 | 
			
		||||
		},
 | 
			
		||||
		Process: specs.Process{
 | 
			
		||||
			Args:            args,
 | 
			
		||||
			Env:             env,
 | 
			
		||||
			Terminal:        tty,
 | 
			
		||||
			Cwd:             cwd,
 | 
			
		||||
			NoNewPrivileges: true,
 | 
			
		||||
			User: specs.User{
 | 
			
		||||
				UID: uid,
 | 
			
		||||
				GID: gid,
 | 
			
		||||
			},
 | 
			
		||||
			Capabilities: &specs.LinuxCapabilities{
 | 
			
		||||
				Bounding:    capabilities,
 | 
			
		||||
				Permitted:   capabilities,
 | 
			
		||||
				Inheritable: capabilities,
 | 
			
		||||
				Effective:   capabilities,
 | 
			
		||||
				Ambient:     capabilities,
 | 
			
		||||
			},
 | 
			
		||||
			Rlimits: []specs.LinuxRlimit{
 | 
			
		||||
				{
 | 
			
		||||
					Type: "RLIMIT_NOFILE",
 | 
			
		||||
					Hard: uint64(1024),
 | 
			
		||||
					Soft: uint64(1024),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		Mounts: []specs.Mount{
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/proc",
 | 
			
		||||
				Type:        "proc",
 | 
			
		||||
				Source:      "proc",
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/dev",
 | 
			
		||||
				Type:        "tmpfs",
 | 
			
		||||
				Source:      "tmpfs",
 | 
			
		||||
				Options:     []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/dev/pts",
 | 
			
		||||
				Type:        "devpts",
 | 
			
		||||
				Source:      "devpts",
 | 
			
		||||
				Options:     []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/dev/shm",
 | 
			
		||||
				Type:        "tmpfs",
 | 
			
		||||
				Source:      "shm",
 | 
			
		||||
				Options:     []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/dev/mqueue",
 | 
			
		||||
				Type:        "mqueue",
 | 
			
		||||
				Source:      "mqueue",
 | 
			
		||||
				Options:     []string{"nosuid", "noexec", "nodev"},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/sys",
 | 
			
		||||
				Type:        "sysfs",
 | 
			
		||||
				Source:      "sysfs",
 | 
			
		||||
				Options:     []string{"nosuid", "noexec", "nodev", "ro"},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/run",
 | 
			
		||||
				Type:        "tmpfs",
 | 
			
		||||
				Source:      "tmpfs",
 | 
			
		||||
				Options:     []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/etc/resolv.conf",
 | 
			
		||||
				Type:        "bind",
 | 
			
		||||
				Source:      "/etc/resolv.conf",
 | 
			
		||||
				Options:     []string{"rbind", "ro"},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/etc/hosts",
 | 
			
		||||
				Type:        "bind",
 | 
			
		||||
				Source:      "/etc/hosts",
 | 
			
		||||
				Options:     []string{"rbind", "ro"},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Destination: "/etc/localtime",
 | 
			
		||||
				Type:        "bind",
 | 
			
		||||
				Source:      "/etc/localtime",
 | 
			
		||||
				Options:     []string{"rbind", "ro"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		Hostname: id,
 | 
			
		||||
		Linux: &specs.Linux{
 | 
			
		||||
			Resources: &specs.LinuxResources{
 | 
			
		||||
				Devices: []specs.LinuxDeviceCgroup{
 | 
			
		||||
					{
 | 
			
		||||
						Allow:  false,
 | 
			
		||||
						Access: rwm,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			Namespaces: []specs.LinuxNamespace{
 | 
			
		||||
				{
 | 
			
		||||
					Type: "pid",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type: "ipc",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type: "uts",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type: "mount",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	if !context.Bool("net-host") {
 | 
			
		||||
		s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{
 | 
			
		||||
			Type: "network",
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	for _, mount := range context.StringSlice("mount") {
 | 
			
		||||
		m, err := parseMountFlag(mount)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.Mounts = append(s.Mounts, m)
 | 
			
		||||
	}
 | 
			
		||||
	return s, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func customSpec(configPath string, rootfs string) (*specs.Spec, error) {
 | 
			
		||||
	b, err := ioutil.ReadFile(configPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var s specs.Spec
 | 
			
		||||
	if err := json.Unmarshal(b, &s); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if rootfs == "" {
 | 
			
		||||
		if s.Root.Path != defaultRootfsPath {
 | 
			
		||||
			logrus.Warnf("ignoring Root.Path %q, setting %q forcibly", s.Root.Path, defaultRootfsPath)
 | 
			
		||||
			s.Root.Path = defaultRootfsPath
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		s.Root.Path = rootfs
 | 
			
		||||
	}
 | 
			
		||||
	return &s, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getConfig(context *cli.Context, imageConfig *ocispec.ImageConfig, rootfs string) (*specs.Spec, error) {
 | 
			
		||||
	config := context.String("runtime-config")
 | 
			
		||||
	if config == "" {
 | 
			
		||||
		return spec(context.String("id"), imageConfig, context, rootfs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return customSpec(config, rootfs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newContainerSpec(context *cli.Context, config *ocispec.ImageConfig, imageRef string) ([]byte, error) {
 | 
			
		||||
	s, err := getConfig(context, config, context.String("rootfs"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if s.Annotations == nil {
 | 
			
		||||
		s.Annotations = make(map[string]string)
 | 
			
		||||
	}
 | 
			
		||||
	s.Annotations["image"] = imageRef
 | 
			
		||||
	return json.Marshal(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCreateContainerRequest(context *cli.Context, id, snapshot, image string, spec []byte) (*containersapi.CreateContainerRequest, error) {
 | 
			
		||||
	create := &containersapi.CreateContainerRequest{
 | 
			
		||||
		Container: containersapi.Container{
 | 
			
		||||
			ID:    id,
 | 
			
		||||
			Image: image,
 | 
			
		||||
			Spec: &protobuf.Any{
 | 
			
		||||
				TypeUrl: specs.Version,
 | 
			
		||||
				Value:   spec,
 | 
			
		||||
			},
 | 
			
		||||
			Runtime: context.String("runtime"),
 | 
			
		||||
			RootFS:  snapshot,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return create, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCreateTaskRequest(context *cli.Context, id, tmpDir string, checkpoint *ocispec.Descriptor, mounts []mountt.Mount) (*execution.CreateRequest, error) {
 | 
			
		||||
	create := &execution.CreateRequest{
 | 
			
		||||
		ContainerID: id,
 | 
			
		||||
		Terminal:    context.Bool("tty"),
 | 
			
		||||
		Stdin:       filepath.Join(tmpDir, "stdin"),
 | 
			
		||||
		Stdout:      filepath.Join(tmpDir, "stdout"),
 | 
			
		||||
		Stderr:      filepath.Join(tmpDir, "stderr"),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, m := range mounts {
 | 
			
		||||
		create.Rootfs = append(create.Rootfs, &mount.Mount{
 | 
			
		||||
			Type:    m.Type,
 | 
			
		||||
			Source:  m.Source,
 | 
			
		||||
			Options: m.Options,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if checkpoint != nil {
 | 
			
		||||
		create.Checkpoint = &descriptor.Descriptor{
 | 
			
		||||
			MediaType: checkpoint.MediaType,
 | 
			
		||||
			Size_:     checkpoint.Size,
 | 
			
		||||
			Digest:    checkpoint.Digest,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return create, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleConsoleResize(ctx context.Context, service execution.TasksClient, id string, pid uint32, con console.Console) error {
 | 
			
		||||
func handleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error {
 | 
			
		||||
	// do an initial resize of the console
 | 
			
		||||
	size, err := con.Size()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := service.Pty(ctx, &execution.PtyRequest{
 | 
			
		||||
		ContainerID: id,
 | 
			
		||||
		Pid:         pid,
 | 
			
		||||
		Width:       uint32(size.Width),
 | 
			
		||||
		Height:      uint32(size.Height),
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
 | 
			
		||||
		logrus.WithError(err).Error("resize pty")
 | 
			
		||||
	}
 | 
			
		||||
	s := make(chan os.Signal, 16)
 | 
			
		||||
	signal.Notify(s, unix.SIGWINCH)
 | 
			
		||||
@@ -350,15 +36,86 @@ func handleConsoleResize(ctx context.Context, service execution.TasksClient, id
 | 
			
		||||
				logrus.WithError(err).Error("get pty size")
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if _, err := service.Pty(ctx, &execution.PtyRequest{
 | 
			
		||||
				ContainerID: id,
 | 
			
		||||
				Pid:         pid,
 | 
			
		||||
				Width:       uint32(size.Width),
 | 
			
		||||
				Height:      uint32(size.Height),
 | 
			
		||||
			}); err != nil {
 | 
			
		||||
			if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
 | 
			
		||||
				logrus.WithError(err).Error("resize pty")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func withTTY() containerd.SpecOpts {
 | 
			
		||||
	return containerd.WithTTY
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setHostNetworking() containerd.SpecOpts {
 | 
			
		||||
	return containerd.WithHostNamespace(specs.NetworkNamespace)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		err             error
 | 
			
		||||
		checkpointIndex digest.Digest
 | 
			
		||||
 | 
			
		||||
		ref  = context.Args().First()
 | 
			
		||||
		id   = context.Args().Get(1)
 | 
			
		||||
		args = context.Args()[2:]
 | 
			
		||||
		tty  = context.Bool("tty")
 | 
			
		||||
	)
 | 
			
		||||
	if raw := context.String("checkpoint"); raw != "" {
 | 
			
		||||
		if checkpointIndex, err = digest.Parse(raw); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	image, err := client.GetImage(ctx, ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if checkpointIndex == "" {
 | 
			
		||||
		opts := []containerd.SpecOpts{
 | 
			
		||||
			containerd.WithImageConfig(ctx, image),
 | 
			
		||||
			withEnv(context),
 | 
			
		||||
			withMounts(context),
 | 
			
		||||
		}
 | 
			
		||||
		if len(args) > 0 {
 | 
			
		||||
			opts = append(opts, containerd.WithProcessArgs(args...))
 | 
			
		||||
		}
 | 
			
		||||
		if tty {
 | 
			
		||||
			opts = append(opts, withTTY())
 | 
			
		||||
		}
 | 
			
		||||
		if context.Bool("net-host") {
 | 
			
		||||
			opts = append(opts, setHostNetworking())
 | 
			
		||||
		}
 | 
			
		||||
		spec, err := containerd.GenerateSpec(opts...)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		var rootfs containerd.NewContainerOpts
 | 
			
		||||
		if context.Bool("readonly") {
 | 
			
		||||
			rootfs = containerd.WithNewReadonlyRootFS(id, image)
 | 
			
		||||
		} else {
 | 
			
		||||
			rootfs = containerd.WithNewRootFS(id, image)
 | 
			
		||||
		}
 | 
			
		||||
		return client.NewContainer(ctx, id,
 | 
			
		||||
			containerd.WithSpec(spec),
 | 
			
		||||
			containerd.WithImage(image),
 | 
			
		||||
			rootfs,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
	return client.NewContainer(ctx, id, containerd.WithCheckpoint(v1.Descriptor{
 | 
			
		||||
		Digest: checkpointIndex,
 | 
			
		||||
	}, id))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTask(ctx gocontext.Context, container containerd.Container, checkpoint digest.Digest, tty bool) (containerd.Task, error) {
 | 
			
		||||
	if checkpoint == "" {
 | 
			
		||||
		io := containerd.Stdio
 | 
			
		||||
		if tty {
 | 
			
		||||
			io = containerd.StdioTerminal
 | 
			
		||||
		}
 | 
			
		||||
		return container.NewTask(ctx, io)
 | 
			
		||||
	}
 | 
			
		||||
	return container.NewTask(ctx, containerd.Stdio, containerd.WithTaskCheckpoint(v1.Descriptor{
 | 
			
		||||
		Digest: checkpoint,
 | 
			
		||||
	}))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	gocontext "context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
@@ -10,6 +10,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/containerd/console"
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	containersapi "github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/containerd/containerd/log"
 | 
			
		||||
@@ -17,6 +18,7 @@ import (
 | 
			
		||||
	"github.com/containerd/containerd/windows"
 | 
			
		||||
	"github.com/containerd/containerd/windows/hcs"
 | 
			
		||||
	protobuf "github.com/gogo/protobuf/types"
 | 
			
		||||
	digest "github.com/opencontainers/go-digest"
 | 
			
		||||
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
@@ -169,7 +171,7 @@ func newCreateTaskRequest(context *cli.Context, id, tmpDir string, checkpoint *o
 | 
			
		||||
	return create, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleConsoleResize(ctx context.Context, service execution.TasksClient, id string, pid uint32, con console.Console) error {
 | 
			
		||||
func handleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error {
 | 
			
		||||
	// do an initial resize of the console
 | 
			
		||||
	size, err := con.Size()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -187,13 +189,8 @@ func handleConsoleResize(ctx context.Context, service execution.TasksClient, id
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if size.Width != prevSize.Width || size.Height != prevSize.Height {
 | 
			
		||||
				if _, err := service.Pty(ctx, &execution.PtyRequest{
 | 
			
		||||
					ContainerID: id,
 | 
			
		||||
					Pid:         pid,
 | 
			
		||||
					Width:       uint32(size.Width),
 | 
			
		||||
					Height:      uint32(size.Height),
 | 
			
		||||
				}); err != nil {
 | 
			
		||||
					log.G(ctx).WithError(err).Error("resize pty")
 | 
			
		||||
				if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
 | 
			
		||||
					logrus.WithError(err).Error("resize pty")
 | 
			
		||||
				}
 | 
			
		||||
				prevSize = size
 | 
			
		||||
			}
 | 
			
		||||
@@ -201,3 +198,68 @@ func handleConsoleResize(ctx context.Context, service execution.TasksClient, id
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func withTTY() containerd.SpecOpts {
 | 
			
		||||
	con := console.Current()
 | 
			
		||||
	size, err := con.Size()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.WithError(err).Error("console size")
 | 
			
		||||
	}
 | 
			
		||||
	return containerd.WithTTY(int(size.Width), int(size.Height))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setHostNetworking() containerd.SpecOpts {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		err error
 | 
			
		||||
 | 
			
		||||
		ref  = context.Args().First()
 | 
			
		||||
		id   = context.Args().Get(1)
 | 
			
		||||
		args = context.Args()[2:]
 | 
			
		||||
		tty  = context.Bool("tty")
 | 
			
		||||
	)
 | 
			
		||||
	image, err := client.GetImage(ctx, ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	opts := []containerd.SpecOpts{
 | 
			
		||||
		containerd.WithImageConfig(ctx, image),
 | 
			
		||||
		withEnv(context),
 | 
			
		||||
		withMounts(context),
 | 
			
		||||
	}
 | 
			
		||||
	if len(args) > 0 {
 | 
			
		||||
		opts = append(opts, containerd.WithProcessArgs(args...))
 | 
			
		||||
	}
 | 
			
		||||
	if tty {
 | 
			
		||||
		opts = append(opts, withTTY())
 | 
			
		||||
	}
 | 
			
		||||
	if context.Bool("net-host") {
 | 
			
		||||
		opts = append(opts, setHostNetworking())
 | 
			
		||||
	}
 | 
			
		||||
	spec, err := containerd.GenerateSpec(opts...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var rootfs containerd.NewContainerOpts
 | 
			
		||||
	if context.Bool("readonly") {
 | 
			
		||||
		rootfs = containerd.WithNewReadonlyRootFS(id, image)
 | 
			
		||||
	} else {
 | 
			
		||||
		rootfs = containerd.WithNewRootFS(id, image)
 | 
			
		||||
	}
 | 
			
		||||
	return client.NewContainer(ctx, id,
 | 
			
		||||
		containerd.WithSpec(spec),
 | 
			
		||||
		containerd.WithImage(image),
 | 
			
		||||
		rootfs,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTask(ctx gocontext.Context, container containerd.Container, _ digest.Digest, tty bool) (containerd.Task, error) {
 | 
			
		||||
	io := containerd.Stdio
 | 
			
		||||
	if tty {
 | 
			
		||||
		io = containerd.StdioTerminal
 | 
			
		||||
	}
 | 
			
		||||
	return container.NewTask(ctx, io)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import (
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	containersapi "github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	contentapi "github.com/containerd/containerd/api/services/content"
 | 
			
		||||
	diffapi "github.com/containerd/containerd/api/services/diff"
 | 
			
		||||
@@ -70,6 +71,10 @@ func getNamespacesService(clicontext *cli.Context) (namespaces.Store, error) {
 | 
			
		||||
	return namespacesservice.NewStoreFromClient(namespacesapi.NewNamespacesClient(conn)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newClient(context *cli.Context) (*containerd.Client, error) {
 | 
			
		||||
	return containerd.New(context.GlobalString("address"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getContainersService(context *cli.Context) (containersapi.ContainersClient, error) {
 | 
			
		||||
	conn, err := getGRPCConnection(context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -153,23 +158,14 @@ func waitContainer(events execution.Tasks_EventsClient, id string, pid uint32) (
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func forwardAllSignals(containers execution.TasksClient, id string) chan os.Signal {
 | 
			
		||||
func forwardAllSignals(ctx gocontext.Context, task killer) chan os.Signal {
 | 
			
		||||
	sigc := make(chan os.Signal, 128)
 | 
			
		||||
	signal.Notify(sigc)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for s := range sigc {
 | 
			
		||||
			logrus.Debug("Forwarding signal ", s)
 | 
			
		||||
			killRequest := &execution.KillRequest{
 | 
			
		||||
				ContainerID: id,
 | 
			
		||||
				Signal:      uint32(s.(syscall.Signal)),
 | 
			
		||||
				PidOrAll: &execution.KillRequest_All{
 | 
			
		||||
					All: false,
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
			_, err := containers.Kill(gocontext.Background(), killRequest)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logrus.Fatalln(err)
 | 
			
		||||
			logrus.Debug("forwarding signal ", s)
 | 
			
		||||
			if err := task.Kill(ctx, s.(syscall.Signal)); err != nil {
 | 
			
		||||
				logrus.WithError(err).Errorf("forward signal %s", s)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/version"
 | 
			
		||||
	empty "github.com/golang/protobuf/ptypes/empty"
 | 
			
		||||
	"github.com/urfave/cli"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -21,14 +20,15 @@ var versionCommand = cli.Command{
 | 
			
		||||
		fmt.Printf("  Version: %s\n", version.Version)
 | 
			
		||||
		fmt.Printf("  Revision: %s\n", version.Revision)
 | 
			
		||||
		fmt.Println("")
 | 
			
		||||
		vs, err := getVersionService(context)
 | 
			
		||||
		client, err := newClient(context)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		v, err := vs.Version(gocontext.Background(), &empty.Empty{})
 | 
			
		||||
		v, err := client.Version(gocontext.Background())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fmt.Println("Server:")
 | 
			
		||||
		fmt.Printf("  Version: %s\n", v.Version)
 | 
			
		||||
		fmt.Printf("  Revision: %s\n", v.Revision)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										92
									
								
								container.go
									
									
									
									
									
								
							
							
						
						
									
										92
									
								
								container.go
									
									
									
									
									
								
							@@ -3,21 +3,30 @@ package containerd
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"google.golang.org/grpc"
 | 
			
		||||
	"google.golang.org/grpc/codes"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	"github.com/containerd/containerd/api/services/execution"
 | 
			
		||||
	"github.com/containerd/containerd/api/types/mount"
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ErrNoRunningTask = errors.New("no running task")
 | 
			
		||||
 | 
			
		||||
type Container interface {
 | 
			
		||||
	ID() string
 | 
			
		||||
	Proto() containers.Container
 | 
			
		||||
	Delete(context.Context) error
 | 
			
		||||
	NewTask(context.Context, IOCreation, ...NewTaskOpts) (Task, error)
 | 
			
		||||
	Spec() (*specs.Spec, error)
 | 
			
		||||
	Task() Task
 | 
			
		||||
	LoadTask(context.Context, IOAttach) (Task, error)
 | 
			
		||||
	Task(context.Context, IOAttach) (Task, error)
 | 
			
		||||
	Image(context.Context) (Image, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func containerFromProto(client *Client, c containers.Container) *container {
 | 
			
		||||
@@ -30,10 +39,11 @@ func containerFromProto(client *Client, c containers.Container) *container {
 | 
			
		||||
var _ = (Container)(&container{})
 | 
			
		||||
 | 
			
		||||
type container struct {
 | 
			
		||||
	client *Client
 | 
			
		||||
	mu sync.Mutex
 | 
			
		||||
 | 
			
		||||
	c    containers.Container
 | 
			
		||||
	task *task
 | 
			
		||||
	client *Client
 | 
			
		||||
	c      containers.Container
 | 
			
		||||
	task   *task
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID returns the container's unique id
 | 
			
		||||
@@ -41,6 +51,10 @@ func (c *container) ID() string {
 | 
			
		||||
	return c.c.ID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) Proto() containers.Container {
 | 
			
		||||
	return c.c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Spec returns the current OCI specification for the container
 | 
			
		||||
func (c *container) Spec() (*specs.Spec, error) {
 | 
			
		||||
	var s specs.Spec
 | 
			
		||||
@@ -58,7 +72,6 @@ func (c *container) Delete(ctx context.Context) (err error) {
 | 
			
		||||
	if c.c.RootFS != "" {
 | 
			
		||||
		err = c.client.SnapshotService().Remove(ctx, c.c.RootFS)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, cerr := c.client.ContainerService().Delete(ctx, &containers.DeleteContainerRequest{
 | 
			
		||||
		ID: c.c.ID,
 | 
			
		||||
	}); err == nil {
 | 
			
		||||
@@ -67,13 +80,39 @@ func (c *container) Delete(ctx context.Context) (err error) {
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) Task() Task {
 | 
			
		||||
	return c.task
 | 
			
		||||
func (c *container) Task(ctx context.Context, attach IOAttach) (Task, error) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
	if c.task == nil {
 | 
			
		||||
		t, err := c.loadTask(ctx, attach)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		c.task = t.(*task)
 | 
			
		||||
	}
 | 
			
		||||
	return c.task, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Image returns the image that the container is based on
 | 
			
		||||
func (c *container) Image(ctx context.Context) (Image, error) {
 | 
			
		||||
	if c.c.Image == "" {
 | 
			
		||||
		return nil, fmt.Errorf("container is not based on an image")
 | 
			
		||||
	}
 | 
			
		||||
	i, err := c.client.ImageService().Get(ctx, c.c.Image)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &image{
 | 
			
		||||
		client: c.client,
 | 
			
		||||
		i:      i,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NewTaskOpts func(context.Context, *Client, *execution.CreateRequest) error
 | 
			
		||||
 | 
			
		||||
func (c *container) NewTask(ctx context.Context, ioCreate IOCreation, opts ...NewTaskOpts) (Task, error) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
	i, err := ioCreate()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -126,28 +165,33 @@ func (c *container) NewTask(ctx context.Context, ioCreate IOCreation, opts ...Ne
 | 
			
		||||
	return t, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *container) LoadTask(ctx context.Context, ioAttach IOAttach) (Task, error) {
 | 
			
		||||
func (c *container) loadTask(ctx context.Context, ioAttach IOAttach) (Task, error) {
 | 
			
		||||
	response, err := c.client.TaskService().Info(ctx, &execution.InfoRequest{
 | 
			
		||||
		ContainerID: c.c.ID,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if grpc.Code(errors.Cause(err)) == codes.NotFound {
 | 
			
		||||
			return nil, ErrNoRunningTask
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	// get the existing fifo paths from the task information stored by the daemon
 | 
			
		||||
	paths := &FifoSet{
 | 
			
		||||
		Dir: getFifoDir([]string{
 | 
			
		||||
			response.Task.Stdin,
 | 
			
		||||
			response.Task.Stdout,
 | 
			
		||||
			response.Task.Stderr,
 | 
			
		||||
		}),
 | 
			
		||||
		In:       response.Task.Stdin,
 | 
			
		||||
		Out:      response.Task.Stdout,
 | 
			
		||||
		Err:      response.Task.Stderr,
 | 
			
		||||
		Terminal: response.Task.Terminal,
 | 
			
		||||
	}
 | 
			
		||||
	i, err := ioAttach(paths)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	var i *IO
 | 
			
		||||
	if ioAttach != nil {
 | 
			
		||||
		// get the existing fifo paths from the task information stored by the daemon
 | 
			
		||||
		paths := &FifoSet{
 | 
			
		||||
			Dir: getFifoDir([]string{
 | 
			
		||||
				response.Task.Stdin,
 | 
			
		||||
				response.Task.Stdout,
 | 
			
		||||
				response.Task.Stderr,
 | 
			
		||||
			}),
 | 
			
		||||
			In:       response.Task.Stdin,
 | 
			
		||||
			Out:      response.Task.Stdout,
 | 
			
		||||
			Err:      response.Task.Stderr,
 | 
			
		||||
			Terminal: response.Task.Terminal,
 | 
			
		||||
		}
 | 
			
		||||
		if i, err = ioAttach(paths); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	t := &task{
 | 
			
		||||
		client:      c.client,
 | 
			
		||||
 
 | 
			
		||||
@@ -558,7 +558,7 @@ func TestContainerAttach(t *testing.T) {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if task, err = container.LoadTask(ctx, WithAttach(r, ow, ioutil.Discard)); err != nil {
 | 
			
		||||
	if task, err = container.Task(ctx, WithAttach(r, ow, ioutil.Discard)); err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !windows
 | 
			
		||||
 | 
			
		||||
package containerd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -19,20 +21,6 @@ import (
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func WithSpec(spec *specs.Spec) NewContainerOpts {
 | 
			
		||||
	return func(ctx context.Context, client *Client, c *containers.Container) error {
 | 
			
		||||
		data, err := json.Marshal(spec)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		c.Spec = &protobuf.Any{
 | 
			
		||||
			TypeUrl: spec.Version,
 | 
			
		||||
			Value:   data,
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WithCheckpoint(desc v1.Descriptor, rootfsID string) NewContainerOpts {
 | 
			
		||||
	// set image and rw, and spec
 | 
			
		||||
	return func(ctx context.Context, client *Client, c *containers.Container) error {
 | 
			
		||||
@@ -110,3 +110,7 @@ func (p *process) Resize(ctx context.Context, w, h uint32) error {
 | 
			
		||||
	})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *process) Delete() error {
 | 
			
		||||
	return p.io.Close()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								spec_unix.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								spec_unix.go
									
									
									
									
									
								
							@@ -10,7 +10,9 @@ import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	"github.com/containerd/containerd/images"
 | 
			
		||||
	protobuf "github.com/gogo/protobuf/types"
 | 
			
		||||
	"github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
)
 | 
			
		||||
@@ -255,3 +257,17 @@ func WithImageConfig(ctx context.Context, i Image) SpecOpts {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WithSpec(spec *specs.Spec) NewContainerOpts {
 | 
			
		||||
	return func(ctx context.Context, client *Client, c *containers.Container) error {
 | 
			
		||||
		data, err := json.Marshal(spec)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		c.Spec = &protobuf.Any{
 | 
			
		||||
			TypeUrl: spec.Version,
 | 
			
		||||
			Value:   data,
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,9 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/containerd/containerd/api/services/containers"
 | 
			
		||||
	"github.com/containerd/containerd/images"
 | 
			
		||||
	protobuf "github.com/gogo/protobuf/types"
 | 
			
		||||
	"github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
)
 | 
			
		||||
@@ -76,3 +78,17 @@ func WithTTY(width, height int) SpecOpts {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WithSpec(spec *specs.Spec) NewContainerOpts {
 | 
			
		||||
	return func(ctx context.Context, client *Client, c *containers.Container) error {
 | 
			
		||||
		data, err := json.Marshal(spec)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		c.Spec = &protobuf.Any{
 | 
			
		||||
			TypeUrl: spec.Version,
 | 
			
		||||
			Value:   data,
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user