소스 검색

docker run [-a [stdin|stdout|stderr] [...]]: choose which streams to attach to when running a command. Fixes #234.

Solomon Hykes 12 년 전
부모
커밋
c04af2a330
2개의 변경된 파일87개의 추가작업 그리고 40개의 파일을 삭제
  1. 46 18
      commands.go
  2. 41 22
      container.go

+ 46 - 18
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) {

+ 41 - 22
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