mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #34784 from dnephin/fix-client-not-found
Cleanup client not found errors
This commit is contained in:
commit
bb0e8ee51c
31 changed files with 86 additions and 263 deletions
|
@ -2,7 +2,6 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -20,10 +19,7 @@ func (cli *Client) CheckpointList(ctx context.Context, container string, options
|
||||||
|
|
||||||
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
|
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp.statusCode == http.StatusNotFound {
|
return checkpoints, wrapResponseError(err, resp, "container", container)
|
||||||
return checkpoints, containerNotFoundError{container}
|
|
||||||
}
|
|
||||||
return checkpoints, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&checkpoints)
|
err = json.NewDecoder(resp.body).Decode(&checkpoints)
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -17,10 +16,7 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C
|
||||||
}
|
}
|
||||||
resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
|
resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp.statusCode == http.StatusNotFound {
|
return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id)
|
||||||
return swarm.Config{}, nil, configNotFoundError{id}
|
|
||||||
}
|
|
||||||
return swarm.Config{}, nil, err
|
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
|
|
||||||
|
|
|
@ -9,5 +9,5 @@ func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
|
||||||
}
|
}
|
||||||
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
|
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return wrapResponseError(err, resp, "config", id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
|
||||||
serverResp, err := cli.post(ctx, "/containers/create", query, body, nil)
|
serverResp, err := cli.post(ctx, "/containers/create", query, body, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if serverResp.statusCode == 404 && strings.Contains(err.Error(), "No such image") {
|
if serverResp.statusCode == 404 && strings.Contains(err.Error(), "No such image") {
|
||||||
return response, imageNotFoundError{config.Image}
|
return response, objectNotFoundError{object: "image", id: config.Image}
|
||||||
}
|
}
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -15,10 +14,7 @@ import (
|
||||||
func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error) {
|
func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error) {
|
||||||
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
|
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if serverResp.statusCode == http.StatusNotFound {
|
return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID)
|
||||||
return types.ContainerJSON{}, containerNotFoundError{containerID}
|
|
||||||
}
|
|
||||||
return types.ContainerJSON{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var response types.ContainerJSON
|
var response types.ContainerJSON
|
||||||
|
@ -35,10 +31,7 @@ func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID stri
|
||||||
}
|
}
|
||||||
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil)
|
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if serverResp.statusCode == http.StatusNotFound {
|
return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID)
|
||||||
return types.ContainerJSON{}, nil, containerNotFoundError{containerID}
|
|
||||||
}
|
|
||||||
return types.ContainerJSON{}, nil, err
|
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
|
|
||||||
|
|
|
@ -23,5 +23,5 @@ func (cli *Client) ContainerRemove(ctx context.Context, containerID string, opti
|
||||||
|
|
||||||
resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil)
|
resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return wrapResponseError(err, resp, "container", containerID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,9 +18,16 @@ func TestContainerRemoveError(t *testing.T) {
|
||||||
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
}
|
}
|
||||||
err := client.ContainerRemove(context.Background(), "container_id", types.ContainerRemoveOptions{})
|
err := client.ContainerRemove(context.Background(), "container_id", types.ContainerRemoveOptions{})
|
||||||
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
assert.EqualError(t, err, "Error response from daemon: Server error")
|
||||||
t.Fatalf("expected a Server Error, got %v", err)
|
}
|
||||||
|
|
||||||
|
func TestContainerRemoveNotFoundError(t *testing.T) {
|
||||||
|
client := &Client{
|
||||||
|
client: newMockClient(errorMock(http.StatusNotFound, "missing")),
|
||||||
}
|
}
|
||||||
|
err := client.ContainerRemove(context.Background(), "container_id", types.ContainerRemoveOptions{})
|
||||||
|
assert.EqualError(t, err, "Error: No such container: container_id")
|
||||||
|
assert.True(t, IsErrNotFound(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerRemove(t *testing.T) {
|
func TestContainerRemove(t *testing.T) {
|
||||||
|
@ -53,7 +61,5 @@ func TestContainerRemove(t *testing.T) {
|
||||||
RemoveVolumes: true,
|
RemoveVolumes: true,
|
||||||
Force: true,
|
Force: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
177
client/errors.go
177
client/errors.go
|
@ -3,6 +3,8 @@ package client
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -43,19 +45,28 @@ func IsErrNotFound(err error) bool {
|
||||||
return ok && te.NotFound()
|
return ok && te.NotFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
// imageNotFoundError implements an error returned when an image is not in the docker host.
|
type objectNotFoundError struct {
|
||||||
type imageNotFoundError struct {
|
object string
|
||||||
imageID string
|
id string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
func (e objectNotFoundError) NotFound() bool {
|
||||||
func (e imageNotFoundError) NotFound() bool {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error returns a string representation of an imageNotFoundError
|
func (e objectNotFoundError) Error() string {
|
||||||
func (e imageNotFoundError) Error() string {
|
return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
|
||||||
return fmt.Sprintf("Error: No such image: %s", e.imageID)
|
}
|
||||||
|
|
||||||
|
func wrapResponseError(err error, resp serverResponse, object, id string) error {
|
||||||
|
switch {
|
||||||
|
case err == nil:
|
||||||
|
return nil
|
||||||
|
case resp.statusCode == http.StatusNotFound:
|
||||||
|
return objectNotFoundError{object: object, id: id}
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrImageNotFound returns true if the error is caused
|
// IsErrImageNotFound returns true if the error is caused
|
||||||
|
@ -66,21 +77,6 @@ func IsErrImageNotFound(err error) bool {
|
||||||
return IsErrNotFound(err)
|
return IsErrNotFound(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// containerNotFoundError implements an error returned when a container is not in the docker host.
|
|
||||||
type containerNotFoundError struct {
|
|
||||||
containerID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
|
||||||
func (e containerNotFoundError) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of a containerNotFoundError
|
|
||||||
func (e containerNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such container: %s", e.containerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrContainerNotFound returns true if the error is caused
|
// IsErrContainerNotFound returns true if the error is caused
|
||||||
// when a container is not found in the docker host.
|
// when a container is not found in the docker host.
|
||||||
//
|
//
|
||||||
|
@ -89,21 +85,6 @@ func IsErrContainerNotFound(err error) bool {
|
||||||
return IsErrNotFound(err)
|
return IsErrNotFound(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// networkNotFoundError implements an error returned when a network is not in the docker host.
|
|
||||||
type networkNotFoundError struct {
|
|
||||||
networkID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
|
||||||
func (e networkNotFoundError) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of a networkNotFoundError
|
|
||||||
func (e networkNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such network: %s", e.networkID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrNetworkNotFound returns true if the error is caused
|
// IsErrNetworkNotFound returns true if the error is caused
|
||||||
// when a network is not found in the docker host.
|
// when a network is not found in the docker host.
|
||||||
//
|
//
|
||||||
|
@ -112,21 +93,6 @@ func IsErrNetworkNotFound(err error) bool {
|
||||||
return IsErrNotFound(err)
|
return IsErrNotFound(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// volumeNotFoundError implements an error returned when a volume is not in the docker host.
|
|
||||||
type volumeNotFoundError struct {
|
|
||||||
volumeID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
|
||||||
func (e volumeNotFoundError) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of a volumeNotFoundError
|
|
||||||
func (e volumeNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such volume: %s", e.volumeID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrVolumeNotFound returns true if the error is caused
|
// IsErrVolumeNotFound returns true if the error is caused
|
||||||
// when a volume is not found in the docker host.
|
// when a volume is not found in the docker host.
|
||||||
//
|
//
|
||||||
|
@ -152,43 +118,12 @@ func IsErrUnauthorized(err error) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodeNotFoundError implements an error returned when a node is not found.
|
|
||||||
type nodeNotFoundError struct {
|
|
||||||
nodeID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of a nodeNotFoundError
|
|
||||||
func (e nodeNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such node: %s", e.nodeID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
|
||||||
func (e nodeNotFoundError) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrNodeNotFound returns true if the error is caused
|
// IsErrNodeNotFound returns true if the error is caused
|
||||||
// when a node is not found.
|
// when a node is not found.
|
||||||
//
|
//
|
||||||
// Deprecated: Use IsErrNotFound
|
// Deprecated: Use IsErrNotFound
|
||||||
func IsErrNodeNotFound(err error) bool {
|
func IsErrNodeNotFound(err error) bool {
|
||||||
_, ok := err.(nodeNotFoundError)
|
return IsErrNotFound(err)
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// serviceNotFoundError implements an error returned when a service is not found.
|
|
||||||
type serviceNotFoundError struct {
|
|
||||||
serviceID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of a serviceNotFoundError
|
|
||||||
func (e serviceNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such service: %s", e.serviceID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
|
||||||
func (e serviceNotFoundError) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrServiceNotFound returns true if the error is caused
|
// IsErrServiceNotFound returns true if the error is caused
|
||||||
|
@ -196,23 +131,7 @@ func (e serviceNotFoundError) NotFound() bool {
|
||||||
//
|
//
|
||||||
// Deprecated: Use IsErrNotFound
|
// Deprecated: Use IsErrNotFound
|
||||||
func IsErrServiceNotFound(err error) bool {
|
func IsErrServiceNotFound(err error) bool {
|
||||||
_, ok := err.(serviceNotFoundError)
|
return IsErrNotFound(err)
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// taskNotFoundError implements an error returned when a task is not found.
|
|
||||||
type taskNotFoundError struct {
|
|
||||||
taskID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of a taskNotFoundError
|
|
||||||
func (e taskNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such task: %s", e.taskID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
|
||||||
func (e taskNotFoundError) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrTaskNotFound returns true if the error is caused
|
// IsErrTaskNotFound returns true if the error is caused
|
||||||
|
@ -220,8 +139,7 @@ func (e taskNotFoundError) NotFound() bool {
|
||||||
//
|
//
|
||||||
// Deprecated: Use IsErrNotFound
|
// Deprecated: Use IsErrNotFound
|
||||||
func IsErrTaskNotFound(err error) bool {
|
func IsErrTaskNotFound(err error) bool {
|
||||||
_, ok := err.(taskNotFoundError)
|
return IsErrNotFound(err)
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type pluginPermissionDenied struct {
|
type pluginPermissionDenied struct {
|
||||||
|
@ -248,43 +166,12 @@ func (cli *Client) NewVersionError(APIrequired, feature string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// secretNotFoundError implements an error returned when a secret is not found.
|
|
||||||
type secretNotFoundError struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of a secretNotFoundError
|
|
||||||
func (e secretNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: no such secret: %s", e.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
|
||||||
func (e secretNotFoundError) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrSecretNotFound returns true if the error is caused
|
// IsErrSecretNotFound returns true if the error is caused
|
||||||
// when a secret is not found.
|
// when a secret is not found.
|
||||||
//
|
//
|
||||||
// Deprecated: Use IsErrNotFound
|
// Deprecated: Use IsErrNotFound
|
||||||
func IsErrSecretNotFound(err error) bool {
|
func IsErrSecretNotFound(err error) bool {
|
||||||
_, ok := err.(secretNotFoundError)
|
return IsErrNotFound(err)
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// configNotFoundError implements an error returned when a config is not found.
|
|
||||||
type configNotFoundError struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of a configNotFoundError
|
|
||||||
func (e configNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: no such config: %s", e.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
|
||||||
func (e configNotFoundError) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrConfigNotFound returns true if the error is caused
|
// IsErrConfigNotFound returns true if the error is caused
|
||||||
|
@ -292,23 +179,7 @@ func (e configNotFoundError) NotFound() bool {
|
||||||
//
|
//
|
||||||
// Deprecated: Use IsErrNotFound
|
// Deprecated: Use IsErrNotFound
|
||||||
func IsErrConfigNotFound(err error) bool {
|
func IsErrConfigNotFound(err error) bool {
|
||||||
_, ok := err.(configNotFoundError)
|
return IsErrNotFound(err)
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// pluginNotFoundError implements an error returned when a plugin is not in the docker host.
|
|
||||||
type pluginNotFoundError struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFound indicates that this error type is of NotFound
|
|
||||||
func (e pluginNotFoundError) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of a pluginNotFoundError
|
|
||||||
func (e pluginNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such plugin: %s", e.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrPluginNotFound returns true if the error is caused
|
// IsErrPluginNotFound returns true if the error is caused
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -14,10 +13,7 @@ import (
|
||||||
func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) {
|
func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) {
|
||||||
serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil)
|
serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if serverResp.statusCode == http.StatusNotFound {
|
return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID)
|
||||||
return types.ImageInspect{}, nil, imageNotFoundError{imageID}
|
|
||||||
}
|
|
||||||
return types.ImageInspect{}, nil, err
|
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -20,15 +19,12 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options type
|
||||||
query.Set("noprune", "1")
|
query.Set("noprune", "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dels []types.ImageDeleteResponseItem
|
||||||
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
|
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp.statusCode == http.StatusNotFound {
|
return dels, wrapResponseError(err, resp, "image", imageID)
|
||||||
return nil, imageNotFoundError{imageID}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var dels []types.ImageDeleteResponseItem
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&dels)
|
err = json.NewDecoder(resp.body).Decode(&dels)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return dels, err
|
return dels, err
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,20 +20,17 @@ func TestImageRemoveError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.ImageRemove(context.Background(), "image_id", types.ImageRemoveOptions{})
|
_, err := client.ImageRemove(context.Background(), "image_id", types.ImageRemoveOptions{})
|
||||||
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
assert.EqualError(t, err, "Error response from daemon: Server error")
|
||||||
t.Fatalf("expected a Server Error, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImageRemoveImageNotFound(t *testing.T) {
|
func TestImageRemoveImageNotFound(t *testing.T) {
|
||||||
client := &Client{
|
client := &Client{
|
||||||
client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
|
client: newMockClient(errorMock(http.StatusNotFound, "missing")),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.ImageRemove(context.Background(), "unknown", types.ImageRemoveOptions{})
|
_, err := client.ImageRemove(context.Background(), "unknown", types.ImageRemoveOptions{})
|
||||||
if err == nil || !IsErrNotFound(err) {
|
assert.EqualError(t, err, "Error: No such image: unknown")
|
||||||
t.Fatalf("expected an imageNotFoundError error, got %v", err)
|
assert.True(t, IsErrNotFound(err))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImageRemove(t *testing.T) {
|
func TestImageRemove(t *testing.T) {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -33,10 +32,7 @@ func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string,
|
||||||
}
|
}
|
||||||
resp, err = cli.get(ctx, "/networks/"+networkID, query, nil)
|
resp, err = cli.get(ctx, "/networks/"+networkID, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp.statusCode == http.StatusNotFound {
|
return networkResource, nil, wrapResponseError(err, resp, "network", networkID)
|
||||||
return networkResource, nil, networkNotFoundError{networkID}
|
|
||||||
}
|
|
||||||
return networkResource, nil, err
|
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
|
|
||||||
|
|
|
@ -21,20 +21,17 @@ func TestNetworkInspectError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.NetworkInspect(context.Background(), "nothing", types.NetworkInspectOptions{})
|
_, err := client.NetworkInspect(context.Background(), "nothing", types.NetworkInspectOptions{})
|
||||||
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
assert.EqualError(t, err, "Error response from daemon: Server error")
|
||||||
t.Fatalf("expected a Server Error, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetworkInspectContainerNotFound(t *testing.T) {
|
func TestNetworkInspectNotFoundError(t *testing.T) {
|
||||||
client := &Client{
|
client := &Client{
|
||||||
client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
|
client: newMockClient(errorMock(http.StatusNotFound, "missing")),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.NetworkInspect(context.Background(), "unknown", types.NetworkInspectOptions{})
|
_, err := client.NetworkInspect(context.Background(), "unknown", types.NetworkInspectOptions{})
|
||||||
if err == nil || !IsErrNetworkNotFound(err) {
|
assert.EqualError(t, err, "Error: No such network: unknown")
|
||||||
t.Fatalf("expected a networkNotFound error, got %v", err)
|
assert.True(t, IsErrNotFound(err))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetworkInspect(t *testing.T) {
|
func TestNetworkInspect(t *testing.T) {
|
||||||
|
|
|
@ -6,5 +6,5 @@ import "golang.org/x/net/context"
|
||||||
func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error {
|
func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error {
|
||||||
resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil)
|
resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return wrapResponseError(err, resp, "network", networkID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -14,10 +13,7 @@ import (
|
||||||
func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) {
|
func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) {
|
||||||
serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
|
serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if serverResp.statusCode == http.StatusNotFound {
|
return swarm.Node{}, nil, wrapResponseError(err, serverResp, "node", nodeID)
|
||||||
return swarm.Node{}, nil, nodeNotFoundError{nodeID}
|
|
||||||
}
|
|
||||||
return swarm.Node{}, nil, err
|
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
|
|
||||||
|
|
|
@ -17,5 +17,5 @@ func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options types.
|
||||||
|
|
||||||
resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil)
|
resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return wrapResponseError(err, resp, "node", nodeID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,5 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
||||||
}
|
}
|
||||||
ping.OSType = serverResp.header.Get("OSType")
|
ping.OSType = serverResp.header.Get("OSType")
|
||||||
}
|
}
|
||||||
|
return ping, cli.checkResponseErr(serverResp)
|
||||||
err = cli.checkResponseErr(serverResp)
|
|
||||||
return ping, err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -14,10 +13,7 @@ import (
|
||||||
func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) {
|
func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) {
|
||||||
resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil)
|
resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp.statusCode == http.StatusNotFound {
|
return nil, nil, wrapResponseError(err, resp, "plugin", name)
|
||||||
return nil, nil, pluginNotFoundError{name}
|
|
||||||
}
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
|
|
|
@ -16,5 +16,5 @@ func (cli *Client) PluginRemove(ctx context.Context, name string, options types.
|
||||||
|
|
||||||
resp, err := cli.delete(ctx, "/plugins/"+name, query, nil)
|
resp, err := cli.delete(ctx, "/plugins/"+name, query, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return wrapResponseError(err, resp, "plugin", name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,7 +203,7 @@ func (cli *Client) checkResponseErr(serverResp serverResponse) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(body) == 0 {
|
if len(body) == 0 {
|
||||||
return fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), serverResp.reqURL)
|
return fmt.Errorf("request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), serverResp.reqURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ct string
|
var ct string
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -17,10 +16,7 @@ func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.S
|
||||||
}
|
}
|
||||||
resp, err := cli.get(ctx, "/secrets/"+id, nil, nil)
|
resp, err := cli.get(ctx, "/secrets/"+id, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp.statusCode == http.StatusNotFound {
|
return swarm.Secret{}, nil, wrapResponseError(err, resp, "secret", id)
|
||||||
return swarm.Secret{}, nil, secretNotFoundError{id}
|
|
||||||
}
|
|
||||||
return swarm.Secret{}, nil, err
|
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
|
|
||||||
|
|
|
@ -9,5 +9,5 @@ func (cli *Client) SecretRemove(ctx context.Context, id string) error {
|
||||||
}
|
}
|
||||||
resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil)
|
resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return wrapResponseError(err, resp, "secret", id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -19,10 +18,7 @@ func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string,
|
||||||
query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults))
|
query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults))
|
||||||
serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
|
serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if serverResp.statusCode == http.StatusNotFound {
|
return swarm.Service{}, nil, wrapResponseError(err, serverResp, "service", serviceID)
|
||||||
return swarm.Service{}, nil, serviceNotFoundError{serviceID}
|
|
||||||
}
|
|
||||||
return swarm.Service{}, nil, err
|
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
|
|
||||||
|
|
|
@ -6,5 +6,5 @@ import "golang.org/x/net/context"
|
||||||
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error {
|
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error {
|
||||||
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return wrapResponseError(err, resp, "service", serviceID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,9 +18,17 @@ func TestServiceRemoveError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.ServiceRemove(context.Background(), "service_id")
|
err := client.ServiceRemove(context.Background(), "service_id")
|
||||||
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
assert.EqualError(t, err, "Error response from daemon: Server error")
|
||||||
t.Fatalf("expected a Server Error, got %v", err)
|
}
|
||||||
|
|
||||||
|
func TestServiceRemoveNotFoundError(t *testing.T) {
|
||||||
|
client := &Client{
|
||||||
|
client: newMockClient(errorMock(http.StatusNotFound, "missing")),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := client.ServiceRemove(context.Background(), "service_id")
|
||||||
|
assert.EqualError(t, err, "Error: No such service: service_id")
|
||||||
|
assert.True(t, IsErrNotFound(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceRemove(t *testing.T) {
|
func TestServiceRemove(t *testing.T) {
|
||||||
|
|
|
@ -4,10 +4,8 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,10 +13,7 @@ import (
|
||||||
func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
|
func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
|
||||||
serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil)
|
serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if serverResp.statusCode == http.StatusNotFound {
|
return swarm.Task{}, nil, wrapResponseError(err, serverResp, "task", taskID)
|
||||||
return swarm.Task{}, nil, taskNotFoundError{taskID}
|
|
||||||
}
|
|
||||||
return swarm.Task{}, nil, err
|
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(serverResp)
|
defer ensureReaderClosed(serverResp)
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -23,16 +22,13 @@ func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (t
|
||||||
// request url will not contain a trailing / which calls the volume list API
|
// request url will not contain a trailing / which calls the volume list API
|
||||||
// instead of volume inspect
|
// instead of volume inspect
|
||||||
if volumeID == "" {
|
if volumeID == "" {
|
||||||
return types.Volume{}, nil, volumeNotFoundError{volumeID}
|
return types.Volume{}, nil, objectNotFoundError{object: "volume", id: volumeID}
|
||||||
}
|
}
|
||||||
|
|
||||||
var volume types.Volume
|
var volume types.Volume
|
||||||
resp, err := cli.get(ctx, path.Join("/volumes", volumeID), nil, nil)
|
resp, err := cli.get(ctx, path.Join("/volumes", volumeID), nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp.statusCode == http.StatusNotFound {
|
return volume, nil, wrapResponseError(err, resp, "volume", volumeID)
|
||||||
return volume, nil, volumeNotFoundError{volumeID}
|
|
||||||
}
|
|
||||||
return volume, nil, err
|
|
||||||
}
|
}
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
|
|
||||||
|
|
|
@ -17,5 +17,5 @@ func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool
|
||||||
}
|
}
|
||||||
resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil)
|
resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return wrapResponseError(err, resp, "volume", volumeID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1617,7 +1617,7 @@ func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *check.C) {
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
err = cli.ContainerRemove(context.Background(), "", types.ContainerRemoveOptions{})
|
err = cli.ContainerRemove(context.Background(), "", types.ContainerRemoveOptions{})
|
||||||
c.Assert(err.Error(), checker.Contains, "Error response from daemon: page not found")
|
c.Assert(err.Error(), checker.Contains, "No such container")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
|
func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
|
||||||
|
|
|
@ -55,10 +55,8 @@ func (s *DockerSwarmSuite) TestAPISwarmConfigsDelete(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
expected := "no such config"
|
|
||||||
|
|
||||||
_, _, err = cli.ConfigInspectWithRaw(context.Background(), id)
|
_, _, err = cli.ConfigInspectWithRaw(context.Background(), id)
|
||||||
c.Assert(err.Error(), checker.Contains, expected)
|
c.Assert(err.Error(), checker.Contains, "No such config")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSwarmSuite) TestAPISwarmConfigsUpdate(c *check.C) {
|
func (s *DockerSwarmSuite) TestAPISwarmConfigsUpdate(c *check.C) {
|
||||||
|
|
|
@ -64,14 +64,12 @@ func (s *DockerSwarmSuite) TestAPISwarmSecretsDelete(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
expected := "no such secret"
|
|
||||||
_, _, err = cli.SecretInspectWithRaw(context.Background(), id)
|
_, _, err = cli.SecretInspectWithRaw(context.Background(), id)
|
||||||
c.Assert(err.Error(), checker.Contains, expected)
|
c.Assert(err.Error(), checker.Contains, "No such secret")
|
||||||
|
|
||||||
id = "non-existing"
|
id = "non-existing"
|
||||||
expected = "secret non-existing not found"
|
|
||||||
err = cli.SecretRemove(context.Background(), id)
|
err = cli.SecretRemove(context.Background(), id)
|
||||||
c.Assert(err.Error(), checker.Contains, expected)
|
c.Assert(err.Error(), checker.Contains, "No such secret: non-existing")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSwarmSuite) TestAPISwarmSecretsUpdate(c *check.C) {
|
func (s *DockerSwarmSuite) TestAPISwarmSecretsUpdate(c *check.C) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue