mirror of
https://github.com/moby/moby.git
synced 2025-08-01 05:47:11 +03:00
client: normalize and validate empty ID / name arguments to fail early
In situations where an empty ID was passed, the client would construct an invalid API endpoint URL, which either resulted in the "not found" handler being hit (resulting in a "page not found" error), or even the wrong endpoint being hit if the client follows redirects. For example, `/containers/<empty id>/json` (inspect) redirects to `/containers/json` (docker ps)) Given that empty IDs should never be expected (especially if they're part of the API URL path), we can validate these and return early. Its worth noting that a few methods already had an error in place; those methods were related to the situation mentioned above, where (e.g.) an "inspect" would redirect to a "list" endpoint. The existing errors, for convenience, mimicked a "not found" error; this patch changes such errors to an "Invalid Parameter" instead, which is more correct, but it could be a breaking change for some edge cases where users parsed the output; git grep 'objectNotFoundError{' client/config_inspect.go: return swarm.Config{}, nil, objectNotFoundError{object: "config", id: id} client/container_inspect.go: return container.InspectResponse{}, nil, objectNotFoundError{object: "container", id: containerID} client/container_inspect.go: return container.InspectResponse{}, objectNotFoundError{object: "container", id: containerID} client/distribution_inspect.go: return distributionInspect, objectNotFoundError{object: "distribution", id: imageRef} client/image_inspect.go: return image.InspectResponse{}, nil, objectNotFoundError{object: "image", id: imageID} client/network_inspect.go: return network.Inspect{}, nil, objectNotFoundError{object: "network", id: networkID} client/node_inspect.go: return swarm.Node{}, nil, objectNotFoundError{object: "node", id: nodeID} client/plugin_inspect.go: return nil, nil, objectNotFoundError{object: "plugin", id: name} client/secret_inspect.go: return swarm.Secret{}, nil, objectNotFoundError{object: "secret", id: id} client/service_inspect.go: return swarm.Service{}, nil, objectNotFoundError{object: "service", id: serviceID} client/task_inspect.go: return swarm.Task{}, nil, objectNotFoundError{object: "task", id: taskID} client/volume_inspect.go: return volume.Volume{}, nil, objectNotFoundError{object: "volume", id: volumeID} Two such errors are still left, as "ID or name" would probably be confusing, but perhaps we can use a more generic error to include those as well (e.g. "invalid <object> reference: value is empty"); client/distribution_inspect.go: return distributionInspect, objectNotFoundError{object: "distribution", id: imageRef} client/image_inspect.go: return image.InspectResponse{}, nil, objectNotFoundError{object: "image", id: imageID} Before this patch: docker container start "" Error response from daemon: page not found Error: failed to start containers: docker container start " " Error response from daemon: No such container: Error: failed to start containers: With this patch: docker container start "" invalid container name or ID: value is empty Error: failed to start containers: docker container start " " invalid container name or ID: value is empty Error: failed to start containers: Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@ -7,8 +7,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CheckpointCreate creates a checkpoint from the given container with the given name
|
// CheckpointCreate creates a checkpoint from the given container with the given name
|
||||||
func (cli *Client) CheckpointCreate(ctx context.Context, container string, options checkpoint.CreateOptions) error {
|
func (cli *Client) CheckpointCreate(ctx context.Context, containerID string, options checkpoint.CreateOptions) error {
|
||||||
resp, err := cli.post(ctx, "/containers/"+container+"/checkpoints", nil, options, nil)
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := cli.post(ctx, "/containers/"+containerID+"/checkpoints", nil, options, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,14 @@ func TestCheckpointCreateError(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.CheckpointCreate(context.Background(), "", checkpoint.CreateOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.CheckpointCreate(context.Background(), " ", checkpoint.CreateOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckpointCreate(t *testing.T) {
|
func TestCheckpointCreate(t *testing.T) {
|
||||||
@ -36,7 +44,7 @@ func TestCheckpointCreate(t *testing.T) {
|
|||||||
client := &Client{
|
client := &Client{
|
||||||
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
||||||
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
||||||
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, req.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Method != http.MethodPost {
|
if req.Method != http.MethodPost {
|
||||||
|
@ -9,6 +9,11 @@ import (
|
|||||||
|
|
||||||
// CheckpointDelete deletes the checkpoint with the given name from the given container
|
// CheckpointDelete deletes the checkpoint with the given name from the given container
|
||||||
func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, options checkpoint.DeleteOptions) error {
|
func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, options checkpoint.DeleteOptions) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.CheckpointDir != "" {
|
if options.CheckpointDir != "" {
|
||||||
query.Set("dir", options.CheckpointDir)
|
query.Set("dir", options.CheckpointDir)
|
||||||
|
@ -25,6 +25,14 @@ func TestCheckpointDeleteError(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.CheckpointDelete(context.Background(), "", checkpoint.DeleteOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.CheckpointDelete(context.Background(), " ", checkpoint.DeleteOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckpointDelete(t *testing.T) {
|
func TestCheckpointDelete(t *testing.T) {
|
||||||
|
@ -11,8 +11,9 @@ import (
|
|||||||
|
|
||||||
// ConfigInspectWithRaw returns the config information with raw data
|
// ConfigInspectWithRaw returns the config information with raw data
|
||||||
func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error) {
|
func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error) {
|
||||||
if id == "" {
|
id, err := trimID("contig", id)
|
||||||
return swarm.Config{}, nil, objectNotFoundError{object: "config", id: id}
|
if err != nil {
|
||||||
|
return swarm.Config{}, nil, err
|
||||||
}
|
}
|
||||||
if err := cli.NewVersionError(ctx, "1.30", "config inspect"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.30", "config inspect"); err != nil {
|
||||||
return swarm.Config{}, nil, err
|
return swarm.Config{}, nil, err
|
||||||
|
@ -33,7 +33,12 @@ func TestConfigInspectWithEmptyID(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
_, _, err := client.ConfigInspectWithRaw(context.Background(), "")
|
_, _, err := client.ConfigInspectWithRaw(context.Background(), "")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, _, err = client.ConfigInspectWithRaw(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigInspectUnsupported(t *testing.T) {
|
func TestConfigInspectUnsupported(t *testing.T) {
|
||||||
|
@ -4,6 +4,10 @@ import "context"
|
|||||||
|
|
||||||
// ConfigRemove removes a config.
|
// ConfigRemove removes a config.
|
||||||
func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
|
func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
|
||||||
|
id, err := trimID("config", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := cli.NewVersionError(ctx, "1.30", "config remove"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.30", "config remove"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,14 @@ func TestConfigRemoveError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.ConfigRemove(context.Background(), "config_id")
|
err := client.ConfigRemove(context.Background(), "config_id")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ConfigRemove(context.Background(), "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ConfigRemove(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigRemove(t *testing.T) {
|
func TestConfigRemove(t *testing.T) {
|
||||||
|
@ -9,6 +9,10 @@ import (
|
|||||||
|
|
||||||
// ConfigUpdate attempts to update a config
|
// ConfigUpdate attempts to update a config
|
||||||
func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error {
|
func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error {
|
||||||
|
id, err := trimID("config", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := cli.NewVersionError(ctx, "1.30", "config update"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.30", "config update"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,14 @@ func TestConfigUpdateError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.ConfigUpdate(context.Background(), "config_id", swarm.Version{}, swarm.ConfigSpec{})
|
err := client.ConfigUpdate(context.Background(), "config_id", swarm.Version{}, swarm.ConfigSpec{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ConfigUpdate(context.Background(), "", swarm.Version{}, swarm.ConfigSpec{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ConfigUpdate(context.Background(), " ", swarm.Version{}, swarm.ConfigSpec{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigUpdate(t *testing.T) {
|
func TestConfigUpdate(t *testing.T) {
|
||||||
|
@ -33,7 +33,12 @@ import (
|
|||||||
//
|
//
|
||||||
// You can use github.com/docker/docker/pkg/stdcopy.StdCopy to demultiplex this
|
// You can use github.com/docker/docker/pkg/stdcopy.StdCopy to demultiplex this
|
||||||
// stream.
|
// stream.
|
||||||
func (cli *Client) ContainerAttach(ctx context.Context, container string, options container.AttachOptions) (types.HijackedResponse, error) {
|
func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options container.AttachOptions) (types.HijackedResponse, error) {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return types.HijackedResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.Stream {
|
if options.Stream {
|
||||||
query.Set("stream", "1")
|
query.Set("stream", "1")
|
||||||
@ -54,7 +59,7 @@ func (cli *Client) ContainerAttach(ctx context.Context, container string, option
|
|||||||
query.Set("logs", "1")
|
query.Set("logs", "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
return cli.postHijacked(ctx, "/containers/"+container+"/attach", query, nil, http.Header{
|
return cli.postHijacked(ctx, "/containers/"+containerID+"/attach", query, nil, http.Header{
|
||||||
"Content-Type": {"text/plain"},
|
"Content-Type": {"text/plain"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ContainerCommit applies changes to a container and creates a new tagged image.
|
// ContainerCommit applies changes to a container and creates a new tagged image.
|
||||||
func (cli *Client) ContainerCommit(ctx context.Context, container string, options container.CommitOptions) (types.IDResponse, error) {
|
func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options container.CommitOptions) (types.IDResponse, error) {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return types.IDResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
var repository, tag string
|
var repository, tag string
|
||||||
if options.Reference != "" {
|
if options.Reference != "" {
|
||||||
ref, err := reference.ParseNormalizedNamed(options.Reference)
|
ref, err := reference.ParseNormalizedNamed(options.Reference)
|
||||||
@ -32,7 +37,7 @@ func (cli *Client) ContainerCommit(ctx context.Context, container string, option
|
|||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("container", container)
|
query.Set("container", containerID)
|
||||||
query.Set("repo", repository)
|
query.Set("repo", repository)
|
||||||
query.Set("tag", tag)
|
query.Set("tag", tag)
|
||||||
query.Set("comment", options.Comment)
|
query.Set("comment", options.Comment)
|
||||||
|
@ -23,6 +23,14 @@ func TestContainerCommitError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, err := client.ContainerCommit(context.Background(), "nothing", container.CommitOptions{})
|
_, err := client.ContainerCommit(context.Background(), "nothing", container.CommitOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ContainerCommit(context.Background(), "", container.CommitOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerCommit(context.Background(), " ", container.CommitOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerCommit(t *testing.T) {
|
func TestContainerCommit(t *testing.T) {
|
||||||
|
@ -16,11 +16,15 @@ import (
|
|||||||
|
|
||||||
// ContainerStatPath returns stat information about a path inside the container filesystem.
|
// ContainerStatPath returns stat information about a path inside the container filesystem.
|
||||||
func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path string) (container.PathStat, error) {
|
func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path string) (container.PathStat, error) {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return container.PathStat{}, err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
|
query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
|
||||||
|
|
||||||
urlStr := "/containers/" + containerID + "/archive"
|
response, err := cli.head(ctx, "/containers/"+containerID+"/archive", query, nil)
|
||||||
response, err := cli.head(ctx, urlStr, query, nil)
|
|
||||||
defer ensureReaderClosed(response)
|
defer ensureReaderClosed(response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return container.PathStat{}, err
|
return container.PathStat{}, err
|
||||||
@ -31,6 +35,11 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
|
|||||||
// CopyToContainer copies content into the container filesystem.
|
// CopyToContainer copies content into the container filesystem.
|
||||||
// Note that `content` must be a Reader for a TAR archive
|
// Note that `content` must be a Reader for a TAR archive
|
||||||
func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath string, content io.Reader, options container.CopyToContainerOptions) error {
|
func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath string, content io.Reader, options container.CopyToContainerOptions) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("path", filepath.ToSlash(dstPath)) // Normalize the paths used in the API.
|
query.Set("path", filepath.ToSlash(dstPath)) // Normalize the paths used in the API.
|
||||||
// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
|
// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
|
||||||
@ -42,9 +51,7 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath str
|
|||||||
query.Set("copyUIDGID", "true")
|
query.Set("copyUIDGID", "true")
|
||||||
}
|
}
|
||||||
|
|
||||||
apiPath := "/containers/" + containerID + "/archive"
|
response, err := cli.putRaw(ctx, "/containers/"+containerID+"/archive", query, content, nil)
|
||||||
|
|
||||||
response, err := cli.putRaw(ctx, apiPath, query, content, nil)
|
|
||||||
defer ensureReaderClosed(response)
|
defer ensureReaderClosed(response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -56,11 +63,15 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath str
|
|||||||
// CopyFromContainer gets the content from the container and returns it as a Reader
|
// CopyFromContainer gets the content from the container and returns it as a Reader
|
||||||
// for a TAR archive to manipulate it in the host. It's up to the caller to close the reader.
|
// for a TAR archive to manipulate it in the host. It's up to the caller to close the reader.
|
||||||
func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, container.PathStat, error) {
|
func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, container.PathStat, error) {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, container.PathStat{}, err
|
||||||
|
}
|
||||||
|
|
||||||
query := make(url.Values, 1)
|
query := make(url.Values, 1)
|
||||||
query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
|
query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
|
||||||
|
|
||||||
apiPath := "/containers/" + containerID + "/archive"
|
response, err := cli.get(ctx, "/containers/"+containerID+"/archive", query, nil)
|
||||||
response, err := cli.get(ctx, apiPath, query, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, container.PathStat{}, err
|
return nil, container.PathStat{}, err
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,14 @@ func TestContainerStatPathError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, err := client.ContainerStatPath(context.Background(), "container_id", "path")
|
_, err := client.ContainerStatPath(context.Background(), "container_id", "path")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ContainerStatPath(context.Background(), "", "path")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerStatPath(context.Background(), " ", "path")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerStatPathNotFoundError(t *testing.T) {
|
func TestContainerStatPathNotFoundError(t *testing.T) {
|
||||||
@ -99,6 +107,14 @@ func TestCopyToContainerError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := client.CopyToContainer(context.Background(), "container_id", "path/to/file", bytes.NewReader([]byte("")), container.CopyToContainerOptions{})
|
err := client.CopyToContainer(context.Background(), "container_id", "path/to/file", bytes.NewReader([]byte("")), container.CopyToContainerOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.CopyToContainer(context.Background(), "", "path/to/file", bytes.NewReader([]byte("")), container.CopyToContainerOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.CopyToContainer(context.Background(), " ", "path/to/file", bytes.NewReader([]byte("")), container.CopyToContainerOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCopyToContainerNotFoundError(t *testing.T) {
|
func TestCopyToContainerNotFoundError(t *testing.T) {
|
||||||
@ -173,6 +189,14 @@ func TestCopyFromContainerError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, _, err := client.CopyFromContainer(context.Background(), "container_id", "path/to/file")
|
_, _, err := client.CopyFromContainer(context.Background(), "container_id", "path/to/file")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, _, err = client.CopyFromContainer(context.Background(), "", "path/to/file")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, _, err = client.CopyFromContainer(context.Background(), " ", "path/to/file")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCopyFromContainerNotFoundError(t *testing.T) {
|
func TestCopyFromContainerNotFoundError(t *testing.T) {
|
||||||
|
@ -10,14 +10,21 @@ import (
|
|||||||
|
|
||||||
// ContainerDiff shows differences in a container filesystem since it was started.
|
// ContainerDiff shows differences in a container filesystem since it was started.
|
||||||
func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) {
|
func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) {
|
||||||
var changes []container.FilesystemChange
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil)
|
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil)
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return changes, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var changes []container.FilesystemChange
|
||||||
err = json.NewDecoder(serverResp.body).Decode(&changes)
|
err = json.NewDecoder(serverResp.body).Decode(&changes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return changes, err
|
return changes, err
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,14 @@ func TestContainerDiffError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, err := client.ContainerDiff(context.Background(), "nothing")
|
_, err := client.ContainerDiff(context.Background(), "nothing")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ContainerDiff(context.Background(), "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerDiff(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerDiff(t *testing.T) {
|
func TestContainerDiff(t *testing.T) {
|
||||||
|
@ -11,8 +11,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ContainerExecCreate creates a new exec configuration to run an exec process.
|
// ContainerExecCreate creates a new exec configuration to run an exec process.
|
||||||
func (cli *Client) ContainerExecCreate(ctx context.Context, container string, options container.ExecOptions) (types.IDResponse, error) {
|
func (cli *Client) ContainerExecCreate(ctx context.Context, containerID string, options container.ExecOptions) (types.IDResponse, error) {
|
||||||
var response types.IDResponse
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return types.IDResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure we negotiated (if the client is configured to do so),
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
// as code below contains API-version specific handling of options.
|
// as code below contains API-version specific handling of options.
|
||||||
@ -20,21 +23,23 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, container string, op
|
|||||||
// Normally, version-negotiation (if enabled) would not happen until
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
// the API request is made.
|
// the API request is made.
|
||||||
if err := cli.checkVersion(ctx); err != nil {
|
if err := cli.checkVersion(ctx); err != nil {
|
||||||
return response, err
|
return types.IDResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cli.NewVersionError(ctx, "1.25", "env"); len(options.Env) != 0 && err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "env"); len(options.Env) != 0 && err != nil {
|
||||||
return response, err
|
return types.IDResponse{}, err
|
||||||
}
|
}
|
||||||
if versions.LessThan(cli.ClientVersion(), "1.42") {
|
if versions.LessThan(cli.ClientVersion(), "1.42") {
|
||||||
options.ConsoleSize = nil
|
options.ConsoleSize = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, options, nil)
|
resp, err := cli.post(ctx, "/containers/"+containerID+"/exec", nil, options, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return types.IDResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var response types.IDResponse
|
||||||
err = json.NewDecoder(resp.body).Decode(&response)
|
err = json.NewDecoder(resp.body).Decode(&response)
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,17 @@ func TestContainerExecCreateError(t *testing.T) {
|
|||||||
client := &Client{
|
client := &Client{
|
||||||
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.ContainerExecCreate(context.Background(), "container_id", container.ExecOptions{})
|
_, err := client.ContainerExecCreate(context.Background(), "container_id", container.ExecOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ContainerExecCreate(context.Background(), "", container.ExecOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerExecCreate(context.Background(), " ", container.ExecOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestContainerExecCreateConnectionError verifies that connection errors occurring
|
// TestContainerExecCreateConnectionError verifies that connection errors occurring
|
||||||
@ -33,7 +42,7 @@ func TestContainerExecCreateConnectionError(t *testing.T) {
|
|||||||
client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid"))
|
client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid"))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
_, err = client.ContainerExecCreate(context.Background(), "", container.ExecOptions{})
|
_, err = client.ContainerExecCreate(context.Background(), "container_id", container.ExecOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, IsErrConnectionFailed))
|
assert.Check(t, is.ErrorType(err, IsErrConnectionFailed))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,11 @@ import (
|
|||||||
// and returns them as an io.ReadCloser. It's up to the caller
|
// and returns them as an io.ReadCloser. It's up to the caller
|
||||||
// to close the stream.
|
// to close the stream.
|
||||||
func (cli *Client) ContainerExport(ctx context.Context, containerID string) (io.ReadCloser, error) {
|
func (cli *Client) ContainerExport(ctx context.Context, containerID string) (io.ReadCloser, error) {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/export", url.Values{}, nil)
|
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/export", url.Values{}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -20,6 +20,14 @@ func TestContainerExportError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, err := client.ContainerExport(context.Background(), "nothing")
|
_, err := client.ContainerExport(context.Background(), "nothing")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ContainerExport(context.Background(), "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerExport(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerExport(t *testing.T) {
|
func TestContainerExport(t *testing.T) {
|
||||||
|
@ -12,9 +12,11 @@ import (
|
|||||||
|
|
||||||
// ContainerInspect returns the container information.
|
// ContainerInspect returns the container information.
|
||||||
func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (container.InspectResponse, error) {
|
func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (container.InspectResponse, error) {
|
||||||
if containerID == "" {
|
containerID, err := trimID("container", containerID)
|
||||||
return container.InspectResponse{}, objectNotFoundError{object: "container", id: containerID}
|
if err != nil {
|
||||||
|
return container.InspectResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
|
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -28,9 +30,11 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (co
|
|||||||
|
|
||||||
// ContainerInspectWithRaw returns the container information and its raw representation.
|
// ContainerInspectWithRaw returns the container information and its raw representation.
|
||||||
func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID string, getSize bool) (container.InspectResponse, []byte, error) {
|
func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID string, getSize bool) (container.InspectResponse, []byte, error) {
|
||||||
if containerID == "" {
|
containerID, err := trimID("container", containerID)
|
||||||
return container.InspectResponse{}, nil, objectNotFoundError{object: "container", id: containerID}
|
if err != nil {
|
||||||
|
return container.InspectResponse{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if getSize {
|
if getSize {
|
||||||
query.Set("size", "1")
|
query.Set("size", "1")
|
||||||
|
@ -24,6 +24,14 @@ func TestContainerInspectError(t *testing.T) {
|
|||||||
|
|
||||||
_, err := client.ContainerInspect(context.Background(), "nothing")
|
_, err := client.ContainerInspect(context.Background(), "nothing")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ContainerInspect(context.Background(), "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerInspect(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerInspectContainerNotFound(t *testing.T) {
|
func TestContainerInspectContainerNotFound(t *testing.T) {
|
||||||
@ -42,7 +50,12 @@ func TestContainerInspectWithEmptyID(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
_, _, err := client.ContainerInspectWithRaw(context.Background(), "", true)
|
_, _, err := client.ContainerInspectWithRaw(context.Background(), "", true)
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, _, err = client.ContainerInspectWithRaw(context.Background(), " ", true)
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerInspect(t *testing.T) {
|
func TestContainerInspect(t *testing.T) {
|
||||||
|
@ -7,6 +7,11 @@ import (
|
|||||||
|
|
||||||
// ContainerKill terminates the container process but does not remove the container from the docker host.
|
// ContainerKill terminates the container process but does not remove the container from the docker host.
|
||||||
func (cli *Client) ContainerKill(ctx context.Context, containerID, signal string) error {
|
func (cli *Client) ContainerKill(ctx context.Context, containerID, signal string) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if signal != "" {
|
if signal != "" {
|
||||||
query.Set("signal", signal)
|
query.Set("signal", signal)
|
||||||
|
@ -20,6 +20,14 @@ func TestContainerKillError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := client.ContainerKill(context.Background(), "nothing", "SIGKILL")
|
err := client.ContainerKill(context.Background(), "nothing", "SIGKILL")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ContainerKill(context.Background(), "", "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ContainerKill(context.Background(), " ", "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerKill(t *testing.T) {
|
func TestContainerKill(t *testing.T) {
|
||||||
|
@ -33,7 +33,12 @@ import (
|
|||||||
//
|
//
|
||||||
// You can use github.com/docker/docker/pkg/stdcopy.StdCopy to demultiplex this
|
// You can use github.com/docker/docker/pkg/stdcopy.StdCopy to demultiplex this
|
||||||
// stream.
|
// stream.
|
||||||
func (cli *Client) ContainerLogs(ctx context.Context, container string, options container.LogsOptions) (io.ReadCloser, error) {
|
func (cli *Client) ContainerLogs(ctx context.Context, containerID string, options container.LogsOptions) (io.ReadCloser, error) {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.ShowStdout {
|
if options.ShowStdout {
|
||||||
query.Set("stdout", "1")
|
query.Set("stdout", "1")
|
||||||
@ -72,7 +77,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options
|
|||||||
}
|
}
|
||||||
query.Set("tail", options.Tail)
|
query.Set("tail", options.Tail)
|
||||||
|
|
||||||
resp, err := cli.get(ctx, "/containers/"+container+"/logs", query, nil)
|
resp, err := cli.get(ctx, "/containers/"+containerID+"/logs", query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,14 @@ func TestContainerLogsNotFoundError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, err := client.ContainerLogs(context.Background(), "container_id", container.LogsOptions{})
|
_, err := client.ContainerLogs(context.Background(), "container_id", container.LogsOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
||||||
|
|
||||||
|
_, err = client.ContainerLogs(context.Background(), "", container.LogsOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerLogs(context.Background(), " ", container.LogsOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerLogsError(t *testing.T) {
|
func TestContainerLogsError(t *testing.T) {
|
||||||
|
@ -4,6 +4,11 @@ import "context"
|
|||||||
|
|
||||||
// ContainerPause pauses the main process of a given container without terminating it.
|
// ContainerPause pauses the main process of a given container without terminating it.
|
||||||
func (cli *Client) ContainerPause(ctx context.Context, containerID string) error {
|
func (cli *Client) ContainerPause(ctx context.Context, containerID string) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/pause", nil, nil, nil)
|
resp, err := cli.post(ctx, "/containers/"+containerID+"/pause", nil, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
|
@ -9,6 +9,11 @@ import (
|
|||||||
|
|
||||||
// ContainerRemove kills and removes a container from the docker host.
|
// ContainerRemove kills and removes a container from the docker host.
|
||||||
func (cli *Client) ContainerRemove(ctx context.Context, containerID string, options container.RemoveOptions) error {
|
func (cli *Client) ContainerRemove(ctx context.Context, containerID string, options container.RemoveOptions) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.RemoveVolumes {
|
if options.RemoveVolumes {
|
||||||
query.Set("v", "1")
|
query.Set("v", "1")
|
||||||
|
@ -21,6 +21,14 @@ func TestContainerRemoveError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := client.ContainerRemove(context.Background(), "container_id", container.RemoveOptions{})
|
err := client.ContainerRemove(context.Background(), "container_id", container.RemoveOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ContainerRemove(context.Background(), "", container.RemoveOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ContainerRemove(context.Background(), " ", container.RemoveOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerRemoveNotFoundError(t *testing.T) {
|
func TestContainerRemoveNotFoundError(t *testing.T) {
|
||||||
|
@ -7,6 +7,11 @@ import (
|
|||||||
|
|
||||||
// ContainerRename changes the name of a given container.
|
// ContainerRename changes the name of a given container.
|
||||||
func (cli *Client) ContainerRename(ctx context.Context, containerID, newContainerName string) error {
|
func (cli *Client) ContainerRename(ctx context.Context, containerID, newContainerName string) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("name", newContainerName)
|
query.Set("name", newContainerName)
|
||||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/rename", query, nil, nil)
|
resp, err := cli.post(ctx, "/containers/"+containerID+"/rename", query, nil, nil)
|
||||||
|
@ -20,6 +20,14 @@ func TestContainerRenameError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := client.ContainerRename(context.Background(), "nothing", "newNothing")
|
err := client.ContainerRename(context.Background(), "nothing", "newNothing")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ContainerRename(context.Background(), "", "newNothing")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ContainerRename(context.Background(), " ", "newNothing")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerRename(t *testing.T) {
|
func TestContainerRename(t *testing.T) {
|
||||||
|
@ -10,11 +10,19 @@ import (
|
|||||||
|
|
||||||
// ContainerResize changes the size of the tty for a container.
|
// ContainerResize changes the size of the tty for a container.
|
||||||
func (cli *Client) ContainerResize(ctx context.Context, containerID string, options container.ResizeOptions) error {
|
func (cli *Client) ContainerResize(ctx context.Context, containerID string, options container.ResizeOptions) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return cli.resize(ctx, "/containers/"+containerID, options.Height, options.Width)
|
return cli.resize(ctx, "/containers/"+containerID, options.Height, options.Width)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerExecResize changes the size of the tty for an exec process running inside a container.
|
// ContainerExecResize changes the size of the tty for an exec process running inside a container.
|
||||||
func (cli *Client) ContainerExecResize(ctx context.Context, execID string, options container.ResizeOptions) error {
|
func (cli *Client) ContainerExecResize(ctx context.Context, execID string, options container.ResizeOptions) error {
|
||||||
|
execID, err := trimID("exec", execID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return cli.resize(ctx, "/exec/"+execID, options.Height, options.Width)
|
return cli.resize(ctx, "/exec/"+execID, options.Height, options.Width)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,14 @@ func TestContainerResizeError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := client.ContainerResize(context.Background(), "container_id", container.ResizeOptions{})
|
err := client.ContainerResize(context.Background(), "container_id", container.ResizeOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ContainerResize(context.Background(), "", container.ResizeOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ContainerResize(context.Background(), " ", container.ResizeOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerExecResizeError(t *testing.T) {
|
func TestContainerExecResizeError(t *testing.T) {
|
||||||
|
@ -13,6 +13,11 @@ import (
|
|||||||
// It makes the daemon wait for the container to be up again for
|
// It makes the daemon wait for the container to be up again for
|
||||||
// a specific amount of time, given the timeout.
|
// a specific amount of time, given the timeout.
|
||||||
func (cli *Client) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
|
func (cli *Client) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.Timeout != nil {
|
if options.Timeout != nil {
|
||||||
query.Set("t", strconv.Itoa(*options.Timeout))
|
query.Set("t", strconv.Itoa(*options.Timeout))
|
||||||
|
@ -21,6 +21,14 @@ func TestContainerRestartError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := client.ContainerRestart(context.Background(), "nothing", container.StopOptions{})
|
err := client.ContainerRestart(context.Background(), "nothing", container.StopOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ContainerRestart(context.Background(), "", container.StopOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ContainerRestart(context.Background(), " ", container.StopOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestContainerRestartConnectionError verifies that connection errors occurring
|
// TestContainerRestartConnectionError verifies that connection errors occurring
|
||||||
|
@ -9,6 +9,11 @@ import (
|
|||||||
|
|
||||||
// ContainerStart sends a request to the docker daemon to start a container.
|
// ContainerStart sends a request to the docker daemon to start a container.
|
||||||
func (cli *Client) ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error {
|
func (cli *Client) ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if len(options.CheckpointID) != 0 {
|
if len(options.CheckpointID) != 0 {
|
||||||
query.Set("checkpoint", options.CheckpointID)
|
query.Set("checkpoint", options.CheckpointID)
|
||||||
|
@ -22,6 +22,14 @@ func TestContainerStartError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := client.ContainerStart(context.Background(), "nothing", container.StartOptions{})
|
err := client.ContainerStart(context.Background(), "nothing", container.StartOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ContainerStart(context.Background(), "", container.StartOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ContainerStart(context.Background(), " ", container.StartOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerStart(t *testing.T) {
|
func TestContainerStart(t *testing.T) {
|
||||||
|
@ -10,6 +10,11 @@ import (
|
|||||||
// ContainerStats returns near realtime stats for a given container.
|
// ContainerStats returns near realtime stats for a given container.
|
||||||
// It's up to the caller to close the io.ReadCloser returned.
|
// It's up to the caller to close the io.ReadCloser returned.
|
||||||
func (cli *Client) ContainerStats(ctx context.Context, containerID string, stream bool) (container.StatsResponseReader, error) {
|
func (cli *Client) ContainerStats(ctx context.Context, containerID string, stream bool) (container.StatsResponseReader, error) {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return container.StatsResponseReader{}, err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("stream", "0")
|
query.Set("stream", "0")
|
||||||
if stream {
|
if stream {
|
||||||
@ -30,6 +35,11 @@ func (cli *Client) ContainerStats(ctx context.Context, containerID string, strea
|
|||||||
// ContainerStatsOneShot gets a single stat entry from a container.
|
// ContainerStatsOneShot gets a single stat entry from a container.
|
||||||
// It differs from `ContainerStats` in that the API should not wait to prime the stats
|
// It differs from `ContainerStats` in that the API should not wait to prime the stats
|
||||||
func (cli *Client) ContainerStatsOneShot(ctx context.Context, containerID string) (container.StatsResponseReader, error) {
|
func (cli *Client) ContainerStatsOneShot(ctx context.Context, containerID string) (container.StatsResponseReader, error) {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return container.StatsResponseReader{}, err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("stream", "0")
|
query.Set("stream", "0")
|
||||||
query.Set("one-shot", "1")
|
query.Set("one-shot", "1")
|
||||||
|
@ -20,6 +20,14 @@ func TestContainerStatsError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, err := client.ContainerStats(context.Background(), "nothing", false)
|
_, err := client.ContainerStats(context.Background(), "nothing", false)
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ContainerStats(context.Background(), "", false)
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerStats(context.Background(), " ", false)
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerStats(t *testing.T) {
|
func TestContainerStats(t *testing.T) {
|
||||||
|
@ -17,6 +17,11 @@ import (
|
|||||||
// otherwise the engine default. A negative timeout value can be specified,
|
// otherwise the engine default. A negative timeout value can be specified,
|
||||||
// meaning no timeout, i.e. no forceful termination is performed.
|
// meaning no timeout, i.e. no forceful termination is performed.
|
||||||
func (cli *Client) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error {
|
func (cli *Client) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.Timeout != nil {
|
if options.Timeout != nil {
|
||||||
query.Set("t", strconv.Itoa(*options.Timeout))
|
query.Set("t", strconv.Itoa(*options.Timeout))
|
||||||
|
@ -19,8 +19,16 @@ func TestContainerStopError(t *testing.T) {
|
|||||||
client := &Client{
|
client := &Client{
|
||||||
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
}
|
}
|
||||||
err := client.ContainerStop(context.Background(), "nothing", container.StopOptions{})
|
err := client.ContainerStop(context.Background(), "container_id", container.StopOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ContainerStop(context.Background(), "", container.StopOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ContainerStop(context.Background(), " ", container.StopOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestContainerStopConnectionError verifies that connection errors occurring
|
// TestContainerStopConnectionError verifies that connection errors occurring
|
||||||
@ -31,7 +39,7 @@ func TestContainerStopConnectionError(t *testing.T) {
|
|||||||
client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid"))
|
client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid"))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = client.ContainerStop(context.Background(), "nothing", container.StopOptions{})
|
err = client.ContainerStop(context.Background(), "container_id", container.StopOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, IsErrConnectionFailed))
|
assert.Check(t, is.ErrorType(err, IsErrConnectionFailed))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +48,7 @@ func TestContainerStop(t *testing.T) {
|
|||||||
client := &Client{
|
client := &Client{
|
||||||
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
||||||
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
||||||
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, req.URL)
|
||||||
}
|
}
|
||||||
s := req.URL.Query().Get("signal")
|
s := req.URL.Query().Get("signal")
|
||||||
if s != "SIGKILL" {
|
if s != "SIGKILL" {
|
||||||
|
@ -11,7 +11,11 @@ import (
|
|||||||
|
|
||||||
// ContainerTop shows process information from within a container.
|
// ContainerTop shows process information from within a container.
|
||||||
func (cli *Client) ContainerTop(ctx context.Context, containerID string, arguments []string) (container.ContainerTopOKBody, error) {
|
func (cli *Client) ContainerTop(ctx context.Context, containerID string, arguments []string) (container.ContainerTopOKBody, error) {
|
||||||
var response container.ContainerTopOKBody
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return container.ContainerTopOKBody{}, err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if len(arguments) > 0 {
|
if len(arguments) > 0 {
|
||||||
query.Set("ps_args", strings.Join(arguments, " "))
|
query.Set("ps_args", strings.Join(arguments, " "))
|
||||||
@ -20,9 +24,10 @@ func (cli *Client) ContainerTop(ctx context.Context, containerID string, argumen
|
|||||||
resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil)
|
resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return container.ContainerTopOKBody{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var response container.ContainerTopOKBody
|
||||||
err = json.NewDecoder(resp.body).Decode(&response)
|
err = json.NewDecoder(resp.body).Decode(&response)
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,14 @@ func TestContainerTopError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, err := client.ContainerTop(context.Background(), "nothing", []string{})
|
_, err := client.ContainerTop(context.Background(), "nothing", []string{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ContainerTop(context.Background(), "", []string{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerTop(context.Background(), " ", []string{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerTop(t *testing.T) {
|
func TestContainerTop(t *testing.T) {
|
||||||
|
@ -4,6 +4,11 @@ import "context"
|
|||||||
|
|
||||||
// ContainerUnpause resumes the process execution within a container
|
// ContainerUnpause resumes the process execution within a container
|
||||||
func (cli *Client) ContainerUnpause(ctx context.Context, containerID string) error {
|
func (cli *Client) ContainerUnpause(ctx context.Context, containerID string) error {
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/unpause", nil, nil, nil)
|
resp, err := cli.post(ctx, "/containers/"+containerID+"/unpause", nil, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
|
@ -20,6 +20,14 @@ func TestContainerUnpauseError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := client.ContainerUnpause(context.Background(), "nothing")
|
err := client.ContainerUnpause(context.Background(), "nothing")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ContainerUnpause(context.Background(), "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ContainerUnpause(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerUnpause(t *testing.T) {
|
func TestContainerUnpause(t *testing.T) {
|
||||||
|
@ -9,13 +9,18 @@ import (
|
|||||||
|
|
||||||
// ContainerUpdate updates the resources of a container.
|
// ContainerUpdate updates the resources of a container.
|
||||||
func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) {
|
func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) {
|
||||||
var response container.ContainerUpdateOKBody
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return container.ContainerUpdateOKBody{}, err
|
||||||
|
}
|
||||||
|
|
||||||
serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil)
|
serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil)
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return container.ContainerUpdateOKBody{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var response container.ContainerUpdateOKBody
|
||||||
err = json.NewDecoder(serverResp.body).Decode(&response)
|
err = json.NewDecoder(serverResp.body).Decode(&response)
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,14 @@ func TestContainerUpdateError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, err := client.ContainerUpdate(context.Background(), "nothing", container.UpdateConfig{})
|
_, err := client.ContainerUpdate(context.Background(), "nothing", container.UpdateConfig{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ContainerUpdate(context.Background(), "", container.UpdateConfig{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ContainerUpdate(context.Background(), " ", container.UpdateConfig{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerUpdate(t *testing.T) {
|
func TestContainerUpdate(t *testing.T) {
|
||||||
|
@ -33,6 +33,12 @@ func (cli *Client) ContainerWait(ctx context.Context, containerID string, condit
|
|||||||
resultC := make(chan container.WaitResponse)
|
resultC := make(chan container.WaitResponse)
|
||||||
errC := make(chan error, 1)
|
errC := make(chan error, 1)
|
||||||
|
|
||||||
|
containerID, err := trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
errC <- err
|
||||||
|
return resultC, errC
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure we negotiated (if the client is configured to do so),
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
// as code below contains API-version specific handling of options.
|
// as code below contains API-version specific handling of options.
|
||||||
//
|
//
|
||||||
|
@ -8,6 +8,16 @@ import (
|
|||||||
|
|
||||||
// NetworkConnect connects a container to an existent network in the docker host.
|
// NetworkConnect connects a container to an existent network in the docker host.
|
||||||
func (cli *Client) NetworkConnect(ctx context.Context, networkID, containerID string, config *network.EndpointSettings) error {
|
func (cli *Client) NetworkConnect(ctx context.Context, networkID, containerID string, config *network.EndpointSettings) error {
|
||||||
|
networkID, err := trimID("network", networkID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
containerID, err = trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
nc := network.ConnectOptions{
|
nc := network.ConnectOptions{
|
||||||
Container: containerID,
|
Container: containerID,
|
||||||
EndpointConfig: config,
|
EndpointConfig: config,
|
||||||
|
@ -23,6 +23,15 @@ func TestNetworkConnectError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.NetworkConnect(context.Background(), "network_id", "container_id", nil)
|
err := client.NetworkConnect(context.Background(), "network_id", "container_id", nil)
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
// Empty network ID or container ID
|
||||||
|
err = client.NetworkConnect(context.Background(), "", "container_id", nil)
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.NetworkConnect(context.Background(), "network_id", "", nil)
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetworkConnectEmptyNilEndpointSettings(t *testing.T) {
|
func TestNetworkConnectEmptyNilEndpointSettings(t *testing.T) {
|
||||||
|
@ -8,6 +8,16 @@ import (
|
|||||||
|
|
||||||
// NetworkDisconnect disconnects a container from an existent network in the docker host.
|
// NetworkDisconnect disconnects a container from an existent network in the docker host.
|
||||||
func (cli *Client) NetworkDisconnect(ctx context.Context, networkID, containerID string, force bool) error {
|
func (cli *Client) NetworkDisconnect(ctx context.Context, networkID, containerID string, force bool) error {
|
||||||
|
networkID, err := trimID("network", networkID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
containerID, err = trimID("container", containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
nd := network.DisconnectOptions{
|
nd := network.DisconnectOptions{
|
||||||
Container: containerID,
|
Container: containerID,
|
||||||
Force: force,
|
Force: force,
|
||||||
|
@ -23,6 +23,15 @@ func TestNetworkDisconnectError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.NetworkDisconnect(context.Background(), "network_id", "container_id", false)
|
err := client.NetworkDisconnect(context.Background(), "network_id", "container_id", false)
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
// Empty network ID or container ID
|
||||||
|
err = client.NetworkDisconnect(context.Background(), "", "container_id", false)
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.NetworkDisconnect(context.Background(), "network_id", "", false)
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetworkDisconnect(t *testing.T) {
|
func TestNetworkDisconnect(t *testing.T) {
|
||||||
|
@ -18,8 +18,9 @@ func (cli *Client) NetworkInspect(ctx context.Context, networkID string, options
|
|||||||
|
|
||||||
// NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
|
// NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
|
||||||
func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, options network.InspectOptions) (network.Inspect, []byte, error) {
|
func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, options network.InspectOptions) (network.Inspect, []byte, error) {
|
||||||
if networkID == "" {
|
networkID, err := trimID("network", networkID)
|
||||||
return network.Inspect{}, nil, objectNotFoundError{object: "network", id: networkID}
|
if err != nil {
|
||||||
|
return network.Inspect{}, nil, err
|
||||||
}
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.Verbose {
|
if options.Verbose {
|
||||||
|
@ -69,7 +69,12 @@ func TestNetworkInspect(t *testing.T) {
|
|||||||
t.Run("empty ID", func(t *testing.T) {
|
t.Run("empty ID", func(t *testing.T) {
|
||||||
// verify that the client does not create a request if the network-ID/name is empty.
|
// verify that the client does not create a request if the network-ID/name is empty.
|
||||||
_, err := client.NetworkInspect(context.Background(), "", network.InspectOptions{})
|
_, err := client.NetworkInspect(context.Background(), "", network.InspectOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.NetworkInspect(context.Background(), " ", network.InspectOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
})
|
})
|
||||||
t.Run("no options", func(t *testing.T) {
|
t.Run("no options", func(t *testing.T) {
|
||||||
r, err := client.NetworkInspect(context.Background(), "network_id", network.InspectOptions{})
|
r, err := client.NetworkInspect(context.Background(), "network_id", network.InspectOptions{})
|
||||||
|
@ -4,6 +4,10 @@ import "context"
|
|||||||
|
|
||||||
// NetworkRemove removes an existent network from the docker host.
|
// NetworkRemove removes an existent network from the docker host.
|
||||||
func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error {
|
func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error {
|
||||||
|
networkID, err := trimID("network", networkID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil)
|
resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
|
@ -21,6 +21,14 @@ func TestNetworkRemoveError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.NetworkRemove(context.Background(), "network_id")
|
err := client.NetworkRemove(context.Background(), "network_id")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.NetworkRemove(context.Background(), "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.NetworkRemove(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetworkRemove(t *testing.T) {
|
func TestNetworkRemove(t *testing.T) {
|
||||||
|
@ -11,8 +11,9 @@ import (
|
|||||||
|
|
||||||
// NodeInspectWithRaw returns the node information.
|
// NodeInspectWithRaw returns the node information.
|
||||||
func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) {
|
func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) {
|
||||||
if nodeID == "" {
|
nodeID, err := trimID("node", nodeID)
|
||||||
return swarm.Node{}, nil, objectNotFoundError{object: "node", id: nodeID}
|
if err != nil {
|
||||||
|
return swarm.Node{}, nil, err
|
||||||
}
|
}
|
||||||
serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
|
serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
|
@ -42,7 +42,12 @@ func TestNodeInspectWithEmptyID(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
_, _, err := client.NodeInspectWithRaw(context.Background(), "")
|
_, _, err := client.NodeInspectWithRaw(context.Background(), "")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, _, err = client.NodeInspectWithRaw(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeInspect(t *testing.T) {
|
func TestNodeInspect(t *testing.T) {
|
||||||
|
@ -9,6 +9,11 @@ import (
|
|||||||
|
|
||||||
// NodeRemove removes a Node.
|
// NodeRemove removes a Node.
|
||||||
func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options types.NodeRemoveOptions) error {
|
func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options types.NodeRemoveOptions) error {
|
||||||
|
nodeID, err := trimID("node", nodeID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.Force {
|
if options.Force {
|
||||||
query.Set("force", "1")
|
query.Set("force", "1")
|
||||||
|
@ -22,6 +22,14 @@ func TestNodeRemoveError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.NodeRemove(context.Background(), "node_id", types.NodeRemoveOptions{Force: false})
|
err := client.NodeRemove(context.Background(), "node_id", types.NodeRemoveOptions{Force: false})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.NodeRemove(context.Background(), "", types.NodeRemoveOptions{Force: false})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.NodeRemove(context.Background(), " ", types.NodeRemoveOptions{Force: false})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeRemove(t *testing.T) {
|
func TestNodeRemove(t *testing.T) {
|
||||||
|
@ -9,6 +9,11 @@ import (
|
|||||||
|
|
||||||
// NodeUpdate updates a Node.
|
// NodeUpdate updates a Node.
|
||||||
func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||||
|
nodeID, err := trimID("node", nodeID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("version", version.String())
|
query.Set("version", version.String())
|
||||||
resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, node, nil)
|
resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, node, nil)
|
||||||
|
@ -22,6 +22,14 @@ func TestNodeUpdateError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.NodeUpdate(context.Background(), "node_id", swarm.Version{}, swarm.NodeSpec{})
|
err := client.NodeUpdate(context.Background(), "node_id", swarm.Version{}, swarm.NodeSpec{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.NodeUpdate(context.Background(), "", swarm.Version{}, swarm.NodeSpec{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.NodeUpdate(context.Background(), " ", swarm.Version{}, swarm.NodeSpec{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeUpdate(t *testing.T) {
|
func TestNodeUpdate(t *testing.T) {
|
||||||
|
@ -9,6 +9,10 @@ import (
|
|||||||
|
|
||||||
// PluginDisable disables a plugin
|
// PluginDisable disables a plugin
|
||||||
func (cli *Client) PluginDisable(ctx context.Context, name string, options types.PluginDisableOptions) error {
|
func (cli *Client) PluginDisable(ctx context.Context, name string, options types.PluginDisableOptions) error {
|
||||||
|
name, err := trimID("plugin", name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.Force {
|
if options.Force {
|
||||||
query.Set("force", "1")
|
query.Set("force", "1")
|
||||||
|
@ -22,6 +22,14 @@ func TestPluginDisableError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.PluginDisable(context.Background(), "plugin_name", types.PluginDisableOptions{})
|
err := client.PluginDisable(context.Background(), "plugin_name", types.PluginDisableOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.PluginDisable(context.Background(), "", types.PluginDisableOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.PluginDisable(context.Background(), " ", types.PluginDisableOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginDisable(t *testing.T) {
|
func TestPluginDisable(t *testing.T) {
|
||||||
|
@ -10,6 +10,10 @@ import (
|
|||||||
|
|
||||||
// PluginEnable enables a plugin
|
// PluginEnable enables a plugin
|
||||||
func (cli *Client) PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error {
|
func (cli *Client) PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error {
|
||||||
|
name, err := trimID("plugin", name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("timeout", strconv.Itoa(options.Timeout))
|
query.Set("timeout", strconv.Itoa(options.Timeout))
|
||||||
|
|
||||||
|
@ -22,6 +22,14 @@ func TestPluginEnableError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.PluginEnable(context.Background(), "plugin_name", types.PluginEnableOptions{})
|
err := client.PluginEnable(context.Background(), "plugin_name", types.PluginEnableOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.PluginEnable(context.Background(), "", types.PluginEnableOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.PluginEnable(context.Background(), " ", types.PluginEnableOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginEnable(t *testing.T) {
|
func TestPluginEnable(t *testing.T) {
|
||||||
|
@ -11,8 +11,9 @@ import (
|
|||||||
|
|
||||||
// PluginInspectWithRaw inspects an existing plugin
|
// PluginInspectWithRaw inspects an existing plugin
|
||||||
func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) {
|
func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) {
|
||||||
if name == "" {
|
name, err := trimID("plugin", name)
|
||||||
return nil, nil, objectNotFoundError{object: "plugin", id: name}
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil)
|
resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
|
@ -33,7 +33,12 @@ func TestPluginInspectWithEmptyID(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
_, _, err := client.PluginInspectWithRaw(context.Background(), "")
|
_, _, err := client.PluginInspectWithRaw(context.Background(), "")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, _, err = client.PluginInspectWithRaw(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginInspect(t *testing.T) {
|
func TestPluginInspect(t *testing.T) {
|
||||||
|
@ -10,6 +10,10 @@ import (
|
|||||||
|
|
||||||
// PluginPush pushes a plugin to a registry
|
// PluginPush pushes a plugin to a registry
|
||||||
func (cli *Client) PluginPush(ctx context.Context, name string, registryAuth string) (io.ReadCloser, error) {
|
func (cli *Client) PluginPush(ctx context.Context, name string, registryAuth string) (io.ReadCloser, error) {
|
||||||
|
name, err := trimID("plugin", name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, nil, http.Header{
|
resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, nil, http.Header{
|
||||||
registry.AuthHeader: {registryAuth},
|
registry.AuthHeader: {registryAuth},
|
||||||
})
|
})
|
||||||
|
@ -22,6 +22,14 @@ func TestPluginPushError(t *testing.T) {
|
|||||||
|
|
||||||
_, err := client.PluginPush(context.Background(), "plugin_name", "")
|
_, err := client.PluginPush(context.Background(), "plugin_name", "")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.PluginPush(context.Background(), "", "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.PluginPush(context.Background(), " ", "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginPush(t *testing.T) {
|
func TestPluginPush(t *testing.T) {
|
||||||
|
@ -9,6 +9,11 @@ import (
|
|||||||
|
|
||||||
// PluginRemove removes a plugin
|
// PluginRemove removes a plugin
|
||||||
func (cli *Client) PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error {
|
func (cli *Client) PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error {
|
||||||
|
name, err := trimID("plugin", name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.Force {
|
if options.Force {
|
||||||
query.Set("force", "1")
|
query.Set("force", "1")
|
||||||
|
@ -22,6 +22,14 @@ func TestPluginRemoveError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.PluginRemove(context.Background(), "plugin_name", types.PluginRemoveOptions{})
|
err := client.PluginRemove(context.Background(), "plugin_name", types.PluginRemoveOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.PluginRemove(context.Background(), "", types.PluginRemoveOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.PluginRemove(context.Background(), " ", types.PluginRemoveOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginRemove(t *testing.T) {
|
func TestPluginRemove(t *testing.T) {
|
||||||
|
@ -6,6 +6,11 @@ import (
|
|||||||
|
|
||||||
// PluginSet modifies settings for an existing plugin
|
// PluginSet modifies settings for an existing plugin
|
||||||
func (cli *Client) PluginSet(ctx context.Context, name string, args []string) error {
|
func (cli *Client) PluginSet(ctx context.Context, name string, args []string) error {
|
||||||
|
name, err := trimID("plugin", name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := cli.post(ctx, "/plugins/"+name+"/set", nil, args, nil)
|
resp, err := cli.post(ctx, "/plugins/"+name+"/set", nil, args, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
|
@ -21,6 +21,14 @@ func TestPluginSetError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.PluginSet(context.Background(), "plugin_name", []string{})
|
err := client.PluginSet(context.Background(), "plugin_name", []string{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.PluginSet(context.Background(), "", []string{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.PluginSet(context.Background(), " ", []string{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginSet(t *testing.T) {
|
func TestPluginSet(t *testing.T) {
|
||||||
|
@ -13,7 +13,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// PluginUpgrade upgrades a plugin
|
// PluginUpgrade upgrades a plugin
|
||||||
func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
|
func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (io.ReadCloser, error) {
|
||||||
|
name, err := trimID("plugin", name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err := cli.NewVersionError(ctx, "1.26", "plugin upgrade"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.26", "plugin upgrade"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,12 @@ import (
|
|||||||
|
|
||||||
// SecretInspectWithRaw returns the secret information with raw data
|
// SecretInspectWithRaw returns the secret information with raw data
|
||||||
func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.Secret, []byte, error) {
|
func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.Secret, []byte, error) {
|
||||||
if err := cli.NewVersionError(ctx, "1.25", "secret inspect"); err != nil {
|
id, err := trimID("secret", id)
|
||||||
|
if err != nil {
|
||||||
return swarm.Secret{}, nil, err
|
return swarm.Secret{}, nil, err
|
||||||
}
|
}
|
||||||
if id == "" {
|
if err := cli.NewVersionError(ctx, "1.25", "secret inspect"); err != nil {
|
||||||
return swarm.Secret{}, nil, objectNotFoundError{object: "secret", id: id}
|
return swarm.Secret{}, nil, err
|
||||||
}
|
}
|
||||||
resp, err := cli.get(ctx, "/secrets/"+id, nil, nil)
|
resp, err := cli.get(ctx, "/secrets/"+id, nil, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
|
@ -53,7 +53,12 @@ func TestSecretInspectWithEmptyID(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
_, _, err := client.SecretInspectWithRaw(context.Background(), "")
|
_, _, err := client.SecretInspectWithRaw(context.Background(), "")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, _, err = client.SecretInspectWithRaw(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSecretInspect(t *testing.T) {
|
func TestSecretInspect(t *testing.T) {
|
||||||
|
@ -4,6 +4,10 @@ import "context"
|
|||||||
|
|
||||||
// SecretRemove removes a secret.
|
// SecretRemove removes a secret.
|
||||||
func (cli *Client) SecretRemove(ctx context.Context, id string) error {
|
func (cli *Client) SecretRemove(ctx context.Context, id string) error {
|
||||||
|
id, err := trimID("secret", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := cli.NewVersionError(ctx, "1.25", "secret remove"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "secret remove"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,14 @@ func TestSecretRemoveError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.SecretRemove(context.Background(), "secret_id")
|
err := client.SecretRemove(context.Background(), "secret_id")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.SecretRemove(context.Background(), "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.SecretRemove(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSecretRemove(t *testing.T) {
|
func TestSecretRemove(t *testing.T) {
|
||||||
|
@ -9,6 +9,10 @@ import (
|
|||||||
|
|
||||||
// SecretUpdate attempts to update a secret.
|
// SecretUpdate attempts to update a secret.
|
||||||
func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
|
func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
|
||||||
|
id, err := trimID("secret", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := cli.NewVersionError(ctx, "1.25", "secret update"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.25", "secret update"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,14 @@ func TestSecretUpdateError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.SecretUpdate(context.Background(), "secret_id", swarm.Version{}, swarm.SecretSpec{})
|
err := client.SecretUpdate(context.Background(), "secret_id", swarm.Version{}, swarm.SecretSpec{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.SecretUpdate(context.Background(), "", swarm.Version{}, swarm.SecretSpec{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.SecretUpdate(context.Background(), " ", swarm.Version{}, swarm.SecretSpec{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSecretUpdate(t *testing.T) {
|
func TestSecretUpdate(t *testing.T) {
|
||||||
@ -41,7 +49,7 @@ func TestSecretUpdate(t *testing.T) {
|
|||||||
version: "1.25",
|
version: "1.25",
|
||||||
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
||||||
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
||||||
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, req.URL)
|
||||||
}
|
}
|
||||||
if req.Method != http.MethodPost {
|
if req.Method != http.MethodPost {
|
||||||
return nil, fmt.Errorf("expected POST method, got %s", req.Method)
|
return nil, fmt.Errorf("expected POST method, got %s", req.Method)
|
||||||
|
@ -14,9 +14,11 @@ import (
|
|||||||
|
|
||||||
// ServiceInspectWithRaw returns the service information and the raw data.
|
// ServiceInspectWithRaw returns the service information and the raw data.
|
||||||
func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts types.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||||
if serviceID == "" {
|
serviceID, err := trimID("service", serviceID)
|
||||||
return swarm.Service{}, nil, objectNotFoundError{object: "service", id: serviceID}
|
if err != nil {
|
||||||
|
return swarm.Service{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults))
|
query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults))
|
||||||
serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
|
serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
|
||||||
|
@ -43,7 +43,12 @@ func TestServiceInspectWithEmptyID(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
_, _, err := client.ServiceInspectWithRaw(context.Background(), "", types.ServiceInspectOptions{})
|
_, _, err := client.ServiceInspectWithRaw(context.Background(), "", types.ServiceInspectOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, _, err = client.ServiceInspectWithRaw(context.Background(), " ", types.ServiceInspectOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceInspect(t *testing.T) {
|
func TestServiceInspect(t *testing.T) {
|
||||||
|
@ -14,6 +14,11 @@ import (
|
|||||||
// ServiceLogs returns the logs generated by a service in an io.ReadCloser.
|
// ServiceLogs returns the logs generated by a service in an io.ReadCloser.
|
||||||
// It's up to the caller to close the stream.
|
// It's up to the caller to close the stream.
|
||||||
func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options container.LogsOptions) (io.ReadCloser, error) {
|
func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options container.LogsOptions) (io.ReadCloser, error) {
|
||||||
|
serviceID, err := trimID("service", serviceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.ShowStdout {
|
if options.ShowStdout {
|
||||||
query.Set("stdout", "1")
|
query.Set("stdout", "1")
|
||||||
|
@ -29,6 +29,14 @@ func TestServiceLogsError(t *testing.T) {
|
|||||||
Since: "2006-01-02TZ",
|
Since: "2006-01-02TZ",
|
||||||
})
|
})
|
||||||
assert.Check(t, is.ErrorContains(err, `parsing time "2006-01-02TZ"`))
|
assert.Check(t, is.ErrorContains(err, `parsing time "2006-01-02TZ"`))
|
||||||
|
|
||||||
|
_, err = client.ServiceLogs(context.Background(), "", container.LogsOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ServiceLogs(context.Background(), " ", container.LogsOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceLogs(t *testing.T) {
|
func TestServiceLogs(t *testing.T) {
|
||||||
|
@ -4,6 +4,11 @@ import "context"
|
|||||||
|
|
||||||
// ServiceRemove kills and removes a service.
|
// ServiceRemove kills and removes a service.
|
||||||
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error {
|
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error {
|
||||||
|
serviceID, err := trimID("service", serviceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
|
@ -21,6 +21,14 @@ func TestServiceRemoveError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.ServiceRemove(context.Background(), "service_id")
|
err := client.ServiceRemove(context.Background(), "service_id")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.ServiceRemove(context.Background(), "")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.ServiceRemove(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceRemoveNotFoundError(t *testing.T) {
|
func TestServiceRemoveNotFoundError(t *testing.T) {
|
||||||
|
@ -16,7 +16,10 @@ import (
|
|||||||
// It should be the value as set *before* the update. You can find this value in the Meta field
|
// It should be the value as set *before* the update. You can find this value in the Meta field
|
||||||
// of swarm.Service, which can be found using ServiceInspectWithRaw.
|
// of swarm.Service, which can be found using ServiceInspectWithRaw.
|
||||||
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error) {
|
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error) {
|
||||||
response := swarm.ServiceUpdateResponse{}
|
serviceID, err := trimID("service", serviceID)
|
||||||
|
if err != nil {
|
||||||
|
return swarm.ServiceUpdateResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure we negotiated (if the client is configured to do so),
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
// as code below contains API-version specific handling of options.
|
// as code below contains API-version specific handling of options.
|
||||||
@ -24,7 +27,7 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
|||||||
// Normally, version-negotiation (if enabled) would not happen until
|
// Normally, version-negotiation (if enabled) would not happen until
|
||||||
// the API request is made.
|
// the API request is made.
|
||||||
if err := cli.checkVersion(ctx); err != nil {
|
if err := cli.checkVersion(ctx); err != nil {
|
||||||
return response, err
|
return swarm.ServiceUpdateResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
@ -39,7 +42,7 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
|||||||
query.Set("version", version.String())
|
query.Set("version", version.String())
|
||||||
|
|
||||||
if err := validateServiceSpec(service); err != nil {
|
if err := validateServiceSpec(service); err != nil {
|
||||||
return response, err
|
return swarm.ServiceUpdateResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure that the image is tagged
|
// ensure that the image is tagged
|
||||||
@ -74,9 +77,10 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
|||||||
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
|
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return swarm.ServiceUpdateResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response := swarm.ServiceUpdateResponse{}
|
||||||
err = json.NewDecoder(resp.body).Decode(&response)
|
err = json.NewDecoder(resp.body).Decode(&response)
|
||||||
if resolveWarning != "" {
|
if resolveWarning != "" {
|
||||||
response.Warnings = append(response.Warnings, resolveWarning)
|
response.Warnings = append(response.Warnings, resolveWarning)
|
||||||
|
@ -23,6 +23,14 @@ func TestServiceUpdateError(t *testing.T) {
|
|||||||
|
|
||||||
_, err := client.ServiceUpdate(context.Background(), "service_id", swarm.Version{}, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
|
_, err := client.ServiceUpdate(context.Background(), "service_id", swarm.Version{}, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
_, err = client.ServiceUpdate(context.Background(), "", swarm.Version{}, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, err = client.ServiceUpdate(context.Background(), " ", swarm.Version{}, swarm.ServiceSpec{}, types.ServiceUpdateOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestServiceUpdateConnectionError verifies that connection errors occurring
|
// TestServiceUpdateConnectionError verifies that connection errors occurring
|
||||||
|
@ -11,9 +11,11 @@ import (
|
|||||||
|
|
||||||
// TaskInspectWithRaw returns the task information and its raw representation.
|
// TaskInspectWithRaw returns the task information and its raw representation.
|
||||||
func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
|
func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
|
||||||
if taskID == "" {
|
taskID, err := trimID("task", taskID)
|
||||||
return swarm.Task{}, nil, objectNotFoundError{object: "task", id: taskID}
|
if err != nil {
|
||||||
|
return swarm.Task{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil)
|
serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil)
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -33,7 +33,12 @@ func TestTaskInspectWithEmptyID(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
_, _, err := client.TaskInspectWithRaw(context.Background(), "")
|
_, _, err := client.TaskInspectWithRaw(context.Background(), "")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, _, err = client.TaskInspectWithRaw(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTaskInspect(t *testing.T) {
|
func TestTaskInspect(t *testing.T) {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
@ -13,6 +14,23 @@ import (
|
|||||||
|
|
||||||
var headerRegexp = lazyregexp.New(`\ADocker/.+\s\((.+)\)\z`)
|
var headerRegexp = lazyregexp.New(`\ADocker/.+\s\((.+)\)\z`)
|
||||||
|
|
||||||
|
type emptyIDError string
|
||||||
|
|
||||||
|
func (e emptyIDError) InvalidParameter() {}
|
||||||
|
|
||||||
|
func (e emptyIDError) Error() string {
|
||||||
|
return "invalid " + string(e) + " name or ID: value is empty"
|
||||||
|
}
|
||||||
|
|
||||||
|
// trimID trims the given object-ID / name, returning an error if it's empty.
|
||||||
|
func trimID(objType, id string) (string, error) {
|
||||||
|
id = strings.TrimSpace(id)
|
||||||
|
if len(id) == 0 {
|
||||||
|
return "", emptyIDError(objType)
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
// getDockerOS returns the operating system based on the server header from the daemon.
|
// getDockerOS returns the operating system based on the server header from the daemon.
|
||||||
func getDockerOS(serverHeader string) string {
|
func getDockerOS(serverHeader string) string {
|
||||||
var osType string
|
var osType string
|
||||||
|
@ -17,8 +17,9 @@ func (cli *Client) VolumeInspect(ctx context.Context, volumeID string) (volume.V
|
|||||||
|
|
||||||
// VolumeInspectWithRaw returns the information about a specific volume in the docker host and its raw representation
|
// VolumeInspectWithRaw returns the information about a specific volume in the docker host and its raw representation
|
||||||
func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (volume.Volume, []byte, error) {
|
func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (volume.Volume, []byte, error) {
|
||||||
if volumeID == "" {
|
volumeID, err := trimID("volume", volumeID)
|
||||||
return volume.Volume{}, nil, objectNotFoundError{object: "volume", id: volumeID}
|
if err != nil {
|
||||||
|
return volume.Volume{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var vol volume.Volume
|
var vol volume.Volume
|
||||||
|
@ -42,7 +42,12 @@ func TestVolumeInspectWithEmptyID(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
_, _, err := client.VolumeInspectWithRaw(context.Background(), "")
|
_, _, err := client.VolumeInspectWithRaw(context.Background(), "")
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
_, _, err = client.VolumeInspectWithRaw(context.Background(), " ")
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVolumeInspect(t *testing.T) {
|
func TestVolumeInspect(t *testing.T) {
|
||||||
|
@ -9,6 +9,11 @@ import (
|
|||||||
|
|
||||||
// VolumeRemove removes a volume from the docker host.
|
// VolumeRemove removes a volume from the docker host.
|
||||||
func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool) error {
|
func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool) error {
|
||||||
|
volumeID, err := trimID("volume", volumeID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if force {
|
if force {
|
||||||
// Make sure we negotiated (if the client is configured to do so),
|
// Make sure we negotiated (if the client is configured to do so),
|
||||||
|
@ -21,6 +21,14 @@ func TestVolumeRemoveError(t *testing.T) {
|
|||||||
|
|
||||||
err := client.VolumeRemove(context.Background(), "volume_id", false)
|
err := client.VolumeRemove(context.Background(), "volume_id", false)
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.VolumeRemove(context.Background(), "", false)
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.VolumeRemove(context.Background(), " ", false)
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestVolumeRemoveConnectionError verifies that connection errors occurring
|
// TestVolumeRemoveConnectionError verifies that connection errors occurring
|
||||||
|
@ -11,6 +11,10 @@ import (
|
|||||||
// VolumeUpdate updates a volume. This only works for Cluster Volumes, and
|
// VolumeUpdate updates a volume. This only works for Cluster Volumes, and
|
||||||
// only some fields can be updated.
|
// only some fields can be updated.
|
||||||
func (cli *Client) VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options volume.UpdateOptions) error {
|
func (cli *Client) VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options volume.UpdateOptions) error {
|
||||||
|
volumeID, err := trimID("volume", volumeID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := cli.NewVersionError(ctx, "1.42", "volume update"); err != nil {
|
if err := cli.NewVersionError(ctx, "1.42", "volume update"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,16 @@ func TestVolumeUpdateError(t *testing.T) {
|
|||||||
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.VolumeUpdate(context.Background(), "", swarm.Version{}, volumetypes.UpdateOptions{})
|
err := client.VolumeUpdate(context.Background(), "volume", swarm.Version{}, volumetypes.UpdateOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||||
|
|
||||||
|
err = client.VolumeUpdate(context.Background(), "", swarm.Version{}, volumetypes.UpdateOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
|
err = client.VolumeUpdate(context.Background(), " ", swarm.Version{}, volumetypes.UpdateOptions{})
|
||||||
|
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVolumeUpdate(t *testing.T) {
|
func TestVolumeUpdate(t *testing.T) {
|
||||||
|
@ -1391,7 +1391,8 @@ func (s *DockerAPISuite) TestContainerAPIDeleteWithEmptyName(c *testing.T) {
|
|||||||
defer apiClient.Close()
|
defer apiClient.Close()
|
||||||
|
|
||||||
err = apiClient.ContainerRemove(testutil.GetContext(c), "", container.RemoveOptions{})
|
err = apiClient.ContainerRemove(testutil.GetContext(c), "", container.RemoveOptions{})
|
||||||
assert.Check(c, errdefs.IsNotFound(err))
|
assert.Check(c, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||||
|
assert.Check(c, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) {
|
func (s *DockerAPISuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) {
|
||||||
|
Reference in New Issue
Block a user