diff --git a/client/config_inspect.go b/client/config_inspect.go index b49fdf568a..7ae1376ea7 100644 --- a/client/config_inspect.go +++ b/client/config_inspect.go @@ -11,6 +11,9 @@ import ( // ConfigInspectWithRaw returns the config information with raw data func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error) { + if id == "" { + return swarm.Config{}, nil, objectNotFoundError{object: "config", id: id} + } if err := cli.NewVersionError("1.30", "config inspect"); err != nil { return swarm.Config{}, nil, err } diff --git a/client/config_inspect_test.go b/client/config_inspect_test.go index 18776a90a4..c6d73e5c02 100644 --- a/client/config_inspect_test.go +++ b/client/config_inspect_test.go @@ -10,10 +10,34 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) +func TestConfigInspectNotFound(t *testing.T) { + client := &Client{ + client: newMockClient(errorMock(http.StatusNotFound, "Server error")), + } + + _, _, err := client.ConfigInspectWithRaw(context.Background(), "unknown") + if err == nil || !IsErrNotFound(err) { + t.Fatalf("expected a NotFoundError error, got %v", err) + } +} + +func TestConfigInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, _, err := client.ConfigInspectWithRaw(context.Background(), "") + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} + func TestConfigInspectUnsupported(t *testing.T) { client := &Client{ version: "1.29", diff --git a/client/container_inspect.go b/client/container_inspect.go index 1ec45e823f..d1dbd47408 100644 --- a/client/container_inspect.go +++ b/client/container_inspect.go @@ -12,6 +12,9 @@ import ( // ContainerInspect returns the container information. func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error) { + if containerID == "" { + return types.ContainerJSON{}, objectNotFoundError{object: "container", id: containerID} + } serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil) if err != nil { return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID) @@ -25,6 +28,9 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (ty // ContainerInspectWithRaw returns the container information and its raw representation. func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID string, getSize bool) (types.ContainerJSON, []byte, error) { + if containerID == "" { + return types.ContainerJSON{}, nil, objectNotFoundError{object: "container", id: containerID} + } query := url.Values{} if getSize { query.Set("size", "1") diff --git a/client/container_inspect_test.go b/client/container_inspect_test.go index b181053a2b..eeb903e450 100644 --- a/client/container_inspect_test.go +++ b/client/container_inspect_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/docker/docker/api/types" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -35,6 +36,18 @@ func TestContainerInspectContainerNotFound(t *testing.T) { } } +func TestContainerInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, _, err := client.ContainerInspectWithRaw(context.Background(), "", true) + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} + func TestContainerInspect(t *testing.T) { expectedURL := "/containers/container_id/json" client := &Client{ diff --git a/client/distribution_inspect.go b/client/distribution_inspect.go index 5f55945c41..4644ea46f7 100644 --- a/client/distribution_inspect.go +++ b/client/distribution_inspect.go @@ -12,6 +12,9 @@ import ( func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registrytypes.DistributionInspect, error) { // Contact the registry to retrieve digest and platform information var distributionInspect registrytypes.DistributionInspect + if image == "" { + return distributionInspect, objectNotFoundError{object: "distribution", id: image} + } if err := cli.NewVersionError("1.30", "distribution inspect"); err != nil { return distributionInspect, err diff --git a/client/distribution_inspect_test.go b/client/distribution_inspect_test.go index af5de27e72..90b35a285b 100644 --- a/client/distribution_inspect_test.go +++ b/client/distribution_inspect_test.go @@ -4,6 +4,7 @@ import ( "net/http" "testing" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -16,3 +17,15 @@ func TestDistributionInspectUnsupported(t *testing.T) { _, err := client.DistributionInspect(context.Background(), "foobar:1.0", "") assert.EqualError(t, err, `"distribution inspect" requires API version 1.30, but the Docker daemon API version is 1.29`) } + +func TestDistributionInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, err := client.DistributionInspect(context.Background(), "", "") + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} diff --git a/client/image_inspect.go b/client/image_inspect.go index 75057bf324..1581133254 100644 --- a/client/image_inspect.go +++ b/client/image_inspect.go @@ -11,6 +11,9 @@ import ( // ImageInspectWithRaw returns the image information and its raw representation. func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) { + if imageID == "" { + return types.ImageInspect{}, nil, objectNotFoundError{object: "image", id: imageID} + } serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil) if err != nil { return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID) diff --git a/client/image_inspect_test.go b/client/image_inspect_test.go index 47910e9c52..e42d251f51 100644 --- a/client/image_inspect_test.go +++ b/client/image_inspect_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/docker/docker/api/types" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -36,6 +37,18 @@ func TestImageInspectImageNotFound(t *testing.T) { } } +func TestImageInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, _, err := client.ImageInspectWithRaw(context.Background(), "") + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} + func TestImageInspect(t *testing.T) { expectedURL := "/images/image_id/json" expectedTags := []string{"tag1", "tag2"} diff --git a/client/network_inspect.go b/client/network_inspect.go index 3308f18ded..0e18caf1b1 100644 --- a/client/network_inspect.go +++ b/client/network_inspect.go @@ -18,6 +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. func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, []byte, error) { + if networkID == "" { + return types.NetworkResource{}, nil, objectNotFoundError{object: "network", id: networkID} + } var ( networkResource types.NetworkResource resp serverResponse diff --git a/client/network_inspect_test.go b/client/network_inspect_test.go index 95fcb0d646..7031611911 100644 --- a/client/network_inspect_test.go +++ b/client/network_inspect_test.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -34,6 +35,18 @@ func TestNetworkInspectNotFoundError(t *testing.T) { assert.True(t, IsErrNotFound(err)) } +func TestNetworkInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, _, err := client.NetworkInspectWithRaw(context.Background(), "", types.NetworkInspectOptions{}) + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} + func TestNetworkInspect(t *testing.T) { expectedURL := "/networks/network_id" client := &Client{ diff --git a/client/node_inspect.go b/client/node_inspect.go index 454db9f17b..7c0c1d473e 100644 --- a/client/node_inspect.go +++ b/client/node_inspect.go @@ -11,6 +11,9 @@ import ( // NodeInspectWithRaw returns the node information. func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) { + if nodeID == "" { + return swarm.Node{}, nil, objectNotFoundError{object: "node", id: nodeID} + } serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil) if err != nil { return swarm.Node{}, nil, wrapResponseError(err, serverResp, "node", nodeID) diff --git a/client/node_inspect_test.go b/client/node_inspect_test.go index 2fcd0d6d94..efda287771 100644 --- a/client/node_inspect_test.go +++ b/client/node_inspect_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -35,6 +36,18 @@ func TestNodeInspectNodeNotFound(t *testing.T) { } } +func TestNodeInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, _, err := client.NodeInspectWithRaw(context.Background(), "") + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} + func TestNodeInspect(t *testing.T) { expectedURL := "/nodes/node_id" client := &Client{ diff --git a/client/plugin_inspect.go b/client/plugin_inspect.go index 8957a805ec..983b1090c1 100644 --- a/client/plugin_inspect.go +++ b/client/plugin_inspect.go @@ -11,6 +11,9 @@ import ( // PluginInspectWithRaw inspects an existing plugin func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) { + if name == "" { + return nil, nil, objectNotFoundError{object: "plugin", id: name} + } resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil) if err != nil { return nil, nil, wrapResponseError(err, resp, "plugin", name) diff --git a/client/plugin_inspect_test.go b/client/plugin_inspect_test.go index e3958b4427..eada16dbb8 100644 --- a/client/plugin_inspect_test.go +++ b/client/plugin_inspect_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/docker/docker/api/types" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -24,6 +25,18 @@ func TestPluginInspectError(t *testing.T) { } } +func TestPluginInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, _, err := client.PluginInspectWithRaw(context.Background(), "") + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} + func TestPluginInspect(t *testing.T) { expectedURL := "/plugins/plugin_name" client := &Client{ diff --git a/client/secret_inspect.go b/client/secret_inspect.go index 6ff7549e5c..9342e3bc07 100644 --- a/client/secret_inspect.go +++ b/client/secret_inspect.go @@ -14,6 +14,9 @@ func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.S if err := cli.NewVersionError("1.25", "secret inspect"); err != nil { return swarm.Secret{}, nil, err } + if id == "" { + return swarm.Secret{}, nil, objectNotFoundError{object: "secret", id: id} + } resp, err := cli.get(ctx, "/secrets/"+id, nil, nil) if err != nil { return swarm.Secret{}, nil, wrapResponseError(err, resp, "secret", id) diff --git a/client/secret_inspect_test.go b/client/secret_inspect_test.go index 5fba8bab1f..eb63162c18 100644 --- a/client/secret_inspect_test.go +++ b/client/secret_inspect_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -47,6 +48,18 @@ func TestSecretInspectSecretNotFound(t *testing.T) { } } +func TestSecretInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, _, err := client.SecretInspectWithRaw(context.Background(), "") + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} + func TestSecretInspect(t *testing.T) { expectedURL := "/v1.25/secrets/secret_id" client := &Client{ diff --git a/client/service_inspect.go b/client/service_inspect.go index 67bcc9cfaf..b4c2944226 100644 --- a/client/service_inspect.go +++ b/client/service_inspect.go @@ -14,6 +14,9 @@ import ( // 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) { + if serviceID == "" { + return swarm.Service{}, nil, objectNotFoundError{object: "service", id: serviceID} + } query := url.Values{} query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults)) serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil) diff --git a/client/service_inspect_test.go b/client/service_inspect_test.go index b5286886f2..be5165a130 100644 --- a/client/service_inspect_test.go +++ b/client/service_inspect_test.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -36,6 +37,18 @@ func TestServiceInspectServiceNotFound(t *testing.T) { } } +func TestServiceInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, _, err := client.ServiceInspectWithRaw(context.Background(), "", types.ServiceInspectOptions{}) + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} + func TestServiceInspect(t *testing.T) { expectedURL := "/services/service_id" client := &Client{ diff --git a/client/task_inspect.go b/client/task_inspect.go index a7b31219a6..13e0a85cde 100644 --- a/client/task_inspect.go +++ b/client/task_inspect.go @@ -11,6 +11,9 @@ import ( // TaskInspectWithRaw returns the task information and its raw representation.. func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) { + if taskID == "" { + return swarm.Task{}, nil, objectNotFoundError{object: "task", id: taskID} + } serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil) if err != nil { return swarm.Task{}, nil, wrapResponseError(err, serverResp, "task", taskID) diff --git a/client/task_inspect_test.go b/client/task_inspect_test.go index 6adba9219f..6ca98dde9c 100644 --- a/client/task_inspect_test.go +++ b/client/task_inspect_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -24,6 +25,18 @@ func TestTaskInspectError(t *testing.T) { } } +func TestTaskInspectWithEmptyID(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return nil, errors.New("should not make request") + }), + } + _, _, err := client.TaskInspectWithRaw(context.Background(), "") + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } +} + func TestTaskInspect(t *testing.T) { expectedURL := "/tasks/task_id" client := &Client{ diff --git a/client/volume_inspect.go b/client/volume_inspect.go index b72e64d793..eb90f66fc6 100644 --- a/client/volume_inspect.go +++ b/client/volume_inspect.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "io/ioutil" - "path" "github.com/docker/docker/api/types" "golang.org/x/net/context" @@ -18,15 +17,12 @@ func (cli *Client) VolumeInspect(ctx context.Context, volumeID string) (types.Vo // 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) (types.Volume, []byte, error) { - // The empty ID needs to be handled here because with an empty ID the - // request url will not contain a trailing / which calls the volume list API - // instead of volume inspect if volumeID == "" { return types.Volume{}, nil, objectNotFoundError{object: "volume", id: volumeID} } var volume types.Volume - resp, err := cli.get(ctx, path.Join("/volumes", volumeID), nil, nil) + resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil) if err != nil { return volume, nil, wrapResponseError(err, resp, "volume", volumeID) } diff --git a/client/volume_inspect_test.go b/client/volume_inspect_test.go index 1ec10b210a..c97f5c7215 100644 --- a/client/volume_inspect_test.go +++ b/client/volume_inspect_test.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/internal/testutil" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/context" @@ -35,20 +36,15 @@ func TestVolumeInspectNotFound(t *testing.T) { } func TestVolumeInspectWithEmptyID(t *testing.T) { - expectedURL := "/volumes/" - client := &Client{ client: newMockClient(func(req *http.Request) (*http.Response, error) { - assert.Equal(t, req.URL.Path, expectedURL) - return &http.Response{ - StatusCode: http.StatusNotFound, - Body: ioutil.NopCloser(bytes.NewReader(nil)), - }, nil + return nil, errors.New("should not make request") }), } - _, err := client.VolumeInspect(context.Background(), "") - testutil.ErrorContains(t, err, "No such volume: ") - + _, _, err := client.VolumeInspectWithRaw(context.Background(), "") + if !IsErrNotFound(err) { + t.Fatalf("Expected NotFoundError, got %v", err) + } } func TestVolumeInspect(t *testing.T) {