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