mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
'docker run -i' optionally opens stdin. 'docker attach' attaches to a running container (including stdin). 'docker run -t' allocates a tty (still buggy)
This commit is contained in:
parent
9906a9af8f
commit
7a50153c32
2 changed files with 166 additions and 12 deletions
82
container.go
82
container.go
|
@ -13,6 +13,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
"github.com/kr/pty"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
|
@ -32,6 +33,8 @@ type Container struct {
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
stdout *writeBroadcaster
|
stdout *writeBroadcaster
|
||||||
stderr *writeBroadcaster
|
stderr *writeBroadcaster
|
||||||
|
stdin io.ReadCloser
|
||||||
|
stdinPipe io.WriteCloser
|
||||||
|
|
||||||
stdoutLog *bytes.Buffer
|
stdoutLog *bytes.Buffer
|
||||||
stderrLog *bytes.Buffer
|
stderrLog *bytes.Buffer
|
||||||
|
@ -40,6 +43,8 @@ type Container struct {
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Hostname string
|
Hostname string
|
||||||
Ram int64
|
Ram int64
|
||||||
|
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||||
|
OpenStdin bool // Open stdin
|
||||||
}
|
}
|
||||||
|
|
||||||
func createContainer(id string, root string, command string, args []string, layers []string, config *Config) (*Container, error) {
|
func createContainer(id string, root string, command string, args []string, layers []string, config *Config) (*Container, error) {
|
||||||
|
@ -59,6 +64,11 @@ func createContainer(id string, root string, command string, args []string, laye
|
||||||
stdoutLog: new(bytes.Buffer),
|
stdoutLog: new(bytes.Buffer),
|
||||||
stderrLog: new(bytes.Buffer),
|
stderrLog: new(bytes.Buffer),
|
||||||
}
|
}
|
||||||
|
if container.Config.OpenStdin {
|
||||||
|
container.stdin, container.stdinPipe = io.Pipe()
|
||||||
|
} else {
|
||||||
|
container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
||||||
|
}
|
||||||
container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
|
container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
|
||||||
container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
|
container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
|
||||||
|
|
||||||
|
@ -94,6 +104,11 @@ func loadContainer(containerPath string) (*Container, error) {
|
||||||
if err := container.Filesystem.createMountPoints(); err != nil {
|
if err := container.Filesystem.createMountPoints(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if container.Config.OpenStdin {
|
||||||
|
container.stdin, container.stdinPipe = io.Pipe()
|
||||||
|
} else {
|
||||||
|
container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
||||||
|
}
|
||||||
container.State = newState()
|
container.State = newState()
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
@ -180,9 +195,67 @@ func (container *Container) Start() error {
|
||||||
params = append(params, container.Args...)
|
params = append(params, container.Args...)
|
||||||
|
|
||||||
container.cmd = exec.Command("/usr/bin/lxc-start", params...)
|
container.cmd = exec.Command("/usr/bin/lxc-start", params...)
|
||||||
|
|
||||||
|
if container.Config.Tty {
|
||||||
|
Pty, tty, err := pty.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
container.cmd.Stdout = tty
|
||||||
|
container.cmd.Stderr = tty
|
||||||
|
if container.stdin != nil {
|
||||||
|
container.cmd.Stdin = tty
|
||||||
|
}
|
||||||
|
container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
|
||||||
|
if err := container.cmd.Start(); err != nil {
|
||||||
|
Pty.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tty.Close()
|
||||||
|
// Attach Pty to stdout
|
||||||
|
go func() {
|
||||||
|
defer container.stdout.Close()
|
||||||
|
for {
|
||||||
|
data := make([]byte, 1024)
|
||||||
|
n, err := Pty.Read(data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("STDOUT <%s>\n", data)
|
||||||
|
if _, err = container.stdout.Write(data[:n]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("STDOUT SENT\n")
|
||||||
|
}
|
||||||
|
//io.Copy(container.stdout, Pty)
|
||||||
|
//container.stdout.Close()
|
||||||
|
}()
|
||||||
|
// Attach Pty to stderr
|
||||||
|
go func() {
|
||||||
|
io.Copy(container.stderr, Pty)
|
||||||
|
container.stderr.Close()
|
||||||
|
}()
|
||||||
|
// Attach Pty to stdin
|
||||||
|
if container.stdin != nil {
|
||||||
|
go func() {
|
||||||
|
defer Pty.Close()
|
||||||
|
io.Copy(Pty, container.stdin)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
container.cmd.Stdout = container.stdout
|
container.cmd.Stdout = container.stdout
|
||||||
container.cmd.Stderr = container.stderr
|
container.cmd.Stderr = container.stderr
|
||||||
|
if container.stdin != nil {
|
||||||
|
stdin, err := container.cmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
defer stdin.Close()
|
||||||
|
io.Copy(stdin, container.stdin)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := container.cmd.Start(); err != nil {
|
if err := container.cmd.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -214,6 +287,13 @@ func (container *Container) Output() (output []byte, err error) {
|
||||||
return output, err
|
return output, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StdinPipe() returns a pipe connected to the standard input of the container's
|
||||||
|
// active process.
|
||||||
|
//
|
||||||
|
func (container *Container) StdinPipe() (io.WriteCloser, error) {
|
||||||
|
return container.stdinPipe, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (container *Container) StdoutPipe() (io.ReadCloser, error) {
|
func (container *Container) StdoutPipe() (io.ReadCloser, error) {
|
||||||
reader, writer := io.Pipe()
|
reader, writer := io.Pipe()
|
||||||
container.stdout.AddWriter(writer)
|
container.stdout.AddWriter(writer)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ func (srv *Server) Help() string {
|
||||||
{"info", "Display system-wide information"},
|
{"info", "Display system-wide information"},
|
||||||
{"tar", "Stream the contents of a container as a tar archive"},
|
{"tar", "Stream the contents of a container as a tar archive"},
|
||||||
{"web", "Generate a web UI"},
|
{"web", "Generate a web UI"},
|
||||||
|
{"attach", "Attach to a running container"},
|
||||||
} {
|
} {
|
||||||
help += fmt.Sprintf(" %-10.10s%s\n", cmd...)
|
help += fmt.Sprintf(" %-10.10s%s\n", cmd...)
|
||||||
}
|
}
|
||||||
|
@ -507,9 +509,10 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (srv *Server) CreateContainer(img *image.Image, cmd string, args ...string) (*docker.Container, error) {
|
func (srv *Server) CreateContainer(img *image.Image, tty bool, openStdin bool, cmd string, args ...string) (*docker.Container, error) {
|
||||||
id := future.RandomId()
|
id := future.RandomId()
|
||||||
container, err := srv.containers.Create(id, cmd, args, img.Layers, &docker.Config{Hostname: id})
|
container, err := srv.containers.Create(id, cmd, args, img.Layers,
|
||||||
|
&docker.Config{Hostname: id, Tty: tty, OpenStdin: openStdin})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -520,10 +523,57 @@ func (srv *Server) CreateContainer(img *image.Image, cmd string, args ...string)
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
|
cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container")
|
||||||
|
fl_i := cmd.Bool("i", false, "Attach to stdin")
|
||||||
|
fl_o := cmd.Bool("o", true, "Attach to stdout")
|
||||||
|
fl_e := cmd.Bool("e", true, "Attach to stderr")
|
||||||
|
if err := cmd.Parse(args); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if cmd.NArg() != 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
name := cmd.Arg(0)
|
||||||
|
container := srv.containers.Get(name)
|
||||||
|
if container == nil {
|
||||||
|
return errors.New("No such container: " + name)
|
||||||
|
}
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
if *fl_i {
|
||||||
|
c_stdin, err := container.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() { io.Copy(c_stdin, stdin); wg.Add(-1); }()
|
||||||
|
}
|
||||||
|
if *fl_o {
|
||||||
|
c_stdout, err := container.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() { io.Copy(stdout, c_stdout); wg.Add(-1); }()
|
||||||
|
}
|
||||||
|
if *fl_e {
|
||||||
|
c_stderr, err := container.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() { io.Copy(stdout, c_stderr); wg.Add(-1); }()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
flags := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
|
flags := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
|
||||||
fl_attach := flags.Bool("a", false, "Attach stdin and stdout")
|
fl_attach := flags.Bool("a", false, "Attach stdin and stdout")
|
||||||
//fl_tty := flags.Bool("t", false, "Allocate a pseudo-tty")
|
fl_stdin := flags.Bool("i", false, "Keep stdin open even if not attached")
|
||||||
|
fl_tty := flags.Bool("t", false, "Allocate a pseudo-tty")
|
||||||
if err := flags.Parse(args); err != nil {
|
if err := flags.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -538,31 +588,55 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
return errors.New("No such image: " + name)
|
return errors.New("No such image: " + name)
|
||||||
}
|
}
|
||||||
// Create new container
|
// Create new container
|
||||||
container, err := srv.CreateContainer(img, cmd[0], cmd[1:]...)
|
container, err := srv.CreateContainer(img, *fl_tty, *fl_stdin, cmd[0], cmd[1:]...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Error creating container: " + err.Error())
|
return errors.New("Error creating container: " + err.Error())
|
||||||
}
|
}
|
||||||
// Run the container
|
if *fl_stdin {
|
||||||
if *fl_attach {
|
cmd_stdin, err := container.StdinPipe()
|
||||||
cmd_stdout, err := container.StdoutPipe()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if *fl_attach {
|
||||||
|
future.Go(func() error {
|
||||||
|
log.Printf("CmdRun(): start receiving stdin\n")
|
||||||
|
_, err := io.Copy(cmd_stdin, stdin);
|
||||||
|
log.Printf("CmdRun(): done receiving stdin\n")
|
||||||
|
cmd_stdin.Close()
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Run the container
|
||||||
|
if *fl_attach {
|
||||||
cmd_stderr, err := container.StderrPipe()
|
cmd_stderr, err := container.StderrPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
cmd_stdout, err := container.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := container.Start(); err != nil {
|
if err := container.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sending_stdout := future.Go(func() error { _, err := io.Copy(stdout, cmd_stdout); return err })
|
sending_stdout := future.Go(func() error {
|
||||||
sending_stderr := future.Go(func() error { _, err := io.Copy(stdout, cmd_stderr); return err })
|
_, err := io.Copy(stdout, cmd_stdout);
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
sending_stderr := future.Go(func() error {
|
||||||
|
_, err := io.Copy(stdout, cmd_stderr);
|
||||||
|
return err
|
||||||
|
})
|
||||||
err_sending_stdout := <-sending_stdout
|
err_sending_stdout := <-sending_stdout
|
||||||
err_sending_stderr := <-sending_stderr
|
err_sending_stderr := <-sending_stderr
|
||||||
if err_sending_stdout != nil {
|
if err_sending_stdout != nil {
|
||||||
return err_sending_stdout
|
return err_sending_stdout
|
||||||
}
|
}
|
||||||
|
if err_sending_stderr != nil {
|
||||||
return err_sending_stderr
|
return err_sending_stderr
|
||||||
|
}
|
||||||
|
container.Wait()
|
||||||
} else {
|
} else {
|
||||||
if err := container.Start(); err != nil {
|
if err := container.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue