github.com/circular-dark/docker@v1.7.0/api/client/create.go (about)

     1  package client
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/url"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/graph/tags"
    14  	"github.com/docker/docker/pkg/parsers"
    15  	"github.com/docker/docker/registry"
    16  	"github.com/docker/docker/runconfig"
    17  	"github.com/docker/docker/utils"
    18  )
    19  
    20  func (cli *DockerCli) pullImage(image string) error {
    21  	return cli.pullImageCustomOut(image, cli.out)
    22  }
    23  
    24  func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
    25  	v := url.Values{}
    26  	repos, tag := parsers.ParseRepositoryTag(image)
    27  	// pull only the image tagged 'latest' if no tag was specified
    28  	if tag == "" {
    29  		tag = tags.DEFAULTTAG
    30  	}
    31  	v.Set("fromImage", repos)
    32  	v.Set("tag", tag)
    33  
    34  	// Resolve the Repository name from fqn to RepositoryInfo
    35  	repoInfo, err := registry.ParseRepositoryInfo(repos)
    36  	if err != nil {
    37  		return err
    38  	}
    39  
    40  	// Resolve the Auth config relevant for this server
    41  	authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index)
    42  	buf, err := json.Marshal(authConfig)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	registryAuthHeader := []string{
    48  		base64.URLEncoding.EncodeToString(buf),
    49  	}
    50  	sopts := &streamOpts{
    51  		rawTerminal: true,
    52  		out:         out,
    53  		headers:     map[string][]string{"X-Registry-Auth": registryAuthHeader},
    54  	}
    55  	if err := cli.stream("POST", "/images/create?"+v.Encode(), sopts); err != nil {
    56  		return err
    57  	}
    58  	return nil
    59  }
    60  
    61  type cidFile struct {
    62  	path    string
    63  	file    *os.File
    64  	written bool
    65  }
    66  
    67  func newCIDFile(path string) (*cidFile, error) {
    68  	if _, err := os.Stat(path); err == nil {
    69  		return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
    70  	}
    71  
    72  	f, err := os.Create(path)
    73  	if err != nil {
    74  		return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
    75  	}
    76  
    77  	return &cidFile{path: path, file: f}, nil
    78  }
    79  
    80  func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
    81  	containerValues := url.Values{}
    82  	if name != "" {
    83  		containerValues.Set("name", name)
    84  	}
    85  
    86  	mergedConfig := runconfig.MergeConfigs(config, hostConfig)
    87  
    88  	var containerIDFile *cidFile
    89  	if cidfile != "" {
    90  		var err error
    91  		if containerIDFile, err = newCIDFile(cidfile); err != nil {
    92  			return nil, err
    93  		}
    94  		defer containerIDFile.Close()
    95  	}
    96  
    97  	//create the container
    98  	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil)
    99  	//if image not found try to pull it
   100  	if statusCode == 404 && strings.Contains(err.Error(), config.Image) {
   101  		repo, tag := parsers.ParseRepositoryTag(config.Image)
   102  		if tag == "" {
   103  			tag = tags.DEFAULTTAG
   104  		}
   105  		fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", utils.ImageReference(repo, tag))
   106  
   107  		// we don't want to write to stdout anything apart from container.ID
   108  		if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil {
   109  			return nil, err
   110  		}
   111  		// Retry
   112  		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil); err != nil {
   113  			return nil, err
   114  		}
   115  	} else if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	var response types.ContainerCreateResponse
   120  	if err := json.NewDecoder(stream).Decode(&response); err != nil {
   121  		return nil, err
   122  	}
   123  	for _, warning := range response.Warnings {
   124  		fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
   125  	}
   126  	if containerIDFile != nil {
   127  		if err = containerIDFile.Write(response.ID); err != nil {
   128  			return nil, err
   129  		}
   130  	}
   131  	return &response, nil
   132  }
   133  
   134  // CmdCreate creates a new container from a given image.
   135  //
   136  // Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
   137  func (cli *DockerCli) CmdCreate(args ...string) error {
   138  	cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container", true)
   139  
   140  	// These are flags not stored in Config/HostConfig
   141  	var (
   142  		flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
   143  	)
   144  
   145  	config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
   146  	if err != nil {
   147  		cmd.ReportError(err.Error(), true)
   148  		os.Exit(1)
   149  	}
   150  	if config.Image == "" {
   151  		cmd.Usage()
   152  		return nil
   153  	}
   154  	response, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
   155  	if err != nil {
   156  		return err
   157  	}
   158  	fmt.Fprintf(cli.out, "%s\n", response.ID)
   159  	return nil
   160  }