diff --git a/api/errors/daemon.go b/api/errors/daemon.go index 20aa093e08..2280740464 100644 --- a/api/errors/daemon.go +++ b/api/errors/daemon.go @@ -18,4 +18,475 @@ var ( Description: "The specified container can not be found", HTTPStatusCode: http.StatusNotFound, }) + + // ErrorCodeUnregisteredContainer is generated when we try to load + // a storage driver for an unregistered container + ErrorCodeUnregisteredContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "UNREGISTEREDCONTAINER", + Message: "Can't load storage driver for unregistered container %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeContainerBeingRemoved is generated when an attempt to start + // a container is made but its in the process of being removed, or is dead. + ErrorCodeContainerBeingRemoved = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "CONTAINERBEINGREMOVED", + Message: "Container is marked for removal and cannot be started.", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeUnpauseContainer is generated when we attempt to stop a + // container but its paused. + ErrorCodeUnpauseContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "UNPAUSECONTAINER", + Message: "Container %s is paused. Unpause the container before stopping", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeAlreadyPaused is generated when we attempt to pause a + // container when its already paused. + ErrorCodeAlreadyPaused = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "ALREADYPAUSED", + Message: "Container %s is already paused", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNotPaused is generated when we attempt to unpause a + // container when its not paused. + ErrorCodeNotPaused = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NOTPAUSED", + Message: "Container %s is not paused", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeImageUnregContainer is generated when we attempt to get the + // image of an unknown/unregistered container. + ErrorCodeImageUnregContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "IMAGEUNREGCONTAINER", + Message: "Can't get image of unregistered container", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeEmptyID is generated when an ID is the emptry string. + ErrorCodeEmptyID = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "EMPTYID", + Message: "Invalid empty id", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeLoggingFactory is generated when we could not load the + // log driver. + ErrorCodeLoggingFactory = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "LOGGINGFACTORY", + Message: "Failed to get logging factory: %v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeInitLogger is generated when we could not initialize + // the logging driver. + ErrorCodeInitLogger = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "INITLOGGER", + Message: "Failed to initialize logging driver: %v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNotRunning is generated when we need to verify that + // a container is running, but its not. + ErrorCodeNotRunning = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NOTRUNNING", + Message: "Container %s is not running", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeLinkNotRunning is generated when we try to link to a + // container that is not running. + ErrorCodeLinkNotRunning = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "LINKNOTRUNNING", + Message: "Cannot link to a non running container: %s AS %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeDeviceInfo is generated when there is an error while trying + // to get info about a custom device. + // container that is not running. + ErrorCodeDeviceInfo = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "DEVICEINFO", + Message: "error gathering device information while adding custom device %q: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeEmptyEndpoint is generated when the endpoint for a port + // map is nil. + ErrorCodeEmptyEndpoint = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "EMPTYENDPOINT", + Message: "invalid endpoint while building port map info", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeEmptyNetwork is generated when the networkSettings for a port + // map is nil. + ErrorCodeEmptyNetwork = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "EMPTYNETWORK", + Message: "invalid networksettings while building port map info", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeParsingPort is generated when there is an error parsing + // a "port" string. + ErrorCodeParsingPort = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "PARSINGPORT", + Message: "Error parsing Port value(%v):%v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNoSandbox is generated when we can't find the specified + // sandbox(network) by ID. + ErrorCodeNoSandbox = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NOSANDBOX", + Message: "error locating sandbox id %s: %v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNetworkUpdate is generated when there is an error while + // trying update a network/sandbox config. + ErrorCodeNetworkUpdate = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NETWORKUPDATE", + Message: "Update network failed: %v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNetworkRefresh is generated when there is an error while + // trying refresh a network/sandbox config. + ErrorCodeNetworkRefresh = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NETWORKREFRESH", + Message: "Update network failed: Failure in refresh sandbox %s: %v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeHostPort is generated when there was an error while trying + // to parse a "host/por" string. + ErrorCodeHostPort = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "HOSTPORT", + Message: "Error parsing HostPort value(%s):%v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNetworkConflict is generated when we try to public a service + // in network mode. + ErrorCodeNetworkConflict = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NETWORKCONFLICT", + Message: "conflicting options: publishing a service and network mode", + HTTPStatusCode: http.StatusConflict, + }) + + // ErrorCodeJoinInfo is generated when we failed to update a container's + // join info. + ErrorCodeJoinInfo = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "JOININFO", + Message: "Updating join info failed: %v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeIPCRunning is generated when we try to join a container's + // IPC but its running. + ErrorCodeIPCRunning = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "IPCRUNNING", + Message: "cannot join IPC of a non running container: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNotADir is generated when we try to create a directory + // but the path isn't a dir. + ErrorCodeNotADir = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NOTADIR", + Message: "Cannot mkdir: %s is not a directory", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeParseContainer is generated when the reference to a + // container doesn't include a ":" (another container). + ErrorCodeParseContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "PARSECONTAINER", + Message: "no container specified to join network", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeJoinSelf is generated when we try to network to ourselves. + ErrorCodeJoinSelf = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "JOINSELF", + Message: "cannot join own network", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeJoinRunning is generated when we try to network to ourselves. + ErrorCodeJoinRunning = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "JOINRUNNING", + Message: "cannot join network of a non running container: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeModeNotContainer is generated when we try to network to + // another container but the mode isn't 'container'. + ErrorCodeModeNotContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "MODENOTCONTAINER", + Message: "network mode not set to container", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeRemovingVolume is generated when we try remove a mount + // point (volume) but fail. + ErrorCodeRemovingVolume = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "REMOVINGVOLUME", + Message: "Error removing volumes:\n%v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeInvalidNetworkMode is generated when an invalid network + // mode value is specified. + ErrorCodeInvalidNetworkMode = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "INVALIDNETWORKMODE", + Message: "invalid network mode: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeGetGraph is generated when there was an error while + // trying to find a graph/image. + ErrorCodeGetGraph = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "GETGRAPH", + Message: "Failed to graph.Get on ImageID %s - %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeGetLayer is generated when there was an error while + // trying to retrieve a particular layer of an image. + ErrorCodeGetLayer = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "GETLAYER", + Message: "Failed to get layer path from graphdriver %s for ImageID %s - %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodePutLayer is generated when there was an error while + // trying to 'put' a particular layer of an image. + ErrorCodePutLayer = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "PUTLAYER", + Message: "Failed to put layer path from graphdriver %s for ImageID %s - %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeGetLayerMetadata is generated when there was an error while + // trying to retrieve the metadata of a layer of an image. + ErrorCodeGetLayerMetadata = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "GETLAYERMETADATA", + Message: "Failed to get layer metadata - %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeEmptyConfig is generated when the input config data + // is empty. + ErrorCodeEmptyConfig = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "EMPTYCONFIG", + Message: "Config cannot be empty in order to create a container", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNoSuchImageHash is generated when we can't find the + // specified image by its hash + ErrorCodeNoSuchImageHash = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NOSUCHIMAGEHASH", + Message: "No such image: %s", + HTTPStatusCode: http.StatusNotFound, + }) + + // ErrorCodeNoSuchImageTag is generated when we can't find the + // specified image byt its name/tag. + ErrorCodeNoSuchImageTag = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NOSUCHIMAGETAG", + Message: "No such image: %s:%s", + HTTPStatusCode: http.StatusNotFound, + }) + + // ErrorCodeMountOverFile is generated when we try to mount a volume + // over an existing file (but not a dir). + ErrorCodeMountOverFile = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "MOUNTOVERFILE", + Message: "cannot mount volume over existing file, file exists %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeMountSetup is generated when we can't define a mount point + // due to the source and destination are defined. + ErrorCodeMountSetup = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "MOUNTSETUP", + Message: "Unable to setup mount point, neither source nor volume defined", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeVolumeInvalidMode is generated when we the mode of a volume + // mount is invalid. + ErrorCodeVolumeInvalidMode = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "VOLUMEINVALIDMODE", + Message: "invalid mode for volumes-from: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeVolumeInvalid is generated when the format fo the + // volume specification isn't valid. + ErrorCodeVolumeInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "VOLUMEINVALID", + Message: "Invalid volume specification: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeVolumeAbs is generated when path to a volume isn't absolute. + ErrorCodeVolumeAbs = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "VOLUMEABS", + Message: "Invalid volume destination path: %s mount path must be absolute.", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeVolumeFromBlank is generated when path to a volume is blank. + ErrorCodeVolumeFromBlank = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "VOLUMEFROMBLANK", + Message: "malformed volumes-from specification: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeVolumeMode is generated when 'mode' for a volume + // isn't a valid. + ErrorCodeVolumeMode = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "VOLUMEMODE", + Message: "invalid mode for volumes-from: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeVolumeDup is generated when we try to mount two volumes + // to the same path. + ErrorCodeVolumeDup = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "VOLUMEDUP", + Message: "Duplicate bind mount %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeCantUnpause is generated when there's an error while trying + // to unpause a container. + ErrorCodeCantUnpause = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "CANTUNPAUSE", + Message: "Cannot unpause container %s: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodePSError is generated when trying to run 'ps'. + ErrorCodePSError = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "PSError", + Message: "Error running ps: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNoPID is generated when looking for the PID field in the + // ps output. + ErrorCodeNoPID = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NOPID", + Message: "Couldn't find PID field in ps output", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeBadPID is generated when we can't convert a PID to an int. + ErrorCodeBadPID = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "BADPID", + Message: "Unexpected pid '%s': %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeNoTop is generated when we try to run 'top' but can't + // because we're on windows. + ErrorCodeNoTop = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "NOTOP", + Message: "Top is not supported on Windows", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeStopped is generated when we try to stop a container + // that is already stopped. + ErrorCodeStopped = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "STOPPED", + Message: "Container already stopped", + HTTPStatusCode: http.StatusNotModified, + }) + + // ErrorCodeCantStop is generated when we try to stop a container + // but failed for some reason. + ErrorCodeCantStop = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "CANTSTOP", + Message: "Cannot stop container %s: %s\n", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeBadCPUFields is generated the number of CPU fields is + // less than 8. + ErrorCodeBadCPUFields = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "BADCPUFIELDS", + Message: "invalid number of cpu fields", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeBadCPUInt is generated the CPU field can't be parsed as an int. + ErrorCodeBadCPUInt = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "BADCPUINT", + Message: "Unable to convert value %s to int: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeBadStatFormat is generated the output of the stat info + // isn't parseable. + ErrorCodeBadStatFormat = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "BADSTATFORMAT", + Message: "invalid stat format", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeTimedOut is generated when a timer expires. + ErrorCodeTimedOut = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "TIMEDOUT", + Message: "Timed out: %v", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeAlreadyRemoving is generated when we try to remove a + // container that is already being removed. + ErrorCodeAlreadyRemoving = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "ALREADYREMOVING", + Message: "Status is already RemovalInProgress", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeStartPaused is generated when we start a paused container. + ErrorCodeStartPaused = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "STARTPAUSED", + Message: "Cannot start a paused container, try unpause instead.", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeAlreadyStarted is generated when we try to start a container + // that is already running. + ErrorCodeAlreadyStarted = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "ALREADYSTARTED", + Message: "Container already started", + HTTPStatusCode: http.StatusNotModified, + }) + + // ErrorCodeHostConfigStart is generated when a HostConfig is passed + // into the start command. + ErrorCodeHostConfigStart = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "HOSTCONFIGSTART", + Message: "Supplying a hostconfig on start is not supported. It should be supplied on create", + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeCantStart is generated when an error occurred while + // trying to start a container. + ErrorCodeCantStart = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "CANTSTART", + Message: "Cannot start container %s: %s", + HTTPStatusCode: http.StatusInternalServerError, + }) ) diff --git a/api/server/container.go b/api/server/container.go index 419160d231..47b74036e5 100644 --- a/api/server/container.go +++ b/api/server/container.go @@ -12,12 +12,15 @@ import ( "golang.org/x/net/websocket" "github.com/Sirupsen/logrus" + "github.com/docker/distribution/registry/api/errcode" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/api/types" "github.com/docker/docker/context" "github.com/docker/docker/daemon" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/runconfig" + "github.com/docker/docker/utils" ) func (s *Server) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -144,7 +147,7 @@ func (s *Server) getContainersLogs(ctx context.Context, w http.ResponseWriter, r // The client may be expecting all of the data we're sending to // be multiplexed, so send it through OutStream, which will // have been set up to handle that if needed. - fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", err) + fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err)) } return nil @@ -184,10 +187,6 @@ func (s *Server) postContainersStart(ctx context.Context, w http.ResponseWriter, } if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil { - if err.Error() == "Container already started" { - w.WriteHeader(http.StatusNotModified) - return nil - } return err } w.WriteHeader(http.StatusNoContent) @@ -205,10 +204,6 @@ func (s *Server) postContainersStop(ctx context.Context, w http.ResponseWriter, seconds, _ := strconv.Atoi(r.Form.Get("t")) if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil { - if err.Error() == "Container already stopped" { - w.WriteHeader(http.StatusNotModified) - return nil - } return err } w.WriteHeader(http.StatusNoContent) @@ -236,7 +231,9 @@ func (s *Server) postContainersKill(ctx context.Context, w http.ResponseWriter, } if err := s.daemon.ContainerKill(name, uint64(sig)); err != nil { - _, isStopped := err.(daemon.ErrContainerNotRunning) + theErr, isDerr := err.(errcode.ErrorCoder) + isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning + // Return error that's not caused because the container is stopped. // Return error if the container is not running and the api is >= 1.20 // to keep backwards compatibility. diff --git a/daemon/container.go b/daemon/container.go index c73e7aadb7..2a8848a3e1 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -15,6 +15,7 @@ import ( "github.com/opencontainers/runc/libcontainer/label" "github.com/Sirupsen/logrus" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/jsonfilelog" @@ -39,15 +40,6 @@ var ( ErrRootFSReadOnly = errors.New("container rootfs is marked read-only") ) -// ErrContainerNotRunning holds the id of the container that is not running. -type ErrContainerNotRunning struct { - id string -} - -func (e ErrContainerNotRunning) Error() string { - return fmt.Sprintf("Container %s is not running", e.id) -} - type streamConfig struct { stdout *broadcastwriter.BroadcastWriter stderr *broadcastwriter.BroadcastWriter @@ -229,7 +221,7 @@ func (container *Container) getRootResourcePath(path string) (string, error) { func (container *Container) exportContainerRw() (archive.Archive, error) { if container.daemon == nil { - return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID) + return nil, derr.ErrorCodeUnregisteredContainer.WithArgs(container.ID) } archive, err := container.daemon.diff(container) if err != nil { @@ -255,7 +247,7 @@ func (container *Container) Start() (err error) { } if container.removalInProgress || container.Dead { - return fmt.Errorf("Container is marked for removal and cannot be started.") + return derr.ErrorCodeContainerBeingRemoved } // if we encounter an error during start we need to ensure that any other @@ -381,11 +373,11 @@ func (container *Container) killSig(sig int) error { // We could unpause the container for them rather than returning this error if container.Paused { - return fmt.Errorf("Container %s is paused. Unpause the container before stopping", container.ID) + return derr.ErrorCodeUnpauseContainer.WithArgs(container.ID) } if !container.Running { - return ErrContainerNotRunning{container.ID} + return derr.ErrorCodeNotRunning.WithArgs(container.ID) } // signal to the monitor that it should not restart the container @@ -422,12 +414,12 @@ func (container *Container) pause() error { // We cannot Pause the container which is not running if !container.Running { - return ErrContainerNotRunning{container.ID} + return derr.ErrorCodeNotRunning.WithArgs(container.ID) } // We cannot Pause the container which is already paused if container.Paused { - return fmt.Errorf("Container %s is already paused", container.ID) + return derr.ErrorCodeAlreadyPaused.WithArgs(container.ID) } if err := container.daemon.execDriver.Pause(container.command); err != nil { @@ -444,12 +436,12 @@ func (container *Container) unpause() error { // We cannot unpause the container which is not running if !container.Running { - return ErrContainerNotRunning{container.ID} + return derr.ErrorCodeNotRunning.WithArgs(container.ID) } // We cannot unpause the container which is not paused if !container.Paused { - return fmt.Errorf("Container %s is not paused", container.ID) + return derr.ErrorCodeNotPaused.WithArgs(container.ID) } if err := container.daemon.execDriver.Unpause(container.command); err != nil { @@ -463,7 +455,7 @@ func (container *Container) unpause() error { // Kill forcefully terminates a container. func (container *Container) Kill() error { if !container.IsRunning() { - return ErrContainerNotRunning{container.ID} + return derr.ErrorCodeNotRunning.WithArgs(container.ID) } // 1. Send SIGKILL @@ -556,7 +548,7 @@ func (container *Container) Restart(seconds int) error { // to the given height and width. The container must be running. func (container *Container) Resize(h, w int) error { if !container.IsRunning() { - return ErrContainerNotRunning{container.ID} + return derr.ErrorCodeNotRunning.WithArgs(container.ID) } if err := container.command.ProcessConfig.Terminal.Resize(h, w); err != nil { return err @@ -597,7 +589,7 @@ func (container *Container) changes() ([]archive.Change, error) { func (container *Container) getImage() (*image.Image, error) { if container.daemon == nil { - return nil, fmt.Errorf("Can't get image of unregistered container") + return nil, derr.ErrorCodeImageUnregContainer } return container.daemon.graph.Get(container.ImageID) } @@ -624,7 +616,7 @@ func (container *Container) rootfsPath() string { func validateID(id string) error { if id == "" { - return fmt.Errorf("Invalid empty id") + return derr.ErrorCodeEmptyID } return nil } @@ -722,7 +714,7 @@ func (container *Container) getLogger() (logger.Logger, error) { } c, err := logger.GetLogDriver(cfg.Type) if err != nil { - return nil, fmt.Errorf("Failed to get logging factory: %v", err) + return nil, derr.ErrorCodeLoggingFactory.WithArgs(err) } ctx := logger.Context{ Config: cfg.Config, @@ -753,7 +745,7 @@ func (container *Container) startLogging() error { l, err := container.getLogger() if err != nil { - return fmt.Errorf("Failed to initialize logging driver: %v", err) + return derr.ErrorCodeInitLogger.WithArgs(err) } copier := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l) diff --git a/daemon/container_unix.go b/daemon/container_unix.go index 87a9f7d37f..11adcb46ce 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -15,6 +15,7 @@ import ( "time" "github.com/Sirupsen/logrus" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/links" "github.com/docker/docker/daemon/network" @@ -86,7 +87,7 @@ func (container *Container) setupLinkedContainers() ([]string, error) { if len(children) > 0 { for linkAlias, child := range children { if !child.IsRunning() { - return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias) + return nil, derr.ErrorCodeLinkNotRunning.WithArgs(child.Name, linkAlias) } link := links.NewLink( @@ -168,7 +169,7 @@ func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs. return devs, nil } - return devs, fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err) + return devs, derr.ErrorCodeDeviceInfo.WithArgs(deviceMapping.PathOnHost, err) } func populateCommand(c *Container, env []string) error { @@ -511,11 +512,11 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { if ep == nil { - return nil, fmt.Errorf("invalid endpoint while building port map info") + return nil, derr.ErrorCodeEmptyEndpoint } if networkSettings == nil { - return nil, fmt.Errorf("invalid networksettings while building port map info") + return nil, derr.ErrorCodeEmptyNetwork } driverInfo, err := ep.DriverInfo() @@ -539,7 +540,7 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSett for _, tp := range exposedPorts { natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port))) if err != nil { - return nil, fmt.Errorf("Error parsing Port value(%v):%v", tp.Port, err) + return nil, derr.ErrorCodeParsingPort.WithArgs(tp.Port, err) } networkSettings.Ports[natPort] = nil } @@ -567,11 +568,11 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSett func (container *Container) buildEndpointInfo(ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { if ep == nil { - return nil, fmt.Errorf("invalid endpoint while building port map info") + return nil, derr.ErrorCodeEmptyEndpoint } if networkSettings == nil { - return nil, fmt.Errorf("invalid networksettings while building port map info") + return nil, derr.ErrorCodeEmptyNetwork } epInfo := ep.Info() @@ -648,16 +649,16 @@ func (container *Container) updateNetwork() error { sb, err := ctrl.SandboxByID(sid) if err != nil { - return fmt.Errorf("error locating sandbox id %s: %v", sid, err) + return derr.ErrorCodeNoSandbox.WithArgs(sid, err) } options, err := container.buildSandboxOptions() if err != nil { - return fmt.Errorf("Update network failed: %v", err) + return derr.ErrorCodeNetworkUpdate.WithArgs(err) } if err := sb.Refresh(options...); err != nil { - return fmt.Errorf("Update network failed: Failure in refresh sandbox %s: %v", sid, err) + return derr.ErrorCodeNetworkRefresh.WithArgs(sid, err) } return nil @@ -711,7 +712,7 @@ func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointO portStart, portEnd, err = newP.Range() } if err != nil { - return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err) + return nil, derr.ErrorCodeHostPort.WithArgs(binding[i].HostPort, err) } pbCopy.HostPort = uint16(portStart) pbCopy.HostPortEnd = uint16(portEnd) @@ -812,7 +813,7 @@ func (container *Container) allocateNetwork() error { networkDriver = controller.Config().Daemon.DefaultDriver } } else if service != "" { - return fmt.Errorf("conflicting options: publishing a service and network mode") + return derr.ErrorCodeNetworkConflict } if runconfig.NetworkMode(networkDriver).IsBridge() && container.daemon.configStore.DisableBridge { @@ -903,7 +904,7 @@ func (container *Container) configureNetwork(networkName, service, networkDriver } if err := container.updateJoinInfo(ep); err != nil { - return fmt.Errorf("Updating join info failed: %v", err) + return derr.ErrorCodeJoinInfo.WithArgs(err) } return nil @@ -954,7 +955,7 @@ func (container *Container) getIpcContainer() (*Container, error) { return nil, err } if !c.IsRunning() { - return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID) + return nil, derr.ErrorCodeIPCRunning } return c, nil } @@ -979,7 +980,7 @@ func (container *Container) setupWorkingDirectory() error { } } if pthInfo != nil && !pthInfo.IsDir() { - return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir) + return derr.ErrorCodeNotADir.WithArgs(container.Config.WorkingDir) } } return nil @@ -990,21 +991,21 @@ func (container *Container) getNetworkedContainer() (*Container, error) { switch parts[0] { case "container": if len(parts) != 2 { - return nil, fmt.Errorf("no container specified to join network") + return nil, derr.ErrorCodeParseContainer } nc, err := container.daemon.Get(parts[1]) if err != nil { return nil, err } if container == nc { - return nil, fmt.Errorf("cannot join own network") + return nil, derr.ErrorCodeJoinSelf } if !nc.IsRunning() { - return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1]) + return nil, derr.ErrorCodeJoinRunning.WithArgs(parts[1]) } return nc, nil default: - return nil, fmt.Errorf("network mode not set to container") + return nil, derr.ErrorCodeModeNotContainer } } @@ -1200,7 +1201,7 @@ func (container *Container) removeMountPoints(rm bool) error { } } if len(rmErrors) > 0 { - return fmt.Errorf("Error removing volumes:\n%v", strings.Join(rmErrors, "\n")) + return derr.ErrorCodeRemovingVolume.WithArgs(strings.Join(rmErrors, "\n")) } return nil } diff --git a/daemon/container_windows.go b/daemon/container_windows.go index f72b6bb72e..0d9609eea4 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -3,9 +3,9 @@ package daemon import ( - "fmt" "strings" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/daemon/execdriver" ) @@ -64,7 +64,7 @@ func populateCommand(c *Container, env []string) error { } } default: - return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode) + return derr.ErrorCodeInvalidNetworkMode.WithArgs(c.hostConfig.NetworkMode) } pid := &execdriver.Pid{} @@ -90,22 +90,22 @@ func populateCommand(c *Container, env []string) error { var layerPaths []string img, err := c.daemon.graph.Get(c.ImageID) if err != nil { - return fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err) + return derr.ErrorCodeGetGraph.WithArgs(c.ImageID, err) } for i := img; i != nil && err == nil; i, err = c.daemon.graph.GetParent(i) { lp, err := c.daemon.driver.Get(i.ID, "") if err != nil { - return fmt.Errorf("Failed to get layer path from graphdriver %s for ImageID %s - %s", c.daemon.driver.String(), i.ID, err) + return derr.ErrorCodeGetLayer.WithArgs(c.daemon.driver.String(), i.ID, err) } layerPaths = append(layerPaths, lp) err = c.daemon.driver.Put(i.ID) if err != nil { - return fmt.Errorf("Failed to put layer path from graphdriver %s for ImageID %s - %s", c.daemon.driver.String(), i.ID, err) + return derr.ErrorCodePutLayer.WithArgs(c.daemon.driver.String(), i.ID, err) } } m, err := c.daemon.driver.GetMetadata(c.ID) if err != nil { - return fmt.Errorf("Failed to get layer metadata - %s", err) + return derr.ErrorCodeGetLayerMetadata.WithArgs(err) } layerFolder := m["dir"] diff --git a/daemon/create.go b/daemon/create.go index 6c63259cef..c616b81990 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -1,10 +1,10 @@ package daemon import ( - "fmt" "strings" "github.com/Sirupsen/logrus" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/api/types" "github.com/docker/docker/graph/tags" "github.com/docker/docker/image" @@ -17,7 +17,7 @@ import ( // ContainerCreate takes configs and creates a container. func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hostConfig *runconfig.HostConfig, adjustCPUShares bool) (*Container, []string, error) { if config == nil { - return nil, nil, fmt.Errorf("Config cannot be empty in order to create a container") + return nil, nil, derr.ErrorCodeEmptyConfig } warnings, err := daemon.verifyContainerSettings(hostConfig, config) @@ -31,13 +31,13 @@ func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hos if err != nil { if daemon.Graph().IsNotExist(err, config.Image) { if strings.Contains(config.Image, "@") { - return nil, warnings, fmt.Errorf("No such image: %s", config.Image) + return nil, warnings, derr.ErrorCodeNoSuchImageHash.WithArgs(config.Image) } img, tag := parsers.ParseRepositoryTag(config.Image) if tag == "" { tag = tags.DefaultTag } - return nil, warnings, fmt.Errorf("No such image: %s:%s", img, tag) + return nil, warnings, derr.ErrorCodeNoSuchImageTag.WithArgs(img, tag) } return nil, warnings, err } diff --git a/daemon/create_unix.go b/daemon/create_unix.go index a0b45e02c3..5b70cfadae 100644 --- a/daemon/create_unix.go +++ b/daemon/create_unix.go @@ -3,11 +3,11 @@ package daemon import ( - "fmt" "os" "path/filepath" "strings" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/image" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/runconfig" @@ -41,7 +41,7 @@ func createContainerPlatformSpecificSettings(container *Container, config *runco stat, err := os.Stat(path) if err == nil && !stat.IsDir() { - return fmt.Errorf("cannot mount volume over existing file, file exists %s", path) + return derr.ErrorCodeMountOverFile.WithArgs(path) } volumeDriver := hostConfig.VolumeDriver diff --git a/daemon/start.go b/daemon/start.go index f9b479ee0c..f6c1236765 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -1,10 +1,11 @@ package daemon import ( - "fmt" "runtime" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/runconfig" + "github.com/docker/docker/utils" ) // ContainerStart starts a container. @@ -15,11 +16,11 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf } if container.isPaused() { - return fmt.Errorf("Cannot start a paused container, try unpause instead.") + return derr.ErrorCodeStartPaused } if container.IsRunning() { - return fmt.Errorf("Container already started") + return derr.ErrorCodeAlreadyStarted } // Windows does not have the backwards compatibility issue here. @@ -33,7 +34,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf } } else { if hostConfig != nil { - return fmt.Errorf("Supplying a hostconfig on start is not supported. It should be supplied on create") + return derr.ErrorCodeHostConfigStart } } @@ -44,7 +45,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf } if err := container.Start(); err != nil { - return fmt.Errorf("Cannot start container %s: %s", name, err) + return derr.ErrorCodeCantStart.WithArgs(name, utils.GetErrorMessage(err)) } return nil diff --git a/daemon/state.go b/daemon/state.go index 9e2590791f..79e205c704 100644 --- a/daemon/state.go +++ b/daemon/state.go @@ -5,6 +5,7 @@ import ( "sync" "time" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/pkg/units" ) @@ -111,7 +112,7 @@ func wait(waitChan <-chan struct{}, timeout time.Duration) error { } select { case <-time.After(timeout): - return fmt.Errorf("Timed out: %v", timeout) + return derr.ErrorCodeTimedOut.WithArgs(timeout) case <-waitChan: return nil } @@ -251,7 +252,7 @@ func (s *State) setRemovalInProgress() error { s.Lock() defer s.Unlock() if s.removalInProgress { - return fmt.Errorf("Status is already RemovalInProgress") + return derr.ErrorCodeAlreadyRemoving } s.removalInProgress = true return nil diff --git a/daemon/stats_collector_unix.go b/daemon/stats_collector_unix.go index 37d9b69bdf..69a04d3fab 100644 --- a/daemon/stats_collector_unix.go +++ b/daemon/stats_collector_unix.go @@ -4,7 +4,6 @@ package daemon import ( "bufio" - "fmt" "os" "strconv" "strings" @@ -12,6 +11,7 @@ import ( "time" "github.com/Sirupsen/logrus" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/pkg/pubsub" "github.com/opencontainers/runc/libcontainer/system" @@ -154,13 +154,13 @@ func (s *statsCollector) getSystemCPUUsage() (uint64, error) { switch parts[0] { case "cpu": if len(parts) < 8 { - return 0, fmt.Errorf("invalid number of cpu fields") + return 0, derr.ErrorCodeBadCPUFields } var totalClockTicks uint64 for _, i := range parts[1:8] { v, err := strconv.ParseUint(i, 10, 64) if err != nil { - return 0, fmt.Errorf("Unable to convert value %s to int: %s", i, err) + return 0, derr.ErrorCodeBadCPUInt.WithArgs(i, err) } totalClockTicks += v } @@ -168,5 +168,5 @@ func (s *statsCollector) getSystemCPUUsage() (uint64, error) { s.clockTicksPerSecond, nil } } - return 0, fmt.Errorf("invalid stat format") + return 0, derr.ErrorCodeBadStatFormat } diff --git a/daemon/stop.go b/daemon/stop.go index 8650f0af87..d5db86af10 100644 --- a/daemon/stop.go +++ b/daemon/stop.go @@ -1,6 +1,8 @@ package daemon -import "fmt" +import ( + derr "github.com/docker/docker/api/errors" +) // ContainerStop looks for the given container and terminates it, // waiting the given number of seconds before forcefully killing the @@ -14,10 +16,10 @@ func (daemon *Daemon) ContainerStop(name string, seconds int) error { return err } if !container.IsRunning() { - return fmt.Errorf("Container already stopped") + return derr.ErrorCodeStopped } if err := container.Stop(seconds); err != nil { - return fmt.Errorf("Cannot stop container %s: %s\n", name, err) + return derr.ErrorCodeCantStop.WithArgs(name, err) } return nil } diff --git a/daemon/top_unix.go b/daemon/top_unix.go index 41cfa0a76c..1790f23e27 100644 --- a/daemon/top_unix.go +++ b/daemon/top_unix.go @@ -3,11 +3,11 @@ package daemon import ( - "fmt" "os/exec" "strconv" "strings" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/api/types" ) @@ -27,7 +27,7 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*types.Container } if !container.IsRunning() { - return nil, fmt.Errorf("Container %s is not running", name) + return nil, derr.ErrorCodeNotRunning.WithArgs(name) } pids, err := daemon.ExecutionDriver().GetPidsForContainer(container.ID) @@ -37,7 +37,7 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*types.Container output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output() if err != nil { - return nil, fmt.Errorf("Error running ps: %s", err) + return nil, derr.ErrorCodePSError.WithArgs(err) } procList := &types.ContainerProcessList{} @@ -52,7 +52,7 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*types.Container } } if pidIndex == -1 { - return nil, fmt.Errorf("Couldn't find PID field in ps output") + return nil, derr.ErrorCodeNoPID } // loop through the output and extract the PID from each line @@ -63,7 +63,7 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*types.Container fields := strings.Fields(line) p, err := strconv.Atoi(fields[pidIndex]) if err != nil { - return nil, fmt.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err) + return nil, derr.ErrorCodeBadPID.WithArgs(fields[pidIndex], err) } for _, pid := range pids { diff --git a/daemon/top_windows.go b/daemon/top_windows.go index 74a3c96993..6eca86c241 100644 --- a/daemon/top_windows.go +++ b/daemon/top_windows.go @@ -1,12 +1,11 @@ package daemon import ( - "fmt" - + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/api/types" ) // ContainerTop is not supported on Windows and returns an error. func (daemon *Daemon) ContainerTop(name string, psArgs string) (*types.ContainerProcessList, error) { - return nil, fmt.Errorf("Top is not supported on Windows") + return nil, derr.ErrorCodeNoTop } diff --git a/daemon/unpause.go b/daemon/unpause.go index 2e9558cb48..48b16e951f 100644 --- a/daemon/unpause.go +++ b/daemon/unpause.go @@ -1,6 +1,8 @@ package daemon -import "fmt" +import ( + derr "github.com/docker/docker/api/errors" +) // ContainerUnpause unpauses a container func (daemon *Daemon) ContainerUnpause(name string) error { @@ -10,7 +12,7 @@ func (daemon *Daemon) ContainerUnpause(name string) error { } if err := container.unpause(); err != nil { - return fmt.Errorf("Cannot unpause container %s: %s", name, err) + return derr.ErrorCodeCantUnpause.WithArgs(name, err) } return nil diff --git a/daemon/volumes.go b/daemon/volumes.go index 134283bc81..909e2e7dc9 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/Sirupsen/logrus" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/system" @@ -59,7 +60,7 @@ func (m *mountPoint) Setup() (string, error) { return m.Source, nil } - return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined") + return "", derr.ErrorCodeMountSetup } // hasResource checks whether the given absolute path for a container is in diff --git a/daemon/volumes_unix.go b/daemon/volumes_unix.go index 1670e0fe0e..5bb9b8d0c1 100644 --- a/daemon/volumes_unix.go +++ b/daemon/volumes_unix.go @@ -3,7 +3,6 @@ package daemon import ( - "fmt" "io/ioutil" "os" "path/filepath" @@ -11,6 +10,7 @@ import ( "strings" "github.com/Sirupsen/logrus" + derr "github.com/docker/docker/api/errors" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/pkg/system" "github.com/docker/docker/runconfig" @@ -72,18 +72,18 @@ func parseBindMount(spec, volumeDriver string) (*mountPoint, error) { bind.Destination = arr[1] mode := arr[2] if !volume.ValidMountMode(mode) { - return nil, fmt.Errorf("invalid mode for volumes-from: %s", mode) + return nil, derr.ErrorCodeVolumeInvalidMode.WithArgs(mode) } bind.RW = volume.ReadWrite(mode) // Mode field is used by SELinux to decide whether to apply label bind.Mode = mode default: - return nil, fmt.Errorf("Invalid volume specification: %s", spec) + return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec) } //validate the volumes destination path if !filepath.IsAbs(bind.Destination) { - return nil, fmt.Errorf("Invalid volume destination path: %s mount path must be absolute.", bind.Destination) + return nil, derr.ErrorCodeVolumeAbs.WithArgs(bind.Destination) } name, source, err := parseVolumeSource(arr[0]) @@ -263,7 +263,7 @@ func (daemon *Daemon) verifyVolumesInfo(container *Container) error { // parseVolumesFrom ensure that the supplied volumes-from is valid. func parseVolumesFrom(spec string) (string, string, error) { if len(spec) == 0 { - return "", "", fmt.Errorf("malformed volumes-from specification: %s", spec) + return "", "", derr.ErrorCodeVolumeFromBlank.WithArgs(spec) } specParts := strings.SplitN(spec, ":", 2) @@ -273,7 +273,7 @@ func parseVolumesFrom(spec string) (string, string, error) { if len(specParts) == 2 { mode = specParts[1] if !volume.ValidMountMode(mode) { - return "", "", fmt.Errorf("invalid mode for volumes-from: %s", mode) + return "", "", derr.ErrorCodeVolumeMode.WithArgs(mode) } } return id, mode, nil @@ -336,7 +336,7 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc } if binds[bind.Destination] { - return fmt.Errorf("Duplicate bind mount %s", bind.Destination) + return derr.ErrorCodeVolumeDup.WithArgs(bind.Destination) } if len(bind.Name) > 0 && len(bind.Driver) > 0 { diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index cbc08b6834..057474af78 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -3000,7 +3000,7 @@ func (s *DockerSuite) TestRunContainerNetworkModeToSelf(c *check.C) { testRequires(c, DaemonIsLinux) out, _, err := dockerCmdWithError("run", "--name=me", "--net=container:me", "busybox", "true") if err == nil || !strings.Contains(out, "cannot join own network") { - c.Fatalf("using container net mode to self should result in an error") + c.Fatalf("using container net mode to self should result in an error\nerr: %q\nout: %s", err, out) } }