mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Remove static errors from errors package.
Moving all strings to the errors package wasn't a good idea after all. Our custom implementation of Go errors predates everything that's nice and good about working with errors in Go. Take as an example what we have to do to get an error message: ```go func GetErrorMessage(err error) string { switch err.(type) { case errcode.Error: e, _ := err.(errcode.Error) return e.Message case errcode.ErrorCode: ec, _ := err.(errcode.ErrorCode) return ec.Message() default: return err.Error() } } ``` This goes against every good practice for Go development. The language already provides a simple, intuitive and standard way to get error messages, that is calling the `Error()` method from an error. Reinventing the error interface is a mistake. Our custom implementation also makes very hard to reason about errors, another nice thing about Go. I found several (>10) error declarations that we don't use anywhere. This is a clear sign about how little we know about the errors we return. I also found several error usages where the number of arguments was different than the parameters declared in the error, another clear example of how difficult is to reason about errors. Moreover, our custom implementation didn't really make easier for people to return custom HTTP status code depending on the errors. Again, it's hard to reason about when to set custom codes and how. Take an example what we have to do to extract the message and status code from an error before returning a response from the API: ```go switch err.(type) { case errcode.ErrorCode: daError, _ := err.(errcode.ErrorCode) statusCode = daError.Descriptor().HTTPStatusCode errMsg = daError.Message() case errcode.Error: // For reference, if you're looking for a particular error // then you can do something like : // import ( derr "github.com/docker/docker/errors" ) // if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... } daError, _ := err.(errcode.Error) statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode errMsg = daError.Message default: // This part of will be removed once we've // converted everything over to use the errcode package // FIXME: this is brittle and should not be necessary. // If we need to differentiate between different possible error types, // we should create appropriate error types with clearly defined meaning errStr := strings.ToLower(err.Error()) for keyword, status := range map[string]int{ "not found": http.StatusNotFound, "no such": http.StatusNotFound, "bad parameter": http.StatusBadRequest, "conflict": http.StatusConflict, "impossible": http.StatusNotAcceptable, "wrong login/password": http.StatusUnauthorized, "hasn't been activated": http.StatusForbidden, } { if strings.Contains(errStr, keyword) { statusCode = status break } } } ``` You can notice two things in that code: 1. We have to explain how errors work, because our implementation goes against how easy to use Go errors are. 2. At no moment we arrived to remove that `switch` statement that was the original reason to use our custom implementation. This change removes all our status errors from the errors package and puts them back in their specific contexts. IT puts the messages back with their contexts. That way, we know right away when errors used and how to generate their messages. It uses custom interfaces to reason about errors. Errors that need to response with a custom status code MUST implementent this simple interface: ```go type errorWithStatus interface { HTTPErrorStatusCode() int } ``` This interface is very straightforward to implement. It also preserves Go errors real behavior, getting the message is as simple as using the `Error()` method. I included helper functions to generate errors that use custom status code in `errors/errors.go`. By doing this, we remove the hard dependency we have eeverywhere to our custom errors package. Yes, you can use it as a helper to generate error, but it's still very easy to generate errors without it. Please, read this fantastic blog post about errors in Go: http://dave.cheney.net/2014/12/24/inspecting-errors Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
parent
c47674efda
commit
a793564b25
67 changed files with 452 additions and 1626 deletions
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
|
@ -21,6 +20,11 @@ import (
|
|||
"github.com/docker/libnetwork/resolvconf/dns"
|
||||
)
|
||||
|
||||
const (
|
||||
errCmdNotFound = "Container command not found or does not exist."
|
||||
errCmdCouldNotBeInvoked = "Container command could not be invoked."
|
||||
)
|
||||
|
||||
func (cid *cidFile) Close() error {
|
||||
cid.file.Close()
|
||||
|
||||
|
@ -46,20 +50,13 @@ func (cid *cidFile) Write(id string) error {
|
|||
// return 125 for generic docker daemon failures
|
||||
func runStartContainerErr(err error) error {
|
||||
trimmedErr := strings.Trim(err.Error(), "Error response from daemon: ")
|
||||
statusError := Cli.StatusError{}
|
||||
derrCmdNotFound := derr.ErrorCodeCmdNotFound.Message()
|
||||
derrCouldNotInvoke := derr.ErrorCodeCmdCouldNotBeInvoked.Message()
|
||||
derrNoSuchImage := derr.ErrorCodeNoSuchImageHash.Message()
|
||||
derrNoSuchImageTag := derr.ErrorCodeNoSuchImageTag.Message()
|
||||
statusError := Cli.StatusError{StatusCode: 125}
|
||||
|
||||
switch trimmedErr {
|
||||
case derrCmdNotFound:
|
||||
case errCmdNotFound:
|
||||
statusError = Cli.StatusError{StatusCode: 127}
|
||||
case derrCouldNotInvoke:
|
||||
case errCmdCouldNotBeInvoked:
|
||||
statusError = Cli.StatusError{StatusCode: 126}
|
||||
case derrNoSuchImage, derrNoSuchImageTag:
|
||||
statusError = Cli.StatusError{StatusCode: 125}
|
||||
default:
|
||||
statusError = Cli.StatusError{StatusCode: 125}
|
||||
}
|
||||
return statusError
|
||||
}
|
||||
|
|
69
api/server/httputils/errors.go
Normal file
69
api/server/httputils/errors.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package httputils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// httpStatusError is an interface
|
||||
// that errors with custom status codes
|
||||
// implement to tell the api layer
|
||||
// which response status to set.
|
||||
type httpStatusError interface {
|
||||
HTTPErrorStatusCode() int
|
||||
}
|
||||
|
||||
// inputValidationError is an interface
|
||||
// that errors generated by invalid
|
||||
// inputs can implement to tell the
|
||||
// api layer to set a 400 status code
|
||||
// in the response.
|
||||
type inputValidationError interface {
|
||||
IsValidationError() bool
|
||||
}
|
||||
|
||||
// WriteError decodes a specific docker error and sends it in the response.
|
||||
func WriteError(w http.ResponseWriter, err error) {
|
||||
if err == nil || w == nil {
|
||||
logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling")
|
||||
return
|
||||
}
|
||||
|
||||
var statusCode int
|
||||
errMsg := err.Error()
|
||||
|
||||
switch e := err.(type) {
|
||||
case httpStatusError:
|
||||
statusCode = e.HTTPErrorStatusCode()
|
||||
case inputValidationError:
|
||||
statusCode = http.StatusBadRequest
|
||||
default:
|
||||
// FIXME: this is brittle and should not be necessary, but we still need to identify if
|
||||
// there are errors falling back into this logic.
|
||||
// If we need to differentiate between different possible error types,
|
||||
// we should create appropriate error types that implement the httpStatusError interface.
|
||||
errStr := strings.ToLower(errMsg)
|
||||
for keyword, status := range map[string]int{
|
||||
"not found": http.StatusNotFound,
|
||||
"no such": http.StatusNotFound,
|
||||
"bad parameter": http.StatusBadRequest,
|
||||
"conflict": http.StatusConflict,
|
||||
"impossible": http.StatusNotAcceptable,
|
||||
"wrong login/password": http.StatusUnauthorized,
|
||||
"hasn't been activated": http.StatusForbidden,
|
||||
} {
|
||||
if strings.Contains(errStr, keyword) {
|
||||
statusCode = status
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if statusCode == 0 {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
http.Error(w, errMsg, statusCode)
|
||||
}
|
|
@ -9,8 +9,6 @@ import (
|
|||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
)
|
||||
|
@ -85,78 +83,6 @@ func ParseMultipartForm(r *http.Request) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// WriteError decodes a specific docker error and sends it in the response.
|
||||
func WriteError(w http.ResponseWriter, err error) {
|
||||
if err == nil || w == nil {
|
||||
logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling")
|
||||
return
|
||||
}
|
||||
|
||||
statusCode := http.StatusInternalServerError
|
||||
errMsg := err.Error()
|
||||
|
||||
// Based on the type of error we get we need to process things
|
||||
// slightly differently to extract the error message.
|
||||
// In the 'errcode.*' cases there are two different type of
|
||||
// error that could be returned. errocode.ErrorCode is the base
|
||||
// type of error object - it is just an 'int' that can then be
|
||||
// used as the look-up key to find the message. errorcode.Error
|
||||
// extends errorcode.Error by adding error-instance specific
|
||||
// data, like 'details' or variable strings to be inserted into
|
||||
// the message.
|
||||
//
|
||||
// Ideally, we should just be able to call err.Error() for all
|
||||
// cases but the errcode package doesn't support that yet.
|
||||
//
|
||||
// Additionally, in both errcode cases, there might be an http
|
||||
// status code associated with it, and if so use it.
|
||||
switch err.(type) {
|
||||
case errcode.ErrorCode:
|
||||
daError, _ := err.(errcode.ErrorCode)
|
||||
statusCode = daError.Descriptor().HTTPStatusCode
|
||||
errMsg = daError.Message()
|
||||
|
||||
case errcode.Error:
|
||||
// For reference, if you're looking for a particular error
|
||||
// then you can do something like :
|
||||
// import ( derr "github.com/docker/docker/errors" )
|
||||
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
|
||||
|
||||
daError, _ := err.(errcode.Error)
|
||||
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
|
||||
errMsg = daError.Message
|
||||
|
||||
default:
|
||||
// This part of will be removed once we've
|
||||
// converted everything over to use the errcode package
|
||||
|
||||
// FIXME: this is brittle and should not be necessary.
|
||||
// If we need to differentiate between different possible error types,
|
||||
// we should create appropriate error types with clearly defined meaning
|
||||
errStr := strings.ToLower(err.Error())
|
||||
for keyword, status := range map[string]int{
|
||||
"not found": http.StatusNotFound,
|
||||
"no such": http.StatusNotFound,
|
||||
"bad parameter": http.StatusBadRequest,
|
||||
"conflict": http.StatusConflict,
|
||||
"impossible": http.StatusNotAcceptable,
|
||||
"wrong login/password": http.StatusUnauthorized,
|
||||
"hasn't been activated": http.StatusForbidden,
|
||||
} {
|
||||
if strings.Contains(errStr, keyword) {
|
||||
statusCode = status
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if statusCode == 0 {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
http.Error(w, errMsg, statusCode)
|
||||
}
|
||||
|
||||
// WriteJSON writes the value v to the http response stream as json with standard json encoding.
|
||||
func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
|
@ -6,11 +6,18 @@ import (
|
|||
"runtime"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type badRequestError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (badRequestError) HTTPErrorStatusCode() int {
|
||||
return http.StatusBadRequest
|
||||
}
|
||||
|
||||
// NewVersionMiddleware creates a new Version middleware.
|
||||
func NewVersionMiddleware(versionCheck string, defaultVersion, minVersion version.Version) Middleware {
|
||||
serverVersion := version.Version(versionCheck)
|
||||
|
@ -23,10 +30,10 @@ func NewVersionMiddleware(versionCheck string, defaultVersion, minVersion versio
|
|||
}
|
||||
|
||||
if apiVersion.GreaterThan(defaultVersion) {
|
||||
return errors.ErrorCodeNewerClientVersion.WithArgs(apiVersion, defaultVersion)
|
||||
return badRequestError{fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", apiVersion, defaultVersion)}
|
||||
}
|
||||
if apiVersion.LessThan(minVersion) {
|
||||
return errors.ErrorCodeOldClientVersion.WithArgs(apiVersion, minVersion)
|
||||
return badRequestError{fmt.Errorf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", apiVersion, minVersion)}
|
||||
}
|
||||
|
||||
header := fmt.Sprintf("Docker/%s (%s)", serverVersion, runtime.GOOS)
|
||||
|
|
|
@ -53,12 +53,12 @@ func TestVersionMiddlewareWithErrors(t *testing.T) {
|
|||
err := h(ctx, resp, req, vars)
|
||||
|
||||
if !strings.Contains(err.Error(), "client version 0.1 is too old. Minimum supported API version is 1.2.0") {
|
||||
t.Fatalf("Expected ErrorCodeOldClientVersion, got %v", err)
|
||||
t.Fatalf("Expected too old client error, got %v", err)
|
||||
}
|
||||
|
||||
vars["version"] = "100000"
|
||||
err = h(ctx, resp, req, vars)
|
||||
if !strings.Contains(err.Error(), "client is newer than server") {
|
||||
t.Fatalf("Expected ErrorCodeNewerClientVersion, got %v", err)
|
||||
t.Fatalf("Expected client newer than server error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -17,7 +16,6 @@ import (
|
|||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/go-units"
|
||||
|
@ -117,7 +115,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
|
|||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(sf.FormatError(errors.New(utils.GetErrorMessage(err))))
|
||||
_, err = w.Write(sf.FormatError(err))
|
||||
if err != nil {
|
||||
logrus.Warnf("could not write error response: %v", err)
|
||||
}
|
||||
|
|
|
@ -11,15 +11,12 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
|
@ -126,7 +123,7 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
|
|||
// 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", utils.GetErrorMessage(err))
|
||||
fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %v\n", err)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
@ -182,6 +179,10 @@ func (s *containerRouter) postContainersStop(ctx context.Context, w http.Respons
|
|||
return nil
|
||||
}
|
||||
|
||||
type errContainerIsRunning interface {
|
||||
ContainerIsRunning() bool
|
||||
}
|
||||
|
||||
func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
|
@ -199,15 +200,17 @@ func (s *containerRouter) postContainersKill(ctx context.Context, w http.Respons
|
|||
}
|
||||
|
||||
if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
|
||||
theErr, isDerr := err.(errcode.ErrorCoder)
|
||||
isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning
|
||||
var isStopped bool
|
||||
if e, ok := err.(errContainerIsRunning); ok {
|
||||
isStopped = !e.ContainerIsRunning()
|
||||
}
|
||||
|
||||
// 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.
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if version.GreaterThanOrEqualTo("1.20") || !isStopped {
|
||||
return fmt.Errorf("Cannot kill container %s: %v", name, utils.GetErrorMessage(err))
|
||||
return fmt.Errorf("Cannot kill container %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,7 +433,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
|||
|
||||
hijacker, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return derr.ErrorCodeNoHijackConnection.WithArgs(containerName)
|
||||
return fmt.Errorf("error attaching to container %s, hijack connection missing", containerName)
|
||||
}
|
||||
|
||||
setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/engine-api/types"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
@ -46,7 +45,7 @@ func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.Re
|
|||
// Register an instance of Exec in container.
|
||||
id, err := s.backend.ContainerExecCreate(execConfig)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error setting up exec command in container %s: %s", name, utils.GetErrorMessage(err))
|
||||
logrus.Errorf("Error setting up exec command in container %s: %v", name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -113,7 +112,7 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
|||
if execStartCheck.Detach {
|
||||
return err
|
||||
}
|
||||
logrus.Errorf("Error running exec in container: %v\n", utils.GetErrorMessage(err))
|
||||
logrus.Errorf("Error running exec in container: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ type Backend interface {
|
|||
|
||||
type containerBackend interface {
|
||||
Commit(name string, config *types.ContainerCommitConfig) (imageID string, err error)
|
||||
Exists(containerName string) bool
|
||||
}
|
||||
|
||||
type imageBackend interface {
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/builder/dockerfile"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/reference"
|
||||
|
@ -49,10 +48,6 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *
|
|||
c = &container.Config{}
|
||||
}
|
||||
|
||||
if !s.backend.Exists(cname) {
|
||||
return derr.ErrorCodeNoSuchContainer.WithArgs(cname)
|
||||
}
|
||||
|
||||
newConfig, err := dockerfile.BuildFromConfig(c, r.Form["changes"])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -8,8 +8,6 @@ import (
|
|||
// Backend is all the methods that need to be implemented
|
||||
// to provide network specific functionality.
|
||||
type Backend interface {
|
||||
NetworkControllerEnabled() bool
|
||||
|
||||
FindNetwork(idName string) (libnetwork.Network, error)
|
||||
GetNetworkByName(idName string) (libnetwork.Network, error)
|
||||
GetNetworksByID(partialID string) []libnetwork.Network
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
import "github.com/docker/docker/api/server/router"
|
||||
|
||||
// networkRouter is a router to talk with the network controller
|
||||
type networkRouter struct {
|
||||
|
@ -32,24 +25,13 @@ func (r *networkRouter) Routes() []router.Route {
|
|||
func (r *networkRouter) initRoutes() {
|
||||
r.routes = []router.Route{
|
||||
// GET
|
||||
router.NewGetRoute("/networks", r.controllerEnabledMiddleware(r.getNetworksList)),
|
||||
router.NewGetRoute("/networks/{id:.*}", r.controllerEnabledMiddleware(r.getNetwork)),
|
||||
router.NewGetRoute("/networks", r.getNetworksList),
|
||||
router.NewGetRoute("/networks/{id:.*}", r.getNetwork),
|
||||
// POST
|
||||
router.NewPostRoute("/networks/create", r.controllerEnabledMiddleware(r.postNetworkCreate)),
|
||||
router.NewPostRoute("/networks/{id:.*}/connect", r.controllerEnabledMiddleware(r.postNetworkConnect)),
|
||||
router.NewPostRoute("/networks/{id:.*}/disconnect", r.controllerEnabledMiddleware(r.postNetworkDisconnect)),
|
||||
router.NewPostRoute("/networks/create", r.postNetworkCreate),
|
||||
router.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect),
|
||||
router.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect),
|
||||
// DELETE
|
||||
router.NewDeleteRoute("/networks/{id:.*}", r.controllerEnabledMiddleware(r.deleteNetwork)),
|
||||
router.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *networkRouter) controllerEnabledMiddleware(handler httputils.APIFunc) httputils.APIFunc {
|
||||
if r.backend.NetworkControllerEnabled() {
|
||||
return handler
|
||||
}
|
||||
return networkControllerDisabled
|
||||
}
|
||||
|
||||
func networkControllerDisabled(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
return errors.ErrorNetworkControllerNotEnabled.WithArgs()
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/pkg/authorization"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/gorilla/mux"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
@ -134,7 +133,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
|
|||
}
|
||||
|
||||
if err := handlerFunc(ctx, w, r, vars); err != nil {
|
||||
logrus.Errorf("Handler for %s %s returned error: %s", r.Method, r.URL.Path, utils.GetErrorMessage(err))
|
||||
logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
||||
httputils.WriteError(w, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/builder"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
|
@ -40,12 +39,12 @@ func nullDispatch(b *Builder, args []string, attributes map[string]bool, origina
|
|||
//
|
||||
func env(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) == 0 {
|
||||
return derr.ErrorCodeAtLeastOneArg.WithArgs("ENV")
|
||||
return errAtLeastOneArgument("ENV")
|
||||
}
|
||||
|
||||
if len(args)%2 != 0 {
|
||||
// should never get here, but just in case
|
||||
return derr.ErrorCodeTooManyArgs.WithArgs("ENV")
|
||||
return errTooManyArguments("ENV")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -99,7 +98,7 @@ func env(b *Builder, args []string, attributes map[string]bool, original string)
|
|||
// Sets the maintainer metadata.
|
||||
func maintainer(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) != 1 {
|
||||
return derr.ErrorCodeExactlyOneArg.WithArgs("MAINTAINER")
|
||||
return errExactlyOneArgument("MAINTAINER")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -116,11 +115,11 @@ func maintainer(b *Builder, args []string, attributes map[string]bool, original
|
|||
//
|
||||
func label(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) == 0 {
|
||||
return derr.ErrorCodeAtLeastOneArg.WithArgs("LABEL")
|
||||
return errAtLeastOneArgument("LABEL")
|
||||
}
|
||||
if len(args)%2 != 0 {
|
||||
// should never get here, but just in case
|
||||
return derr.ErrorCodeTooManyArgs.WithArgs("LABEL")
|
||||
return errTooManyArguments("LABEL")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -152,7 +151,7 @@ func label(b *Builder, args []string, attributes map[string]bool, original strin
|
|||
//
|
||||
func add(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) < 2 {
|
||||
return derr.ErrorCodeAtLeastTwoArgs.WithArgs("ADD")
|
||||
return errAtLeastOneArgument("ADD")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -168,7 +167,7 @@ func add(b *Builder, args []string, attributes map[string]bool, original string)
|
|||
//
|
||||
func dispatchCopy(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) < 2 {
|
||||
return derr.ErrorCodeAtLeastTwoArgs.WithArgs("COPY")
|
||||
return errAtLeastOneArgument("COPY")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -184,7 +183,7 @@ func dispatchCopy(b *Builder, args []string, attributes map[string]bool, origina
|
|||
//
|
||||
func from(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) != 1 {
|
||||
return derr.ErrorCodeExactlyOneArg.WithArgs("FROM")
|
||||
return errExactlyOneArgument("FROM")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -233,7 +232,7 @@ func from(b *Builder, args []string, attributes map[string]bool, original string
|
|||
//
|
||||
func onbuild(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) == 0 {
|
||||
return derr.ErrorCodeAtLeastOneArg.WithArgs("ONBUILD")
|
||||
return errAtLeastOneArgument("ONBUILD")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -243,9 +242,9 @@ func onbuild(b *Builder, args []string, attributes map[string]bool, original str
|
|||
triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0]))
|
||||
switch triggerInstruction {
|
||||
case "ONBUILD":
|
||||
return derr.ErrorCodeChainOnBuild
|
||||
return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
||||
case "MAINTAINER", "FROM":
|
||||
return derr.ErrorCodeBadOnBuildCmd.WithArgs(triggerInstruction)
|
||||
return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
|
||||
}
|
||||
|
||||
original = regexp.MustCompile(`(?i)^\s*ONBUILD\s*`).ReplaceAllString(original, "")
|
||||
|
@ -260,7 +259,7 @@ func onbuild(b *Builder, args []string, attributes map[string]bool, original str
|
|||
//
|
||||
func workdir(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) != 1 {
|
||||
return derr.ErrorCodeExactlyOneArg.WithArgs("WORKDIR")
|
||||
return errExactlyOneArgument("WORKDIR")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -293,7 +292,7 @@ func workdir(b *Builder, args []string, attributes map[string]bool, original str
|
|||
//
|
||||
func run(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if b.image == "" && !b.noBaseImage {
|
||||
return derr.ErrorCodeMissingFrom
|
||||
return fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -491,7 +490,7 @@ func expose(b *Builder, args []string, attributes map[string]bool, original stri
|
|||
portsTab := args
|
||||
|
||||
if len(args) == 0 {
|
||||
return derr.ErrorCodeAtLeastOneArg.WithArgs("EXPOSE")
|
||||
return errAtLeastOneArgument("EXPOSE")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -530,7 +529,7 @@ func expose(b *Builder, args []string, attributes map[string]bool, original stri
|
|||
//
|
||||
func user(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) != 1 {
|
||||
return derr.ErrorCodeExactlyOneArg.WithArgs("USER")
|
||||
return errExactlyOneArgument("USER")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -547,7 +546,7 @@ func user(b *Builder, args []string, attributes map[string]bool, original string
|
|||
//
|
||||
func volume(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if len(args) == 0 {
|
||||
return derr.ErrorCodeAtLeastOneArg.WithArgs("VOLUME")
|
||||
return errAtLeastOneArgument("VOLUME")
|
||||
}
|
||||
|
||||
if err := b.flags.Parse(); err != nil {
|
||||
|
@ -560,7 +559,7 @@ func volume(b *Builder, args []string, attributes map[string]bool, original stri
|
|||
for _, v := range args {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
return derr.ErrorCodeVolumeEmpty
|
||||
return fmt.Errorf("Volume specified can not be an empty string")
|
||||
}
|
||||
b.runConfig.Volumes[v] = struct{}{}
|
||||
}
|
||||
|
@ -631,3 +630,15 @@ func arg(b *Builder, args []string, attributes map[string]bool, original string)
|
|||
|
||||
return b.commit("", b.runConfig.Cmd, fmt.Sprintf("ARG %s", arg))
|
||||
}
|
||||
|
||||
func errAtLeastOneArgument(command string) error {
|
||||
return fmt.Errorf("%s requires at least one argument", command)
|
||||
}
|
||||
|
||||
func errExactlyOneArgument(command string) error {
|
||||
return fmt.Errorf("%s requires exactly one argument", command)
|
||||
}
|
||||
|
||||
func errTooManyArguments(command string) error {
|
||||
return fmt.Errorf("Bad input to %s, too many arguments", command)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
|
@ -199,7 +198,7 @@ func (container *Container) SetupWorkingDirectory() error {
|
|||
if err := system.MkdirAll(pth, 0755); err != nil {
|
||||
pthInfo, err2 := os.Stat(pth)
|
||||
if err2 == nil && pthInfo != nil && !pthInfo.IsDir() {
|
||||
return derr.ErrorCodeNotADir.WithArgs(container.Config.WorkingDir)
|
||||
return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
|
||||
}
|
||||
|
||||
return err
|
||||
|
@ -277,13 +276,6 @@ func (container *Container) ConfigPath() (string, error) {
|
|||
return container.GetRootResourcePath(configFileName)
|
||||
}
|
||||
|
||||
func validateID(id string) error {
|
||||
if id == "" {
|
||||
return derr.ErrorCodeEmptyID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true if the container exposes a certain port
|
||||
func (container *Container) exposes(p nat.Port) bool {
|
||||
_, exists := container.Config.ExposedPorts[p]
|
||||
|
@ -307,7 +299,7 @@ func (container *Container) GetLogConfig(defaultConfig containertypes.LogConfig)
|
|||
func (container *Container) StartLogger(cfg containertypes.LogConfig) (logger.Logger, error) {
|
||||
c, err := logger.GetLogDriver(cfg.Type)
|
||||
if err != nil {
|
||||
return nil, derr.ErrorCodeLoggingFactory.WithArgs(err)
|
||||
return nil, fmt.Errorf("Failed to get logging factory: %v", err)
|
||||
}
|
||||
ctx := logger.Context{
|
||||
Config: cfg.Config,
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
|
@ -34,6 +33,11 @@ import (
|
|||
// DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container
|
||||
const DefaultSHMSize int64 = 67108864
|
||||
|
||||
var (
|
||||
errInvalidEndpoint = fmt.Errorf("invalid endpoint while building port map info")
|
||||
errInvalidNetwork = fmt.Errorf("invalid network settings while building port map info")
|
||||
)
|
||||
|
||||
// Container holds the fields specific to unixen implementations.
|
||||
// See CommonContainer for standard fields common to all containers.
|
||||
type Container struct {
|
||||
|
@ -116,12 +120,12 @@ func (container *Container) GetEndpointInNetwork(n libnetwork.Network) (libnetwo
|
|||
|
||||
func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint) error {
|
||||
if ep == nil {
|
||||
return derr.ErrorCodeEmptyEndpoint
|
||||
return errInvalidEndpoint
|
||||
}
|
||||
|
||||
networkSettings := container.NetworkSettings
|
||||
if networkSettings == nil {
|
||||
return derr.ErrorCodeEmptyNetwork
|
||||
return errInvalidNetwork
|
||||
}
|
||||
|
||||
if len(networkSettings.Ports) == 0 {
|
||||
|
@ -151,7 +155,7 @@ func getEndpointPortMapInfo(ep libnetwork.Endpoint) (nat.PortMap, error) {
|
|||
for _, tp := range exposedPorts {
|
||||
natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port)))
|
||||
if err != nil {
|
||||
return pm, derr.ErrorCodeParsingPort.WithArgs(tp.Port, err)
|
||||
return pm, fmt.Errorf("Error parsing Port value(%v):%v", tp.Port, err)
|
||||
}
|
||||
pm[natPort] = nil
|
||||
}
|
||||
|
@ -195,12 +199,12 @@ func getSandboxPortMapInfo(sb libnetwork.Sandbox) nat.PortMap {
|
|||
// BuildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint.
|
||||
func (container *Container) BuildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint) error {
|
||||
if ep == nil {
|
||||
return derr.ErrorCodeEmptyEndpoint
|
||||
return errInvalidEndpoint
|
||||
}
|
||||
|
||||
networkSettings := container.NetworkSettings
|
||||
if networkSettings == nil {
|
||||
return derr.ErrorCodeEmptyNetwork
|
||||
return errInvalidNetwork
|
||||
}
|
||||
|
||||
epInfo := ep.Info()
|
||||
|
@ -377,7 +381,7 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epC
|
|||
portStart, portEnd, err = newP.Range()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, derr.ErrorCodeHostPort.WithArgs(binding[i].HostPort, err)
|
||||
return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err)
|
||||
}
|
||||
pbCopy.HostPort = uint16(portStart)
|
||||
pbCopy.HostPortEnd = uint16(portEnd)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
@ -10,10 +11,8 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
||||
|
@ -190,7 +189,7 @@ func (m *containerMonitor) start() error {
|
|||
if m.container.RestartCount == 0 {
|
||||
m.container.ExitCode = 127
|
||||
m.resetContainer(false)
|
||||
return derr.ErrorCodeCmdNotFound
|
||||
return fmt.Errorf("Container command not found or does not exist.")
|
||||
}
|
||||
}
|
||||
// set to 126 for container cmd can't be invoked errors
|
||||
|
@ -198,7 +197,7 @@ func (m *containerMonitor) start() error {
|
|||
if m.container.RestartCount == 0 {
|
||||
m.container.ExitCode = 126
|
||||
m.resetContainer(false)
|
||||
return derr.ErrorCodeCmdCouldNotBeInvoked
|
||||
return fmt.Errorf("Container command could not be invoked.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,7 +205,7 @@ func (m *containerMonitor) start() error {
|
|||
m.container.ExitCode = -1
|
||||
m.resetContainer(false)
|
||||
|
||||
return derr.ErrorCodeCantStart.WithArgs(m.container.ID, utils.GetErrorMessage(err))
|
||||
return fmt.Errorf("Cannot start container %s: %v", m.container.ID, err)
|
||||
}
|
||||
|
||||
logrus.Errorf("Error running container: %s", err)
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
|
@ -113,7 +112,7 @@ func wait(waitChan <-chan struct{}, timeout time.Duration) error {
|
|||
}
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
return derr.ErrorCodeTimedOut.WithArgs(timeout)
|
||||
return fmt.Errorf("Timed out: %v", timeout)
|
||||
case <-waitChan:
|
||||
return nil
|
||||
}
|
||||
|
@ -256,14 +255,15 @@ func (s *State) IsRestarting() bool {
|
|||
}
|
||||
|
||||
// SetRemovalInProgress sets the container state as being removed.
|
||||
func (s *State) SetRemovalInProgress() error {
|
||||
// It returns true if the container was already in that state.
|
||||
func (s *State) SetRemovalInProgress() bool {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
if s.RemovalInProgress {
|
||||
return derr.ErrorCodeAlreadyRemoving
|
||||
return true
|
||||
}
|
||||
s.RemovalInProgress = true
|
||||
return nil
|
||||
return false
|
||||
}
|
||||
|
||||
// ResetRemovalInProgress make the RemovalInProgress state to false.
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
)
|
||||
|
||||
|
@ -17,10 +17,11 @@ import (
|
|||
func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error {
|
||||
container, err := daemon.GetContainer(prefixOrName)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName)
|
||||
return err
|
||||
}
|
||||
if container.IsPaused() {
|
||||
return derr.ErrorCodePausedContainer.WithArgs(prefixOrName)
|
||||
err := fmt.Errorf("Container %s is paused. Unpause the container before attach", prefixOrName)
|
||||
return errors.NewRequestConflictError(err)
|
||||
}
|
||||
|
||||
inStream, outStream, errStream, err := c.GetStreams()
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/daemon/links"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
|
@ -45,7 +45,7 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s
|
|||
|
||||
for linkAlias, child := range children {
|
||||
if !child.IsRunning() {
|
||||
return nil, derr.ErrorCodeLinkNotRunning.WithArgs(child.Name, linkAlias)
|
||||
return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
|
||||
}
|
||||
|
||||
childBridgeSettings := child.NetworkSettings.Networks["bridge"]
|
||||
|
@ -509,7 +509,7 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
|
|||
|
||||
sb, err := ctrl.SandboxByID(sid)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeNoSandbox.WithArgs(sid, err)
|
||||
return fmt.Errorf("error locating sandbox id %s: %v", sid, err)
|
||||
}
|
||||
|
||||
// Find if container is connected to the default bridge network
|
||||
|
@ -532,11 +532,11 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
|
|||
|
||||
options, err := daemon.buildSandboxOptions(container, n)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeNetworkUpdate.WithArgs(err)
|
||||
return fmt.Errorf("Update network failed: %v", err)
|
||||
}
|
||||
|
||||
if err := sb.Refresh(options...); err != nil {
|
||||
return derr.ErrorCodeNetworkRefresh.WithArgs(sid, err)
|
||||
return fmt.Errorf("Update network failed: Failure in refresh sandbox %s: %v", sid, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -730,7 +730,7 @@ func (daemon *Daemon) updateNetworkConfig(container *container.Container, idOrNa
|
|||
func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
|
||||
if !container.Running {
|
||||
if container.RemovalInProgress || container.Dead {
|
||||
return derr.ErrorCodeRemovalContainer.WithArgs(container.ID)
|
||||
return errRemovalContainer(container.ID)
|
||||
}
|
||||
if _, err := daemon.updateNetworkConfig(container, idOrName, endpointConfig, true); err != nil {
|
||||
return err
|
||||
|
@ -810,7 +810,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
|
|||
}
|
||||
|
||||
if err := container.UpdateJoinInfo(n, ep); err != nil {
|
||||
return derr.ErrorCodeJoinInfo.WithArgs(err)
|
||||
return fmt.Errorf("Updating join info failed: %v", err)
|
||||
}
|
||||
|
||||
daemon.LogNetworkEventWithAttributes(n, "connect", map[string]string{"container": container.ID})
|
||||
|
@ -833,7 +833,7 @@ func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n li
|
|||
}
|
||||
if !container.Running {
|
||||
if container.RemovalInProgress || container.Dead {
|
||||
return derr.ErrorCodeRemovalContainer.WithArgs(container.ID)
|
||||
return errRemovalContainer(container.ID)
|
||||
}
|
||||
if _, ok := container.NetworkSettings.Networks[n.Name()]; ok {
|
||||
delete(container.NetworkSettings.Networks, n.Name())
|
||||
|
@ -950,7 +950,7 @@ func (daemon *Daemon) setNetworkNamespaceKey(containerID string, pid int) error
|
|||
search := libnetwork.SandboxContainerWalker(&sandbox, containerID)
|
||||
daemon.netController.WalkSandboxes(search)
|
||||
if sandbox == nil {
|
||||
return derr.ErrorCodeNoSandbox.WithArgs(containerID, "no sandbox found")
|
||||
return fmt.Errorf("error locating sandbox id %s: no sandbox found", containerID)
|
||||
}
|
||||
|
||||
return sandbox.SetKey(path)
|
||||
|
@ -963,10 +963,10 @@ func (daemon *Daemon) getIpcContainer(container *container.Container) (*containe
|
|||
return nil, err
|
||||
}
|
||||
if !c.IsRunning() {
|
||||
return nil, derr.ErrorCodeIPCRunning.WithArgs(containerID)
|
||||
return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID)
|
||||
}
|
||||
if c.IsRestarting() {
|
||||
return nil, derr.ErrorCodeContainerRestarting.WithArgs(containerID)
|
||||
return nil, errContainerIsRestarting(container.ID)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
@ -977,13 +977,14 @@ func (daemon *Daemon) getNetworkedContainer(containerID, connectedContainerID st
|
|||
return nil, err
|
||||
}
|
||||
if containerID == nc.ID {
|
||||
return nil, derr.ErrorCodeJoinSelf
|
||||
return nil, fmt.Errorf("cannot join own network")
|
||||
}
|
||||
if !nc.IsRunning() {
|
||||
return nil, derr.ErrorCodeJoinRunning.WithArgs(connectedContainerID)
|
||||
err := fmt.Errorf("cannot join network of a non running container: %s", connectedContainerID)
|
||||
return nil, errors.NewRequestConflictError(err)
|
||||
}
|
||||
if nc.IsRestarting() {
|
||||
return nil, derr.ErrorCodeContainerRestarting.WithArgs(connectedContainerID)
|
||||
return nil, errContainerIsRestarting(connectedContainerID)
|
||||
}
|
||||
return nc, nil
|
||||
}
|
||||
|
@ -1141,7 +1142,7 @@ func getDevicesFromPath(deviceMapping containertypes.DeviceMapping) (devs []*con
|
|||
return devs, nil
|
||||
}
|
||||
|
||||
return devs, derr.ErrorCodeDeviceInfo.WithArgs(deviceMapping.PathOnHost, err)
|
||||
return devs, fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err)
|
||||
}
|
||||
|
||||
func mergeDevices(defaultDevices, userDevices []*configs.Device) []*configs.Device {
|
||||
|
@ -1172,3 +1173,7 @@ func isLinkable(child *container.Container) bool {
|
|||
_, ok := child.NetworkSettings.Networks["bridge"]
|
||||
return ok
|
||||
}
|
||||
|
||||
func errRemovalContainer(containerID string) error {
|
||||
return fmt.Errorf("Container %s is marked for removal and cannot be connected or disconnected to the network", containerID)
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/daemon/execdriver/windows"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/layer"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"github.com/docker/libnetwork"
|
||||
|
@ -64,7 +64,7 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
|
|||
}
|
||||
}
|
||||
default:
|
||||
return derr.ErrorCodeInvalidNetworkMode.WithArgs(c.HostConfig.NetworkMode)
|
||||
return fmt.Errorf("invalid network mode: %s", c.HostConfig.NetworkMode)
|
||||
}
|
||||
|
||||
// TODO Windows. More resource controls to be implemented later.
|
||||
|
@ -88,7 +88,7 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
|
|||
var layerPaths []string
|
||||
img, err := daemon.imageStore.Get(c.ImageID)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeGetGraph.WithArgs(c.ImageID, err)
|
||||
return fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err)
|
||||
}
|
||||
|
||||
if img.RootFS != nil && img.RootFS.Type == "layers+base" {
|
||||
|
@ -97,7 +97,7 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
|
|||
img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
|
||||
path, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID())
|
||||
if err != nil {
|
||||
return derr.ErrorCodeGetLayer.WithArgs(err)
|
||||
return fmt.Errorf("Failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err)
|
||||
}
|
||||
// Reverse order, expecting parent most first
|
||||
layerPaths = append([]string{path}, layerPaths...)
|
||||
|
@ -106,7 +106,7 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
|
|||
|
||||
m, err := c.RWLayer.Metadata()
|
||||
if err != nil {
|
||||
return derr.ErrorCodeGetLayerMetadata.WithArgs(err)
|
||||
return fmt.Errorf("Failed to get layer metadata - %s", err)
|
||||
}
|
||||
layerFolder := m["dir"]
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
|
@ -18,7 +19,7 @@ import (
|
|||
// ContainerCreate creates a container.
|
||||
func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (types.ContainerCreateResponse, error) {
|
||||
if params.Config == nil {
|
||||
return types.ContainerCreateResponse{}, derr.ErrorCodeEmptyConfig
|
||||
return types.ContainerCreateResponse{}, fmt.Errorf("Config cannot be empty in order to create a container")
|
||||
}
|
||||
|
||||
warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false)
|
||||
|
@ -174,7 +175,7 @@ func (daemon *Daemon) VolumeCreate(name, driverName string, opts map[string]stri
|
|||
v, err := daemon.volumes.Create(name, driverName, opts)
|
||||
if err != nil {
|
||||
if volumestore.IsNameConflict(err) {
|
||||
return nil, derr.ErrorVolumeNameTaken.WithArgs(name)
|
||||
return nil, fmt.Errorf("A volume named %s already exists. Choose a different volume name.", name)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
|
@ -41,7 +41,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
|
|||
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && !stat.IsDir() {
|
||||
return derr.ErrorCodeMountOverFile.WithArgs(path)
|
||||
return fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
|
||||
}
|
||||
|
||||
v, err := daemon.volumes.CreateWithRef(name, hostConfig.VolumeDriver, container.ID, nil)
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -15,6 +14,7 @@ import (
|
|||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/docker/docker/daemon/exec"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/daemon/execdriver/execdrivers"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/engine-api/types"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
eventtypes "github.com/docker/engine-api/types/events"
|
||||
|
@ -43,7 +44,6 @@ import (
|
|||
dmetadata "github.com/docker/docker/distribution/metadata"
|
||||
"github.com/docker/docker/distribution/xfer"
|
||||
"github.com/docker/docker/dockerversion"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/image/tarexport"
|
||||
"github.com/docker/docker/layer"
|
||||
|
@ -90,7 +90,7 @@ var (
|
|||
validContainerNameChars = utils.RestrictedNameChars
|
||||
validContainerNamePattern = utils.RestrictedNamePattern
|
||||
|
||||
errSystemNotSupported = errors.New("The Docker daemon is not supported on this platform.")
|
||||
errSystemNotSupported = fmt.Errorf("The Docker daemon is not supported on this platform.")
|
||||
)
|
||||
|
||||
// ErrImageDoesNotExist is error returned when no image can be found for a reference.
|
||||
|
@ -157,7 +157,8 @@ func (daemon *Daemon) GetContainer(prefixOrName string) (*container.Container, e
|
|||
if indexError != nil {
|
||||
// When truncindex defines an error type, use that instead
|
||||
if indexError == truncindex.ErrNotExist {
|
||||
return nil, derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName)
|
||||
err := fmt.Errorf("No such container: %s", prefixOrName)
|
||||
return nil, errors.NewRequestNotFoundError(err)
|
||||
}
|
||||
return nil, indexError
|
||||
}
|
||||
|
@ -1211,7 +1212,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
|
|||
|
||||
if !h.EmptyLayer {
|
||||
if len(img.RootFS.DiffIDs) <= layerCounter {
|
||||
return nil, errors.New("too many non-empty layers in History section")
|
||||
return nil, fmt.Errorf("too many non-empty layers in History section")
|
||||
}
|
||||
|
||||
rootFS.Append(img.RootFS.DiffIDs[layerCounter])
|
||||
|
@ -1499,7 +1500,8 @@ func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingCo
|
|||
for k := range nwConfig.EndpointsConfig {
|
||||
l = append(l, k)
|
||||
}
|
||||
return derr.ErrorCodeMultipleNetworkConnect.WithArgs(fmt.Sprintf("%v", l))
|
||||
err := fmt.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", "))
|
||||
return errors.NewBadRequestError(err)
|
||||
}
|
||||
|
||||
func configureVolumes(config *Config, rootUID, rootGID int) (*store.VolumeStore, error) {
|
||||
|
@ -1671,7 +1673,7 @@ func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *lib
|
|||
|
||||
func validateID(id string) error {
|
||||
if id == "" {
|
||||
return derr.ErrorCodeEmptyID
|
||||
return fmt.Errorf("Invalid empty id")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
|
@ -312,17 +311,17 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi
|
|||
}
|
||||
cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(resources.CpusetCpus)
|
||||
if err != nil {
|
||||
return warnings, derr.ErrorCodeInvalidCpusetCpus.WithArgs(resources.CpusetCpus)
|
||||
return warnings, fmt.Errorf("Invalid value %s for cpuset cpus.", resources.CpusetCpus)
|
||||
}
|
||||
if !cpusAvailable {
|
||||
return warnings, derr.ErrorCodeNotAvailableCpusetCpus.WithArgs(resources.CpusetCpus, sysInfo.Cpus)
|
||||
return warnings, fmt.Errorf("Requested CPUs are not available - requested %s, available: %s.", resources.CpusetCpus, sysInfo.Cpus)
|
||||
}
|
||||
memsAvailable, err := sysInfo.IsCpusetMemsAvailable(resources.CpusetMems)
|
||||
if err != nil {
|
||||
return warnings, derr.ErrorCodeInvalidCpusetMems.WithArgs(resources.CpusetMems)
|
||||
return warnings, fmt.Errorf("Invalid value %s for cpuset mems.", resources.CpusetMems)
|
||||
}
|
||||
if !memsAvailable {
|
||||
return warnings, derr.ErrorCodeNotAvailableCpusetMems.WithArgs(resources.CpusetMems, sysInfo.Mems)
|
||||
return warnings, fmt.Errorf("Requested memory nodes are not available - requested %s, available: %s.", resources.CpusetMems, sysInfo.Mems)
|
||||
}
|
||||
|
||||
// blkio subsystem checks and adjustments
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/layer"
|
||||
volumestore "github.com/docker/docker/volume/store"
|
||||
"github.com/docker/engine-api/types"
|
||||
|
@ -25,12 +25,8 @@ func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig)
|
|||
}
|
||||
|
||||
// Container state RemovalInProgress should be used to avoid races.
|
||||
if err = container.SetRemovalInProgress(); err != nil {
|
||||
if err == derr.ErrorCodeAlreadyRemoving {
|
||||
// do not fail when the removal is in progress started by other request.
|
||||
return nil
|
||||
}
|
||||
return derr.ErrorCodeRmState.WithArgs(container.ID, err)
|
||||
if inProgress := container.SetRemovalInProgress(); inProgress {
|
||||
return nil
|
||||
}
|
||||
defer container.ResetRemovalInProgress()
|
||||
|
||||
|
@ -84,10 +80,11 @@ func (daemon *Daemon) rmLink(container *container.Container, name string) error
|
|||
func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemove bool) (err error) {
|
||||
if container.IsRunning() {
|
||||
if !forceRemove {
|
||||
return derr.ErrorCodeRmRunning.WithArgs(container.ID)
|
||||
err := fmt.Errorf("You cannot remove a running container %s. Stop the container before attempting removal or use -f", container.ID)
|
||||
return errors.NewRequestConflictError(err)
|
||||
}
|
||||
if err := daemon.Kill(container); err != nil {
|
||||
return derr.ErrorCodeRmFailed.WithArgs(container.ID, err)
|
||||
return fmt.Errorf("Could not kill running container %s, cannot remove - %v", container.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,17 +120,17 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
|
|||
}()
|
||||
|
||||
if err = os.RemoveAll(container.Root); err != nil {
|
||||
return derr.ErrorCodeRmFS.WithArgs(container.ID, err)
|
||||
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
|
||||
}
|
||||
|
||||
metadata, err := daemon.layerStore.ReleaseRWLayer(container.RWLayer)
|
||||
layer.LogReleaseMetadata(metadata)
|
||||
if err != nil && err != layer.ErrMountDoesNotExist {
|
||||
return derr.ErrorCodeRmDriverFS.WithArgs(daemon.GraphDriverName(), container.ID, err)
|
||||
return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.GraphDriverName(), container.ID, err)
|
||||
}
|
||||
|
||||
if err = daemon.execDriver.Clean(container.ID); err != nil {
|
||||
return derr.ErrorCodeRmExecDriver.WithArgs(container.ID, err)
|
||||
return fmt.Errorf("Unable to remove execdriver data for %s: %s", container.ID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -149,9 +146,10 @@ func (daemon *Daemon) VolumeRm(name string) error {
|
|||
|
||||
if err := daemon.volumes.Remove(v); err != nil {
|
||||
if volumestore.IsInUse(err) {
|
||||
return derr.ErrorCodeRmVolumeInUse.WithArgs(err)
|
||||
err := fmt.Errorf("Unable to remove volume, volume still in use: %v", err)
|
||||
return errors.NewRequestConflictError(err)
|
||||
}
|
||||
return derr.ErrorCodeRmVolume.WithArgs(name, err)
|
||||
return fmt.Errorf("Error while removing volume %s: %v", name, err)
|
||||
}
|
||||
daemon.LogVolumeEvent(v.Name(), "destroy", map[string]string{"driver": v.DriverName()})
|
||||
return nil
|
||||
|
|
|
@ -32,9 +32,7 @@ func TestContainerDoubleDelete(t *testing.T) {
|
|||
daemon.containers.Add(container.ID, container)
|
||||
|
||||
// Mark the container as having a delete in progress
|
||||
if err := container.SetRemovalInProgress(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
container.SetRemovalInProgress()
|
||||
|
||||
// Try to remove the container when it's start is removalInProgress.
|
||||
// It should ignore the container and not return an error.
|
||||
|
|
|
@ -1,26 +1,57 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/reference"
|
||||
)
|
||||
|
||||
func (d *Daemon) imageNotExistToErrcode(err error) error {
|
||||
if dne, isDNE := err.(ErrImageDoesNotExist); isDNE {
|
||||
if strings.Contains(dne.RefOrID, "@") {
|
||||
return derr.ErrorCodeNoSuchImageHash.WithArgs(dne.RefOrID)
|
||||
e := fmt.Errorf("No such image: %s", dne.RefOrID)
|
||||
return errors.NewRequestNotFoundError(e)
|
||||
}
|
||||
tag := reference.DefaultTag
|
||||
ref, err := reference.ParseNamed(dne.RefOrID)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeNoSuchImageTag.WithArgs(dne.RefOrID, tag)
|
||||
e := fmt.Errorf("No such image: %s:%s", dne.RefOrID, tag)
|
||||
return errors.NewRequestNotFoundError(e)
|
||||
}
|
||||
if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
||||
tag = tagged.Tag()
|
||||
}
|
||||
return derr.ErrorCodeNoSuchImageTag.WithArgs(ref.Name(), tag)
|
||||
e := fmt.Errorf("No such image: %s:%s", ref.Name(), tag)
|
||||
return errors.NewRequestNotFoundError(e)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type errNotRunning struct {
|
||||
containerID string
|
||||
}
|
||||
|
||||
func (e errNotRunning) Error() string {
|
||||
return fmt.Sprintf("Container %s is not running", e.containerID)
|
||||
}
|
||||
|
||||
func (e errNotRunning) ContainerIsRunning() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func errContainerIsRestarting(containerID string) error {
|
||||
err := fmt.Errorf("Container %s is restarting, wait until the container is running", containerID)
|
||||
return errors.NewRequestConflictError(err)
|
||||
}
|
||||
|
||||
func errExecNotFound(id string) error {
|
||||
err := fmt.Errorf("No such exec instance '%s' found in daemon", id)
|
||||
return errors.NewRequestNotFoundError(err)
|
||||
}
|
||||
|
||||
func errExecPaused(id string) error {
|
||||
err := fmt.Errorf("Container %s is paused, unpause the container before exec", id)
|
||||
return errors.NewRequestConflictError(err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -9,7 +10,7 @@ import (
|
|||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/pools"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
|
@ -47,19 +48,19 @@ func (d *Daemon) getExecConfig(name string) (*exec.Config, error) {
|
|||
if ec != nil {
|
||||
if container := d.containers.Get(ec.ContainerID); container != nil {
|
||||
if !container.IsRunning() {
|
||||
return nil, derr.ErrorCodeContainerNotRunning.WithArgs(container.ID, container.State.String())
|
||||
return nil, fmt.Errorf("Container %s is not running: %s", container.ID, container.State.String())
|
||||
}
|
||||
if container.IsPaused() {
|
||||
return nil, derr.ErrorCodeExecPaused.WithArgs(container.ID)
|
||||
return nil, errExecPaused(container.ID)
|
||||
}
|
||||
if container.IsRestarting() {
|
||||
return nil, derr.ErrorCodeContainerRestarting.WithArgs(container.ID)
|
||||
return nil, errContainerIsRestarting(container.ID)
|
||||
}
|
||||
return ec, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, derr.ErrorCodeNoExecID.WithArgs(name)
|
||||
return nil, errExecNotFound(name)
|
||||
}
|
||||
|
||||
func (d *Daemon) unregisterExecCommand(container *container.Container, execConfig *exec.Config) {
|
||||
|
@ -74,13 +75,13 @@ func (d *Daemon) getActiveContainer(name string) (*container.Container, error) {
|
|||
}
|
||||
|
||||
if !container.IsRunning() {
|
||||
return nil, derr.ErrorCodeNotRunning.WithArgs(name)
|
||||
return nil, errNotRunning{container.ID}
|
||||
}
|
||||
if container.IsPaused() {
|
||||
return nil, derr.ErrorCodeExecPaused.WithArgs(name)
|
||||
return nil, errExecPaused(name)
|
||||
}
|
||||
if container.IsRestarting() {
|
||||
return nil, derr.ErrorCodeContainerRestarting.WithArgs(name)
|
||||
return nil, errContainerIsRestarting(container.ID)
|
||||
}
|
||||
return container, nil
|
||||
}
|
||||
|
@ -137,18 +138,19 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
|
|||
|
||||
ec, err := d.getExecConfig(name)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeNoExecID.WithArgs(name)
|
||||
return errExecNotFound(name)
|
||||
}
|
||||
|
||||
ec.Lock()
|
||||
if ec.ExitCode != nil {
|
||||
ec.Unlock()
|
||||
return derr.ErrorCodeExecExited.WithArgs(ec.ID)
|
||||
err := fmt.Errorf("Error: Exec command %s has already run", ec.ID)
|
||||
return errors.NewRequestConflictError(err)
|
||||
}
|
||||
|
||||
if ec.Running {
|
||||
ec.Unlock()
|
||||
return derr.ErrorCodeExecRunning.WithArgs(ec.ID)
|
||||
return fmt.Errorf("Error: Exec command %s is already running", ec.ID)
|
||||
}
|
||||
ec.Running = true
|
||||
ec.Unlock()
|
||||
|
@ -194,12 +196,12 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
|
|||
select {
|
||||
case err := <-attachErr:
|
||||
if err != nil {
|
||||
return derr.ErrorCodeExecAttach.WithArgs(err)
|
||||
return fmt.Errorf("attach failed with error: %v", err)
|
||||
}
|
||||
return nil
|
||||
case err := <-execErr:
|
||||
if aErr := <-attachErr; aErr != nil && err == nil {
|
||||
return derr.ErrorCodeExecAttach.WithArgs(aErr)
|
||||
return fmt.Errorf("attach failed with error: %v", aErr)
|
||||
}
|
||||
if err == nil {
|
||||
return nil
|
||||
|
@ -207,9 +209,9 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
|
|||
|
||||
// Maybe the container stopped while we were trying to exec
|
||||
if !c.IsRunning() {
|
||||
return derr.ErrorCodeExecContainerStopped
|
||||
return fmt.Errorf("container stopped while running exec: %s", c.ID)
|
||||
}
|
||||
return derr.ErrorCodeExecCantRun.WithArgs(ec.ID, c.ID, err)
|
||||
return fmt.Errorf("Cannot run exec command %s in container %s: %s", ec.ID, c.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package exec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
@ -116,7 +116,7 @@ func (c *Config) Resize(h, w int) error {
|
|||
select {
|
||||
case <-c.waitStart:
|
||||
case <-time.After(time.Second):
|
||||
return derr.ErrorCodeExecResize.WithArgs(c.ID)
|
||||
return fmt.Errorf("Exec %s is not running, so it can not be resized.", c.ID)
|
||||
}
|
||||
return c.ProcessConfig.Terminal.Resize(h, w)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/docker/profiles/seccomp"
|
||||
|
||||
|
@ -430,7 +429,7 @@ func (d *Driver) setupMounts(container *configs.Config, c *execdriver.Command) e
|
|||
for _, m := range c.Mounts {
|
||||
for _, cm := range container.Mounts {
|
||||
if cm.Destination == m.Destination {
|
||||
return derr.ErrorCodeMountDup.WithArgs(m.Destination)
|
||||
return fmt.Errorf("Duplicate mount point '%s'", m.Destination)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
)
|
||||
|
@ -19,13 +19,13 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error {
|
|||
|
||||
data, err := daemon.containerExport(container)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeExportFailed.WithArgs(name, err)
|
||||
return fmt.Errorf("Error exporting container %s: %v", name, err)
|
||||
}
|
||||
defer data.Close()
|
||||
|
||||
// Stream the entire contents of the container (basically a volatile snapshot)
|
||||
if _, err := io.Copy(out, data); err != nil {
|
||||
return derr.ErrorCodeExportFailed.WithArgs(name, err)
|
||||
return fmt.Errorf("Error exporting container %s: %v", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/reference"
|
||||
|
@ -82,7 +82,8 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
|
|||
// this image would remain "dangling" and since
|
||||
// we really want to avoid that the client must
|
||||
// explicitly force its removal.
|
||||
return nil, derr.ErrorCodeImgDelUsed.WithArgs(imageRef, stringid.TruncateID(container.ID), stringid.TruncateID(imgID.String()))
|
||||
err := fmt.Errorf("conflict: unable to remove repository reference %q (must force) - container %s is using its referenced image %s", imageRef, stringid.TruncateID(container.ID), stringid.TruncateID(imgID.String()))
|
||||
return nil, errors.NewRequestConflictError(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
)
|
||||
|
||||
|
@ -45,11 +44,11 @@ func (daemon *Daemon) killWithSignal(container *container.Container, sig int) er
|
|||
|
||||
// We could unpause the container for them rather than returning this error
|
||||
if container.Paused {
|
||||
return derr.ErrorCodeUnpauseContainer.WithArgs(container.ID)
|
||||
return fmt.Errorf("Container %s is paused. Unpause the container before stopping", container.ID)
|
||||
}
|
||||
|
||||
if !container.Running {
|
||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||
return errNotRunning{container.ID}
|
||||
}
|
||||
|
||||
container.ExitOnNext()
|
||||
|
@ -62,7 +61,7 @@ func (daemon *Daemon) killWithSignal(container *container.Container, sig int) er
|
|||
}
|
||||
|
||||
if err := daemon.kill(container, sig); err != nil {
|
||||
return derr.ErrorCodeCantKill.WithArgs(container.ID, err)
|
||||
return fmt.Errorf("Cannot kill container %s: %s", container.ID, err)
|
||||
}
|
||||
|
||||
attributes := map[string]string{
|
||||
|
@ -75,7 +74,7 @@ func (daemon *Daemon) killWithSignal(container *container.Container, sig int) er
|
|||
// Kill forcefully terminates a container.
|
||||
func (daemon *Daemon) Kill(container *container.Container) error {
|
||||
if !container.IsRunning() {
|
||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||
return errNotRunning{container.ID}
|
||||
}
|
||||
|
||||
// 1. Send SIGKILL
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -10,7 +11,6 @@ import (
|
|||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
timetypes "github.com/docker/engine-api/types/time"
|
||||
|
@ -21,11 +21,11 @@ import (
|
|||
func (daemon *Daemon) ContainerLogs(containerName string, config *backend.ContainerLogsConfig, started chan struct{}) error {
|
||||
container, err := daemon.GetContainer(containerName)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
|
||||
return err
|
||||
}
|
||||
|
||||
if !(config.ShowStdout || config.ShowStderr) {
|
||||
return derr.ErrorCodeNeedStream
|
||||
return fmt.Errorf("You must choose at least one stream")
|
||||
}
|
||||
|
||||
cLog, err := daemon.getLogger(container)
|
||||
|
@ -122,7 +122,7 @@ func (daemon *Daemon) StartLogging(container *container.Container) error {
|
|||
}
|
||||
l, err := container.StartLogger(cfg)
|
||||
if err != nil {
|
||||
return derr.ErrorCodeInitLogger.WithArgs(err)
|
||||
return fmt.Errorf("Failed to initialize logging driver: %v", err)
|
||||
}
|
||||
|
||||
copier := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
volumestore "github.com/docker/docker/volume/store"
|
||||
)
|
||||
|
||||
|
@ -42,7 +42,7 @@ func (daemon *Daemon) removeMountPoints(container *container.Container, rm bool)
|
|||
}
|
||||
}
|
||||
if len(rmErrors) > 0 {
|
||||
return derr.ErrorCodeRemovingVolume.WithArgs(strings.Join(rmErrors, "\n"))
|
||||
return fmt.Errorf("Error removing volumes:\n%v", strings.Join(rmErrors, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,9 +3,10 @@ package daemon
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/engine-api/types/network"
|
||||
"github.com/docker/libnetwork"
|
||||
|
@ -191,7 +192,8 @@ func (daemon *Daemon) DeleteNetwork(networkID string) error {
|
|||
}
|
||||
|
||||
if runconfig.IsPreDefinedNetwork(nw.Name()) {
|
||||
return derr.ErrorCodeCantDeletePredefinedNetwork.WithArgs(nw.Name())
|
||||
err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name())
|
||||
return errors.NewErrorWithStatusCode(err, http.StatusForbidden)
|
||||
}
|
||||
|
||||
if err := nw.Delete(); err != nil {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
)
|
||||
|
||||
// ContainerPause pauses a container
|
||||
|
@ -27,21 +28,21 @@ func (daemon *Daemon) containerPause(container *container.Container) error {
|
|||
|
||||
// We cannot Pause the container which is not running
|
||||
if !container.Running {
|
||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||
return errNotRunning{container.ID}
|
||||
}
|
||||
|
||||
// We cannot Pause the container which is already paused
|
||||
if container.Paused {
|
||||
return derr.ErrorCodeAlreadyPaused.WithArgs(container.ID)
|
||||
return fmt.Errorf("Container %s is already paused", container.ID)
|
||||
}
|
||||
|
||||
// We cannot Pause the container which is restarting
|
||||
if container.Restarting {
|
||||
return derr.ErrorCodeContainerRestarting.WithArgs(container.ID)
|
||||
return errContainerIsRestarting(container.ID)
|
||||
}
|
||||
|
||||
if err := daemon.execDriver.Pause(container.Command); err != nil {
|
||||
return derr.ErrorCodeCantPause.WithArgs(container.ID, err)
|
||||
return fmt.Errorf("Cannot pause container %s: %s", container.ID, err)
|
||||
}
|
||||
container.Paused = true
|
||||
daemon.LogContainerEvent(container, "pause")
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/libnetwork"
|
||||
)
|
||||
|
||||
|
@ -18,7 +18,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
|||
)
|
||||
|
||||
if oldName == "" || newName == "" {
|
||||
return derr.ErrorCodeEmptyRename
|
||||
return fmt.Errorf("Neither old nor new names may be empty")
|
||||
}
|
||||
|
||||
container, err := daemon.GetContainer(oldName)
|
||||
|
@ -31,7 +31,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
|||
container.Lock()
|
||||
defer container.Unlock()
|
||||
if newName, err = daemon.reserveName(container.ID, newName); err != nil {
|
||||
return derr.ErrorCodeRenameTaken.WithArgs(err)
|
||||
return fmt.Errorf("Error when allocating new name: %v", err)
|
||||
}
|
||||
|
||||
container.Name = newName
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
derr "github.com/docker/docker/errors"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
// ContainerResize changes the size of the TTY of the process running
|
||||
// in the container with the given name to the given height and width.
|
||||
|
@ -15,7 +11,7 @@ func (daemon *Daemon) ContainerResize(name string, height, width int) error {
|
|||
}
|
||||
|
||||
if !container.IsRunning() {
|
||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||
return errNotRunning{container.ID}
|
||||
}
|
||||
|
||||
if err = container.Resize(height, width); err == nil {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
)
|
||||
|
||||
// ContainerRestart stops and starts a container. It attempts to
|
||||
|
@ -17,7 +18,7 @@ func (daemon *Daemon) ContainerRestart(name string, seconds int) error {
|
|||
return err
|
||||
}
|
||||
if err := daemon.containerRestart(container, seconds); err != nil {
|
||||
return derr.ErrorCodeCantRestart.WithArgs(name, err)
|
||||
return fmt.Errorf("Cannot restart container %s: %v", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@ package daemon
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/runconfig"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
@ -19,11 +20,12 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
|
|||
}
|
||||
|
||||
if container.IsPaused() {
|
||||
return derr.ErrorCodeStartPaused
|
||||
return fmt.Errorf("Cannot start a paused container, try unpause instead.")
|
||||
}
|
||||
|
||||
if container.IsRunning() {
|
||||
return derr.ErrorCodeAlreadyStarted
|
||||
err := fmt.Errorf("Container already started")
|
||||
return errors.NewErrorWithStatusCode(err, http.StatusNotModified)
|
||||
}
|
||||
|
||||
// Windows does not have the backwards compatibility issue here.
|
||||
|
@ -52,7 +54,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
|
|||
}
|
||||
} else {
|
||||
if hostConfig != nil {
|
||||
return derr.ErrorCodeHostConfigStart
|
||||
return fmt.Errorf("Supplying a hostconfig on start is not supported. It should be supplied on create")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +90,7 @@ func (daemon *Daemon) containerStart(container *container.Container) (err error)
|
|||
}
|
||||
|
||||
if container.RemovalInProgress || container.Dead {
|
||||
return derr.ErrorCodeContainerBeingRemoved
|
||||
return fmt.Errorf("Container is marked for removal and cannot be started.")
|
||||
}
|
||||
|
||||
// if we encounter an error during start we need to ensure that any other
|
||||
|
|
|
@ -4,6 +4,7 @@ package daemon
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -13,7 +14,6 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/pubsub"
|
||||
"github.com/opencontainers/runc/libcontainer/system"
|
||||
)
|
||||
|
@ -163,13 +163,13 @@ func (s *statsCollector) getSystemCPUUsage() (uint64, error) {
|
|||
switch parts[0] {
|
||||
case "cpu":
|
||||
if len(parts) < 8 {
|
||||
return 0, derr.ErrorCodeBadCPUFields
|
||||
return 0, fmt.Errorf("invalid number of cpu fields")
|
||||
}
|
||||
var totalClockTicks uint64
|
||||
for _, i := range parts[1:8] {
|
||||
v, err := strconv.ParseUint(i, 10, 64)
|
||||
if err != nil {
|
||||
return 0, derr.ErrorCodeBadCPUInt.WithArgs(i, err)
|
||||
return 0, fmt.Errorf("Unable to convert value %s to int: %s", i, err)
|
||||
}
|
||||
totalClockTicks += v
|
||||
}
|
||||
|
@ -177,5 +177,5 @@ func (s *statsCollector) getSystemCPUUsage() (uint64, error) {
|
|||
s.clockTicksPerSecond, nil
|
||||
}
|
||||
}
|
||||
return 0, derr.ErrorCodeBadStatFormat
|
||||
return 0, fmt.Errorf("invalid stat format. Error trying to parse the '/proc/stat' file")
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/errors"
|
||||
)
|
||||
|
||||
// ContainerStop looks for the given container and terminates it,
|
||||
|
@ -20,10 +22,11 @@ func (daemon *Daemon) ContainerStop(name string, seconds int) error {
|
|||
return err
|
||||
}
|
||||
if !container.IsRunning() {
|
||||
return derr.ErrorCodeStopped.WithArgs(name)
|
||||
err := fmt.Errorf("Container %s is already stopped", name)
|
||||
return errors.NewErrorWithStatusCode(err, http.StatusNotModified)
|
||||
}
|
||||
if err := daemon.containerStop(container, seconds); err != nil {
|
||||
return derr.ErrorCodeCantStop.WithArgs(name, err)
|
||||
return fmt.Errorf("Cannot stop container %s: %v", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
|
@ -27,11 +27,11 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*types.Container
|
|||
}
|
||||
|
||||
if !container.IsRunning() {
|
||||
return nil, derr.ErrorCodeNotRunning.WithArgs(name)
|
||||
return nil, errNotRunning{container.ID}
|
||||
}
|
||||
|
||||
if container.IsRestarting() {
|
||||
return nil, derr.ErrorCodeContainerRestarting.WithArgs(name)
|
||||
return nil, errContainerIsRestarting(container.ID)
|
||||
}
|
||||
pids, err := daemon.ExecutionDriver().GetPidsForContainer(container.ID)
|
||||
if err != nil {
|
||||
|
@ -40,7 +40,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, derr.ErrorCodePSError.WithArgs(err)
|
||||
return nil, fmt.Errorf("Error running ps: %v", err)
|
||||
}
|
||||
|
||||
procList := &types.ContainerProcessList{}
|
||||
|
@ -55,7 +55,7 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*types.Container
|
|||
}
|
||||
}
|
||||
if pidIndex == -1 {
|
||||
return nil, derr.ErrorCodeNoPID
|
||||
return nil, fmt.Errorf("Couldn't find PID field in ps output")
|
||||
}
|
||||
|
||||
// loop through the output and extract the PID from each line
|
||||
|
@ -66,7 +66,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, derr.ErrorCodeBadPID.WithArgs(fields[pidIndex], err)
|
||||
return nil, fmt.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
derr "github.com/docker/docker/errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/engine-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, derr.ErrorCodeNoTop
|
||||
return nil, fmt.Errorf("Top is not supported on Windows")
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
derr "github.com/docker/docker/errors"
|
||||
)
|
||||
|
||||
// ContainerUnpause unpauses a container
|
||||
|
@ -26,16 +27,16 @@ func (daemon *Daemon) containerUnpause(container *container.Container) error {
|
|||
|
||||
// We cannot unpause the container which is not running
|
||||
if !container.Running {
|
||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||
return errNotRunning{container.ID}
|
||||
}
|
||||
|
||||
// We cannot unpause the container which is not paused
|
||||
if !container.Paused {
|
||||
return derr.ErrorCodeNotPaused.WithArgs(container.ID)
|
||||
return fmt.Errorf("Container %s is not paused", container.ID)
|
||||
}
|
||||
|
||||
if err := daemon.execDriver.Unpause(container.Command); err != nil {
|
||||
return derr.ErrorCodeCantUnpause.WithArgs(container.ID, err)
|
||||
return fmt.Errorf("Cannot unpause container %s: %s", container.ID, err)
|
||||
}
|
||||
|
||||
container.Paused = false
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
||||
|
@ -57,18 +56,16 @@ func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) erro
|
|||
}()
|
||||
|
||||
if container.RemovalInProgress || container.Dead {
|
||||
errMsg := fmt.Errorf("Container is marked for removal and cannot be \"update\".")
|
||||
return derr.ErrorCodeCantUpdate.WithArgs(container.ID, errMsg)
|
||||
return errCannotUpdate(container.ID, fmt.Errorf("Container is marked for removal and cannot be \"update\"."))
|
||||
}
|
||||
|
||||
if container.IsRunning() && hostConfig.KernelMemory != 0 {
|
||||
errMsg := fmt.Errorf("Can not update kernel memory to a running container, please stop it first.")
|
||||
return derr.ErrorCodeCantUpdate.WithArgs(container.ID, errMsg)
|
||||
return errCannotUpdate(container.ID, fmt.Errorf("Can not update kernel memory to a running container, please stop it first."))
|
||||
}
|
||||
|
||||
if err := container.UpdateContainer(hostConfig); err != nil {
|
||||
restoreConfig = true
|
||||
return derr.ErrorCodeCantUpdate.WithArgs(container.ID, err.Error())
|
||||
return errCannotUpdate(container.ID, err)
|
||||
}
|
||||
|
||||
// if Restart Policy changed, we need to update container monitor
|
||||
|
@ -86,7 +83,7 @@ func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) erro
|
|||
if container.IsRunning() && !container.IsRestarting() {
|
||||
if err := daemon.execDriver.Update(container.Command); err != nil {
|
||||
restoreConfig = true
|
||||
return derr.ErrorCodeCantUpdate.WithArgs(container.ID, err.Error())
|
||||
return errCannotUpdate(container.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,3 +91,7 @@ func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) erro
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func errCannotUpdate(containerID string, err error) error {
|
||||
return fmt.Errorf("Cannot update container %s: %v", containerID, err)
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ package daemon
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/docker/engine-api/types"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
|
@ -114,7 +114,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
|
|||
}
|
||||
|
||||
if binds[bind.Destination] {
|
||||
return derr.ErrorCodeMountDup.WithArgs(bind.Destination)
|
||||
return fmt.Errorf("Duplicate mount point '%s'", bind.Destination)
|
||||
}
|
||||
|
||||
if len(bind.Name) > 0 {
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/volume"
|
||||
)
|
||||
|
||||
|
@ -27,7 +27,7 @@ func (daemon *Daemon) setupMounts(container *container.Container) ([]execdriver.
|
|||
s = mount.Volume.Path()
|
||||
}
|
||||
if s == "" {
|
||||
return nil, derr.ErrorCodeVolumeNoSourceForMount.WithArgs(mount.Name, mount.Driver, mount.Destination)
|
||||
return nil, fmt.Errorf("No source for mount name '%s' driver %q destination '%s'", mount.Name, mount.Driver, mount.Destination)
|
||||
}
|
||||
mnts = append(mnts, execdriver.Mount{
|
||||
Source: s,
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/uuid"
|
||||
apiserver "github.com/docker/docker/api/server"
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/api/server/router/build"
|
||||
"github.com/docker/docker/api/server/router/container"
|
||||
"github.com/docker/docker/api/server/router/image"
|
||||
|
@ -396,11 +397,16 @@ func loadDaemonCliConfig(config *daemon.Config, daemonFlags *flag.FlagSet, commo
|
|||
}
|
||||
|
||||
func initRouter(s *apiserver.Server, d *daemon.Daemon) {
|
||||
s.InitRouter(utils.IsDebugEnabled(),
|
||||
routers := []router.Router{
|
||||
container.NewRouter(d),
|
||||
image.NewRouter(d),
|
||||
network.NewRouter(d),
|
||||
systemrouter.NewRouter(d),
|
||||
volume.NewRouter(d),
|
||||
build.NewRouter(dockerfile.NewBuildManager(d)))
|
||||
build.NewRouter(dockerfile.NewBuildManager(d)),
|
||||
}
|
||||
if d.NetworkControllerEnabled() {
|
||||
routers = append(routers, network.NewRouter(d))
|
||||
}
|
||||
|
||||
s.InitRouter(utils.IsDebugEnabled(), routers...)
|
||||
}
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
Docker 'errors' package
|
||||
=======================
|
||||
|
||||
This package contains all of the error messages generated by the Docker
|
||||
engine that might be exposed via the Docker engine's REST API.
|
||||
|
||||
Each top-level engine package will have its own file in this directory
|
||||
so that there's a clear grouping of errors, instead of just one big
|
||||
file. The errors for each package are defined here instead of within
|
||||
their respective package structure so that Docker CLI code that may need
|
||||
to import these error definition files will not need to know or understand
|
||||
the engine's package/directory structure. In other words, all they should
|
||||
need to do is import `.../docker/errors` and they will automatically
|
||||
pick up all Docker engine defined errors. This also gives the engine
|
||||
developers the freedom to change the engine packaging structure (e.g. to
|
||||
CRUD packages) without worrying about breaking existing clients.
|
||||
|
||||
These errors are defined using the 'errcode' package. The `errcode` package
|
||||
allows for each error to be typed and include all information necessary to
|
||||
have further processing done on them if necessary. In particular, each error
|
||||
includes:
|
||||
|
||||
* Value - a unique string (in all caps) associated with this error.
|
||||
Typically, this string is the same name as the variable name of the error
|
||||
(w/o the `ErrorCode` text) but in all caps.
|
||||
|
||||
* Message - the human readable sentence that will be displayed for this
|
||||
error. It can contain '%s' substitutions that allows for the code generating
|
||||
the error to specify values that will be inserted in the string prior to
|
||||
being displayed to the end-user. The `WithArgs()` function can be used to
|
||||
specify the insertion strings. Note, the evaluation of the strings will be
|
||||
done at the time `WithArgs()` is called.
|
||||
|
||||
* Description - additional human readable text to further explain the
|
||||
circumstances of the error situation.
|
||||
|
||||
* HTTPStatusCode - when the error is returned back to a CLI, this value
|
||||
will be used to populate the HTTP status code. If not present the default
|
||||
value will be `StatusInternalServerError`, 500.
|
||||
|
||||
Not all errors generated within the engine's executable will be propagated
|
||||
back to the engine's API layer. For example, it is expected that errors
|
||||
generated by vendored code (under `docker/vendor`) and packaged code
|
||||
(under `docker/pkg`) will be converted into errors defined by this package.
|
||||
|
||||
When processing an errcode error, if you are looking for a particular
|
||||
error then you can do something like:
|
||||
|
||||
```
|
||||
import derr "github.com/docker/docker/errors"
|
||||
|
||||
...
|
||||
|
||||
err := someFunc()
|
||||
if err.ErrorCode() == derr.ErrorCodeNoSuchContainer {
|
||||
...
|
||||
}
|
||||
```
|
|
@ -1,93 +0,0 @@
|
|||
package errors
|
||||
|
||||
// This file contains all of the errors that can be generated from the
|
||||
// docker/builder component.
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrorCodeAtLeastOneArg is generated when the parser comes across a
|
||||
// Dockerfile command that doesn't have any args.
|
||||
ErrorCodeAtLeastOneArg = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "ATLEASTONEARG",
|
||||
Message: "%s requires at least one argument",
|
||||
Description: "The specified command requires at least one argument",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
|
||||
// ErrorCodeExactlyOneArg is generated when the parser comes across a
|
||||
// Dockerfile command that requires exactly one arg but got less/more.
|
||||
ErrorCodeExactlyOneArg = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "EXACTLYONEARG",
|
||||
Message: "%s requires exactly one argument",
|
||||
Description: "The specified command requires exactly one argument",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
|
||||
// ErrorCodeAtLeastTwoArgs is generated when the parser comes across a
|
||||
// Dockerfile command that requires at least two args but got less.
|
||||
ErrorCodeAtLeastTwoArgs = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "ATLEASTTWOARGS",
|
||||
Message: "%s requires at least two arguments",
|
||||
Description: "The specified command requires at least two arguments",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
|
||||
// ErrorCodeTooManyArgs is generated when the parser comes across a
|
||||
// Dockerfile command that has more args than it should
|
||||
ErrorCodeTooManyArgs = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "TOOMANYARGS",
|
||||
Message: "Bad input to %s, too many args",
|
||||
Description: "The specified command was passed too many arguments",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
|
||||
// ErrorCodeChainOnBuild is generated when the parser comes across a
|
||||
// Dockerfile command that is trying to chain ONBUILD commands.
|
||||
ErrorCodeChainOnBuild = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "CHAINONBUILD",
|
||||
Message: "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed",
|
||||
Description: "ONBUILD Dockerfile commands aren't allow on ONBUILD commands",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
|
||||
// ErrorCodeBadOnBuildCmd is generated when the parser comes across a
|
||||
// an ONBUILD Dockerfile command with an invalid trigger/command.
|
||||
ErrorCodeBadOnBuildCmd = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "BADONBUILDCMD",
|
||||
Message: "%s isn't allowed as an ONBUILD trigger",
|
||||
Description: "The specified ONBUILD command isn't allowed",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
|
||||
// ErrorCodeMissingFrom is generated when the Dockerfile is missing
|
||||
// a FROM command.
|
||||
ErrorCodeMissingFrom = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "MISSINGFROM",
|
||||
Message: "Please provide a source image with `from` prior to run",
|
||||
Description: "The Dockerfile is missing a FROM command",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
|
||||
// ErrorCodeNotOnWindows is generated when the specified Dockerfile
|
||||
// command is not supported on Windows.
|
||||
ErrorCodeNotOnWindows = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "NOTONWINDOWS",
|
||||
Message: "%s is not supported on Windows",
|
||||
Description: "The specified Dockerfile command is not supported on Windows",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
|
||||
// ErrorCodeVolumeEmpty is generated when the specified Volume string
|
||||
// is empty.
|
||||
ErrorCodeVolumeEmpty = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "VOLUMEEMPTY",
|
||||
Message: "Volume specified can not be an empty string",
|
||||
Description: "The specified volume can not be an empty string",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
)
|
1013
errors/daemon.go
1013
errors/daemon.go
File diff suppressed because it is too large
Load diff
|
@ -1,6 +0,0 @@
|
|||
package errors
|
||||
|
||||
// This file contains all of the errors that can be generated from the
|
||||
// docker engine but are not tied to any specific top-level component.
|
||||
|
||||
const errGroup = "engine"
|
41
errors/errors.go
Normal file
41
errors/errors.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package errors
|
||||
|
||||
import "net/http"
|
||||
|
||||
// apiError is an error wrapper that also
|
||||
// holds information about response status codes.
|
||||
type apiError struct {
|
||||
error
|
||||
statusCode int
|
||||
}
|
||||
|
||||
// HTTPErrorStatusCode returns a status code.
|
||||
func (e apiError) HTTPErrorStatusCode() int {
|
||||
return e.statusCode
|
||||
}
|
||||
|
||||
// NewErrorWithStatusCode allows you to associate
|
||||
// a specific HTTP Status Code to an error.
|
||||
// The Server will take that code and set
|
||||
// it as the response status.
|
||||
func NewErrorWithStatusCode(err error, code int) error {
|
||||
return apiError{err, code}
|
||||
}
|
||||
|
||||
// NewBadRequestError creates a new API error
|
||||
// that has the 400 HTTP status code associated to it.
|
||||
func NewBadRequestError(err error) error {
|
||||
return NewErrorWithStatusCode(err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// NewRequestNotFoundError creates a new API error
|
||||
// that has the 404 HTTP status code associated to it.
|
||||
func NewRequestNotFoundError(err error) error {
|
||||
return NewErrorWithStatusCode(err, http.StatusNotFound)
|
||||
}
|
||||
|
||||
// NewRequestConflictError creates a new API error
|
||||
// that has the 409 HTTP status code associated to it.
|
||||
func NewRequestConflictError(err error) error {
|
||||
return NewErrorWithStatusCode(err, http.StatusConflict)
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package errors
|
||||
|
||||
// This file contains all of the errors that can be generated from the
|
||||
// docker/image component.
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrorCodeInvalidImageID is generated when image id specified is incorrectly formatted.
|
||||
ErrorCodeInvalidImageID = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "INVALIDIMAGEID",
|
||||
Message: "image ID '%s' is invalid ",
|
||||
Description: "The specified image id is incorrectly formatted",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
)
|
|
@ -1,45 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrorCodeNewerClientVersion is generated when a request from a client
|
||||
// specifies a higher version than the server supports.
|
||||
ErrorCodeNewerClientVersion = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "NEWERCLIENTVERSION",
|
||||
Message: "client is newer than server (client API version: %s, server API version: %s)",
|
||||
Description: "The client version is higher than the server version",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
})
|
||||
|
||||
// ErrorCodeOldClientVersion is generated when a request from a client
|
||||
// specifies a version lower than the minimum version supported by the server.
|
||||
ErrorCodeOldClientVersion = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "OLDCLIENTVERSION",
|
||||
Message: "client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version",
|
||||
Description: "The client version is too old for the server",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
})
|
||||
|
||||
// ErrorNetworkControllerNotEnabled is generated when the networking stack in not enabled
|
||||
// for certain platforms, like windows.
|
||||
ErrorNetworkControllerNotEnabled = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "NETWORK_CONTROLLER_NOT_ENABLED",
|
||||
Message: "the network controller is not enabled for this platform",
|
||||
Description: "Docker's networking stack is disabled for this platform",
|
||||
HTTPStatusCode: http.StatusNotFound,
|
||||
})
|
||||
|
||||
// ErrorCodeNoHijackConnection is generated when a request tries to attach to a container
|
||||
// but the connection to hijack is not provided.
|
||||
ErrorCodeNoHijackConnection = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "HIJACK_CONNECTION_MISSING",
|
||||
Message: "error attaching to container %s, hijack connection missing",
|
||||
Description: "The caller didn't provide a connection to hijack",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
})
|
||||
)
|
|
@ -643,7 +643,7 @@ func (s *DockerSuite) TestContainerApiCreateMultipleNetworksConfig(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(status, checker.Equals, http.StatusBadRequest)
|
||||
// network name order in error message is not deterministic
|
||||
c.Assert(string(b), checker.Contains, "Container cannot be connected to [")
|
||||
c.Assert(string(b), checker.Contains, "Container cannot be connected to network endpoints")
|
||||
c.Assert(string(b), checker.Contains, "net1")
|
||||
c.Assert(string(b), checker.Contains, "net2")
|
||||
c.Assert(string(b), checker.Contains, "net3")
|
||||
|
|
|
@ -463,7 +463,7 @@ func (s *DockerSuite) TestRunVolumesFromInReadWriteMode(c *check.C) {
|
|||
dockerCmd(c, "run", "--name", "parent", "-v", volumeDir, "busybox", "true")
|
||||
dockerCmd(c, "run", "--volumes-from", "parent:rw", "busybox", "touch", fileInVol)
|
||||
|
||||
if out, _, err := dockerCmdWithError("run", "--volumes-from", "parent:bar", "busybox", "touch", fileInVol); err == nil || !strings.Contains(out, `invalid mode: "bar"`) {
|
||||
if out, _, err := dockerCmdWithError("run", "--volumes-from", "parent:bar", "busybox", "touch", fileInVol); err == nil || !strings.Contains(out, `invalid mode: bar`) {
|
||||
c.Fatalf("running --volumes-from parent:bar should have failed with invalid mode: %q", out)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
)
|
||||
|
@ -86,22 +85,3 @@ func ReplaceOrAppendEnvValues(defaults, overrides []string) []string {
|
|||
|
||||
return defaults
|
||||
}
|
||||
|
||||
// GetErrorMessage returns the human readable message associated with
|
||||
// the passed-in error. In some cases the default Error() func returns
|
||||
// something that is less than useful so based on its types this func
|
||||
// will go and get a better piece of text.
|
||||
func GetErrorMessage(err error) string {
|
||||
switch err.(type) {
|
||||
case errcode.Error:
|
||||
e, _ := err.(errcode.Error)
|
||||
return e.Message
|
||||
|
||||
case errcode.ErrorCode:
|
||||
ec, _ := err.(errcode.ErrorCode)
|
||||
return ec.Message()
|
||||
|
||||
default:
|
||||
return err.Error()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
package local
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/docker/volume"
|
||||
|
@ -27,13 +25,21 @@ const (
|
|||
|
||||
var (
|
||||
// ErrNotFound is the typed error returned when the requested volume name can't be found
|
||||
ErrNotFound = errors.New("volume not found")
|
||||
ErrNotFound = fmt.Errorf("volume not found")
|
||||
// volumeNameRegex ensures the name assigned for the volume is valid.
|
||||
// This name is used to create the bind directory, so we need to avoid characters that
|
||||
// would make the path to escape the root directory.
|
||||
volumeNameRegex = utils.RestrictedVolumeNamePattern
|
||||
)
|
||||
|
||||
type validationError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (validationError) IsValidationError() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// New instantiates a new Root instance with the provided scope. Scope
|
||||
// is the base path that the Root instance uses to store its
|
||||
// volumes. The base path is created here if it does not exist.
|
||||
|
@ -142,7 +148,7 @@ func (r *Root) Remove(v volume.Volume) error {
|
|||
|
||||
lv, ok := v.(*localVolume)
|
||||
if !ok {
|
||||
return errors.New("unknown volume type")
|
||||
return fmt.Errorf("unknown volume type")
|
||||
}
|
||||
|
||||
realPath, err := filepath.EvalSymlinks(lv.path)
|
||||
|
@ -188,7 +194,7 @@ func (r *Root) Get(name string) (volume.Volume, error) {
|
|||
|
||||
func (r *Root) validateName(name string) error {
|
||||
if !volumeNameRegex.MatchString(name) {
|
||||
return derr.ErrorCodeVolumeName.WithArgs(name, utils.RestrictedNameChars)
|
||||
return validationError{fmt.Errorf("%q includes invalid characters for a local volume name, only %q are allowed", name, utils.RestrictedNameChars)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package volume
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
|
@ -82,7 +82,7 @@ func (m *MountPoint) Setup() (string, error) {
|
|||
}
|
||||
return m.Source, nil
|
||||
}
|
||||
return "", derr.ErrorCodeMountSetup
|
||||
return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
|
||||
}
|
||||
|
||||
// Path returns the path of a volume in a mount point.
|
||||
|
@ -96,7 +96,7 @@ func (m *MountPoint) Path() string {
|
|||
// ParseVolumesFrom ensure that the supplied volumes-from is valid.
|
||||
func ParseVolumesFrom(spec string) (string, string, error) {
|
||||
if len(spec) == 0 {
|
||||
return "", "", derr.ErrorCodeVolumeFromBlank.WithArgs(spec)
|
||||
return "", "", fmt.Errorf("malformed volumes-from specification: %s", spec)
|
||||
}
|
||||
|
||||
specParts := strings.SplitN(spec, ":", 2)
|
||||
|
@ -106,15 +106,23 @@ func ParseVolumesFrom(spec string) (string, string, error) {
|
|||
if len(specParts) == 2 {
|
||||
mode = specParts[1]
|
||||
if !ValidMountMode(mode) {
|
||||
return "", "", derr.ErrorCodeVolumeInvalidMode.WithArgs(mode)
|
||||
return "", "", errInvalidMode(mode)
|
||||
}
|
||||
// For now don't allow propagation properties while importing
|
||||
// volumes from data container. These volumes will inherit
|
||||
// the same propagation property as of the original volume
|
||||
// in data container. This probably can be relaxed in future.
|
||||
if HasPropagation(mode) {
|
||||
return "", "", derr.ErrorCodeVolumeInvalidMode.WithArgs(mode)
|
||||
return "", "", errInvalidMode(mode)
|
||||
}
|
||||
}
|
||||
return id, mode, nil
|
||||
}
|
||||
|
||||
func errInvalidMode(mode string) error {
|
||||
return fmt.Errorf("invalid mode: %v", mode)
|
||||
}
|
||||
|
||||
func errInvalidSpec(spec string) error {
|
||||
return fmt.Errorf("Invalid volume specification: '%s'", spec)
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ func TestParseMountSpecPropagation(t *testing.T) {
|
|||
"/hostPath:/containerPath:ro,Z,rprivate",
|
||||
}
|
||||
invalid = map[string]string{
|
||||
"/path:/path:ro,rshared,rslave": `invalid mode: "ro,rshared,rslave"`,
|
||||
"/path:/path:ro,z,rshared,rslave": `invalid mode: "ro,z,rshared,rslave"`,
|
||||
"/path:/path:ro,rshared,rslave": `invalid mode: ro,rshared,rslave`,
|
||||
"/path:/path:ro,z,rshared,rslave": `invalid mode: ro,z,rshared,rslave`,
|
||||
"/path:shared": "Invalid volume specification",
|
||||
"/path:slave": "Invalid volume specification",
|
||||
"/path:private": "Invalid volume specification",
|
||||
|
|
|
@ -111,8 +111,8 @@ func TestParseMountSpec(t *testing.T) {
|
|||
"/path:ro": "Invalid volume specification",
|
||||
"/rw:rw": "Invalid volume specification",
|
||||
"path:ro": "Invalid volume specification",
|
||||
"/path:/path:sw": `invalid mode: "sw"`,
|
||||
"/path:/path:rwz": `invalid mode: "rwz"`,
|
||||
"/path:/path:sw": `invalid mode: sw`,
|
||||
"/path:/path:rwz": `invalid mode: rwz`,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,6 @@ import (
|
|||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
derr "github.com/docker/docker/errors"
|
||||
)
|
||||
|
||||
// read-write modes
|
||||
|
@ -47,12 +45,12 @@ func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
|
|||
Propagation: DefaultPropagationMode,
|
||||
}
|
||||
if strings.Count(spec, ":") > 2 {
|
||||
return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
|
||||
return nil, errInvalidSpec(spec)
|
||||
}
|
||||
|
||||
arr := strings.SplitN(spec, ":", 3)
|
||||
if arr[0] == "" {
|
||||
return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
|
||||
return nil, errInvalidSpec(spec)
|
||||
}
|
||||
|
||||
switch len(arr) {
|
||||
|
@ -63,7 +61,7 @@ func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
|
|||
if isValid := ValidMountMode(arr[1]); isValid {
|
||||
// Destination + Mode is not a valid volume - volumes
|
||||
// cannot include a mode. eg /foo:rw
|
||||
return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
|
||||
return nil, errInvalidSpec(spec)
|
||||
}
|
||||
// Host Source Path or Name + Destination
|
||||
mp.Source = arr[0]
|
||||
|
@ -74,23 +72,23 @@ func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
|
|||
mp.Destination = arr[1]
|
||||
mp.Mode = arr[2] // Mode field is used by SELinux to decide whether to apply label
|
||||
if !ValidMountMode(mp.Mode) {
|
||||
return nil, derr.ErrorCodeVolumeInvalidMode.WithArgs(mp.Mode)
|
||||
return nil, errInvalidMode(mp.Mode)
|
||||
}
|
||||
mp.RW = ReadWrite(mp.Mode)
|
||||
mp.Propagation = GetPropagation(mp.Mode)
|
||||
default:
|
||||
return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
|
||||
return nil, errInvalidSpec(spec)
|
||||
}
|
||||
|
||||
//validate the volumes destination path
|
||||
mp.Destination = filepath.Clean(mp.Destination)
|
||||
if !filepath.IsAbs(mp.Destination) {
|
||||
return nil, derr.ErrorCodeVolumeAbs.WithArgs(mp.Destination)
|
||||
return nil, fmt.Errorf("Invalid volume destination path: '%s' mount path must be absolute.", mp.Destination)
|
||||
}
|
||||
|
||||
// Destination cannot be "/"
|
||||
if mp.Destination == "/" {
|
||||
return nil, derr.ErrorCodeVolumeSlash.WithArgs(spec)
|
||||
return nil, fmt.Errorf("Invalid specification: destination can't be '/' in '%s'", spec)
|
||||
}
|
||||
|
||||
name, source := ParseVolumeSource(mp.Source)
|
||||
|
@ -106,7 +104,7 @@ func ParseMountSpec(spec, volumeDriver string) (*MountPoint, error) {
|
|||
// cleanup becomes an issue if container does not unmount
|
||||
// submounts explicitly.
|
||||
if HasPropagation(mp.Mode) {
|
||||
return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
|
||||
return nil, errInvalidSpec(spec)
|
||||
}
|
||||
} else {
|
||||
mp.Source = filepath.Clean(source)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package volume
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
derr "github.com/docker/docker/errors"
|
||||
)
|
||||
|
||||
// read-write modes
|
||||
|
@ -96,7 +96,7 @@ func ParseMountSpec(spec string, volumeDriver string) (*MountPoint, error) {
|
|||
|
||||
// Must have something back
|
||||
if len(match) == 0 {
|
||||
return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
|
||||
return nil, errInvalidSpec(spec)
|
||||
}
|
||||
|
||||
// Pull out the sub expressions from the named capture groups
|
||||
|
@ -116,7 +116,7 @@ func ParseMountSpec(spec string, volumeDriver string) (*MountPoint, error) {
|
|||
|
||||
// Volumes cannot include an explicitly supplied mode eg c:\path:rw
|
||||
if mp.Source == "" && mp.Destination != "" && matchgroups["mode"] != "" {
|
||||
return nil, derr.ErrorCodeVolumeInvalid.WithArgs(spec)
|
||||
return nil, errInvalidSpec(spec)
|
||||
}
|
||||
|
||||
// Note: No need to check if destination is absolute as it must be by
|
||||
|
@ -125,14 +125,14 @@ func ParseMountSpec(spec string, volumeDriver string) (*MountPoint, error) {
|
|||
if filepath.VolumeName(mp.Destination) == mp.Destination {
|
||||
// Ensure the destination path, if a drive letter, is not the c drive
|
||||
if strings.ToLower(mp.Destination) == "c:" {
|
||||
return nil, derr.ErrorCodeVolumeDestIsC.WithArgs(spec)
|
||||
return nil, fmt.Errorf("Destination drive letter in '%s' cannot be c:", spec)
|
||||
}
|
||||
} else {
|
||||
// So we know the destination is a path, not drive letter. Clean it up.
|
||||
mp.Destination = filepath.Clean(mp.Destination)
|
||||
// Ensure the destination path, if a path, is not the c root directory
|
||||
if strings.ToLower(mp.Destination) == `c:\` {
|
||||
return nil, derr.ErrorCodeVolumeDestIsCRoot.WithArgs(spec)
|
||||
return nil, fmt.Errorf(`Destination path in '%s' cannot be c:\`, spec)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,10 +163,10 @@ func ParseMountSpec(spec string, volumeDriver string) (*MountPoint, error) {
|
|||
var fi os.FileInfo
|
||||
var err error
|
||||
if fi, err = os.Stat(mp.Source); err != nil {
|
||||
return nil, derr.ErrorCodeVolumeSourceNotFound.WithArgs(mp.Source, err)
|
||||
return nil, fmt.Errorf("Source directory '%s' could not be found: %s", mp.Source, err)
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
return nil, derr.ErrorCodeVolumeSourceNotDirectory.WithArgs(mp.Source)
|
||||
return nil, fmt.Errorf("Source '%s' is not a directory", mp.Source)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ func IsVolumeNameValid(name string) (bool, error) {
|
|||
}
|
||||
nameExp = regexp.MustCompile(`^` + RXReservedNames + `$`)
|
||||
if nameExp.MatchString(name) {
|
||||
return false, derr.ErrorCodeVolumeNameReservedWord.WithArgs(name)
|
||||
return false, fmt.Errorf("Volume name %q cannot be a reserved word for Windows filenames", name)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue