github.com/DaoCloud/dao@v0.0.0-20161212064103-c3dbfd13ee36/api/client/container/create.go (about)

     1  package container
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  
     8  	"golang.org/x/net/context"
     9  
    10  	"github.com/docker/docker/api/client"
    11  	"github.com/docker/docker/cli"
    12  	"github.com/docker/docker/pkg/jsonmessage"
    13  	// FIXME migrate to docker/distribution/reference
    14  	"github.com/docker/docker/reference"
    15  	"github.com/docker/docker/registry"
    16  	runconfigopts "github.com/docker/docker/runconfig/opts"
    17  	apiclient "github.com/docker/engine-api/client"
    18  	"github.com/docker/engine-api/types"
    19  	"github.com/docker/engine-api/types/container"
    20  	networktypes "github.com/docker/engine-api/types/network"
    21  	"github.com/spf13/cobra"
    22  	"github.com/spf13/pflag"
    23  )
    24  
    25  type createOptions struct {
    26  	name string
    27  }
    28  
    29  // NewCreateCommand creats a new cobra.Command for `docker create`
    30  func NewCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
    31  	var opts createOptions
    32  	var copts *runconfigopts.ContainerOptions
    33  
    34  	cmd := &cobra.Command{
    35  		Use:   "create [OPTIONS] IMAGE [COMMAND] [ARG...]",
    36  		Short: "创建一个新的容器,属于容器实体,未开始运行",
    37  		Args:  cli.RequiresMinArgs(1),
    38  		RunE: func(cmd *cobra.Command, args []string) error {
    39  			copts.Image = args[0]
    40  			if len(args) > 1 {
    41  				copts.Args = args[1:]
    42  			}
    43  			return runCreate(dockerCli, cmd.Flags(), &opts, copts)
    44  		},
    45  	}
    46  
    47  	flags := cmd.Flags()
    48  	flags.SetInterspersed(false)
    49  
    50  	flags.StringVar(&opts.name, "name", "", "为容器授予一个名称")
    51  
    52  	// Add an explicit help that doesn't have a `-h` to prevent the conflict
    53  	// with hostname
    54  	flags.Bool("help", false, "打印命令用途")
    55  
    56  	client.AddTrustedFlags(flags, true)
    57  	copts = runconfigopts.AddFlags(flags)
    58  	return cmd
    59  }
    60  
    61  func runCreate(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *createOptions, copts *runconfigopts.ContainerOptions) error {
    62  	config, hostConfig, networkingConfig, err := runconfigopts.Parse(flags, copts)
    63  	if err != nil {
    64  		reportError(dockerCli.Err(), "create", err.Error(), true)
    65  		return cli.StatusError{StatusCode: 125}
    66  	}
    67  	response, err := createContainer(context.Background(), dockerCli, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID)
    72  	return nil
    73  }
    74  
    75  func pullImage(ctx context.Context, dockerCli *client.DockerCli, image string, out io.Writer) error {
    76  	ref, err := reference.ParseNamed(image)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	// Resolve the Repository name from fqn to RepositoryInfo
    82  	repoInfo, err := registry.ParseRepositoryInfo(ref)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	authConfig := dockerCli.ResolveAuthConfig(ctx, repoInfo.Index)
    88  	encodedAuth, err := client.EncodeAuthToBase64(authConfig)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	options := types.ImageCreateOptions{
    94  		RegistryAuth: encodedAuth,
    95  	}
    96  
    97  	responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	defer responseBody.Close()
   102  
   103  	return jsonmessage.DisplayJSONMessagesStream(
   104  		responseBody,
   105  		out,
   106  		dockerCli.OutFd(),
   107  		dockerCli.IsTerminalOut(),
   108  		nil)
   109  }
   110  
   111  type cidFile struct {
   112  	path    string
   113  	file    *os.File
   114  	written bool
   115  }
   116  
   117  func (cid *cidFile) Close() error {
   118  	cid.file.Close()
   119  
   120  	if !cid.written {
   121  		if err := os.Remove(cid.path); err != nil {
   122  			return fmt.Errorf("删除容器ID文件'%s'失败: %s \n", cid.path, err)
   123  		}
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  func (cid *cidFile) Write(id string) error {
   130  	if _, err := cid.file.Write([]byte(id)); err != nil {
   131  		return fmt.Errorf("写容器ID至容器ID文件失败: %s", err)
   132  	}
   133  	cid.written = true
   134  	return nil
   135  }
   136  
   137  func newCIDFile(path string) (*cidFile, error) {
   138  	if _, err := os.Stat(path); err == nil {
   139  		return nil, fmt.Errorf("容器ID文件已找到,请确保其他的容器并没有在运行或被删除 %s", path)
   140  	}
   141  
   142  	f, err := os.Create(path)
   143  	if err != nil {
   144  		return nil, fmt.Errorf("创建容器ID文件失败: %s", err)
   145  	}
   146  
   147  	return &cidFile{path: path, file: f}, nil
   148  }
   149  
   150  func createContainer(ctx context.Context, dockerCli *client.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
   151  	stderr := dockerCli.Err()
   152  
   153  	var containerIDFile *cidFile
   154  	if cidfile != "" {
   155  		var err error
   156  		if containerIDFile, err = newCIDFile(cidfile); err != nil {
   157  			return nil, err
   158  		}
   159  		defer containerIDFile.Close()
   160  	}
   161  
   162  	var trustedRef reference.Canonical
   163  	_, ref, err := reference.ParseIDOrReference(config.Image)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	if ref != nil {
   168  		ref = reference.WithDefaultTag(ref)
   169  
   170  		if ref, ok := ref.(reference.NamedTagged); ok && client.IsTrusted() {
   171  			var err error
   172  			trustedRef, err = dockerCli.TrustedReference(ctx, ref)
   173  			if err != nil {
   174  				return nil, err
   175  			}
   176  			config.Image = trustedRef.String()
   177  		}
   178  	}
   179  
   180  	//create the container
   181  	response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
   182  
   183  	//if image not found try to pull it
   184  	if err != nil {
   185  		if apiclient.IsErrImageNotFound(err) && ref != nil {
   186  			fmt.Fprintf(stderr, "本地无法找到镜像 '%s'\n", ref.String())
   187  
   188  			// we don't want to write to stdout anything apart from container.ID
   189  			if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil {
   190  				return nil, err
   191  			}
   192  			if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
   193  				if err := dockerCli.TagTrusted(ctx, trustedRef, ref); err != nil {
   194  					return nil, err
   195  				}
   196  			}
   197  			// Retry
   198  			var retryErr error
   199  			response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
   200  			if retryErr != nil {
   201  				return nil, retryErr
   202  			}
   203  		} else {
   204  			return nil, err
   205  		}
   206  	}
   207  
   208  	for _, warning := range response.Warnings {
   209  		fmt.Fprintf(stderr, "警告: %s\n", warning)
   210  	}
   211  	if containerIDFile != nil {
   212  		if err = containerIDFile.Write(response.ID); err != nil {
   213  			return nil, err
   214  		}
   215  	}
   216  	return &response, nil
   217  }