diff --git a/client.go b/client.go index 998d01c6c..e8fbd37f3 100644 --- a/client.go +++ b/client.go @@ -83,12 +83,12 @@ type Client struct { } // Containers returns all containers created in containerd -func (c *Client) Containers(ctx context.Context) ([]*Container, error) { +func (c *Client) Containers(ctx context.Context) ([]Container, error) { r, err := c.containers().List(ctx, &containers.ListContainersRequest{}) if err != nil { return nil, err } - var out []*Container + var out []Container for _, container := range r.Containers { out = append(out, containerFromProto(c, container)) } @@ -119,9 +119,9 @@ func WithExistingRootFS(id string) NewContainerOpts { // WithNewRootFS allocates a new snapshot to be used by the container as the // root filesystem in read-write mode -func WithNewRootFS(id string, image *Image) NewContainerOpts { +func WithNewRootFS(id string, i Image) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { - diffIDs, err := image.i.RootFS(ctx, client.content()) + diffIDs, err := i.(*image).i.RootFS(ctx, client.content()) if err != nil { return err } @@ -135,9 +135,9 @@ func WithNewRootFS(id string, image *Image) NewContainerOpts { // WithNewReadonlyRootFS allocates a new snapshot to be used by the container as the // root filesystem in read-only mode -func WithNewReadonlyRootFS(id string, image *Image) NewContainerOpts { +func WithNewReadonlyRootFS(id string, i Image) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { - diffIDs, err := image.i.RootFS(ctx, client.content()) + diffIDs, err := i.(*image).i.RootFS(ctx, client.content()) if err != nil { return err } @@ -158,7 +158,7 @@ func WithRuntime(name string) NewContainerOpts { // NewContainer will create a new container in container with the provided id // the id must be unique within the namespace -func (c *Client) NewContainer(ctx context.Context, id string, spec *specs.Spec, opts ...NewContainerOpts) (*Container, error) { +func (c *Client) NewContainer(ctx context.Context, id string, spec *specs.Spec, opts ...NewContainerOpts) (Container, error) { data, err := json.Marshal(spec) if err != nil { return nil, err @@ -258,7 +258,7 @@ func (s *snapshotUnpacker) getLayers(ctx context.Context, image images.Image) ([ return layers, nil } -func (c *Client) Pull(ctx context.Context, ref string, opts ...PullOpts) (*Image, error) { +func (c *Client) Pull(ctx context.Context, ref string, opts ...PullOpts) (Image, error) { pullCtx := defaultPullContext() for _, o := range opts { if err := o(c, pullCtx); err != nil { @@ -296,7 +296,7 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...PullOpts) (*Image return nil, err } } - return &Image{ + return &image{ client: c, i: i, }, nil diff --git a/container.go b/container.go index 22ba64858..328f74489 100644 --- a/container.go +++ b/container.go @@ -10,27 +10,37 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" ) -func containerFromProto(client *Client, c containers.Container) *Container { - return &Container{ +type Container interface { + ID() string + Delete(context.Context) error + NewTask(context.Context, IOCreation) (Task, error) + Spec() (*specs.Spec, error) + Task() Task +} + +func containerFromProto(client *Client, c containers.Container) *container { + return &container{ client: client, c: c, } } -type Container struct { +var _ = (Container)(&container{}) + +type container struct { client *Client c containers.Container - task *Task + task *task } // ID returns the container's unique id -func (c *Container) ID() string { +func (c *container) ID() string { return c.c.ID } // Spec returns the current OCI specification for the container -func (c *Container) Spec() (*specs.Spec, error) { +func (c *container) Spec() (*specs.Spec, error) { var s specs.Spec if err := json.Unmarshal(c.c.Spec.Value, &s); err != nil { return nil, err @@ -40,7 +50,7 @@ func (c *Container) Spec() (*specs.Spec, error) { // Delete deletes an existing container // an error is returned if the container has running tasks -func (c *Container) Delete(ctx context.Context) error { +func (c *container) Delete(ctx context.Context) error { // TODO: should the client be the one removing resources attached // to the container at the moment before we have GC? err := c.client.snapshotter().Remove(ctx, c.c.RootFS) @@ -53,11 +63,11 @@ func (c *Container) Delete(ctx context.Context) error { return err } -func (c *Container) Task() *Task { +func (c *container) Task() Task { return c.task } -func (c *Container) NewTask(ctx context.Context, ioCreate IOCreation) (*Task, error) { +func (c *container) NewTask(ctx context.Context, ioCreate IOCreation) (Task, error) { i, err := ioCreate() if err != nil { return nil, err @@ -85,7 +95,7 @@ func (c *Container) NewTask(ctx context.Context, ioCreate IOCreation) (*Task, er if err != nil { return nil, err } - t := &Task{ + t := &task{ client: c.client, io: i, containerID: response.ContainerID, diff --git a/image.go b/image.go index 83e042ded..6e48a52f3 100644 --- a/image.go +++ b/image.go @@ -2,7 +2,12 @@ package containerd import "github.com/containerd/containerd/images" -type Image struct { +type Image interface { +} + +var _ = (Image)(&image{}) + +type image struct { client *Client i images.Image diff --git a/spec_unix.go b/spec_unix.go index 673f2d6ad..81404c99a 100644 --- a/spec_unix.go +++ b/spec_unix.go @@ -183,8 +183,9 @@ func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts { } } -func WithImage(ctx context.Context, image *Image) SpecOpts { +func WithImage(ctx context.Context, i Image) SpecOpts { return func(s *specs.Spec) error { + image := i.(*image) store := image.client.content() ic, err := image.i.Config(ctx, store) if err != nil { diff --git a/task.go b/task.go index 132a58b46..62e283a91 100644 --- a/task.go +++ b/task.go @@ -10,7 +10,7 @@ import ( "syscall" "github.com/containerd/containerd/api/services/execution" - "github.com/containerd/containerd/api/types/task" + taskapi "github.com/containerd/containerd/api/types/task" "github.com/containerd/fifo" ) @@ -158,7 +158,20 @@ func (g *wgCloser) Close() error { return nil } -type Task struct { +type Task interface { + Delete(context.Context) (uint32, error) + Kill(context.Context, syscall.Signal) error + Pause(context.Context) error + Resume(context.Context) error + Pid() uint32 + Start(context.Context) error + Status(context.Context) (string, error) + Wait(context.Context) (uint32, error) +} + +var _ = (Task)(&task{}) + +type task struct { client *Client io *IO @@ -167,18 +180,18 @@ type Task struct { } // Pid returns the pid or process id for the task -func (t *Task) Pid() uint32 { +func (t *task) Pid() uint32 { return t.pid } -func (t *Task) Start(ctx context.Context) error { +func (t *task) Start(ctx context.Context) error { _, err := t.client.tasks().Start(ctx, &execution.StartRequest{ ContainerID: t.containerID, }) return err } -func (t *Task) Kill(ctx context.Context, s syscall.Signal) error { +func (t *task) Kill(ctx context.Context, s syscall.Signal) error { _, err := t.client.tasks().Kill(ctx, &execution.KillRequest{ Signal: uint32(s), ContainerID: t.containerID, @@ -189,21 +202,21 @@ func (t *Task) Kill(ctx context.Context, s syscall.Signal) error { return err } -func (t *Task) Pause(ctx context.Context) error { +func (t *task) Pause(ctx context.Context) error { _, err := t.client.tasks().Pause(ctx, &execution.PauseRequest{ ContainerID: t.containerID, }) return err } -func (t *Task) Resume(ctx context.Context) error { +func (t *task) Resume(ctx context.Context) error { _, err := t.client.tasks().Resume(ctx, &execution.ResumeRequest{ ContainerID: t.containerID, }) return err } -func (t *Task) Status(ctx context.Context) (string, error) { +func (t *task) Status(ctx context.Context) (string, error) { r, err := t.client.tasks().Info(ctx, &execution.InfoRequest{ ContainerID: t.containerID, }) @@ -214,7 +227,7 @@ func (t *Task) Status(ctx context.Context) (string, error) { } // Wait is a blocking call that will wait for the task to exit and return the exit status -func (t *Task) Wait(ctx context.Context) (uint32, error) { +func (t *task) Wait(ctx context.Context) (uint32, error) { events, err := t.client.tasks().Events(ctx, &execution.EventsRequest{}) if err != nil { return 255, err @@ -224,7 +237,7 @@ func (t *Task) Wait(ctx context.Context) (uint32, error) { if err != nil { return 255, err } - if e.Type != task.Event_EXIT { + if e.Type != taskapi.Event_EXIT { continue } if e.ID == t.containerID && e.Pid == t.pid { @@ -236,7 +249,7 @@ func (t *Task) Wait(ctx context.Context) (uint32, error) { // Delete deletes the task and its runtime state // it returns the exit status of the task and any errors that were encountered // during cleanup -func (t *Task) Delete(ctx context.Context) (uint32, error) { +func (t *task) Delete(ctx context.Context) (uint32, error) { cerr := t.io.Close() r, err := t.client.tasks().Delete(ctx, &execution.DeleteRequest{ ContainerID: t.containerID,