github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/cli/command/container/create.go (about) 1 package container 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 8 "github.com/docker/distribution/reference" 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/container" 11 networktypes "github.com/docker/docker/api/types/network" 12 "github.com/docker/docker/cli" 13 "github.com/docker/docker/cli/command" 14 "github.com/docker/docker/cli/command/image" 15 apiclient "github.com/docker/docker/client" 16 "github.com/docker/docker/pkg/jsonmessage" 17 "github.com/docker/docker/registry" 18 "github.com/spf13/cobra" 19 "github.com/spf13/pflag" 20 "golang.org/x/net/context" 21 ) 22 23 type createOptions struct { 24 name string 25 } 26 27 // NewCreateCommand creates a new cobra.Command for `docker create` 28 func NewCreateCommand(dockerCli *command.DockerCli) *cobra.Command { 29 var opts createOptions 30 var copts *containerOptions 31 32 cmd := &cobra.Command{ 33 Use: "create [OPTIONS] IMAGE [COMMAND] [ARG...]", 34 Short: "Create a new container", 35 Args: cli.RequiresMinArgs(1), 36 RunE: func(cmd *cobra.Command, args []string) error { 37 copts.Image = args[0] 38 if len(args) > 1 { 39 copts.Args = args[1:] 40 } 41 return runCreate(dockerCli, cmd.Flags(), &opts, copts) 42 }, 43 } 44 45 flags := cmd.Flags() 46 flags.SetInterspersed(false) 47 48 flags.StringVar(&opts.name, "name", "", "Assign a name to the container") 49 50 // Add an explicit help that doesn't have a `-h` to prevent the conflict 51 // with hostname 52 flags.Bool("help", false, "Print usage") 53 54 command.AddTrustVerificationFlags(flags) 55 copts = addFlags(flags) 56 return cmd 57 } 58 59 func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *createOptions, copts *containerOptions) error { 60 config, hostConfig, networkingConfig, err := parse(flags, copts) 61 if err != nil { 62 reportError(dockerCli.Err(), "create", err.Error(), true) 63 return cli.StatusError{StatusCode: 125} 64 } 65 response, err := createContainer(context.Background(), dockerCli, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name) 66 if err != nil { 67 return err 68 } 69 fmt.Fprintln(dockerCli.Out(), response.ID) 70 return nil 71 } 72 73 func pullImage(ctx context.Context, dockerCli *command.DockerCli, image string, out io.Writer) error { 74 ref, err := reference.ParseNormalizedNamed(image) 75 if err != nil { 76 return err 77 } 78 79 // Resolve the Repository name from fqn to RepositoryInfo 80 repoInfo, err := registry.ParseRepositoryInfo(ref) 81 if err != nil { 82 return err 83 } 84 85 authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) 86 encodedAuth, err := command.EncodeAuthToBase64(authConfig) 87 if err != nil { 88 return err 89 } 90 91 options := types.ImageCreateOptions{ 92 RegistryAuth: encodedAuth, 93 } 94 95 responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options) 96 if err != nil { 97 return err 98 } 99 defer responseBody.Close() 100 101 return jsonmessage.DisplayJSONMessagesStream( 102 responseBody, 103 out, 104 dockerCli.Out().FD(), 105 dockerCli.Out().IsTerminal(), 106 nil) 107 } 108 109 type cidFile struct { 110 path string 111 file *os.File 112 written bool 113 } 114 115 func (cid *cidFile) Close() error { 116 cid.file.Close() 117 118 if cid.written { 119 return nil 120 } 121 if err := os.Remove(cid.path); err != nil { 122 return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err) 123 } 124 125 return nil 126 } 127 128 func (cid *cidFile) Write(id string) error { 129 if _, err := cid.file.Write([]byte(id)); err != nil { 130 return fmt.Errorf("Failed to write the container ID to the file: %s", err) 131 } 132 cid.written = true 133 return nil 134 } 135 136 func newCIDFile(path string) (*cidFile, error) { 137 if _, err := os.Stat(path); err == nil { 138 return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path) 139 } 140 141 f, err := os.Create(path) 142 if err != nil { 143 return nil, fmt.Errorf("Failed to create the container ID file: %s", err) 144 } 145 146 return &cidFile{path: path, file: f}, nil 147 } 148 149 func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*container.ContainerCreateCreatedBody, error) { 150 stderr := dockerCli.Err() 151 152 var ( 153 containerIDFile *cidFile 154 trustedRef reference.Canonical 155 namedRef reference.Named 156 ) 157 158 if cidfile != "" { 159 var err error 160 if containerIDFile, err = newCIDFile(cidfile); err != nil { 161 return nil, err 162 } 163 defer containerIDFile.Close() 164 } 165 166 ref, err := reference.ParseAnyReference(config.Image) 167 if err != nil { 168 return nil, err 169 } 170 if named, ok := ref.(reference.Named); ok { 171 namedRef = reference.TagNameOnly(named) 172 173 if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() { 174 var err error 175 trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef, nil) 176 if err != nil { 177 return nil, err 178 } 179 config.Image = reference.FamiliarString(trustedRef) 180 } 181 } 182 183 //create the container 184 response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name) 185 186 //if image not found try to pull it 187 if err != nil { 188 if apiclient.IsErrImageNotFound(err) && namedRef != nil { 189 fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef)) 190 191 // we don't want to write to stdout anything apart from container.ID 192 if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil { 193 return nil, err 194 } 195 if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil { 196 if err := image.TagTrusted(ctx, dockerCli, trustedRef, taggedRef); err != nil { 197 return nil, err 198 } 199 } 200 // Retry 201 var retryErr error 202 response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name) 203 if retryErr != nil { 204 return nil, retryErr 205 } 206 } else { 207 return nil, err 208 } 209 } 210 211 for _, warning := range response.Warnings { 212 fmt.Fprintf(stderr, "WARNING: %s\n", warning) 213 } 214 if containerIDFile != nil { 215 if err = containerIDFile.Write(response.ID); err != nil { 216 return nil, err 217 } 218 } 219 return &response, nil 220 }