Add detach event

If we attach to a running container and stream is closed afterwards, we
can never be sure if the container is stopped or detached. Adding a new
type of `detach` event can explicitly notify client that container is
detached, so client will know that there's no need to wait for its exit
code and it can move forward to next step now.

Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
This commit is contained in:
Zhang Wei 2016-05-22 22:04:39 +08:00
parent e2528712db
commit 83ad006d47
4 changed files with 42 additions and 12 deletions

View File

@ -48,6 +48,21 @@ var (
errInvalidNetwork = fmt.Errorf("invalid network settings while building port map info")
)
// AttachError represents errors of attach
type AttachError interface {
IsDetached() bool
}
type detachError struct{}
func (e detachError) IsDetached() bool {
return true
}
func (e detachError) Error() string {
return "detached from container"
}
// CommonContainer holds the fields for a container which are
// applicable across all platforms supported by the daemon.
type CommonContainer struct {
@ -393,7 +408,6 @@ func AttachStreams(ctx context.Context, streamConfig *runconfig.StreamConfig, op
_, err = copyEscapable(cStdin, stdin, keys)
} else {
_, err = io.Copy(cStdin, stdin)
}
if err == io.ErrClosedPipe {
err = nil
@ -492,10 +506,8 @@ func copyEscapable(dst io.Writer, src io.ReadCloser, keys []byte) (written int64
break
}
if i == len(keys)-1 {
if err := src.Close(); err != nil {
return 0, err
}
return 0, nil
src.Close()
return 0, detachError{}
}
nr, er = src.Read(buf)
}

View File

@ -73,9 +73,9 @@ func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadClose
return daemon.containerAttach(container, stdin, stdout, stderr, false, stream, nil)
}
func (daemon *Daemon) containerAttach(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
func (daemon *Daemon) containerAttach(c *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
if logs {
logDriver, err := daemon.getLogger(container)
logDriver, err := daemon.getLogger(c)
if err != nil {
return err
}
@ -105,7 +105,7 @@ func (daemon *Daemon) containerAttach(container *container.Container, stdin io.R
}
}
daemon.LogContainerEvent(container, "attach")
daemon.LogContainerEvent(c, "attach")
//stream
if stream {
@ -119,11 +119,20 @@ func (daemon *Daemon) containerAttach(container *container.Container, stdin io.R
}()
stdinPipe = r
}
<-container.Attach(stdinPipe, stdout, stderr, keys)
err := <-c.Attach(stdinPipe, stdout, stderr, keys)
if err != nil {
e, ok := err.(container.AttachError)
if ok && e.IsDetached() {
daemon.LogContainerEvent(c, "detach")
} else {
logrus.Errorf("attach failed with error: %v", err)
}
}
// If we are in stdinonce mode, wait for the process to end
// otherwise, simply return
if container.Config.StdinOnce && !container.Config.Tty {
container.WaitStop(-1 * time.Second)
if c.Config.StdinOnce && !c.Config.Tty {
c.WaitStop(-1 * time.Second)
}
}
return nil

View File

@ -222,7 +222,11 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R
return fmt.Errorf("context cancelled")
case err := <-attachErr:
if err != nil {
return fmt.Errorf("attach failed with error: %v", err)
e, ok := err.(container.AttachError)
if !ok || !e.IsDetached() {
return fmt.Errorf("attach failed with error: %v", err)
}
d.LogContainerEvent(c, "detach")
}
}
return nil

View File

@ -135,6 +135,11 @@ func (s *DockerSuite) TestRunAttachDetach(c *check.C) {
running := inspectField(c, name, "State.Running")
c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
out, _ = dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c), "-f", "container="+name)
// attach and detach event should be monitored
c.Assert(out, checker.Contains, "attach")
c.Assert(out, checker.Contains, "detach")
}
// TestRunDetach checks attaching and detaching with the escape sequence specified via flags.