|
@@ -10,6 +10,8 @@ import (
|
|
"syscall"
|
|
"syscall"
|
|
"time"
|
|
"time"
|
|
|
|
|
|
|
|
+ "golang.org/x/net/context"
|
|
|
|
+
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/daemon/exec"
|
|
"github.com/docker/docker/daemon/exec"
|
|
"github.com/docker/docker/daemon/execdriver"
|
|
"github.com/docker/docker/daemon/execdriver"
|
|
@@ -62,8 +64,9 @@ type CommonContainer struct {
|
|
monitor *containerMonitor
|
|
monitor *containerMonitor
|
|
ExecCommands *exec.Store `json:"-"`
|
|
ExecCommands *exec.Store `json:"-"`
|
|
// logDriver for closing
|
|
// logDriver for closing
|
|
- LogDriver logger.Logger `json:"-"`
|
|
|
|
- LogCopier *logger.Copier `json:"-"`
|
|
|
|
|
|
+ LogDriver logger.Logger `json:"-"`
|
|
|
|
+ LogCopier *logger.Copier `json:"-"`
|
|
|
|
+ attachContext *attachContext
|
|
}
|
|
}
|
|
|
|
|
|
// NewBaseContainer creates a new container with its
|
|
// NewBaseContainer creates a new container with its
|
|
@@ -71,12 +74,13 @@ type CommonContainer struct {
|
|
func NewBaseContainer(id, root string) *Container {
|
|
func NewBaseContainer(id, root string) *Container {
|
|
return &Container{
|
|
return &Container{
|
|
CommonContainer: CommonContainer{
|
|
CommonContainer: CommonContainer{
|
|
- ID: id,
|
|
|
|
- State: NewState(),
|
|
|
|
- ExecCommands: exec.NewStore(),
|
|
|
|
- Root: root,
|
|
|
|
- MountPoints: make(map[string]*volume.MountPoint),
|
|
|
|
- StreamConfig: runconfig.NewStreamConfig(),
|
|
|
|
|
|
+ ID: id,
|
|
|
|
+ State: NewState(),
|
|
|
|
+ ExecCommands: exec.NewStore(),
|
|
|
|
+ Root: root,
|
|
|
|
+ MountPoints: make(map[string]*volume.MountPoint),
|
|
|
|
+ StreamConfig: runconfig.NewStreamConfig(),
|
|
|
|
+ attachContext: &attachContext{},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -345,12 +349,13 @@ func (container *Container) GetExecIDs() []string {
|
|
// Attach connects to the container's TTY, delegating to standard
|
|
// Attach connects to the container's TTY, delegating to standard
|
|
// streams or websockets depending on the configuration.
|
|
// streams or websockets depending on the configuration.
|
|
func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
|
|
func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
|
|
- return AttachStreams(container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr, keys)
|
|
|
|
|
|
+ ctx := container.InitAttachContext()
|
|
|
|
+ return AttachStreams(ctx, container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr, keys)
|
|
}
|
|
}
|
|
|
|
|
|
// AttachStreams connects streams to a TTY.
|
|
// AttachStreams connects streams to a TTY.
|
|
// Used by exec too. Should this move somewhere else?
|
|
// Used by exec too. Should this move somewhere else?
|
|
-func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
|
|
|
|
|
|
+func AttachStreams(ctx context.Context, streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
|
|
var (
|
|
var (
|
|
cStdout, cStderr io.ReadCloser
|
|
cStdout, cStderr io.ReadCloser
|
|
cStdin io.WriteCloser
|
|
cStdin io.WriteCloser
|
|
@@ -379,21 +384,6 @@ func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, t
|
|
return
|
|
return
|
|
}
|
|
}
|
|
logrus.Debugf("attach: stdin: begin")
|
|
logrus.Debugf("attach: stdin: begin")
|
|
- defer func() {
|
|
|
|
- if stdinOnce && !tty {
|
|
|
|
- cStdin.Close()
|
|
|
|
- } else {
|
|
|
|
- // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
|
|
|
|
- if cStdout != nil {
|
|
|
|
- cStdout.Close()
|
|
|
|
- }
|
|
|
|
- if cStderr != nil {
|
|
|
|
- cStderr.Close()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- wg.Done()
|
|
|
|
- logrus.Debugf("attach: stdin: end")
|
|
|
|
- }()
|
|
|
|
|
|
|
|
var err error
|
|
var err error
|
|
if tty {
|
|
if tty {
|
|
@@ -408,23 +398,26 @@ func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, t
|
|
if err != nil {
|
|
if err != nil {
|
|
logrus.Errorf("attach: stdin: %s", err)
|
|
logrus.Errorf("attach: stdin: %s", err)
|
|
errors <- err
|
|
errors <- err
|
|
- return
|
|
|
|
}
|
|
}
|
|
|
|
+ if stdinOnce && !tty {
|
|
|
|
+ cStdin.Close()
|
|
|
|
+ } else {
|
|
|
|
+ // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
|
|
|
|
+ if cStdout != nil {
|
|
|
|
+ cStdout.Close()
|
|
|
|
+ }
|
|
|
|
+ if cStderr != nil {
|
|
|
|
+ cStderr.Close()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ logrus.Debugf("attach: stdin: end")
|
|
|
|
+ wg.Done()
|
|
}()
|
|
}()
|
|
|
|
|
|
attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) {
|
|
attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) {
|
|
if stream == nil {
|
|
if stream == nil {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- defer func() {
|
|
|
|
- // Make sure stdin gets closed
|
|
|
|
- if stdin != nil {
|
|
|
|
- stdin.Close()
|
|
|
|
- }
|
|
|
|
- streamPipe.Close()
|
|
|
|
- wg.Done()
|
|
|
|
- logrus.Debugf("attach: %s: end", name)
|
|
|
|
- }()
|
|
|
|
|
|
|
|
logrus.Debugf("attach: %s: begin", name)
|
|
logrus.Debugf("attach: %s: begin", name)
|
|
_, err := io.Copy(stream, streamPipe)
|
|
_, err := io.Copy(stream, streamPipe)
|
|
@@ -435,13 +428,39 @@ func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, t
|
|
logrus.Errorf("attach: %s: %v", name, err)
|
|
logrus.Errorf("attach: %s: %v", name, err)
|
|
errors <- err
|
|
errors <- err
|
|
}
|
|
}
|
|
|
|
+ // Make sure stdin gets closed
|
|
|
|
+ if stdin != nil {
|
|
|
|
+ stdin.Close()
|
|
|
|
+ }
|
|
|
|
+ streamPipe.Close()
|
|
|
|
+ logrus.Debugf("attach: %s: end", name)
|
|
|
|
+ wg.Done()
|
|
}
|
|
}
|
|
|
|
|
|
go attachStream("stdout", stdout, cStdout)
|
|
go attachStream("stdout", stdout, cStdout)
|
|
go attachStream("stderr", stderr, cStderr)
|
|
go attachStream("stderr", stderr, cStderr)
|
|
|
|
|
|
return promise.Go(func() error {
|
|
return promise.Go(func() error {
|
|
- wg.Wait()
|
|
|
|
|
|
+ done := make(chan struct{})
|
|
|
|
+ go func() {
|
|
|
|
+ wg.Wait()
|
|
|
|
+ close(done)
|
|
|
|
+ }()
|
|
|
|
+ select {
|
|
|
|
+ case <-done:
|
|
|
|
+ case <-ctx.Done():
|
|
|
|
+ // close all pipes
|
|
|
|
+ if cStdin != nil {
|
|
|
|
+ cStdin.Close()
|
|
|
|
+ }
|
|
|
|
+ if cStdout != nil {
|
|
|
|
+ cStdout.Close()
|
|
|
|
+ }
|
|
|
|
+ if cStderr != nil {
|
|
|
|
+ cStderr.Close()
|
|
|
|
+ }
|
|
|
|
+ <-done
|
|
|
|
+ }
|
|
close(errors)
|
|
close(errors)
|
|
for err := range errors {
|
|
for err := range errors {
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -597,3 +616,31 @@ func (container *Container) UpdateMonitor(restartPolicy containertypes.RestartPo
|
|
}
|
|
}
|
|
monitor.mux.Unlock()
|
|
monitor.mux.Unlock()
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+type attachContext struct {
|
|
|
|
+ ctx context.Context
|
|
|
|
+ cancel context.CancelFunc
|
|
|
|
+ mu sync.Mutex
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// InitAttachContext initialize or returns existing context for attach calls to
|
|
|
|
+// track container liveness.
|
|
|
|
+func (container *Container) InitAttachContext() context.Context {
|
|
|
|
+ container.attachContext.mu.Lock()
|
|
|
|
+ defer container.attachContext.mu.Unlock()
|
|
|
|
+ if container.attachContext.ctx == nil {
|
|
|
|
+ container.attachContext.ctx, container.attachContext.cancel = context.WithCancel(context.Background())
|
|
|
|
+ }
|
|
|
|
+ return container.attachContext.ctx
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// CancelAttachContext cancel attach context. All attach calls should detach
|
|
|
|
+// after this call.
|
|
|
|
+func (container *Container) CancelAttachContext() {
|
|
|
|
+ container.attachContext.mu.Lock()
|
|
|
|
+ if container.attachContext.ctx != nil {
|
|
|
|
+ container.attachContext.cancel()
|
|
|
|
+ container.attachContext.ctx = nil
|
|
|
|
+ }
|
|
|
|
+ container.attachContext.mu.Unlock()
|
|
|
|
+}
|