Move console into execdriver

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
Michael Crosby 2014-02-21 11:47:53 -08:00
parent 81cd2054f6
commit 8c783c1c13
4 changed files with 155 additions and 79 deletions

View file

@ -10,10 +10,8 @@ import (
"github.com/dotcloud/docker/graphdriver"
"github.com/dotcloud/docker/links"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/pkg/term"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"github.com/kr/pty"
"io"
"io/ioutil"
"log"
@ -57,11 +55,11 @@ type Container struct {
Driver string
command *execdriver.Command
console execdriver.Console
stdout *utils.WriteBroadcaster
stderr *utils.WriteBroadcaster
stdin io.ReadCloser
stdinPipe io.WriteCloser
ptyMaster io.Closer
runtime *Runtime
@ -213,56 +211,6 @@ func (container *Container) generateEnvConfig(env []string) error {
return nil
}
func (container *Container) setupPty() error {
ptyMaster, ptySlave, err := pty.Open()
if err != nil {
return err
}
container.ptyMaster = ptyMaster
container.command.Stdout = ptySlave
container.command.Stderr = ptySlave
container.command.Console = ptySlave.Name()
// Copy the PTYs to our broadcasters
go func() {
defer container.stdout.CloseWriters()
utils.Debugf("startPty: begin of stdout pipe")
io.Copy(container.stdout, ptyMaster)
utils.Debugf("startPty: end of stdout pipe")
}()
// stdin
if container.Config.OpenStdin {
container.command.Stdin = ptySlave
container.command.SysProcAttr.Setctty = true
go func() {
defer container.stdin.Close()
utils.Debugf("startPty: begin of stdin pipe")
io.Copy(ptyMaster, container.stdin)
utils.Debugf("startPty: end of stdin pipe")
}()
}
return nil
}
func (container *Container) setupStd() error {
container.command.Stdout = container.stdout
container.command.Stderr = container.stderr
if container.Config.OpenStdin {
stdin, err := container.command.StdinPipe()
if err != nil {
return err
}
go func() {
defer stdin.Close()
utils.Debugf("start: begin of stdin pipe")
io.Copy(stdin, container.stdin)
utils.Debugf("start: end of stdin pipe")
}()
}
return nil
}
func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
var cStdout, cStderr io.ReadCloser
@ -593,14 +541,13 @@ func (container *Container) Start() (err error) {
}
container.waitLock = make(chan struct{})
// Setuping pipes and/or Pty
var setup func() error
if container.Config.Tty {
setup = container.setupPty
} else {
setup = container.setupStd
container.console, err = execdriver.NewConsole(
container.stdin, container.stdout, container.stderr,
container.Config.OpenStdin, container.Config.Tty)
if err != nil {
return err
}
if err := setup(); err != nil {
if err := container.console.AttachTo(container.command); err != nil {
return err
}
@ -887,22 +834,20 @@ func (container *Container) cleanup() {
link.Disable()
}
}
if container.Config.OpenStdin {
if err := container.stdin.Close(); err != nil {
utils.Errorf("%s: Error close stdin: %s", container.ID, err)
}
}
if err := container.stdout.CloseWriters(); err != nil {
if err := container.stdout.Close(); err != nil {
utils.Errorf("%s: Error close stdout: %s", container.ID, err)
}
if err := container.stderr.CloseWriters(); err != nil {
if err := container.stderr.Close(); err != nil {
utils.Errorf("%s: Error close stderr: %s", container.ID, err)
}
if container.ptyMaster != nil {
if err := container.ptyMaster.Close(); err != nil {
utils.Errorf("%s: Error closing Pty master: %s", container.ID, err)
if container.console != nil {
if err := container.console.Close(); err != nil {
utils.Errorf("%s: Error closing console: %s", container.ID, err)
}
}
@ -994,11 +939,7 @@ func (container *Container) Wait() int {
}
func (container *Container) Resize(h, w int) error {
pty, ok := container.ptyMaster.(*os.File)
if !ok {
return fmt.Errorf("ptyMaster does not have Fd() method")
}
return term.SetWinsize(pty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
return container.console.Resize(h, w)
}
func (container *Container) ExportRw() (archive.Archive, error) {
@ -1202,11 +1143,9 @@ func (container *Container) Exposes(p nat.Port) bool {
}
func (container *Container) GetPtyMaster() (*os.File, error) {
if container.ptyMaster == nil {
ttyConsole, ok := container.console.(*execdriver.TtyConsole)
if !ok {
return nil, ErrNoTTY
}
if pty, ok := container.ptyMaster.(*os.File); ok {
return pty, nil
}
return nil, ErrNotATTY
return ttyConsole.Master, nil
}

137
execdriver/console.go Normal file
View file

@ -0,0 +1,137 @@
package execdriver
import (
"github.com/dotcloud/docker/pkg/term"
"github.com/kr/pty"
"io"
"os"
)
type Console interface {
io.Closer
Resize(height, width int) error
AttachTo(command *Command) error
}
type pipes struct {
Stdin io.ReadCloser
Stdout, Stderr io.WriteCloser
}
func (p *pipes) Close() error {
if p.Stderr != nil {
p.Stdin.Close()
}
if p.Stdout != nil {
p.Stdout.Close()
}
if p.Stderr != nil {
p.Stderr.Close()
}
return nil
}
func NewConsole(stdin io.ReadCloser, stdout, stderr io.WriteCloser, useStdin, tty bool) (Console, error) {
p := &pipes{
Stdout: stdout,
Stderr: stderr,
}
if useStdin {
p.Stdin = stdin
}
if tty {
return NewTtyConsole(p)
}
return NewStdConsole(p)
}
type TtyConsole struct {
Master *os.File
Slave *os.File
pipes *pipes
}
func NewTtyConsole(p *pipes) (*TtyConsole, error) {
ptyMaster, ptySlave, err := pty.Open()
if err != nil {
return nil, err
}
tty := &TtyConsole{
Master: ptyMaster,
Slave: ptySlave,
pipes: p,
}
return tty, nil
}
func (t *TtyConsole) Resize(h, w int) error {
return term.SetWinsize(t.Master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
}
func (t *TtyConsole) AttachTo(command *Command) error {
command.Stdout = t.Slave
command.Stderr = t.Slave
command.Console = t.Slave.Name()
go func() {
defer t.pipes.Stdout.Close()
io.Copy(t.pipes.Stdout, t.Master)
}()
if t.pipes.Stdin != nil {
command.Stdin = t.Slave
command.SysProcAttr.Setctty = true
go func() {
defer t.pipes.Stdin.Close()
io.Copy(t.Master, t.pipes.Stdin)
}()
}
return nil
}
func (t *TtyConsole) Close() error {
err := t.Slave.Close()
if merr := t.Master.Close(); err == nil {
err = merr
}
return err
}
type StdConsole struct {
pipes *pipes
}
func NewStdConsole(p *pipes) (*StdConsole, error) {
return &StdConsole{p}, nil
}
func (s *StdConsole) AttachTo(command *Command) error {
command.Stdout = s.pipes.Stdout
command.Stderr = s.pipes.Stderr
if s.pipes.Stdin != nil {
stdin, err := command.StdinPipe()
if err != nil {
return err
}
go func() {
defer stdin.Close()
io.Copy(stdin, s.pipes.Stdin)
}()
}
return nil
}
func (s *StdConsole) Resize(h, w int) error {
// we do not need to reside a non tty
return nil
}
func (s *StdConsole) Close() error {
// nothing to close here
return nil
}

View file

@ -388,7 +388,7 @@ func (w *WriteBroadcaster) Write(p []byte) (n int, err error) {
return len(p), nil
}
func (w *WriteBroadcaster) CloseWriters() error {
func (w *WriteBroadcaster) Close() error {
w.Lock()
defer w.Unlock()
for sw := range w.writers {

View file

@ -110,7 +110,7 @@ func TestWriteBroadcaster(t *testing.T) {
t.Errorf("Buffer contains %v", bufferC.String())
}
writer.CloseWriters()
writer.Close()
}
type devNullCloser int