diff --git a/apis/swagger.yml b/apis/swagger.yml index 1a775798a..4016344f9 100644 --- a/apis/swagger.yml +++ b/apis/swagger.yml @@ -515,6 +515,10 @@ paths: description: "no error" 404: $ref: "#/responses/404ErrorResponse" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/Error" 500: $ref: "#/responses/500ErrorResponse" tags: ["Container"] diff --git a/ctrd/container.go b/ctrd/container.go index 86c88cad8..f16cb5351 100644 --- a/ctrd/container.go +++ b/ctrd/container.go @@ -41,6 +41,15 @@ type containerPack struct { // ContainerStats returns stats of the container. func (c *Client) ContainerStats(ctx context.Context, id string) (*containerdtypes.Metric, error) { + metric, err := c.containerStats(ctx, id) + if err != nil { + return metric, convertCtrdErr(err) + } + return metric, nil +} + +// containerStats returns stats of the container. +func (c *Client) containerStats(ctx context.Context, id string) (*containerdtypes.Metric, error) { if !c.lock.Trylock(id) { return nil, errtypes.ErrLockfailed } @@ -56,6 +65,14 @@ func (c *Client) ContainerStats(ctx context.Context, id string) (*containerdtype // ExecContainer executes a process in container. func (c *Client) ExecContainer(ctx context.Context, process *Process) error { + if err := c.execContainer(ctx, process); err != nil { + return convertCtrdErr(err) + } + return nil +} + +// execContainer executes a process in container. +func (c *Client) execContainer(ctx context.Context, process *Process) error { pack, err := c.watch.get(process.ContainerID) if err != nil { return err @@ -123,6 +140,15 @@ func (c *Client) ExecContainer(ctx context.Context, process *Process) error { // ContainerPID returns the container's init process id. func (c *Client) ContainerPID(ctx context.Context, id string) (int, error) { + pid, err := c.containerPID(ctx, id) + if err != nil { + return pid, convertCtrdErr(err) + } + return pid, nil +} + +// containerPID returns the container's init process id. +func (c *Client) containerPID(ctx context.Context, id string) (int, error) { pack, err := c.watch.get(id) if err != nil { return -1, err @@ -132,6 +158,15 @@ func (c *Client) ContainerPID(ctx context.Context, id string) (int, error) { // ContainerPIDs returns the all processes's ids inside the container. func (c *Client) ContainerPIDs(ctx context.Context, id string) ([]int, error) { + pids, err := c.containerPIDs(ctx, id) + if err != nil { + return pids, convertCtrdErr(err) + } + return pids, nil +} + +// containerPIDs returns the all processes's ids inside the container. +func (c *Client) containerPIDs(ctx context.Context, id string) ([]int, error) { if !c.lock.Trylock(id) { return nil, errtypes.ErrLockfailed } @@ -178,6 +213,14 @@ func (c *Client) ProbeContainer(ctx context.Context, id string, timeout time.Dur // RecoverContainer reload the container from metadata and watch it, if program be restarted. func (c *Client) RecoverContainer(ctx context.Context, id string, io *containerio.IO) error { + if err := c.recoverContainer(ctx, id, io); err != nil { + return convertCtrdErr(err) + } + return nil +} + +// recoverContainer reload the container from metadata and watch it, if program be restarted. +func (c *Client) recoverContainer(ctx context.Context, id string, io *containerio.IO) error { wrapperCli, err := c.Get(ctx) if err != nil { return fmt.Errorf("failed to get a containerd grpc client: %v", err) @@ -226,6 +269,15 @@ func (c *Client) RecoverContainer(ctx context.Context, id string, io *containeri // DestroyContainer kill container and delete it. func (c *Client) DestroyContainer(ctx context.Context, id string, timeout int64) (*Message, error) { + msg, err := c.destroyContainer(ctx, id, timeout) + if err != nil { + return msg, convertCtrdErr(err) + } + return msg, nil +} + +// DestroyContainer kill container and delete it. +func (c *Client) destroyContainer(ctx context.Context, id string, timeout int64) (*Message, error) { // TODO(ziren): if we just want to stop a container, // we may need lease to lock the snapshot of container, // in case, it be deleted by gc. @@ -294,8 +346,16 @@ clean: return msg, c.watch.remove(ctx, id) } -// PauseContainer pause container. +// PauseContainer pauses container. func (c *Client) PauseContainer(ctx context.Context, id string) error { + if err := c.pauseContainer(ctx, id); err != nil { + return convertCtrdErr(err) + } + return nil +} + +// pauseContainer pause container. +func (c *Client) pauseContainer(ctx context.Context, id string) error { if !c.lock.Trylock(id) { return errtypes.ErrLockfailed } @@ -317,8 +377,16 @@ func (c *Client) PauseContainer(ctx context.Context, id string) error { return nil } -// UnpauseContainer unpauses a container. +// UnpauseContainer unpauses container. func (c *Client) UnpauseContainer(ctx context.Context, id string) error { + if err := c.unpauseContainer(ctx, id); err != nil { + return convertCtrdErr(err) + } + return nil +} + +// unpauseContainer unpauses a container. +func (c *Client) unpauseContainer(ctx context.Context, id string) error { if !c.lock.Trylock(id) { return errtypes.ErrLockfailed } @@ -352,7 +420,10 @@ func (c *Client) CreateContainer(ctx context.Context, container *Container) erro } defer c.lock.Unlock(id) - return c.createContainer(ctx, ref, id, container) + if err := c.createContainer(ctx, ref, id, container); err != nil { + return convertCtrdErr(err) + } + return nil } func (c *Client) createContainer(ctx context.Context, ref, id string, container *Container) (err0 error) { @@ -472,28 +543,16 @@ func (c *Client) createTask(ctx context.Context, id string, container containerd return pack, nil } -func (c *Client) listContainerStore(ctx context.Context) ([]string, error) { - wrapperCli, err := c.Get(ctx) - if err != nil { - return nil, fmt.Errorf("failed to get a containerd grpc client: %v", err) - } - - containers, err := wrapperCli.client.ContainerService().List(ctx) - if err != nil { - return nil, err - } - - var cs []string - - for _, c := range containers { - cs = append(cs, c.ID) +// UpdateResources updates the configurations of a container. +func (c *Client) UpdateResources(ctx context.Context, id string, resources types.Resources) error { + if err := c.updateResources(ctx, id, resources); err != nil { + return convertCtrdErr(err) } - - return cs, nil + return nil } -// UpdateResources updates the configurations of a container. -func (c *Client) UpdateResources(ctx context.Context, id string, resources types.Resources) error { +// updateResources updates the configurations of a container. +func (c *Client) updateResources(ctx context.Context, id string, resources types.Resources) error { if !c.lock.Trylock(id) { return errtypes.ErrLockfailed } @@ -515,6 +574,15 @@ func (c *Client) UpdateResources(ctx context.Context, id string, resources types // ResizeContainer changes the size of the TTY of the init process running // in the container to the given height and width. func (c *Client) ResizeContainer(ctx context.Context, id string, opts types.ResizeOptions) error { + if err := c.resizeContainer(ctx, id, opts); err != nil { + return convertCtrdErr(err) + } + return nil +} + +// resizeContainer changes the size of the TTY of the init process running +// in the container to the given height and width. +func (c *Client) resizeContainer(ctx context.Context, id string, opts types.ResizeOptions) error { if !c.lock.Trylock(id) { return errtypes.ErrLockfailed } @@ -530,6 +598,15 @@ func (c *Client) ResizeContainer(ctx context.Context, id string, opts types.Resi // WaitContainer waits until container's status is stopped. func (c *Client) WaitContainer(ctx context.Context, id string) (types.ContainerWaitOKBody, error) { + waitBody, err := c.waitContainer(ctx, id) + if err != nil { + return waitBody, convertCtrdErr(err) + } + return waitBody, nil +} + +// waitContainer waits until container's status is stopped. +func (c *Client) waitContainer(ctx context.Context, id string) (types.ContainerWaitOKBody, error) { wrapperCli, err := c.Get(ctx) if err != nil { return types.ContainerWaitOKBody{}, fmt.Errorf("failed to get a containerd grpc client: %v", err) diff --git a/ctrd/image.go b/ctrd/image.go index 65258a486..64512d655 100644 --- a/ctrd/image.go +++ b/ctrd/image.go @@ -25,6 +25,15 @@ import ( // CreateImageReference creates the image in the meta data in the containerd. func (c *Client) CreateImageReference(ctx context.Context, img ctrdmetaimages.Image) (ctrdmetaimages.Image, error) { + image, err := c.createImageReference(ctx, img) + if err != nil { + return image, convertCtrdErr(err) + } + return image, nil +} + +// createImageReference creates the image in the meta data in the containerd. +func (c *Client) createImageReference(ctx context.Context, img ctrdmetaimages.Image) (ctrdmetaimages.Image, error) { wrapperCli, err := c.Get(ctx) if err != nil { return ctrdmetaimages.Image{}, fmt.Errorf("failed to get a containerd grpc client: %v", err) @@ -35,6 +44,15 @@ func (c *Client) CreateImageReference(ctx context.Context, img ctrdmetaimages.Im // GetImage returns the containerd's Image. func (c *Client) GetImage(ctx context.Context, ref string) (containerd.Image, error) { + img, err := c.getImage(ctx, ref) + if err != nil { + return img, convertCtrdErr(err) + } + return img, nil +} + +// getImage returns the containerd's Image. +func (c *Client) getImage(ctx context.Context, ref string) (containerd.Image, error) { wrapperCli, err := c.Get(ctx) if err != nil { return nil, fmt.Errorf("failed to get a containerd grpc client: %v", err) @@ -45,6 +63,15 @@ func (c *Client) GetImage(ctx context.Context, ref string) (containerd.Image, er // ListImages lists all images. func (c *Client) ListImages(ctx context.Context, filter ...string) ([]containerd.Image, error) { + imgs, err := c.listImages(ctx, filter...) + if err != nil { + return imgs, convertCtrdErr(err) + } + return imgs, nil +} + +// listImages lists all images. +func (c *Client) listImages(ctx context.Context, filter ...string) ([]containerd.Image, error) { wrapperCli, err := c.Get(ctx) if err != nil { return nil, fmt.Errorf("failed to get a containerd grpc client: %v", err) @@ -55,6 +82,14 @@ func (c *Client) ListImages(ctx context.Context, filter ...string) ([]containerd // RemoveImage deletes an image. func (c *Client) RemoveImage(ctx context.Context, ref string) error { + if err := c.removeImage(ctx, ref); err != nil { + return convertCtrdErr(err) + } + return nil +} + +// removeImage deletes an image. +func (c *Client) removeImage(ctx context.Context, ref string) error { wrapperCli, err := c.Get(ctx) if err != nil { return fmt.Errorf("failed to get a containerd grpc client: %v", err) @@ -68,6 +103,15 @@ func (c *Client) RemoveImage(ctx context.Context, ref string) error { // SaveImage saves image to tarstream func (c *Client) SaveImage(ctx context.Context, exporter ctrdmetaimages.Exporter, ref string) (io.ReadCloser, error) { + r, err := c.saveImage(ctx, exporter, ref) + if err != nil { + return r, convertCtrdErr(err) + } + return r, nil +} + +// saveImage saves image to tarstream +func (c *Client) saveImage(ctx context.Context, exporter ctrdmetaimages.Exporter, ref string) (io.ReadCloser, error) { wrapperCli, err := c.Get(ctx) if err != nil { return nil, fmt.Errorf("failed to get a containerd grpc client: %v", err) @@ -94,18 +138,24 @@ func (c *Client) SaveImage(ctx context.Context, exporter ctrdmetaimages.Exporter } } - exportedStream, err := wrapperCli.client.Export(ctx, exporter, desc) - if err != nil { - return nil, err - } - - return exportedStream, nil + return wrapperCli.client.Export(ctx, exporter, desc) } // ImportImage creates a set of images by tarstream. // // NOTE: One tar may have several manifests. func (c *Client) ImportImage(ctx context.Context, importer ctrdmetaimages.Importer, reader io.Reader) ([]containerd.Image, error) { + imgs, err := c.importImage(ctx, importer, reader) + if err != nil { + return imgs, convertCtrdErr(err) + } + return imgs, nil +} + +// importImage creates a set of images by tarstream. +// +// NOTE: One tar may have several manifests. +func (c *Client) importImage(ctx context.Context, importer ctrdmetaimages.Importer, reader io.Reader) ([]containerd.Image, error) { wrapperCli, err := c.Get(ctx) if err != nil { return nil, fmt.Errorf("failed to get a containerd grpc client: %v", err) diff --git a/ctrd/utils.go b/ctrd/utils.go index ecd841d1c..1c14f2d75 100644 --- a/ctrd/utils.go +++ b/ctrd/utils.go @@ -9,9 +9,11 @@ import ( "time" "github.com/alibaba/pouch/apis/types" + "github.com/alibaba/pouch/pkg/errtypes" "github.com/alibaba/pouch/pkg/utils" "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/oci" "github.com/containerd/containerd/remotes" @@ -19,6 +21,7 @@ import ( "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" ) // NewDefaultSpec new a template spec with default. @@ -166,3 +169,31 @@ func toLinuxResources(resources types.Resources) (*specs.LinuxResources, error) return r, nil } + +// convertCtrdErr converts containerd client error into a pouchd manager error. +// containerd client error converts GRPC code from containerd API to containerd client error. +// pouchd manager error is used in the whole managers and API layers to construct status code for API. +// there should be a way convert the previous to the latter one. +func convertCtrdErr(err error) error { + if err == nil { + return nil + } + + if errdefs.IsNotFound(err) { + return errors.Wrap(errtypes.ErrNotfound, err.Error()) + } + + if errdefs.IsAlreadyExists(err) { + return errors.Wrap(errtypes.ErrAlreadyExisted, err.Error()) + } + + if errdefs.IsInvalidArgument(err) { + return errors.Wrap(errtypes.ErrInvalidParam, err.Error()) + } + + if errdefs.IsNotImplemented(err) { + return errors.Wrap(errtypes.ErrNotImplemented, err.Error()) + } + + return err +} diff --git a/ctrd/utils_test.go b/ctrd/utils_test.go new file mode 100644 index 000000000..c67b92b9e --- /dev/null +++ b/ctrd/utils_test.go @@ -0,0 +1,83 @@ +package ctrd + +import ( + "fmt" + "testing" + + "github.com/alibaba/pouch/pkg/errtypes" + + "github.com/containerd/containerd/errdefs" + "github.com/pkg/errors" +) + +func Test_convertCtrdErr(t *testing.T) { + type args struct { + err error + } + tests := []struct { + name string + args args + wantErr bool + returnedErr error + }{ + { + name: "nil", + args: args{ + err: nil, + }, + wantErr: false, + returnedErr: nil, + }, + { + name: "not found", + args: args{ + err: errors.Wrap(errdefs.ErrNotFound, "container asdfghjk"), + }, + wantErr: true, + returnedErr: errors.Wrap(errtypes.ErrNotfound, errors.Wrap(errdefs.ErrNotFound, "container asdfghjk").Error()), + }, + { + name: "invalid params", + args: args{ + err: errors.Wrap(errdefs.ErrInvalidArgument, "container asdfghjk"), + }, + wantErr: true, + returnedErr: errors.Wrap(errtypes.ErrInvalidParam, errors.Wrap(errdefs.ErrInvalidArgument, "container asdfghjk").Error()), + }, + { + name: "already exists", + args: args{ + err: errors.Wrap(errdefs.ErrAlreadyExists, "container asdfghjk"), + }, + wantErr: true, + returnedErr: errors.Wrap(errtypes.ErrAlreadyExisted, errors.Wrap(errdefs.ErrAlreadyExists, "container asdfghjk").Error()), + }, + { + name: "not implemented", + args: args{ + err: errors.Wrap(errdefs.ErrNotImplemented, "container asdfghjk"), + }, + wantErr: true, + returnedErr: errors.Wrap(errtypes.ErrNotImplemented, errors.Wrap(errdefs.ErrNotImplemented, "container asdfghjk").Error()), + }, + { + name: "normal error", + args: args{ + err: fmt.Errorf("this is a normal error"), + }, + wantErr: true, + returnedErr: fmt.Errorf("this is a normal error"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := convertCtrdErr(tt.args.err) + if (err != nil) != tt.wantErr { + t.Errorf("convertCtrdErr() error = %v, wantErr %v", err, tt.wantErr) + } + if tt.wantErr && (err.Error() != tt.returnedErr.Error()) { + t.Errorf("convertCtrdErr() error = %v, wantErr %v, returnedErr: %v", err, tt.wantErr, tt.returnedErr) + } + }) + } +} diff --git a/test/api_container_start_test.go b/test/api_container_start_test.go index 074315ceb..0e3e4c76f 100644 --- a/test/api_container_start_test.go +++ b/test/api_container_start_test.go @@ -64,7 +64,7 @@ func (suite *APIContainerStartSuite) TestStartPausedContainer(c *check.C) { resp, err := request.Post("/containers/" + cname + "/start") c.Assert(err, check.IsNil) - CheckRespStatus(c, resp, 500) + CheckRespStatus(c, resp, 409) } // TestStartDetachKeyWork test detatch-keys works.