mirror of
https://github.com/clearlinux/docker.git
synced 2026-10-03 23:38:24 +00:00
Add "docker create" support
This exposes the already existing "create container" operation. It is very similar to "docker run -d" except it doesn't actually start the container, but just prepares it. It can then be manually started using "docker start" at any point. Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson) Conflicts: api/client/commands.go runconfig/parse.go server/container.go Docker-DCO-1.1-Signed-off-by: Tibor Vass <teabee89@gmail.com> (github: tiborvass)
This commit is contained in:
committed by
Tibor Vass
parent
eb21197c6b
commit
3a90004f3c
+159
-69
@@ -1965,9 +1965,112 @@ func (cli *DockerCli) pullImage(image string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
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)
|
||||
}
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
|
||||
}
|
||||
|
||||
return &cidFile{path: path, file: f}, nil
|
||||
}
|
||||
|
||||
func (cid *cidFile) Close() error {
|
||||
cid.file.Close()
|
||||
|
||||
if !cid.written {
|
||||
if err := os.Remove(cid.path); err != nil {
|
||||
return fmt.Errorf("failed to remove CID file '%s': %s \n", cid.path, 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{}
|
||||
if 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
|
||||
stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), data, false)
|
||||
//if image not found try to pull it
|
||||
if statusCode == 404 {
|
||||
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
|
||||
|
||||
if err = cli.pullImage(config.Image); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Retry
|
||||
if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), data, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} 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
|
||||
config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
|
||||
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
|
||||
}
|
||||
@@ -1976,82 +2079,69 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Retrieve relevant client-side config
|
||||
createResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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 (
|
||||
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())
|
||||
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 container in the background and print 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")
|
||||
)
|
||||
|
||||
// Disable sigProxy in case on TTY
|
||||
config, hostConfig, cmd, err := runconfig.ParseSubcommand(cmd, args, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Image == "" {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
|
||||
if *flDetach {
|
||||
if fl := cmd.Lookup("attach"); fl != nil {
|
||||
flAttach = fl.Value.(*opts.ListOpts)
|
||||
if flAttach.Len() != 0 {
|
||||
return fmt.Errorf("Conflicting options: -a and -d")
|
||||
}
|
||||
}
|
||||
if *flAutoRemove {
|
||||
return fmt.Errorf("Conflicting options: --rm and -d")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
containerValues := url.Values{}
|
||||
if name := flName.Value.String(); name != "" {
|
||||
containerValues.Set("name", name)
|
||||
}
|
||||
|
||||
//create the container
|
||||
stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false)
|
||||
//if image not found try to pull it
|
||||
if statusCode == 404 {
|
||||
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
|
||||
|
||||
if err = cli.pullImage(config.Image); err != nil {
|
||||
return err
|
||||
}
|
||||
// Retry
|
||||
if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil {
|
||||
runResult, err := cli.createContainer(config, nil, hostConfig.ContainerIDFile, *flName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var runResult engine.Env
|
||||
if err := runResult.Decode(stream); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, warning := range runResult.GetList("Warnings") {
|
||||
fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
|
||||
}
|
||||
|
||||
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 sigProxy {
|
||||
sigc := cli.forwardAllSignals(runResult.Get("Id"))
|
||||
defer signal.StopCatch(sigc)
|
||||
@@ -2158,7 +2248,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
var status int
|
||||
|
||||
// Attached mode
|
||||
if autoRemove {
|
||||
if *flAutoRemove {
|
||||
// Autoremove: wait for the container to finish, retrieve
|
||||
// the exit code and remove the container
|
||||
if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user