
This is similar to the version in the registry package, but uses the daemon's default index (as opposed to the default for the client's platform) if using the "official index". Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
177 lines
4.6 KiB
Go
177 lines
4.6 KiB
Go
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
Cli "github.com/docker/docker/cli"
|
|
"github.com/docker/docker/pkg/jsonmessage"
|
|
"github.com/docker/docker/reference"
|
|
"github.com/docker/docker/registry"
|
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
"github.com/docker/engine-api/client"
|
|
"github.com/docker/engine-api/types"
|
|
"github.com/docker/engine-api/types/container"
|
|
networktypes "github.com/docker/engine-api/types/network"
|
|
)
|
|
|
|
func (cli *DockerCli) pullImage(image string) error {
|
|
return cli.pullImageCustomOut(image, cli.out)
|
|
}
|
|
|
|
func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
|
|
ref, err := reference.ParseNamed(image)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var tag string
|
|
switch x := reference.WithDefaultTag(ref).(type) {
|
|
case reference.Canonical:
|
|
tag = x.Digest().String()
|
|
case reference.NamedTagged:
|
|
tag = x.Tag()
|
|
}
|
|
|
|
// Resolve the Repository name from fqn to RepositoryInfo
|
|
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
authConfig := cli.resolveAuthConfig(cli.configFile.AuthConfigs, repoInfo.Index)
|
|
encodedAuth, err := encodeAuthToBase64(authConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
options := types.ImageCreateOptions{
|
|
Parent: ref.Name(),
|
|
Tag: tag,
|
|
RegistryAuth: encodedAuth,
|
|
}
|
|
|
|
responseBody, err := cli.client.ImageCreate(options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer responseBody.Close()
|
|
|
|
return jsonmessage.DisplayJSONMessagesStream(responseBody, out, cli.outFd, cli.isTerminalOut, nil)
|
|
}
|
|
|
|
type cidFile struct {
|
|
path string
|
|
file *os.File
|
|
written bool
|
|
}
|
|
|
|
func newCIDFile(path string) (*cidFile, error) {
|
|
if _, err := os.Stat(path); err == nil {
|
|
return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
|
}
|
|
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
|
|
}
|
|
|
|
return &cidFile{path: path, file: f}, nil
|
|
}
|
|
|
|
func (cli *DockerCli) createContainer(config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
|
var containerIDFile *cidFile
|
|
if cidfile != "" {
|
|
var err error
|
|
if containerIDFile, err = newCIDFile(cidfile); err != nil {
|
|
return nil, err
|
|
}
|
|
defer containerIDFile.Close()
|
|
}
|
|
|
|
ref, err := reference.ParseNamed(config.Image)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ref = reference.WithDefaultTag(ref)
|
|
|
|
var trustedRef reference.Canonical
|
|
|
|
if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() {
|
|
var err error
|
|
trustedRef, err = cli.trustedReference(ref)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config.Image = trustedRef.String()
|
|
}
|
|
|
|
//create the container
|
|
response, err := cli.client.ContainerCreate(config, hostConfig, networkingConfig, name)
|
|
|
|
//if image not found try to pull it
|
|
if err != nil {
|
|
if client.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 ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
|
|
if err := cli.tagTrusted(trustedRef, ref); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
// Retry
|
|
var retryErr error
|
|
response, retryErr = cli.client.ContainerCreate(config, hostConfig, networkingConfig, name)
|
|
if retryErr != nil {
|
|
return nil, retryErr
|
|
}
|
|
} else {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
for _, warning := range response.Warnings {
|
|
fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
|
|
}
|
|
if containerIDFile != nil {
|
|
if err = containerIDFile.Write(response.ID); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return &response, nil
|
|
}
|
|
|
|
// CmdCreate creates a new container from a given image.
|
|
//
|
|
// Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
|
|
func (cli *DockerCli) CmdCreate(args ...string) error {
|
|
cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, Cli.DockerCommands["create"].Description, true)
|
|
addTrustedFlags(cmd, true)
|
|
|
|
// These are flags not stored in Config/HostConfig
|
|
var (
|
|
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
|
)
|
|
|
|
config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
|
|
|
if err != nil {
|
|
cmd.ReportError(err.Error(), true)
|
|
os.Exit(1)
|
|
}
|
|
if config.Image == "" {
|
|
cmd.Usage()
|
|
return nil
|
|
}
|
|
response, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintf(cli.out, "%s\n", response.ID)
|
|
return nil
|
|
}
|