|
@@ -1965,91 +1965,182 @@ func (cli *DockerCli) pullImage(image string) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (cli *DockerCli) CmdRun(args ...string) error {
|
|
|
|
- // FIXME: just use runconfig.Parse already
|
|
|
|
- config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
|
|
+type cidFile struct {
|
|
|
|
+ path string
|
|
|
|
+ file *os.File
|
|
|
|
+ written bool
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func newCIDFile(path string) (*cidFile, error) {
|
|
|
|
+ if _, err := os.Stat(path); err == nil {
|
|
|
|
+ return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
|
}
|
|
}
|
|
- if config.Image == "" {
|
|
|
|
- cmd.Usage()
|
|
|
|
- return nil
|
|
|
|
|
|
+ f, err := os.Create(path)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
|
|
}
|
|
}
|
|
|
|
|
|
- // Retrieve relevant client-side config
|
|
|
|
- var (
|
|
|
|
- flName = cmd.Lookup("name")
|
|
|
|
- flRm = cmd.Lookup("rm")
|
|
|
|
- flSigProxy = cmd.Lookup("sig-proxy")
|
|
|
|
- autoRemove, _ = strconv.ParseBool(flRm.Value.String())
|
|
|
|
- sigProxy, _ = strconv.ParseBool(flSigProxy.Value.String())
|
|
|
|
- )
|
|
|
|
|
|
+ return &cidFile{path: path, file: f}, nil
|
|
|
|
+}
|
|
|
|
|
|
- // Disable sigProxy in case on TTY
|
|
|
|
- if config.Tty {
|
|
|
|
- sigProxy = false
|
|
|
|
- }
|
|
|
|
|
|
+func (cid *cidFile) Close() error {
|
|
|
|
+ cid.file.Close()
|
|
|
|
|
|
- var containerIDFile io.WriteCloser
|
|
|
|
- if len(hostConfig.ContainerIDFile) > 0 {
|
|
|
|
- if _, err := os.Stat(hostConfig.ContainerIDFile); err == nil {
|
|
|
|
- return fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile)
|
|
|
|
- }
|
|
|
|
- if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil {
|
|
|
|
- return fmt.Errorf("Failed to create the container ID file: %s", err)
|
|
|
|
|
|
+ if !cid.written {
|
|
|
|
+ if err := os.Remove(cid.path); err != nil {
|
|
|
|
+ return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
|
|
}
|
|
}
|
|
- defer func() {
|
|
|
|
- containerIDFile.Close()
|
|
|
|
- var (
|
|
|
|
- cidFileInfo os.FileInfo
|
|
|
|
- err error
|
|
|
|
- )
|
|
|
|
- if cidFileInfo, err = os.Stat(hostConfig.ContainerIDFile); err != nil {
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- if cidFileInfo.Size() == 0 {
|
|
|
|
- if err := os.Remove(hostConfig.ContainerIDFile); err != nil {
|
|
|
|
- fmt.Printf("failed to remove Container ID file '%s': %s \n", hostConfig.ContainerIDFile, err)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }()
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (cid *cidFile) Write(id string) error {
|
|
|
|
+ if _, err := cid.file.Write([]byte(id)); err != nil {
|
|
|
|
+ return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
|
|
|
+ }
|
|
|
|
+ cid.written = true
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (engine.Env, error) {
|
|
containerValues := url.Values{}
|
|
containerValues := url.Values{}
|
|
- if name := flName.Value.String(); name != "" {
|
|
|
|
|
|
+ if name != "" {
|
|
containerValues.Set("name", name)
|
|
containerValues.Set("name", name)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ var data interface{}
|
|
|
|
+ if hostConfig != nil {
|
|
|
|
+ data = runconfig.MergeConfigs(config, hostConfig)
|
|
|
|
+ } else {
|
|
|
|
+ data = config
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var containerIDFile *cidFile
|
|
|
|
+ if cidfile != "" {
|
|
|
|
+ var err error
|
|
|
|
+ if containerIDFile, err = newCIDFile(cidfile); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ defer containerIDFile.Close()
|
|
|
|
+ }
|
|
|
|
+
|
|
//create the container
|
|
//create the container
|
|
- stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false)
|
|
|
|
|
|
+ stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), data, false)
|
|
//if image not found try to pull it
|
|
//if image not found try to pull it
|
|
if statusCode == 404 {
|
|
if statusCode == 404 {
|
|
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
|
|
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
|
|
|
|
|
|
if err = cli.pullImage(config.Image); err != nil {
|
|
if err = cli.pullImage(config.Image); err != nil {
|
|
- return err
|
|
|
|
|
|
+ return nil, err
|
|
}
|
|
}
|
|
// Retry
|
|
// Retry
|
|
- if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false); err != nil {
|
|
|
|
- return err
|
|
|
|
|
|
+ if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), data, false); err != nil {
|
|
|
|
+ return nil, err
|
|
}
|
|
}
|
|
} else if err != nil {
|
|
} else if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var result engine.Env
|
|
|
|
+ if err := result.Decode(stream); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for _, warning := range result.GetList("Warnings") {
|
|
|
|
+ fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if containerIDFile != nil {
|
|
|
|
+ if err = containerIDFile.Write(result.Get("Id")); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return result, nil
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (cli *DockerCli) CmdCreate(args ...string) error {
|
|
|
|
+ // FIXME: just use runconfig.Parse already
|
|
|
|
+ cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container")
|
|
|
|
+
|
|
|
|
+ // These are flags not stored in Config/HostConfig
|
|
|
|
+ var (
|
|
|
|
+ flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ config, hostConfig, cmd, err := runconfig.ParseSubcommand(cmd, args, nil)
|
|
|
|
+ if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
+ if config.Image == "" {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
|
|
- var runResult engine.Env
|
|
|
|
- if err := runResult.Decode(stream); err != nil {
|
|
|
|
|
|
+ createResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
|
|
|
|
+ if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
- for _, warning := range runResult.GetList("Warnings") {
|
|
|
|
- fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
|
|
|
|
|
|
+ fmt.Fprintf(cli.out, "%s\n", createResult.Get("Id"))
|
|
|
|
+
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (cli *DockerCli) CmdRun(args ...string) error {
|
|
|
|
+ // FIXME: just use runconfig.Parse already
|
|
|
|
+ cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
|
|
|
|
+
|
|
|
|
+ // These are flags not stored in Config/HostConfig
|
|
|
|
+ var (
|
|
|
|
+ flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
|
|
|
|
+ flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run the container in the background and print the new container ID")
|
|
|
|
+ flSigProxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy received signals to the process (even in non-TTY mode). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.")
|
|
|
|
+ flName = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
|
|
|
|
+ flAttach *opts.ListOpts
|
|
|
|
+
|
|
|
|
+ ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
|
|
|
|
+ ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
|
|
|
|
+ ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ config, hostConfig, cmd, err := runconfig.ParseSubcommand(cmd, args, nil)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ if config.Image == "" {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
}
|
|
}
|
|
|
|
|
|
- if len(hostConfig.ContainerIDFile) > 0 {
|
|
|
|
- if _, err = containerIDFile.Write([]byte(runResult.Get("Id"))); err != nil {
|
|
|
|
- return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
|
|
|
|
|
+ if *flDetach {
|
|
|
|
+ if fl := cmd.Lookup("attach"); fl != nil {
|
|
|
|
+ flAttach = fl.Value.(*opts.ListOpts)
|
|
|
|
+ if flAttach.Len() != 0 {
|
|
|
|
+ return ErrConflictAttachDetach
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if *flAutoRemove {
|
|
|
|
+ return ErrConflictDetachAutoRemove
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ config.AttachStdin = false
|
|
|
|
+ config.AttachStdout = false
|
|
|
|
+ config.AttachStderr = false
|
|
|
|
+ config.StdinOnce = false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Disable flSigProxy in case on TTY
|
|
|
|
+ sigProxy := *flSigProxy
|
|
|
|
+ if config.Tty {
|
|
|
|
+ sigProxy = false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ runResult, err := cli.createContainer(config, nil, hostConfig.ContainerIDFile, *flName)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
}
|
|
}
|
|
|
|
|
|
if sigProxy {
|
|
if sigProxy {
|
|
@@ -2071,6 +2162,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|
}()
|
|
}()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") {
|
|
|
|
+ return ErrConflictRestartPolicyAndAutoRemove
|
|
|
|
+ }
|
|
|
|
+
|
|
// We need to instanciate the chan because the select needs it. It can
|
|
// We need to instanciate the chan because the select needs it. It can
|
|
// be closed but can't be uninitialized.
|
|
// be closed but can't be uninitialized.
|
|
hijacked := make(chan io.Closer)
|
|
hijacked := make(chan io.Closer)
|
|
@@ -2158,7 +2253,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|
var status int
|
|
var status int
|
|
|
|
|
|
// Attached mode
|
|
// Attached mode
|
|
- if autoRemove {
|
|
|
|
|
|
+ if *flAutoRemove {
|
|
// Autoremove: wait for the container to finish, retrieve
|
|
// Autoremove: wait for the container to finish, retrieve
|
|
// the exit code and remove the container
|
|
// the exit code and remove the container
|
|
if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil {
|
|
if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil {
|