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