зеркало из https://github.com/microsoft/docker.git
'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:
Родитель
9906a9af8f
Коммит
7a50153c32
84
container.go
84
container.go
|
@ -13,6 +13,7 @@ import (
|
|||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"github.com/kr/pty"
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
|
@ -32,6 +33,8 @@ type Container struct {
|
|||
cmd *exec.Cmd
|
||||
stdout *writeBroadcaster
|
||||
stderr *writeBroadcaster
|
||||
stdin io.ReadCloser
|
||||
stdinPipe io.WriteCloser
|
||||
|
||||
stdoutLog *bytes.Buffer
|
||||
stderrLog *bytes.Buffer
|
||||
|
@ -40,6 +43,8 @@ type Container struct {
|
|||
type Config struct {
|
||||
Hostname string
|
||||
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) {
|
||||
|
@ -59,6 +64,11 @@ func createContainer(id string, root string, command string, args []string, laye
|
|||
stdoutLog: 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.stderr.AddWriter(NopWriteCloser(container.stderrLog))
|
||||
|
||||
|
@ -94,6 +104,11 @@ func loadContainer(containerPath string) (*Container, error) {
|
|||
if err := container.Filesystem.createMountPoints(); err != nil {
|
||||
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()
|
||||
return container, nil
|
||||
}
|
||||
|
@ -180,9 +195,67 @@ func (container *Container) Start() error {
|
|||
params = append(params, container.Args...)
|
||||
|
||||
container.cmd = exec.Command("/usr/bin/lxc-start", params...)
|
||||
container.cmd.Stdout = container.stdout
|
||||
container.cmd.Stderr = container.stderr
|
||||
|
||||
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.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 {
|
||||
return err
|
||||
}
|
||||
|
@ -214,6 +287,13 @@ func (container *Container) Output() (output []byte, err error) {
|
|||
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) {
|
||||
reader, writer := io.Pipe()
|
||||
container.stdout.AddWriter(writer)
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"net/http"
|
||||
"encoding/json"
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
||||
|
@ -42,6 +43,7 @@ func (srv *Server) Help() string {
|
|||
{"info", "Display system-wide information"},
|
||||
{"tar", "Stream the contents of a container as a tar archive"},
|
||||
{"web", "Generate a web UI"},
|
||||
{"attach", "Attach to a running container"},
|
||||
} {
|
||||
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()
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -520,10 +523,57 @@ func (srv *Server) CreateContainer(img *image.Image, cmd string, args ...string)
|
|||
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 {
|
||||
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_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 {
|
||||
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)
|
||||
}
|
||||
// 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 {
|
||||
return errors.New("Error creating container: " + err.Error())
|
||||
}
|
||||
// Run the container
|
||||
if *fl_attach {
|
||||
cmd_stdout, err := container.StdoutPipe()
|
||||
if *fl_stdin {
|
||||
cmd_stdin, err := container.StdinPipe()
|
||||
if err != nil {
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd_stdout, err := container.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
sending_stdout := future.Go(func() error { _, err := io.Copy(stdout, cmd_stdout); return err })
|
||||
sending_stderr := future.Go(func() error { _, err := io.Copy(stdout, cmd_stderr); return err })
|
||||
sending_stdout := future.Go(func() error {
|
||||
_, 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_stderr := <-sending_stderr
|
||||
if err_sending_stdout != nil {
|
||||
return err_sending_stdout
|
||||
}
|
||||
return err_sending_stderr
|
||||
if err_sending_stderr != nil {
|
||||
return err_sending_stderr
|
||||
}
|
||||
container.Wait()
|
||||
} else {
|
||||
if err := container.Start(); err != nil {
|
||||
return err
|
||||
|
|
Загрузка…
Ссылка в новой задаче