From c04af2a330991fcd52bcce213bcb863cff95d378 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Tue, 2 Apr 2013 18:07:16 -0700 Subject: [PATCH] docker run [-a [stdin|stdout|stderr] [...]]: choose which streams to attach to when running a command. Fixes #234. --- commands.go | 64 +++++++++++++++++++++++++++++++++++++--------------- container.go | 63 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 87 insertions(+), 40 deletions(-) diff --git a/commands.go b/commands.go index 299ae0e4f5..328f3b99a8 100644 --- a/commands.go +++ b/commands.go @@ -827,6 +827,33 @@ func (opts *ListOpts) Set(value string) error { return nil } +// AttachOpts stores arguments to 'docker run -a', eg. which streams to attach to +type AttachOpts map[string]bool + +func NewAttachOpts() *AttachOpts { + opts := make(map[string]bool) + return (*AttachOpts)(&opts) +} + +func (opts *AttachOpts) String() string { + return fmt.Sprint(*opts) +} + +func (opts *AttachOpts) Set(val string) error { + if val != "stdin" && val != "stdout" && val != "stderr" { + return fmt.Errorf("Unsupported stream name: %s", val) + } + (*opts)[val] = true + return nil +} + +func (opts *AttachOpts) Get(val string) bool { + if res, exists := (*opts)[val]; exists { + return res + } + return false +} + func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string) error { cmd := rcli.Subcmd(stdout, "tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository") force := cmd.Bool("f", false, "Force") @@ -870,28 +897,29 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) return err } } - // Run the container - if !config.Detach { - var attachErr chan error - if config.OpenStdin { - Debugf("Attaching with stdin\n") - attachErr = container.Attach(stdin, stdout, stdout) - } else { - Debugf("Attaching without stdin\n") - attachErr = container.Attach(nil, stdout, nil) - } - Debugf("Starting\n") - if err := container.Start(); err != nil { - return err - } - Debugf("Waiting for attach to return\n") - return <-attachErr + var ( + cStdin io.Reader + cStdout, cStderr io.Writer + ) + if config.AttachStdin { + cStdin = stdin } + if config.AttachStdout { + cStdout = stdout + } + if config.AttachStderr { + cStderr = stdout // FIXME: rcli can't differentiate stdout from stderr + } + attachErr := container.Attach(cStdin, cStdout, cStderr) + Debugf("Starting\n") if err := container.Start(); err != nil { return err } - fmt.Fprintln(stdout, container.ShortId()) - return nil + if cStdout == nil && cStderr == nil { + fmt.Fprintln(stdout, container.ShortId()) + } + Debugf("Waiting for attach to return\n") + return <-attachErr } func NewServer() (*Server, error) { diff --git a/container.go b/container.go index a11539180e..c2b21e0c32 100644 --- a/container.go +++ b/container.go @@ -48,18 +48,20 @@ type Container struct { } type Config struct { - Hostname string - User string - Memory int64 // Memory limit (in bytes) - MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap - Detach bool - Ports []int - Tty bool // Attach standard streams to a tty, including stdin if it is not closed. - OpenStdin bool // Open stdin - StdinOnce bool // If true, close stdin after the 1 attached client disconnects. - Env []string - Cmd []string - Image string // Name of the image as it was passed by the operator (eg. could be symbolic) + Hostname string + User string + Memory int64 // Memory limit (in bytes) + MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap + AttachStdin bool + AttachStdout bool + AttachStderr bool + Ports []int + Tty bool // Attach standard streams to a tty, including stdin if it is not closed. + OpenStdin bool // Open stdin + StdinOnce bool // If true, close stdin after the 1 attached client disconnects. + Env []string + Cmd []string + Image string // Name of the image as it was passed by the operator (eg. could be symbolic) } func ParseRun(args []string, stdout io.Writer) (*Config, error) { @@ -70,6 +72,8 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) { flUser := cmd.String("u", "", "Username or UID") flDetach := cmd.Bool("d", false, "Detached mode: leave the container running in the background") + flAttach := NewAttachOpts() + cmd.Var(flAttach, "a", "Attach to stdin, stdout or stderr.") flStdin := cmd.Bool("i", false, "Keep stdin open even if not attached") flTty := cmd.Bool("t", false, "Allocate a pseudo-tty") flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)") @@ -81,6 +85,19 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) { if err := cmd.Parse(args); err != nil { return nil, err } + if *flDetach && len(*flAttach) > 0 { + return nil, fmt.Errorf("Conflicting options: -a and -d") + } + // If neither -d or -a are set, attach to everything by default + if len(*flAttach) == 0 && !*flDetach { + if !*flDetach { + flAttach.Set("stdout") + flAttach.Set("stderr") + if *flStdin { + flAttach.Set("stdin") + } + } + } parsedArgs := cmd.Args() runCmd := []string{} image := "" @@ -91,18 +108,20 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) { runCmd = parsedArgs[1:] } config := &Config{ - Ports: flPorts, - User: *flUser, - Tty: *flTty, - OpenStdin: *flStdin, - Memory: *flMemory, - Detach: *flDetach, - Env: flEnv, - Cmd: runCmd, - Image: image, + Ports: flPorts, + User: *flUser, + Tty: *flTty, + OpenStdin: *flStdin, + Memory: *flMemory, + AttachStdin: flAttach.Get("stdin"), + AttachStdout: flAttach.Get("stdout"), + AttachStderr: flAttach.Get("stderr"), + Env: flEnv, + Cmd: runCmd, + Image: image, } // When allocating stdin in attached mode, close stdin at client disconnect - if config.OpenStdin && !config.Detach { + if config.OpenStdin && config.AttachStdin { config.StdinOnce = true } return config, nil