diff --git a/components/engine/api/client/create.go b/components/engine/api/client/create.go index 172a87bf16..25189fa14a 100644 --- a/components/engine/api/client/create.go +++ b/components/engine/api/client/create.go @@ -5,9 +5,7 @@ import ( "encoding/json" "fmt" "io" - "net/url" "os" - "strings" "github.com/docker/distribution/reference" "github.com/docker/docker/api/client/lib" @@ -88,11 +86,6 @@ func newCIDFile(path string) (*cidFile, error) { } func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) { - containerValues := url.Values{} - if name != "" { - containerValues.Set("name", name) - } - mergedConfig := runconfig.MergeConfigs(config, hostConfig) var containerIDFile *cidFile @@ -133,34 +126,32 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc } //create the container - serverResp, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil) + response, err := cli.client.ContainerCreate(mergedConfig, name) //if image not found try to pull it - if serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) { - fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String()) + if err != nil { + if lib.IsErrImageNotFound(err) { + fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String()) - // we don't want to write to stdout anything apart from container.ID - if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { - return nil, err - } - if trustedRef != nil && !isDigested { - if err := cli.tagTrusted(trustedRef, ref.(reference.NamedTagged)); err != nil { + // we don't want to write to stdout anything apart from container.ID + if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { return nil, err } - } - // Retry - if serverResp, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil); err != nil { + if trustedRef != nil && !isDigested { + if err := cli.tagTrusted(trustedRef, ref.(reference.NamedTagged)); err != nil { + return nil, err + } + } + // Retry + var retryErr error + response, retryErr = cli.client.ContainerCreate(mergedConfig, name) + if retryErr != nil { + return nil, retryErr + } + } else { return nil, err } - } else if err != nil { - return nil, err } - defer serverResp.body.Close() - - var response types.ContainerCreateResponse - if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil { - return nil, err - } for _, warning := range response.Warnings { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } diff --git a/components/engine/api/client/lib/container_create.go b/components/engine/api/client/lib/container_create.go new file mode 100644 index 0000000000..906307f095 --- /dev/null +++ b/components/engine/api/client/lib/container_create.go @@ -0,0 +1,43 @@ +package lib + +import ( + "encoding/json" + "net/url" + "strings" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/runconfig" +) + +// ContainerCreate creates a new container based in the given configuration. +// It can be associated with a name, but it's not mandatory. +func (cli *Client) ContainerCreate(config *runconfig.ContainerConfigWrapper, containerName string) (types.ContainerCreateResponse, error) { + var ( + query url.Values + response types.ContainerCreateResponse + ) + if containerName != "" { + query.Set("name", containerName) + } + + serverResp, err := cli.POST("/containers/create", query, config, nil) + if err != nil { + if serverResp != nil && serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) { + return response, imageNotFoundError{config.Image} + } + return response, err + } + + if serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) { + } + + if err != nil { + return response, err + } + + if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil { + return response, err + } + + return response, nil +} diff --git a/components/engine/api/client/lib/errors.go b/components/engine/api/client/lib/errors.go new file mode 100644 index 0000000000..21a41f03f4 --- /dev/null +++ b/components/engine/api/client/lib/errors.go @@ -0,0 +1,31 @@ +package lib + +import "fmt" + +// imageNotFoundError implements an error returned when an image is not in the docker host. +type imageNotFoundError struct { + imageID string +} + +// Error returns a string representation of an imageNotFoundError +func (i imageNotFoundError) Error() string { + return fmt.Sprintf("Image not found: %s", i.imageID) +} + +// ImageNotFound returns the ID of the image not found on the docker host. +func (i imageNotFoundError) ImageIDNotFound() string { + return i.imageID +} + +// ImageNotFound is an interface that describes errors caused +// when an image is not found in the docker host. +type ImageNotFound interface { + ImageIDNotFound() string +} + +// IsImageNotFound returns true when the error is caused +// when an image is not found in the docker host. +func IsErrImageNotFound(err error) bool { + _, ok := err.(ImageNotFound) + return ok +}