github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/cli/command/container/create.go (about) 1 package container 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 8 "github.com/docker/docker/api/types" 9 "github.com/docker/docker/api/types/container" 10 networktypes "github.com/docker/docker/api/types/network" 11 "github.com/docker/docker/cli" 12 "github.com/docker/docker/cli/command" 13 "github.com/docker/docker/cli/command/image" 14 apiclient "github.com/docker/docker/client" 15 "github.com/docker/docker/pkg/jsonmessage" 16 // FIXME migrate to docker/distribution/reference 17 "github.com/docker/docker/reference" 18 "github.com/docker/docker/registry" 19 "github.com/spf13/cobra" 20 "github.com/spf13/pflag" 21 "golang.org/x/net/context" 22 ) 23 24 type createOptions struct { 25 name string 26 } 27 28 // NewCreateCommand creates a new cobra.Command for `docker create` 29 func NewCreateCommand(dockerCli *command.DockerCli) *cobra.Command { 30 var opts createOptions 31 var copts *containerOptions 32 33 cmd := &cobra.Command{ 34 Use: "create [OPTIONS] IMAGE [COMMAND] [ARG...]", 35 Short: "Create a new container", 36 Args: cli.RequiresMinArgs(1), 37 RunE: func(cmd *cobra.Command, args []string) error { 38 copts.Image = args[0] 39 if len(args) > 1 { 40 copts.Args = args[1:] 41 } 42 return runCreate(dockerCli, cmd.Flags(), &opts, copts) 43 }, 44 } 45 46 flags := cmd.Flags() 47 flags.SetInterspersed(false) 48 49 flags.StringVar(&opts.name, "name", "", "Assign a name to the container") 50 51 // Add an explicit help that doesn't have a `-h` to prevent the conflict 52 // with hostname 53 flags.Bool("help", false, "Print usage") 54 55 command.AddTrustedFlags(flags, true) 56 copts = addFlags(flags) 57 return cmd 58 } 59 60 func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *createOptions, copts *containerOptions) error { 61 config, hostConfig, networkingConfig, err := parse(flags, copts) 62 if err != nil { 63 reportError(dockerCli.Err(), "create", err.Error(), true) 64 return cli.StatusError{StatusCode: 125} 65 } 66 response, err := createContainer(context.Background(), dockerCli, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name) 67 if err != nil { 68 return err 69 } 70 fmt.Fprintln(dockerCli.Out(), response.ID) 71 return nil 72 } 73 74 func pullImage(ctx context.Context, dockerCli *command.DockerCli, image string, out io.Writer) error { 75 ref, err := reference.ParseNamed(image) 76 if err != nil { 77 return err 78 } 79 80 // Resolve the Repository name from fqn to RepositoryInfo 81 repoInfo, err := registry.ParseRepositoryInfo(ref) 82 if err != nil { 83 return err 84 } 85 86 authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) 87 encodedAuth, err := command.EncodeAuthToBase64(authConfig) 88 if err != nil { 89 return err 90 } 91 92 options := types.ImageCreateOptions{ 93 RegistryAuth: encodedAuth, 94 } 95 96 responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options) 97 if err != nil { 98 return err 99 } 100 defer responseBody.Close() 101 102 return jsonmessage.DisplayJSONMessagesStream( 103 responseBody, 104 out, 105 dockerCli.Out().FD(), 106 dockerCli.Out().IsTerminal(), 107 nil) 108 } 109 110 type cidFile struct { 111 path string 112 file *os.File 113 written bool 114 } 115 116 func (cid *cidFile) Close() error { 117 cid.file.Close() 118 119 if cid.written { 120 return nil 121 } 122 if err := os.Remove(cid.path); err != nil { 123 return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err) 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("Failed to write the container ID to the file: %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("Container ID file found, make sure the other container isn't running or delete %s", path) 140 } 141 142 f, err := os.Create(path) 143 if err != nil { 144 return nil, fmt.Errorf("Failed to create the container ID file: %s", err) 145 } 146 147 return &cidFile{path: path, file: f}, nil 148 } 149 150 func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*container.ContainerCreateCreatedBody, 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 && command.IsTrusted() { 171 var err error 172 trustedRef, err = image.TrustedReference(ctx, dockerCli, ref, nil) 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, "Unable to find image '%s' locally\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 := image.TagTrusted(ctx, dockerCli, 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, "WARNING: %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 }