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 }