From 581380cc6cd26f43fe5e69965873769d8dc739ef Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 2 Nov 2015 17:37:45 -0500 Subject: [PATCH 01/17] Move `exportContainerRw` to the daemon. Signed-off-by: David Calavera --- daemon/commit.go | 16 +++++++++++++++- daemon/container.go | 15 --------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/daemon/commit.go b/daemon/commit.go index 62c9a458f4..d92c34ba5d 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -2,6 +2,8 @@ package daemon import ( "github.com/docker/docker/image" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/runconfig" ) @@ -24,7 +26,7 @@ func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*i defer container.unpause() } - rwTar, err := container.exportContainerRw() + rwTar, err := daemon.exportContainerRw(container) if err != nil { return nil, err } @@ -49,3 +51,15 @@ func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*i container.logEvent("commit") return img, nil } + +func (daemon *Daemon) exportContainerRw(container *Container) (archive.Archive, error) { + archive, err := daemon.diff(container) + if err != nil { + return nil, err + } + return ioutils.NewReadCloserWrapper(archive, func() error { + err := archive.Close() + return err + }), + nil +} diff --git a/daemon/container.go b/daemon/container.go index bdfd9f8e54..1731e05b8d 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -225,21 +225,6 @@ func (container *Container) getRootResourcePath(path string) (string, error) { return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root) } -func (container *Container) exportContainerRw() (archive.Archive, error) { - if container.daemon == nil { - return nil, derr.ErrorCodeUnregisteredContainer.WithArgs(container.ID) - } - archive, err := container.daemon.diff(container) - if err != nil { - return nil, err - } - return ioutils.NewReadCloserWrapper(archive, func() error { - err := archive.Close() - return err - }), - nil -} - // Start prepares the container to run by setting up everything the // container needs, such as storage and networking, as well as links // between containers. The container is left waiting for a signal to From 4f2a5ba360d0b00213d31f50a5be074c89124c52 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 2 Nov 2015 18:25:26 -0500 Subject: [PATCH 02/17] Decouple daemon and container to stop and kill containers. Signed-off-by: David Calavera --- builder/builder.go | 2 + builder/dockerfile/internals.go | 2 +- daemon/container.go | 139 +------------------------------- daemon/container_unix.go | 2 +- daemon/daemon.go | 6 +- daemon/daemonbuilder/builder.go | 5 ++ daemon/delete.go | 4 +- daemon/events.go | 10 +++ daemon/kill.go | 89 +++++++++++++++++++- daemon/restart.go | 26 +++++- daemon/state.go | 4 +- daemon/stop.go | 37 ++++++++- 12 files changed, 177 insertions(+), 149 deletions(-) create mode 100644 daemon/events.go diff --git a/builder/builder.go b/builder/builder.go index f693aef9ba..601e230031 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -128,6 +128,8 @@ type Docker interface { // Release releases a list of images that were retained for the time of a build. // TODO: remove Release(sessionID string, activeImages []string) + // Kill stops the container execution abruptly. + Kill(c *daemon.Container) error } // ImageCache abstracts an image cache store. diff --git a/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index 2162cfdc7c..b96772f228 100644 --- a/builder/dockerfile/internals.go +++ b/builder/dockerfile/internals.go @@ -559,7 +559,7 @@ func (b *Builder) run(c *daemon.Container) error { select { case <-b.cancelled: logrus.Debugln("Build cancelled, killing and removing container:", c.ID) - c.Kill() + b.docker.Kill(c) b.removeContainer(c.ID) case <-finished: } diff --git a/daemon/container.go b/daemon/container.go index 1731e05b8d..f90dde799e 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -337,51 +337,10 @@ func (container *Container) cleanup() { } } -// killSig sends the container the given signal. This wrapper for the -// host specific kill command prepares the container before attempting -// to send the signal. An error is returned if the container is paused -// or not running, or if there is a problem returned from the -// underlying kill command. -func (container *Container) killSig(sig int) error { - logrus.Debugf("Sending %d to %s", sig, container.ID) - container.Lock() - defer container.Unlock() - - // We could unpause the container for them rather than returning this error - if container.Paused { - return derr.ErrorCodeUnpauseContainer.WithArgs(container.ID) - } - - if !container.Running { - return derr.ErrorCodeNotRunning.WithArgs(container.ID) - } - - // signal to the monitor that it should not restart the container - // after we send the kill signal +// ExitOnNext signals to the monitor that it should not restart the container +// after we send the kill signal. +func (container *Container) ExitOnNext() { container.monitor.ExitOnNext() - - // if the container is currently restarting we do not need to send the signal - // to the process. Telling the monitor that it should exit on it's next event - // loop is enough - if container.Restarting { - return nil - } - - if err := container.daemon.kill(container, sig); err != nil { - return err - } - container.logEvent("kill") - return nil -} - -// Wrapper aroung killSig() suppressing "no such process" error. -func (container *Container) killPossiblyDeadProcess(sig int) error { - err := container.killSig(sig) - if err == syscall.ESRCH { - logrus.Debugf("Cannot kill process (pid=%d) with signal %d: no such process.", container.getPID(), sig) - return nil - } - return err } func (container *Container) pause() error { @@ -428,98 +387,6 @@ func (container *Container) unpause() error { return nil } -// Kill forcefully terminates a container. -func (container *Container) Kill() error { - if !container.IsRunning() { - return derr.ErrorCodeNotRunning.WithArgs(container.ID) - } - - // 1. Send SIGKILL - if err := container.killPossiblyDeadProcess(int(syscall.SIGKILL)); err != nil { - // While normally we might "return err" here we're not going to - // because if we can't stop the container by this point then - // its probably because its already stopped. Meaning, between - // the time of the IsRunning() call above and now it stopped. - // Also, since the err return will be exec driver specific we can't - // look for any particular (common) error that would indicate - // that the process is already dead vs something else going wrong. - // So, instead we'll give it up to 2 more seconds to complete and if - // by that time the container is still running, then the error - // we got is probably valid and so we return it to the caller. - - if container.IsRunning() { - container.WaitStop(2 * time.Second) - if container.IsRunning() { - return err - } - } - } - - // 2. Wait for the process to die, in last resort, try to kill the process directly - if err := killProcessDirectly(container); err != nil { - return err - } - - container.WaitStop(-1 * time.Second) - return nil -} - -// Stop halts a container by sending a stop signal, waiting for the given -// duration in seconds, and then calling SIGKILL and waiting for the -// process to exit. If a negative duration is given, Stop will wait -// for the initial signal forever. If the container is not running Stop returns -// immediately. -func (container *Container) Stop(seconds int) error { - if !container.IsRunning() { - return nil - } - - // 1. Send a SIGTERM - if err := container.killPossiblyDeadProcess(container.stopSignal()); err != nil { - logrus.Infof("Failed to send SIGTERM to the process, force killing") - if err := container.killPossiblyDeadProcess(9); err != nil { - return err - } - } - - // 2. Wait for the process to exit on its own - if _, err := container.WaitStop(time.Duration(seconds) * time.Second); err != nil { - logrus.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds) - // 3. If it doesn't, then send SIGKILL - if err := container.Kill(); err != nil { - container.WaitStop(-1 * time.Second) - logrus.Warn(err) // Don't return error because we only care that container is stopped, not what function stopped it - } - } - - container.logEvent("stop") - return nil -} - -// Restart attempts to gracefully stop and then start the -// container. When stopping, wait for the given duration in seconds to -// gracefully stop, before forcefully terminating the container. If -// given a negative duration, wait forever for a graceful stop. -func (container *Container) Restart(seconds int) error { - // Avoid unnecessarily unmounting and then directly mounting - // the container when the container stops and then starts - // again - if err := container.Mount(); err == nil { - defer container.Unmount() - } - - if err := container.Stop(seconds); err != nil { - return err - } - - if err := container.Start(); err != nil { - return err - } - - container.logEvent("restart") - return nil -} - // Resize changes the TTY of the process running inside the container // to the given height and width. The container must be running. func (container *Container) Resize(h, w int) error { diff --git a/daemon/container_unix.go b/daemon/container_unix.go index 6cde5705b2..9aef3f33b1 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -64,7 +64,7 @@ type Container struct { func killProcessDirectly(container *Container) error { if _, err := container.WaitStop(10 * time.Second); err != nil { // Ensure that we don't kill ourselves - if pid := container.getPID(); pid != 0 { + if pid := container.GetPID(); pid != 0 { logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(container.ID)) if err := syscall.Kill(pid, 9); err != nil { if err != syscall.ESRCH { diff --git a/daemon/daemon.go b/daemon/daemon.go index dec9236884..138a56b7a3 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -838,7 +838,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo return d, nil } -func stopContainer(c *Container) error { +func (daemon *Daemon) shutdownContainer(c *Container) error { // TODO(windows): Handle docker restart with paused containers if c.isPaused() { // To terminate a process in freezer cgroup, we should send @@ -869,7 +869,7 @@ func stopContainer(c *Container) error { } } // If container failed to exit in 10 seconds of SIGTERM, then using the force - if err := c.Stop(10); err != nil { + if err := daemon.containerStop(c, 10); err != nil { return fmt.Errorf("Stop container %s with error: %v", c.ID, err) } @@ -891,7 +891,7 @@ func (daemon *Daemon) Shutdown() error { group.Add(1) go func(c *Container) { defer group.Done() - if err := stopContainer(c); err != nil { + if err := daemon.shutdownContainer(c); err != nil { logrus.Errorf("Stop container error: %v", err) return } diff --git a/daemon/daemonbuilder/builder.go b/daemon/daemonbuilder/builder.go index fab97face2..fcfcf5e564 100644 --- a/daemon/daemonbuilder/builder.go +++ b/daemon/daemonbuilder/builder.go @@ -205,6 +205,11 @@ func (d Docker) GetCachedImage(imgID string, cfg *runconfig.Config) (string, err return cache.ID, nil } +// Kill stops the container execution abruptly. +func (d Docker) Kill(container *daemon.Container) error { + return d.Daemon.Kill(container) +} + // Following is specific to builder contexts // DetectContextFromRemoteURL returns a context and in certain cases the name of the dockerfile to be used diff --git a/daemon/delete.go b/daemon/delete.go index 49ccc72779..a2e7e5fa82 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -71,7 +71,7 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) { if !forceRemove { return derr.ErrorCodeRmRunning } - if err := container.Kill(); err != nil { + if err := daemon.Kill(container); err != nil { return derr.ErrorCodeRmFailed.WithArgs(err) } } @@ -90,7 +90,7 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) { // if stats are currently getting collected. daemon.statsCollector.stopCollection(container) - if err = container.Stop(3); err != nil { + if err = daemon.containerStop(container, 3); err != nil { return err } diff --git a/daemon/events.go b/daemon/events.go new file mode 100644 index 0000000000..be6d0c5b67 --- /dev/null +++ b/daemon/events.go @@ -0,0 +1,10 @@ +package daemon + +// logContainerEvent generates an event related to a container. +func (daemon *Daemon) logContainerEvent(container *Container, action string) { + daemon.EventsService.Log( + action, + container.ID, + container.Config.Image, + ) +} diff --git a/daemon/kill.go b/daemon/kill.go index dc8f23be2d..91b16ad389 100644 --- a/daemon/kill.go +++ b/daemon/kill.go @@ -4,7 +4,10 @@ import ( "fmt" "runtime" "syscall" + "time" + "github.com/Sirupsen/logrus" + derr "github.com/docker/docker/errors" "github.com/docker/docker/pkg/signal" ) @@ -24,14 +27,96 @@ func (daemon *Daemon) ContainerKill(name string, sig uint64) error { // If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait()) if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL { - if err := container.Kill(); err != nil { + if err := daemon.Kill(container); err != nil { return err } } else { // Otherwise, just send the requested signal - if err := container.killSig(int(sig)); err != nil { + if err := daemon.killWithSignal(container, int(sig)); err != nil { return err } } return nil } + +// killWithSignal sends the container the given signal. This wrapper for the +// host specific kill command prepares the container before attempting +// to send the signal. An error is returned if the container is paused +// or not running, or if there is a problem returned from the +// underlying kill command. +func (daemon *Daemon) killWithSignal(container *Container, sig int) error { + logrus.Debugf("Sending %d to %s", sig, container.ID) + container.Lock() + defer container.Unlock() + + // We could unpause the container for them rather than returning this error + if container.Paused { + return derr.ErrorCodeUnpauseContainer.WithArgs(container.ID) + } + + if !container.Running { + return derr.ErrorCodeNotRunning.WithArgs(container.ID) + } + + container.ExitOnNext() + + // if the container is currently restarting we do not need to send the signal + // to the process. Telling the monitor that it should exit on it's next event + // loop is enough + if container.Restarting { + return nil + } + + if err := daemon.kill(container, sig); err != nil { + return err + } + + daemon.logContainerEvent(container, "kill") + return nil +} + +// Kill forcefully terminates a container. +func (daemon *Daemon) Kill(container *Container) error { + if !container.IsRunning() { + return derr.ErrorCodeNotRunning.WithArgs(container.ID) + } + + // 1. Send SIGKILL + if err := daemon.killPossiblyDeadProcess(container, int(syscall.SIGKILL)); err != nil { + // While normally we might "return err" here we're not going to + // because if we can't stop the container by this point then + // its probably because its already stopped. Meaning, between + // the time of the IsRunning() call above and now it stopped. + // Also, since the err return will be exec driver specific we can't + // look for any particular (common) error that would indicate + // that the process is already dead vs something else going wrong. + // So, instead we'll give it up to 2 more seconds to complete and if + // by that time the container is still running, then the error + // we got is probably valid and so we return it to the caller. + + if container.IsRunning() { + container.WaitStop(2 * time.Second) + if container.IsRunning() { + return err + } + } + } + + // 2. Wait for the process to die, in last resort, try to kill the process directly + if err := killProcessDirectly(container); err != nil { + return err + } + + container.WaitStop(-1 * time.Second) + return nil +} + +// killPossibleDeadProcess is a wrapper aroung killSig() suppressing "no such process" error. +func (daemon *Daemon) killPossiblyDeadProcess(container *Container, sig int) error { + err := daemon.killWithSignal(container, sig) + if err == syscall.ESRCH { + logrus.Debugf("Cannot kill process (pid=%d) with signal %d: no such process.", container.GetPID(), sig) + return nil + } + return err +} diff --git a/daemon/restart.go b/daemon/restart.go index 86ec88dbb1..844218681b 100644 --- a/daemon/restart.go +++ b/daemon/restart.go @@ -15,8 +15,32 @@ func (daemon *Daemon) ContainerRestart(name string, seconds int) error { if err != nil { return err } - if err := container.Restart(seconds); err != nil { + if err := daemon.containerRestart(container, seconds); err != nil { return derr.ErrorCodeCantRestart.WithArgs(name, err) } return nil } + +// containerRestart attempts to gracefully stop and then start the +// container. When stopping, wait for the given duration in seconds to +// gracefully stop, before forcefully terminating the container. If +// given a negative duration, wait forever for a graceful stop. +func (daemon *Daemon) containerRestart(container *Container, seconds int) error { + // Avoid unnecessarily unmounting and then directly mounting + // the container when the container stops and then starts + // again + if err := container.Mount(); err == nil { + defer container.Unmount() + } + + if err := daemon.containerStop(container, seconds); err != nil { + return err + } + + if err := container.Start(); err != nil { + return err + } + + daemon.logContainerEvent(container, "restart") + return nil +} diff --git a/daemon/state.go b/daemon/state.go index 5adeb77e23..b9231f2a6c 100644 --- a/daemon/state.go +++ b/daemon/state.go @@ -134,7 +134,7 @@ func (s *State) waitRunning(timeout time.Duration) (int, error) { if err := wait(waitChan, timeout); err != nil { return -1, err } - return s.getPID(), nil + return s.GetPID(), nil } // WaitStop waits until state is stopped. If state already stopped it returns @@ -164,7 +164,7 @@ func (s *State) IsRunning() bool { } // GetPID holds the process id of a container. -func (s *State) getPID() int { +func (s *State) GetPID() int { s.Lock() res := s.Pid s.Unlock() diff --git a/daemon/stop.go b/daemon/stop.go index 9e33315216..17629acdd6 100644 --- a/daemon/stop.go +++ b/daemon/stop.go @@ -1,6 +1,9 @@ package daemon import ( + "time" + + "github.com/Sirupsen/logrus" derr "github.com/docker/docker/errors" ) @@ -18,8 +21,40 @@ func (daemon *Daemon) ContainerStop(name string, seconds int) error { if !container.IsRunning() { return derr.ErrorCodeStopped } - if err := container.Stop(seconds); err != nil { + if err := daemon.containerStop(container, seconds); err != nil { return derr.ErrorCodeCantStop.WithArgs(name, err) } return nil } + +// containerStop halts a container by sending a stop signal, waiting for the given +// duration in seconds, and then calling SIGKILL and waiting for the +// process to exit. If a negative duration is given, Stop will wait +// for the initial signal forever. If the container is not running Stop returns +// immediately. +func (daemon *Daemon) containerStop(container *Container, seconds int) error { + if !container.IsRunning() { + return nil + } + + // 1. Send a SIGTERM + if err := daemon.killPossiblyDeadProcess(container, container.stopSignal()); err != nil { + logrus.Infof("Failed to send SIGTERM to the process, force killing") + if err := daemon.killPossiblyDeadProcess(container, 9); err != nil { + return err + } + } + + // 2. Wait for the process to exit on its own + if _, err := container.WaitStop(time.Duration(seconds) * time.Second); err != nil { + logrus.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds) + // 3. If it doesn't, then send SIGKILL + if err := daemon.Kill(container); err != nil { + container.WaitStop(-1 * time.Second) + logrus.Warn(err) // Don't return error because we only care that container is stopped, not what function stopped it + } + } + + daemon.logContainerEvent(container, "stop") + return nil +} From 9f79cfdb2f1f6aeb64c84e7cb877fc38d052d187 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 2 Nov 2015 18:39:39 -0500 Subject: [PATCH 03/17] Decouple daemon and container to pause and unpause containers. Signed-off-by: David Calavera --- daemon/commit.go | 4 ++-- daemon/container.go | 44 -------------------------------------------- daemon/daemon.go | 6 +++--- daemon/pause.go | 26 +++++++++++++++++++++++++- daemon/unpause.go | 26 +++++++++++++++++++++++++- 5 files changed, 55 insertions(+), 51 deletions(-) diff --git a/daemon/commit.go b/daemon/commit.go index d92c34ba5d..fd11fc4683 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -22,8 +22,8 @@ type ContainerCommitConfig struct { // The image can optionally be tagged into a repository. func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*image.Image, error) { if c.Pause && !container.isPaused() { - container.pause() - defer container.unpause() + daemon.containerPause(container) + defer daemon.containerUnpause(container) } rwTar, err := daemon.exportContainerRw(container) diff --git a/daemon/container.go b/daemon/container.go index f90dde799e..b17429682a 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -343,50 +343,6 @@ func (container *Container) ExitOnNext() { container.monitor.ExitOnNext() } -func (container *Container) pause() error { - container.Lock() - defer container.Unlock() - - // We cannot Pause the container which is not running - if !container.Running { - return derr.ErrorCodeNotRunning.WithArgs(container.ID) - } - - // We cannot Pause the container which is already paused - if container.Paused { - return derr.ErrorCodeAlreadyPaused.WithArgs(container.ID) - } - - if err := container.daemon.execDriver.Pause(container.command); err != nil { - return err - } - container.Paused = true - container.logEvent("pause") - return nil -} - -func (container *Container) unpause() error { - container.Lock() - defer container.Unlock() - - // We cannot unpause the container which is not running - if !container.Running { - return derr.ErrorCodeNotRunning.WithArgs(container.ID) - } - - // We cannot unpause the container which is not paused - if !container.Paused { - return derr.ErrorCodeNotPaused.WithArgs(container.ID) - } - - if err := container.daemon.execDriver.Unpause(container.command); err != nil { - return err - } - container.Paused = false - container.logEvent("unpause") - return nil -} - // Resize changes the TTY of the process running inside the container // to the given height and width. The container must be running. func (container *Container) Resize(h, w int) error { diff --git a/daemon/daemon.go b/daemon/daemon.go index 138a56b7a3..ad1b2063fd 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -849,10 +849,10 @@ func (daemon *Daemon) shutdownContainer(c *Container) error { if !ok { return fmt.Errorf("System doesn not support SIGTERM") } - if err := c.daemon.kill(c, int(sig)); err != nil { + if err := daemon.kill(c, int(sig)); err != nil { return fmt.Errorf("sending SIGTERM to container %s with error: %v", c.ID, err) } - if err := c.unpause(); err != nil { + if err := daemon.containerUnpause(c); err != nil { return fmt.Errorf("Failed to unpause container %s with error: %v", c.ID, err) } if _, err := c.WaitStop(10 * time.Second); err != nil { @@ -861,7 +861,7 @@ func (daemon *Daemon) shutdownContainer(c *Container) error { if !ok { return fmt.Errorf("System does not support SIGKILL") } - if err := c.daemon.kill(c, int(sig)); err != nil { + if err := daemon.kill(c, int(sig)); err != nil { logrus.Errorf("Failed to SIGKILL container %s", c.ID) } c.WaitStop(-1 * time.Second) diff --git a/daemon/pause.go b/daemon/pause.go index 385e93f9f9..4182e92e2f 100644 --- a/daemon/pause.go +++ b/daemon/pause.go @@ -11,9 +11,33 @@ func (daemon *Daemon) ContainerPause(name string) error { return err } - if err := container.pause(); err != nil { + if err := daemon.containerPause(container); err != nil { return derr.ErrorCodePauseError.WithArgs(name, err) } return nil } + +// containerPause pauses the container execution without stopping the process. +// The execution can be resumed by calling containerUnpause. +func (daemon *Daemon) containerPause(container *Container) error { + container.Lock() + defer container.Unlock() + + // We cannot Pause the container which is not running + if !container.Running { + return derr.ErrorCodeNotRunning.WithArgs(container.ID) + } + + // We cannot Pause the container which is already paused + if container.Paused { + return derr.ErrorCodeAlreadyPaused.WithArgs(container.ID) + } + + if err := daemon.execDriver.Pause(container.command); err != nil { + return err + } + container.Paused = true + daemon.logContainerEvent(container, "pause") + return nil +} diff --git a/daemon/unpause.go b/daemon/unpause.go index 6dd41c511c..4112494fc4 100644 --- a/daemon/unpause.go +++ b/daemon/unpause.go @@ -11,9 +11,33 @@ func (daemon *Daemon) ContainerUnpause(name string) error { return err } - if err := container.unpause(); err != nil { + if err := daemon.containerUnpause(container); err != nil { return derr.ErrorCodeCantUnpause.WithArgs(name, err) } return nil } + +// containerUnpause resumes the container execution after the container is paused. +func (daemon *Daemon) containerUnpause(container *Container) error { + container.Lock() + defer container.Unlock() + + // We cannot unpause the container which is not running + if !container.Running { + return derr.ErrorCodeNotRunning.WithArgs(container.ID) + } + + // We cannot unpause the container which is not paused + if !container.Paused { + return derr.ErrorCodeNotPaused.WithArgs(container.ID) + } + + if err := daemon.execDriver.Unpause(container.command); err != nil { + return err + } + + container.Paused = false + daemon.logContainerEvent(container, "unpause") + return nil +} From 1c94f5f53a47997f76cafb633092fce9dbdab3ea Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 2 Nov 2015 18:53:26 -0500 Subject: [PATCH 04/17] Decouple daemon and container to export containers. Signed-off-by: David Calavera --- daemon/container.go | 24 ------------------------ daemon/export.go | 28 +++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/daemon/container.go b/daemon/container.go index b17429682a..66c1a97460 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -356,30 +356,6 @@ func (container *Container) Resize(h, w int) error { return nil } -func (container *Container) export() (archive.Archive, error) { - if err := container.Mount(); err != nil { - return nil, err - } - - uidMaps, gidMaps := container.daemon.GetUIDGIDMaps() - archive, err := archive.TarWithOptions(container.basefs, &archive.TarOptions{ - Compression: archive.Uncompressed, - UIDMaps: uidMaps, - GIDMaps: gidMaps, - }) - if err != nil { - container.Unmount() - return nil, err - } - arch := ioutils.NewReadCloserWrapper(archive, func() error { - err := archive.Close() - container.Unmount() - return err - }) - container.logEvent("export") - return arch, err -} - // Mount sets container.basefs func (container *Container) Mount() error { return container.daemon.Mount(container) diff --git a/daemon/export.go b/daemon/export.go index d470e5d584..cd31a128e4 100644 --- a/daemon/export.go +++ b/daemon/export.go @@ -4,6 +4,8 @@ import ( "io" derr "github.com/docker/docker/errors" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/ioutils" ) // ContainerExport writes the contents of the container to the given @@ -14,7 +16,7 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error { return err } - data, err := container.export() + data, err := daemon.containerExport(container) if err != nil { return derr.ErrorCodeExportFailed.WithArgs(name, err) } @@ -26,3 +28,27 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error { } return nil } + +func (daemon *Daemon) containerExport(container *Container) (archive.Archive, error) { + if err := daemon.Mount(container); err != nil { + return nil, err + } + + uidMaps, gidMaps := daemon.GetUIDGIDMaps() + archive, err := archive.TarWithOptions(container.basefs, &archive.TarOptions{ + Compression: archive.Uncompressed, + UIDMaps: uidMaps, + GIDMaps: gidMaps, + }) + if err != nil { + daemon.unmount(container) + return nil, err + } + arch := ioutils.NewReadCloserWrapper(archive, func() error { + err := archive.Close() + container.Unmount() + return err + }) + daemon.logContainerEvent(container, "export") + return arch, err +} From 3a497650464b40191bd2cde348e676acf4efe270 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 2 Nov 2015 20:06:09 -0500 Subject: [PATCH 05/17] Decouple daemon and container to mount and unmount filesystems. Side effects: - Decouple daemon and container to start containers. - Decouple daemon and container to copy files. Signed-off-by: David Calavera --- builder/builder.go | 6 ++ builder/dockerfile/dispatchers.go | 4 +- builder/dockerfile/internals.go | 10 ++-- daemon/archive.go | 34 +++++------ daemon/container.go | 93 ++----------------------------- daemon/container_unix.go | 30 ++-------- daemon/container_windows.go | 28 +--------- daemon/create.go | 6 +- daemon/daemon.go | 7 ++- daemon/daemon_unix.go | 14 +++++ daemon/daemon_windows.go | 23 ++++++++ daemon/daemonbuilder/builder.go | 18 +++++- daemon/export.go | 4 +- daemon/inspect.go | 2 +- daemon/list.go | 2 +- daemon/restart.go | 6 +- daemon/start.go | 77 ++++++++++++++++++++++++- 17 files changed, 188 insertions(+), 176 deletions(-) diff --git a/builder/builder.go b/builder/builder.go index 601e230031..c333b6a309 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -130,6 +130,12 @@ type Docker interface { Release(sessionID string, activeImages []string) // Kill stops the container execution abruptly. Kill(c *daemon.Container) error + // Mount mounts the root filesystem for the container. + Mount(c *daemon.Container) error + // Unmount unmounts the root filesystem for the container. + Unmount(c *daemon.Container) error + // Start starts a new container + Start(c *daemon.Container) error } // ImageCache abstracts an image cache store. diff --git a/builder/dockerfile/dispatchers.go b/builder/dockerfile/dispatchers.go index 750d19a7a7..4322573d03 100644 --- a/builder/dockerfile/dispatchers.go +++ b/builder/dockerfile/dispatchers.go @@ -399,8 +399,8 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) // Ensure that we keep the container mounted until the commit // to avoid unmounting and then mounting directly again - c.Mount() - defer c.Unmount() + b.docker.Mount(c) + defer b.docker.Unmount(c) err = b.run(c) if err != nil { diff --git a/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index b96772f228..51caee8d79 100644 --- a/builder/dockerfile/internals.go +++ b/builder/dockerfile/internals.go @@ -67,10 +67,10 @@ func (b *Builder) commit(id string, autoCmd *stringutils.StrSlice, comment strin } id = container.ID - if err := container.Mount(); err != nil { + if err := b.docker.Mount(container); err != nil { return err } - defer container.Unmount() + defer b.docker.Unmount(container) } container, err := b.docker.Container(id) @@ -201,7 +201,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowLocalD if err != nil { return err } - defer container.Unmount() + defer b.docker.Unmount(container) b.tmpContainers[container.ID] = struct{}{} comment := fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest) @@ -524,7 +524,7 @@ func (b *Builder) create() (*daemon.Container, error) { if err != nil { return nil, err } - defer c.Unmount() + defer b.docker.Unmount(c) for _, warning := range warnings { fmt.Fprintf(b.Stdout, " ---> [Warning] %s\n", warning) } @@ -549,7 +549,7 @@ func (b *Builder) run(c *daemon.Container) error { } //start the container - if err := c.Start(); err != nil { + if err := b.docker.Start(c); err != nil { return err } diff --git a/daemon/archive.go b/daemon/archive.go index a5db99e211..c14467aef4 100644 --- a/daemon/archive.go +++ b/daemon/archive.go @@ -30,7 +30,7 @@ func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, err res = res[1:] } - return container.copy(res) + return daemon.containerCopy(container, res) } // ContainerStatPath stats the filesystem resource at the specified path in the @@ -41,7 +41,7 @@ func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.C return nil, err } - return container.StatPath(path) + return daemon.containerStatPath(container, path) } // ContainerArchivePath creates an archive of the filesystem resource at the @@ -53,7 +53,7 @@ func (daemon *Daemon) ContainerArchivePath(name string, path string) (content io return nil, nil, err } - return container.ArchivePath(path) + return daemon.containerArchivePath(container, path) } // ContainerExtractToDir extracts the given archive to the specified location @@ -68,7 +68,7 @@ func (daemon *Daemon) ContainerExtractToDir(name, path string, noOverwriteDirNon return err } - return container.ExtractToDir(path, noOverwriteDirNonDir, content) + return daemon.containerExtractToDir(container, path, noOverwriteDirNonDir, content) } // resolvePath resolves the given path in the container to a resource on the @@ -131,16 +131,16 @@ func (container *Container) statPath(resolvedPath, absPath string) (stat *types. }, nil } -// StatPath stats the filesystem resource at the specified path in this +// containerStatPath stats the filesystem resource at the specified path in this // container. Returns stat info about the resource. -func (container *Container) StatPath(path string) (stat *types.ContainerPathStat, err error) { +func (daemon *Daemon) containerStatPath(container *Container, path string) (stat *types.ContainerPathStat, err error) { container.Lock() defer container.Unlock() - if err = container.Mount(); err != nil { + if err = daemon.Mount(container); err != nil { return nil, err } - defer container.Unmount() + defer daemon.Unmount(container) err = container.mountVolumes() defer container.unmountVolumes(true) @@ -156,10 +156,10 @@ func (container *Container) StatPath(path string) (stat *types.ContainerPathStat return container.statPath(resolvedPath, absPath) } -// ArchivePath creates an archive of the filesystem resource at the specified +// containerArchivePath creates an archive of the filesystem resource at the specified // path in this container. Returns a tar archive of the resource and stat info // about the resource. -func (container *Container) ArchivePath(path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) { +func (daemon *Daemon) containerArchivePath(container *Container, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) { container.Lock() defer func() { @@ -171,7 +171,7 @@ func (container *Container) ArchivePath(path string) (content io.ReadCloser, sta } }() - if err = container.Mount(); err != nil { + if err = daemon.Mount(container); err != nil { return nil, nil, err } @@ -180,7 +180,7 @@ func (container *Container) ArchivePath(path string) (content io.ReadCloser, sta // unmount any volumes container.unmountVolumes(true) // unmount the container's rootfs - container.Unmount() + daemon.Unmount(container) } }() @@ -214,7 +214,7 @@ func (container *Container) ArchivePath(path string) (content io.ReadCloser, sta content = ioutils.NewReadCloserWrapper(data, func() error { err := data.Close() container.unmountVolumes(true) - container.Unmount() + daemon.Unmount(container) container.Unlock() return err }) @@ -224,20 +224,20 @@ func (container *Container) ArchivePath(path string) (content io.ReadCloser, sta return content, stat, nil } -// ExtractToDir extracts the given tar archive to the specified location in the +// containerExtractToDir extracts the given tar archive to the specified location in the // filesystem of this container. The given path must be of a directory in the // container. If it is not, the error will be ErrExtractPointNotDirectory. If // noOverwriteDirNonDir is true then it will be an error if unpacking the // given content would cause an existing directory to be replaced with a non- // directory and vice versa. -func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool, content io.Reader) (err error) { +func (daemon *Daemon) containerExtractToDir(container *Container, path string, noOverwriteDirNonDir bool, content io.Reader) (err error) { container.Lock() defer container.Unlock() - if err = container.Mount(); err != nil { + if err = daemon.Mount(container); err != nil { return err } - defer container.Unmount() + defer daemon.Unmount(container) err = container.mountVolumes() defer container.unmountVolumes(true) diff --git a/daemon/container.go b/daemon/container.go index 66c1a97460..64883450cc 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -225,76 +225,6 @@ func (container *Container) getRootResourcePath(path string) (string, error) { return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root) } -// Start prepares the container to run by setting up everything the -// container needs, such as storage and networking, as well as links -// between containers. The container is left waiting for a signal to -// begin running. -func (container *Container) Start() (err error) { - container.Lock() - defer container.Unlock() - - if container.Running { - return nil - } - - if container.removalInProgress || container.Dead { - return derr.ErrorCodeContainerBeingRemoved - } - - // if we encounter an error during start we need to ensure that any other - // setup has been cleaned up properly - defer func() { - if err != nil { - container.setError(err) - // if no one else has set it, make sure we don't leave it at zero - if container.ExitCode == 0 { - container.ExitCode = 128 - } - container.toDisk() - container.cleanup() - container.logEvent("die") - } - }() - - if err := container.conditionalMountOnStart(); err != nil { - return err - } - - // Make sure NetworkMode has an acceptable value. We do this to ensure - // backwards API compatibility. - container.hostConfig = runconfig.SetDefaultNetModeIfBlank(container.hostConfig) - - if err := container.initializeNetworking(); err != nil { - return err - } - linkedEnv, err := container.setupLinkedContainers() - if err != nil { - return err - } - if err := container.setupWorkingDirectory(); err != nil { - return err - } - env := container.createDaemonEnvironment(linkedEnv) - if err := populateCommand(container, env); err != nil { - return err - } - - if !container.hostConfig.IpcMode.IsContainer() && !container.hostConfig.IpcMode.IsHost() { - if err := container.setupIpcDirs(); err != nil { - return err - } - } - - mounts, err := container.setupMounts() - if err != nil { - return err - } - mounts = append(mounts, container.ipcMounts()...) - - container.command.Mounts = mounts - return container.waitForStart() -} - // streamConfig.StdinPipe returns a WriteCloser which can be used to feed data // to the standard input of the container's active process. // Container.StdoutPipe and Container.StderrPipe each return a ReadCloser @@ -326,7 +256,7 @@ func (container *Container) cleanup() { container.unmountIpcMounts(detachMounted) - container.conditionalUnmountOnCleanup() + container.daemon.conditionalUnmountOnCleanup(container) for _, eConfig := range container.execCommands.s { container.daemon.unregisterExecCommand(eConfig) @@ -356,11 +286,6 @@ func (container *Container) Resize(h, w int) error { return nil } -// Mount sets container.basefs -func (container *Container) Mount() error { - return container.daemon.Mount(container) -} - func (container *Container) changes() ([]archive.Change, error) { container.Lock() defer container.Unlock() @@ -374,12 +299,6 @@ func (container *Container) getImage() (*image.Image, error) { return container.daemon.graph.Get(container.ImageID) } -// Unmount asks the daemon to release the layered filesystems that are -// mounted by the container. -func (container *Container) Unmount() error { - return container.daemon.unmount(container) -} - func (container *Container) hostConfigPath() (string, error) { return container.getRootResourcePath("hostconfig.json") } @@ -401,7 +320,7 @@ func validateID(id string) error { return nil } -func (container *Container) copy(resource string) (rc io.ReadCloser, err error) { +func (daemon *Daemon) containerCopy(container *Container, resource string) (rc io.ReadCloser, err error) { container.Lock() defer func() { @@ -413,7 +332,7 @@ func (container *Container) copy(resource string) (rc io.ReadCloser, err error) } }() - if err := container.Mount(); err != nil { + if err := daemon.Mount(container); err != nil { return nil, err } @@ -422,7 +341,7 @@ func (container *Container) copy(resource string) (rc io.ReadCloser, err error) // unmount any volumes container.unmountVolumes(true) // unmount the container's rootfs - container.Unmount() + daemon.Unmount(container) } }() @@ -458,11 +377,11 @@ func (container *Container) copy(resource string) (rc io.ReadCloser, err error) reader := ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() container.unmountVolumes(true) - container.Unmount() + daemon.Unmount(container) container.Unlock() return err }) - container.logEvent("copy") + daemon.logContainerEvent(container, "copy") return reader, nil } diff --git a/daemon/container_unix.go b/daemon/container_unix.go index 9aef3f33b1..8cd8989f6d 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -379,24 +379,23 @@ func mergeDevices(defaultDevices, userDevices []*configs.Device) []*configs.Devi return append(devs, userDevices...) } -// GetSize returns the real size & virtual size of the container. -func (container *Container) getSize() (int64, int64) { +// getSize returns the real size & virtual size of the container. +func (daemon *Daemon) getSize(container *Container) (int64, int64) { var ( sizeRw, sizeRootfs int64 err error - driver = container.daemon.driver ) - if err := container.Mount(); err != nil { + if err := daemon.Mount(container); err != nil { logrus.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err) return sizeRw, sizeRootfs } - defer container.Unmount() + defer daemon.Unmount(container) initID := fmt.Sprintf("%s-init", container.ID) - sizeRw, err = driver.DiffSize(container.ID, initID) + sizeRw, err = daemon.driver.DiffSize(container.ID, initID) if err != nil { - logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", driver, container.ID, err) + logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", daemon.driver, container.ID, err) // FIXME: GetSize should return an error. Not changing it now in case // there is a side-effect. sizeRw = -1 @@ -1444,20 +1443,3 @@ func (container *Container) ipcMounts() []execdriver.Mount { func detachMounted(path string) error { return syscall.Unmount(path, syscall.MNT_DETACH) } - -// conditionalMountOnStart is a platform specific helper function during the -// container start to call mount. -func (container *Container) conditionalMountOnStart() error { - if err := container.Mount(); err != nil { - return err - } - return nil -} - -// conditionalUnmountOnCleanup is a platform specific helper function called -// during the cleanup of a container to unmount. -func (container *Container) conditionalUnmountOnCleanup() { - if err := container.Unmount(); err != nil { - logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err) - } -} diff --git a/daemon/container_windows.go b/daemon/container_windows.go index ff7c1721f2..f618cd8c23 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -5,7 +5,6 @@ package daemon import ( "strings" - "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" "github.com/docker/docker/volume" @@ -143,8 +142,8 @@ func populateCommand(c *Container, env []string) error { return nil } -// GetSize returns real size & virtual size -func (container *Container) getSize() (int64, int64) { +// getSize returns real size & virtual size +func (daemon *Daemon) getSize(container *Container) (int64, int64) { // TODO Windows return 0, 0 } @@ -191,26 +190,3 @@ func (container *Container) ipcMounts() []execdriver.Mount { func getDefaultRouteMtu() (int, error) { return -1, errSystemNotSupported } - -// conditionalMountOnStart is a platform specific helper function during the -// container start to call mount. -func (container *Container) conditionalMountOnStart() error { - // We do not mount if a Hyper-V container - if !container.hostConfig.Isolation.IsHyperV() { - if err := container.Mount(); err != nil { - return err - } - } - return nil -} - -// conditionalUnmountOnCleanup is a platform specific helper function called -// during the cleanup of a container to unmount. -func (container *Container) conditionalUnmountOnCleanup() { - // We do not unmount if a Hyper-V container - if !container.hostConfig.Isolation.IsHyperV() { - if err := container.Unmount(); err != nil { - logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err) - } - } -} diff --git a/daemon/create.go b/daemon/create.go index d9aa3b29a9..584d452f71 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -114,10 +114,10 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re } } }() - if err := container.Mount(); err != nil { + if err := daemon.Mount(container); err != nil { return nil, err } - defer container.Unmount() + defer daemon.Unmount(container) if err := createContainerPlatformSpecificSettings(container, params.Config, params.HostConfig, img); err != nil { return nil, err @@ -127,7 +127,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re logrus.Errorf("Error saving new container to disk: %v", err) return nil, err } - container.logEvent("create") + daemon.logContainerEvent(container, "create") return container, nil } diff --git a/daemon/daemon.go b/daemon/daemon.go index ad1b2063fd..78e06d74da 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -234,7 +234,7 @@ func (daemon *Daemon) Register(container *Container) error { container.unmountIpcMounts(mount.Unmount) - if err := container.Unmount(); err != nil { + if err := daemon.Unmount(container); err != nil { logrus.Debugf("unmount error %s", err) } if err := container.toDiskLocking(); err != nil { @@ -349,7 +349,7 @@ func (daemon *Daemon) restore() error { if daemon.configStore.AutoRestart && container.shouldRestart() { logrus.Debugf("Starting container %s", container.ID) - if err := container.Start(); err != nil { + if err := daemon.containerStart(container); err != nil { logrus.Errorf("Failed to start container %s: %s", container.ID, err) } } @@ -947,7 +947,8 @@ func (daemon *Daemon) Mount(container *Container) error { return nil } -func (daemon *Daemon) unmount(container *Container) error { +// Unmount unsets the container base filesystem +func (daemon *Daemon) Unmount(container *Container) error { return daemon.driver.Put(container.ID) } diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 22142bcf14..cb63cc97e3 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -610,6 +610,20 @@ func (daemon *Daemon) newBaseContainer(id string) *Container { } } +// conditionalMountOnStart is a platform specific helper function during the +// container start to call mount. +func (daemon *Daemon) conditionalMountOnStart(container *Container) error { + return daemon.Mount(container) +} + +// conditionalUnmountOnCleanup is a platform specific helper function called +// during the cleanup of a container to unmount. +func (daemon *Daemon) conditionalUnmountOnCleanup(container *Container) { + if err := daemon.Unmount(container); err != nil { + logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err) + } +} + // getDefaultRouteMtu returns the MTU for the default route's interface. func getDefaultRouteMtu() (int, error) { routes, err := netlink.RouteList(nil, 0) diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 258891a51f..91a26151ea 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -154,3 +154,26 @@ func (daemon *Daemon) newBaseContainer(id string) *Container { func (daemon *Daemon) cleanupMounts() error { return nil } + +// conditionalMountOnStart is a platform specific helper function during the +// container start to call mount. +func (daemon *Daemon) conditionalMountOnStart(container *Container) error { + // We do not mount if a Hyper-V container + if !container.hostConfig.Isolation.IsHyperV() { + if err := daemon.Mount(container); err != nil { + return err + } + } + return nil +} + +// conditionalUnmountOnCleanup is a platform specific helper function called +// during the cleanup of a container to unmount. +func (daemon *Daemon) conditionalUnmountOnCleanup(container *Container) { + // We do not unmount if a Hyper-V container + if !container.hostConfig.Isolation.IsHyperV() { + if err := daemon.Unmount(container); err != nil { + logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err) + } + } +} diff --git a/daemon/daemonbuilder/builder.go b/daemon/daemonbuilder/builder.go index fcfcf5e564..4bba3b4166 100644 --- a/daemon/daemonbuilder/builder.go +++ b/daemon/daemonbuilder/builder.go @@ -96,7 +96,8 @@ func (d Docker) Create(cfg *runconfig.Config, hostCfg *runconfig.HostConfig) (*d if err != nil { return nil, ccr.Warnings, err } - return container, ccr.Warnings, container.Mount() + + return container, ccr.Warnings, d.Mount(container) } // Remove removes a container specified by `id`. @@ -210,6 +211,21 @@ func (d Docker) Kill(container *daemon.Container) error { return d.Daemon.Kill(container) } +// Mount mounts the root filesystem for the container. +func (d Docker) Mount(c *daemon.Container) error { + return d.Daemon.Mount(c) +} + +// Unmount unmounts the root filesystem for the container. +func (d Docker) Unmount(c *daemon.Container) error { + return d.Daemon.Unmount(c) +} + +// Start starts a container +func (d Docker) Start(c *daemon.Container) error { + return d.Daemon.Start(c) +} + // Following is specific to builder contexts // DetectContextFromRemoteURL returns a context and in certain cases the name of the dockerfile to be used diff --git a/daemon/export.go b/daemon/export.go index cd31a128e4..84b83c5fa5 100644 --- a/daemon/export.go +++ b/daemon/export.go @@ -41,12 +41,12 @@ func (daemon *Daemon) containerExport(container *Container) (archive.Archive, er GIDMaps: gidMaps, }) if err != nil { - daemon.unmount(container) + daemon.Unmount(container) return nil, err } arch := ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() - container.Unmount() + daemon.Unmount(container) return err }) daemon.logContainerEvent(container, "export") diff --git a/daemon/inspect.go b/daemon/inspect.go index 1268bc0168..d75e085c80 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -140,7 +140,7 @@ func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.Co sizeRootFs int64 ) if size { - sizeRw, sizeRootFs = container.getSize() + sizeRw, sizeRootFs = daemon.getSize(container) contJSONBase.SizeRw = &sizeRw contJSONBase.SizeRootFs = &sizeRootFs } diff --git a/daemon/list.go b/daemon/list.go index 054fea4a52..f6a7cc8b61 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -369,7 +369,7 @@ func (daemon *Daemon) transformContainer(container *Container, ctx *listContext) } if ctx.Size { - sizeRw, sizeRootFs := container.getSize() + sizeRw, sizeRootFs := daemon.getSize(container) newC.SizeRw = sizeRw newC.SizeRootFs = sizeRootFs } diff --git a/daemon/restart.go b/daemon/restart.go index 844218681b..a237f3a62c 100644 --- a/daemon/restart.go +++ b/daemon/restart.go @@ -29,15 +29,15 @@ func (daemon *Daemon) containerRestart(container *Container, seconds int) error // Avoid unnecessarily unmounting and then directly mounting // the container when the container stops and then starts // again - if err := container.Mount(); err == nil { - defer container.Unmount() + if err := daemon.Mount(container); err == nil { + defer daemon.Unmount(container) } if err := daemon.containerStop(container, seconds); err != nil { return err } - if err := container.Start(); err != nil { + if err := daemon.containerStart(container); err != nil { return err } diff --git a/daemon/start.go b/daemon/start.go index 6eec10da28..9326d584f5 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -44,9 +44,84 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf return err } - if err := container.Start(); err != nil { + if err := daemon.containerStart(container); err != nil { return derr.ErrorCodeCantStart.WithArgs(name, utils.GetErrorMessage(err)) } return nil } + +// Start starts a container +func (daemon *Daemon) Start(container *Container) error { + return daemon.containerStart(container) +} + +// containerStart prepares the container to run by setting up everything the +// container needs, such as storage and networking, as well as links +// between containers. The container is left waiting for a signal to +// begin running. +func (daemon *Daemon) containerStart(container *Container) (err error) { + container.Lock() + defer container.Unlock() + + if container.Running { + return nil + } + + if container.removalInProgress || container.Dead { + return derr.ErrorCodeContainerBeingRemoved + } + + // if we encounter an error during start we need to ensure that any other + // setup has been cleaned up properly + defer func() { + if err != nil { + container.setError(err) + // if no one else has set it, make sure we don't leave it at zero + if container.ExitCode == 0 { + container.ExitCode = 128 + } + container.toDisk() + container.cleanup() + daemon.logContainerEvent(container, "die") + } + }() + + if err := daemon.conditionalMountOnStart(container); err != nil { + return err + } + + // Make sure NetworkMode has an acceptable value. We do this to ensure + // backwards API compatibility. + container.hostConfig = runconfig.SetDefaultNetModeIfBlank(container.hostConfig) + + if err := container.initializeNetworking(); err != nil { + return err + } + linkedEnv, err := container.setupLinkedContainers() + if err != nil { + return err + } + if err := container.setupWorkingDirectory(); err != nil { + return err + } + env := container.createDaemonEnvironment(linkedEnv) + if err := populateCommand(container, env); err != nil { + return err + } + + if !container.hostConfig.IpcMode.IsContainer() && !container.hostConfig.IpcMode.IsHost() { + if err := container.setupIpcDirs(); err != nil { + return err + } + } + + mounts, err := container.setupMounts() + if err != nil { + return err + } + mounts = append(mounts, container.ipcMounts()...) + + container.command.Mounts = mounts + return container.waitForStart() +} From 6ea9a880dc434446c072ac23b1cab89ec3652140 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 2 Nov 2015 20:14:09 -0500 Subject: [PATCH 06/17] Remove unnecessary `Container.changes` function. Signed-off-by: David Calavera --- daemon/changes.go | 4 +++- daemon/container.go | 6 ------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/daemon/changes.go b/daemon/changes.go index 0748fa9c02..181ac5a973 100644 --- a/daemon/changes.go +++ b/daemon/changes.go @@ -9,5 +9,7 @@ func (daemon *Daemon) ContainerChanges(name string) ([]archive.Change, error) { return nil, err } - return container.changes() + container.Lock() + defer container.Unlock() + return daemon.changes(container) } diff --git a/daemon/container.go b/daemon/container.go index 64883450cc..339585d4e0 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -286,12 +286,6 @@ func (container *Container) Resize(h, w int) error { return nil } -func (container *Container) changes() ([]archive.Change, error) { - container.Lock() - defer container.Unlock() - return container.daemon.changes(container) -} - func (container *Container) getImage() (*image.Image, error) { if container.daemon == nil { return nil, derr.ErrorCodeImageUnregContainer From 89decc4446ad01a5595de1b93c17c37232f8a9aa Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 2 Nov 2015 20:15:25 -0500 Subject: [PATCH 07/17] Remove unused function `Container.getImage`. Signed-off-by: David Calavera --- daemon/container.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/daemon/container.go b/daemon/container.go index 339585d4e0..e96b273beb 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -20,7 +20,6 @@ import ( "github.com/docker/docker/daemon/logger/jsonfilelog" "github.com/docker/docker/daemon/network" derr "github.com/docker/docker/errors" - "github.com/docker/docker/image" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/broadcaster" "github.com/docker/docker/pkg/fileutils" @@ -286,13 +285,6 @@ func (container *Container) Resize(h, w int) error { return nil } -func (container *Container) getImage() (*image.Image, error) { - if container.daemon == nil { - return nil, derr.ErrorCodeImageUnregContainer - } - return container.daemon.graph.Get(container.ImageID) -} - func (container *Container) hostConfigPath() (string, error) { return container.getRootResourcePath("hostconfig.json") } From ebf707ec5fc9e942b89f6405df35cd6c0fabb15b Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 2 Nov 2015 20:19:17 -0500 Subject: [PATCH 08/17] Move `Daemon.containerCopy` to daemon/archive.go It's the only place where it's used. Signed-off-by: David Calavera --- daemon/archive.go | 65 ++++++++++++++++++++++++++++++++++++++++++++ daemon/container.go | 66 --------------------------------------------- 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/daemon/archive.go b/daemon/archive.go index c14467aef4..f9a9cb8afc 100644 --- a/daemon/archive.go +++ b/daemon/archive.go @@ -322,3 +322,68 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n return nil } + +func (daemon *Daemon) containerCopy(container *Container, resource string) (rc io.ReadCloser, err error) { + container.Lock() + + defer func() { + if err != nil { + // Wait to unlock the container until the archive is fully read + // (see the ReadCloseWrapper func below) or if there is an error + // before that occurs. + container.Unlock() + } + }() + + if err := daemon.Mount(container); err != nil { + return nil, err + } + + defer func() { + if err != nil { + // unmount any volumes + container.unmountVolumes(true) + // unmount the container's rootfs + daemon.Unmount(container) + } + }() + + if err := container.mountVolumes(); err != nil { + return nil, err + } + + basePath, err := container.GetResourcePath(resource) + if err != nil { + return nil, err + } + stat, err := os.Stat(basePath) + if err != nil { + return nil, err + } + var filter []string + if !stat.IsDir() { + d, f := filepath.Split(basePath) + basePath = d + filter = []string{f} + } else { + filter = []string{filepath.Base(basePath)} + basePath = filepath.Dir(basePath) + } + archive, err := archive.TarWithOptions(basePath, &archive.TarOptions{ + Compression: archive.Uncompressed, + IncludeFiles: filter, + }) + if err != nil { + return nil, err + } + + reader := ioutils.NewReadCloserWrapper(archive, func() error { + err := archive.Close() + container.unmountVolumes(true) + daemon.Unmount(container) + container.Unlock() + return err + }) + daemon.logContainerEvent(container, "copy") + return reader, nil +} diff --git a/daemon/container.go b/daemon/container.go index e96b273beb..f6884cdec0 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -20,7 +20,6 @@ import ( "github.com/docker/docker/daemon/logger/jsonfilelog" "github.com/docker/docker/daemon/network" derr "github.com/docker/docker/errors" - "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/broadcaster" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/ioutils" @@ -306,71 +305,6 @@ func validateID(id string) error { return nil } -func (daemon *Daemon) containerCopy(container *Container, resource string) (rc io.ReadCloser, err error) { - container.Lock() - - defer func() { - if err != nil { - // Wait to unlock the container until the archive is fully read - // (see the ReadCloseWrapper func below) or if there is an error - // before that occurs. - container.Unlock() - } - }() - - if err := daemon.Mount(container); err != nil { - return nil, err - } - - defer func() { - if err != nil { - // unmount any volumes - container.unmountVolumes(true) - // unmount the container's rootfs - daemon.Unmount(container) - } - }() - - if err := container.mountVolumes(); err != nil { - return nil, err - } - - basePath, err := container.GetResourcePath(resource) - if err != nil { - return nil, err - } - stat, err := os.Stat(basePath) - if err != nil { - return nil, err - } - var filter []string - if !stat.IsDir() { - d, f := filepath.Split(basePath) - basePath = d - filter = []string{f} - } else { - filter = []string{filepath.Base(basePath)} - basePath = filepath.Dir(basePath) - } - archive, err := archive.TarWithOptions(basePath, &archive.TarOptions{ - Compression: archive.Uncompressed, - IncludeFiles: filter, - }) - if err != nil { - return nil, err - } - - reader := ioutils.NewReadCloserWrapper(archive, func() error { - err := archive.Close() - container.unmountVolumes(true) - daemon.Unmount(container) - container.Unlock() - return err - }) - daemon.logContainerEvent(container, "copy") - return reader, nil -} - // Returns true if the container exposes a certain port func (container *Container) exposes(p nat.Port) bool { _, exists := container.Config.ExposedPorts[p] From c1c42db060362e400a475dfb2884f89b86fce317 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 2 Nov 2015 20:49:36 -0500 Subject: [PATCH 09/17] Decouple daemon and container to execute processes. Signed-off-by: David Calavera --- daemon/container.go | 64 ------------------------------------------- daemon/exec.go | 67 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 65 deletions(-) diff --git a/daemon/container.go b/daemon/container.go index f6884cdec0..e9c14c964e 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -420,70 +420,6 @@ func (container *Container) getExecIDs() []string { return container.execCommands.List() } -func (container *Container) exec(ec *ExecConfig) error { - container.Lock() - defer container.Unlock() - - callback := func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error { - if processConfig.Tty { - // The callback is called after the process Start() - // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave - // which we close here. - if c, ok := processConfig.Stdout.(io.Closer); ok { - c.Close() - } - } - close(ec.waitStart) - return nil - } - - // We use a callback here instead of a goroutine and an chan for - // synchronization purposes - cErr := promise.Go(func() error { return container.monitorExec(ec, callback) }) - - // Exec should not return until the process is actually running - select { - case <-ec.waitStart: - case err := <-cErr: - return err - } - - return nil -} - -func (container *Container) monitorExec(ExecConfig *ExecConfig, callback execdriver.DriverCallback) error { - var ( - err error - exitCode int - ) - pipes := execdriver.NewPipes(ExecConfig.streamConfig.stdin, ExecConfig.streamConfig.stdout, ExecConfig.streamConfig.stderr, ExecConfig.OpenStdin) - exitCode, err = container.daemon.Exec(container, ExecConfig, pipes, callback) - if err != nil { - logrus.Errorf("Error running command in existing container %s: %s", container.ID, err) - } - logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode) - if ExecConfig.OpenStdin { - if err := ExecConfig.streamConfig.stdin.Close(); err != nil { - logrus.Errorf("Error closing stdin while running in %s: %s", container.ID, err) - } - } - if err := ExecConfig.streamConfig.stdout.Clean(); err != nil { - logrus.Errorf("Error closing stdout while running in %s: %s", container.ID, err) - } - if err := ExecConfig.streamConfig.stderr.Clean(); err != nil { - logrus.Errorf("Error closing stderr while running in %s: %s", container.ID, err) - } - if ExecConfig.ProcessConfig.Terminal != nil { - if err := ExecConfig.ProcessConfig.Terminal.Close(); err != nil { - logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err) - } - } - // remove the exec command from the container's store only and not the - // daemon's store so that the exec command can be inspected. - container.execCommands.Delete(ExecConfig.ID) - return err -} - // Attach connects to the container's TTY, delegating to standard // streams or websockets depending on the configuration. func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error { diff --git a/daemon/exec.go b/daemon/exec.go index 2e343bf8f0..c1ef8f58d9 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/pkg/broadcaster" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/pools" + "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringutils" "github.com/docker/docker/runconfig" @@ -251,7 +252,7 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io. // the exitStatus) even after the cmd is done running. go func() { - execErr <- container.exec(ec) + execErr <- d.containerExec(container, ec) }() select { @@ -329,3 +330,67 @@ func (d *Daemon) containerExecIds() map[string]struct{} { } return ids } + +func (daemon *Daemon) containerExec(container *Container, ec *ExecConfig) error { + container.Lock() + defer container.Unlock() + + callback := func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error { + if processConfig.Tty { + // The callback is called after the process Start() + // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave + // which we close here. + if c, ok := processConfig.Stdout.(io.Closer); ok { + c.Close() + } + } + close(ec.waitStart) + return nil + } + + // We use a callback here instead of a goroutine and an chan for + // synchronization purposes + cErr := promise.Go(func() error { return daemon.monitorExec(container, ec, callback) }) + + // Exec should not return until the process is actually running + select { + case <-ec.waitStart: + case err := <-cErr: + return err + } + + return nil +} + +func (daemon *Daemon) monitorExec(container *Container, ExecConfig *ExecConfig, callback execdriver.DriverCallback) error { + var ( + err error + exitCode int + ) + pipes := execdriver.NewPipes(ExecConfig.streamConfig.stdin, ExecConfig.streamConfig.stdout, ExecConfig.streamConfig.stderr, ExecConfig.OpenStdin) + exitCode, err = daemon.Exec(container, ExecConfig, pipes, callback) + if err != nil { + logrus.Errorf("Error running command in existing container %s: %s", container.ID, err) + } + logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode) + if ExecConfig.OpenStdin { + if err := ExecConfig.streamConfig.stdin.Close(); err != nil { + logrus.Errorf("Error closing stdin while running in %s: %s", container.ID, err) + } + } + if err := ExecConfig.streamConfig.stdout.Clean(); err != nil { + logrus.Errorf("Error closing stdout while running in %s: %s", container.ID, err) + } + if err := ExecConfig.streamConfig.stderr.Clean(); err != nil { + logrus.Errorf("Error closing stderr while running in %s: %s", container.ID, err) + } + if ExecConfig.ProcessConfig.Terminal != nil { + if err := ExecConfig.ProcessConfig.Terminal.Close(); err != nil { + logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err) + } + } + // remove the exec command from the container's store only and not the + // daemon's store so that the exec command can be inspected. + container.execCommands.Delete(ExecConfig.ID) + return err +} From 2c72015ce3b78b45e33529368fb1c5a724415d87 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 3 Nov 2015 11:42:08 -0500 Subject: [PATCH 10/17] Decouple daemon and container to manage volumes. Signed-off-by: David Calavera --- daemon/container.go | 40 --------------------------------------- daemon/create.go | 2 +- daemon/daemon.go | 2 +- daemon/delete.go | 2 +- daemon/mounts.go | 46 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 43 deletions(-) create mode 100644 daemon/mounts.go diff --git a/daemon/container.go b/daemon/container.go index e9c14c964e..9955b2123b 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -7,7 +7,6 @@ import ( "io" "os" "path/filepath" - "strings" "sync" "syscall" "time" @@ -31,7 +30,6 @@ import ( "github.com/docker/docker/pkg/system" "github.com/docker/docker/runconfig" "github.com/docker/docker/volume" - "github.com/docker/docker/volume/store" ) var ( @@ -667,44 +665,6 @@ func (container *Container) mountVolumes() error { return nil } -func (container *Container) prepareMountPoints() error { - for _, config := range container.MountPoints { - if len(config.Driver) > 0 { - v, err := container.daemon.createVolume(config.Name, config.Driver, nil) - if err != nil { - return err - } - config.Volume = v - } - } - return nil -} - -func (container *Container) removeMountPoints(rm bool) error { - var rmErrors []string - for _, m := range container.MountPoints { - if m.Volume == nil { - continue - } - container.daemon.volumes.Decrement(m.Volume) - if rm { - err := container.daemon.volumes.Remove(m.Volume) - // ErrVolumeInUse is ignored because having this - // volume being referenced by other container is - // not an error, but an implementation detail. - // This prevents docker from logging "ERROR: Volume in use" - // where there is another container using the volume. - if err != nil && err != store.ErrVolumeInUse { - rmErrors = append(rmErrors, err.Error()) - } - } - } - if len(rmErrors) > 0 { - return derr.ErrorCodeRemovingVolume.WithArgs(strings.Join(rmErrors, "\n")) - } - return nil -} - func (container *Container) unmountVolumes(forceSyscall bool) error { var ( volumeMounts []volume.MountPoint diff --git a/daemon/create.go b/daemon/create.go index 584d452f71..75fa775c53 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -109,7 +109,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re } defer func() { if retErr != nil { - if err := container.removeMountPoints(true); err != nil { + if err := daemon.removeMountPoints(container, true); err != nil { logrus.Error(err) } } diff --git a/daemon/daemon.go b/daemon/daemon.go index 78e06d74da..3f17027095 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -246,7 +246,7 @@ func (daemon *Daemon) Register(container *Container) error { return err } - if err := container.prepareMountPoints(); err != nil { + if err := daemon.prepareMountPoints(container); err != nil { return err } diff --git a/daemon/delete.go b/daemon/delete.go index a2e7e5fa82..bf751ec2da 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -58,7 +58,7 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error return err } - if err := container.removeMountPoints(config.RemoveVolume); err != nil { + if err := daemon.removeMountPoints(container, config.RemoveVolume); err != nil { logrus.Error(err) } diff --git a/daemon/mounts.go b/daemon/mounts.go new file mode 100644 index 0000000000..91d5c0953c --- /dev/null +++ b/daemon/mounts.go @@ -0,0 +1,46 @@ +package daemon + +import ( + "strings" + + derr "github.com/docker/docker/errors" + "github.com/docker/docker/volume/store" +) + +func (daemon *Daemon) prepareMountPoints(container *Container) error { + for _, config := range container.MountPoints { + if len(config.Driver) > 0 { + v, err := daemon.createVolume(config.Name, config.Driver, nil) + if err != nil { + return err + } + config.Volume = v + } + } + return nil +} + +func (daemon *Daemon) removeMountPoints(container *Container, rm bool) error { + var rmErrors []string + for _, m := range container.MountPoints { + if m.Volume == nil { + continue + } + container.daemon.volumes.Decrement(m.Volume) + if rm { + err := daemon.volumes.Remove(m.Volume) + // ErrVolumeInUse is ignored because having this + // volume being referenced by other container is + // not an error, but an implementation detail. + // This prevents docker from logging "ERROR: Volume in use" + // where there is another container using the volume. + if err != nil && err != store.ErrVolumeInUse { + rmErrors = append(rmErrors, err.Error()) + } + } + } + if len(rmErrors) > 0 { + return derr.ErrorCodeRemovingVolume.WithArgs(strings.Join(rmErrors, "\n")) + } + return nil +} From ca5ede2d0a23cb84cac3b863c363d0269e6438df Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 3 Nov 2015 12:33:13 -0500 Subject: [PATCH 11/17] Decouple daemon and container to log events. Create a supervisor interface to let the container monitor to emit events. Signed-off-by: David Calavera --- daemon/archive.go | 6 ++-- daemon/attach.go | 63 +++++++++++++++++++++++++++++++++-- daemon/commit.go | 3 +- daemon/container.go | 80 --------------------------------------------- daemon/create.go | 2 +- daemon/delete.go | 4 +-- daemon/events.go | 4 +-- daemon/exec.go | 4 +-- daemon/export.go | 2 +- daemon/kill.go | 2 +- daemon/monitor.go | 24 +++++++++++--- daemon/pause.go | 2 +- daemon/rename.go | 7 ++-- daemon/resize.go | 5 ++- daemon/restart.go | 2 +- daemon/start.go | 19 +++++++++-- daemon/stop.go | 2 +- daemon/top_unix.go | 2 +- daemon/unpause.go | 2 +- 19 files changed, 124 insertions(+), 111 deletions(-) diff --git a/daemon/archive.go b/daemon/archive.go index f9a9cb8afc..61196c91f5 100644 --- a/daemon/archive.go +++ b/daemon/archive.go @@ -219,7 +219,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c return err }) - container.logEvent("archive-path") + daemon.LogContainerEvent(container, "archive-path") return content, stat, nil } @@ -318,7 +318,7 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n return err } - container.logEvent("extract-to-dir") + daemon.LogContainerEvent(container, "extract-to-dir") return nil } @@ -384,6 +384,6 @@ func (daemon *Daemon) containerCopy(container *Container, resource string) (rc i container.Unlock() return err }) - daemon.logContainerEvent(container, "copy") + daemon.LogContainerEvent(container, "copy") return reader, nil } diff --git a/daemon/attach.go b/daemon/attach.go index 44198788ce..64a1223a4d 100644 --- a/daemon/attach.go +++ b/daemon/attach.go @@ -2,7 +2,10 @@ package daemon import ( "io" + "time" + "github.com/Sirupsen/logrus" + "github.com/docker/docker/daemon/logger" "github.com/docker/docker/pkg/stdcopy" ) @@ -43,7 +46,7 @@ func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *ContainerA stderr = errStream } - return container.attachWithLogs(stdin, stdout, stderr, c.Logs, c.Stream) + return daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream) } // ContainerWsAttachWithLogsConfig attach with websockets, since all @@ -60,5 +63,61 @@ func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *Containe if err != nil { return err } - return container.attachWithLogs(c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream) + return daemon.attachWithLogs(container, c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream) +} + +func (daemon *Daemon) attachWithLogs(container *Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool) error { + if logs { + logDriver, err := container.getLogger() + if err != nil { + return err + } + cLog, ok := logDriver.(logger.LogReader) + if !ok { + return logger.ErrReadLogsNotSupported + } + logs := cLog.ReadLogs(logger.ReadConfig{Tail: -1}) + + LogLoop: + for { + select { + case msg, ok := <-logs.Msg: + if !ok { + break LogLoop + } + if msg.Source == "stdout" && stdout != nil { + stdout.Write(msg.Line) + } + if msg.Source == "stderr" && stderr != nil { + stderr.Write(msg.Line) + } + case err := <-logs.Err: + logrus.Errorf("Error streaming logs: %v", err) + break LogLoop + } + } + } + + daemon.LogContainerEvent(container, "attach") + + //stream + if stream { + var stdinPipe io.ReadCloser + if stdin != nil { + r, w := io.Pipe() + go func() { + defer w.Close() + defer logrus.Debugf("Closing buffered stdin pipe") + io.Copy(w, stdin) + }() + stdinPipe = r + } + <-container.Attach(stdinPipe, stdout, stderr) + // 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) + } + } + return nil } diff --git a/daemon/commit.go b/daemon/commit.go index fd11fc4683..35759a032c 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -48,7 +48,8 @@ func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*i return img, err } } - container.logEvent("commit") + + daemon.LogContainerEvent(container, "commit") return img, nil } diff --git a/daemon/container.go b/daemon/container.go index 9955b2123b..8a5b0fd266 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -172,15 +172,6 @@ func (container *Container) writeHostConfig() error { return json.NewEncoder(f).Encode(&container.hostConfig) } -func (container *Container) logEvent(action string) { - d := container.daemon - d.EventsService.Log( - action, - container.ID, - container.Config.Image, - ) -} - // GetResourcePath evaluates `path` in the scope of the container's basefs, with proper path // sanitisation. Symlinks are all scoped to the basefs of the container, as // though the container's basefs was `/`. @@ -278,7 +269,6 @@ func (container *Container) Resize(h, w int) error { if err := container.command.ProcessConfig.Terminal.Resize(h, w); err != nil { return err } - container.logEvent("resize") return nil } @@ -380,20 +370,6 @@ func (container *Container) startLogging() error { return nil } -func (container *Container) waitForStart() error { - container.monitor = newContainerMonitor(container, container.hostConfig.RestartPolicy) - - // block until we either receive an error from the initial start of the container's - // process or until the process is running in the container - select { - case <-container.monitor.startSignal: - case err := <-promise.Go(container.monitor.Start): - return err - } - - return nil -} - func (container *Container) getProcessLabel() string { // even if we have a process label return "" if we are running // in privileged mode @@ -424,62 +400,6 @@ func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr return attach(&container.streamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr) } -func (container *Container) attachWithLogs(stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool) error { - if logs { - logDriver, err := container.getLogger() - if err != nil { - return err - } - cLog, ok := logDriver.(logger.LogReader) - if !ok { - return logger.ErrReadLogsNotSupported - } - logs := cLog.ReadLogs(logger.ReadConfig{Tail: -1}) - - LogLoop: - for { - select { - case msg, ok := <-logs.Msg: - if !ok { - break LogLoop - } - if msg.Source == "stdout" && stdout != nil { - stdout.Write(msg.Line) - } - if msg.Source == "stderr" && stderr != nil { - stderr.Write(msg.Line) - } - case err := <-logs.Err: - logrus.Errorf("Error streaming logs: %v", err) - break LogLoop - } - } - } - - container.logEvent("attach") - - //stream - if stream { - var stdinPipe io.ReadCloser - if stdin != nil { - r, w := io.Pipe() - go func() { - defer w.Close() - defer logrus.Debugf("Closing buffered stdin pipe") - io.Copy(w, stdin) - }() - stdinPipe = r - } - <-container.Attach(stdinPipe, stdout, stderr) - // 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) - } - } - return nil -} - func attach(streamConfig *streamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error { var ( cStdout, cStderr io.ReadCloser diff --git a/daemon/create.go b/daemon/create.go index 75fa775c53..b27a20eed4 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -127,7 +127,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re logrus.Errorf("Error saving new container to disk: %v", err) return nil, err } - daemon.logContainerEvent(container, "create") + daemon.LogContainerEvent(container, "create") return container, nil } diff --git a/daemon/delete.go b/daemon/delete.go index bf751ec2da..1de9ba9c1d 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -111,7 +111,7 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) { daemon.idIndex.Delete(container.ID) daemon.containers.Delete(container.ID) os.RemoveAll(container.root) - container.logEvent("destroy") + daemon.LogContainerEvent(container, "destroy") } }() @@ -140,7 +140,7 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) { daemon.idIndex.Delete(container.ID) daemon.containers.Delete(container.ID) - container.logEvent("destroy") + daemon.LogContainerEvent(container, "destroy") return nil } diff --git a/daemon/events.go b/daemon/events.go index be6d0c5b67..85ac25c0e6 100644 --- a/daemon/events.go +++ b/daemon/events.go @@ -1,7 +1,7 @@ package daemon -// logContainerEvent generates an event related to a container. -func (daemon *Daemon) logContainerEvent(container *Container, action string) { +// LogContainerEvent generates an event related to a container. +func (daemon *Daemon) LogContainerEvent(container *Container, action string) { daemon.EventsService.Log( action, container.ID, diff --git a/daemon/exec.go b/daemon/exec.go index c1ef8f58d9..6ebacd6643 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -188,7 +188,7 @@ func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, erro d.registerExecCommand(ExecConfig) - container.logEvent("exec_create: " + ExecConfig.ProcessConfig.Entrypoint + " " + strings.Join(ExecConfig.ProcessConfig.Arguments, " ")) + d.LogContainerEvent(container, "exec_create: "+ExecConfig.ProcessConfig.Entrypoint+" "+strings.Join(ExecConfig.ProcessConfig.Arguments, " ")) return ExecConfig.ID, nil } @@ -216,7 +216,7 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io. logrus.Debugf("starting exec command %s in container %s", ec.ID, ec.Container.ID) container := ec.Container - container.logEvent("exec_start: " + ec.ProcessConfig.Entrypoint + " " + strings.Join(ec.ProcessConfig.Arguments, " ")) + d.LogContainerEvent(container, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " ")) if ec.OpenStdin { r, w := io.Pipe() diff --git a/daemon/export.go b/daemon/export.go index 84b83c5fa5..0a246ed5af 100644 --- a/daemon/export.go +++ b/daemon/export.go @@ -49,6 +49,6 @@ func (daemon *Daemon) containerExport(container *Container) (archive.Archive, er daemon.Unmount(container) return err }) - daemon.logContainerEvent(container, "export") + daemon.LogContainerEvent(container, "export") return arch, err } diff --git a/daemon/kill.go b/daemon/kill.go index 91b16ad389..8b94fa3a88 100644 --- a/daemon/kill.go +++ b/daemon/kill.go @@ -71,7 +71,7 @@ func (daemon *Daemon) killWithSignal(container *Container, sig int) error { return err } - daemon.logContainerEvent(container, "kill") + daemon.LogContainerEvent(container, "kill") return nil } diff --git a/daemon/monitor.go b/daemon/monitor.go index 4af0d2a2fd..4a24b7d94b 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -17,6 +17,12 @@ const ( loggerCloseTimeout = 10 * time.Second ) +// containerSupervisor defines the interface that a supervisor must implement +type containerSupervisor interface { + // LogContainerEvent generates events related to a given container + LogContainerEvent(*Container, string) +} + // containerMonitor monitors the execution of a container's main process. // If a restart policy is specified for the container the monitor will ensure that the // process is restarted based on the rules of the policy. When the container is finally stopped @@ -25,6 +31,9 @@ const ( type containerMonitor struct { mux sync.Mutex + // supervisor keeps track of the container and the events it generates + supervisor containerSupervisor + // container is the container being monitored container *Container @@ -57,8 +66,9 @@ type containerMonitor struct { // newContainerMonitor returns an initialized containerMonitor for the provided container // honoring the provided restart policy -func newContainerMonitor(container *Container, policy runconfig.RestartPolicy) *containerMonitor { +func (daemon *Daemon) newContainerMonitor(container *Container, policy runconfig.RestartPolicy) *containerMonitor { return &containerMonitor{ + supervisor: daemon, container: container, restartPolicy: policy, timeIncrement: defaultTimeIncrement, @@ -138,7 +148,7 @@ func (m *containerMonitor) Start() error { pipes := execdriver.NewPipes(m.container.stdin, m.container.stdout, m.container.stderr, m.container.Config.OpenStdin) - m.container.logEvent("start") + m.logEvent("start") m.lastStartTime = time.Now() @@ -162,7 +172,7 @@ func (m *containerMonitor) Start() error { if m.shouldRestart(exitStatus.ExitCode) { m.container.setRestarting(&exitStatus) - m.container.logEvent("die") + m.logEvent("die") m.resetContainer(true) // sleep with a small time increment between each restart to help avoid issues cased by quickly @@ -177,7 +187,7 @@ func (m *containerMonitor) Start() error { continue } - m.container.logEvent("die") + m.logEvent("die") m.resetContainer(true) return err } @@ -249,7 +259,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid go func() { _, ok := <-chOOM if ok { - m.container.logEvent("oom") + m.logEvent("oom") } }() @@ -345,3 +355,7 @@ func (m *containerMonitor) resetContainer(lock bool) { SysProcAttr: c.SysProcAttr, } } + +func (m *containerMonitor) logEvent(action string) { + m.supervisor.LogContainerEvent(m.container, action) +} diff --git a/daemon/pause.go b/daemon/pause.go index 4182e92e2f..be7f63394b 100644 --- a/daemon/pause.go +++ b/daemon/pause.go @@ -38,6 +38,6 @@ func (daemon *Daemon) containerPause(container *Container) error { return err } container.Paused = true - daemon.logContainerEvent(container, "pause") + daemon.LogContainerEvent(container, "pause") return nil } diff --git a/daemon/rename.go b/daemon/rename.go index 1aab1f9303..36421dcd7d 100644 --- a/daemon/rename.go +++ b/daemon/rename.go @@ -1,10 +1,11 @@ package daemon import ( + "strings" + "github.com/Sirupsen/logrus" derr "github.com/docker/docker/errors" "github.com/docker/libnetwork" - "strings" ) // ContainerRename changes the name of a container, using the oldName @@ -54,7 +55,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error { } if !container.Running { - container.logEvent("rename") + daemon.LogContainerEvent(container, "rename") return nil } @@ -78,6 +79,6 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error { return err } - container.logEvent("rename") + daemon.LogContainerEvent(container, "rename") return nil } diff --git a/daemon/resize.go b/daemon/resize.go index 199bc48b43..7fa5b6652a 100644 --- a/daemon/resize.go +++ b/daemon/resize.go @@ -8,7 +8,10 @@ func (daemon *Daemon) ContainerResize(name string, height, width int) error { return err } - return container.Resize(height, width) + if err = container.Resize(height, width); err == nil { + daemon.LogContainerEvent(container, "resize") + } + return err } // ContainerExecResize changes the size of the TTY of the process diff --git a/daemon/restart.go b/daemon/restart.go index a237f3a62c..fcfe6128fa 100644 --- a/daemon/restart.go +++ b/daemon/restart.go @@ -41,6 +41,6 @@ func (daemon *Daemon) containerRestart(container *Container, seconds int) error return err } - daemon.logContainerEvent(container, "restart") + daemon.LogContainerEvent(container, "restart") return nil } diff --git a/daemon/start.go b/daemon/start.go index 9326d584f5..de86b9e476 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -4,6 +4,7 @@ import ( "runtime" derr "github.com/docker/docker/errors" + "github.com/docker/docker/pkg/promise" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" ) @@ -83,7 +84,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { } container.toDisk() container.cleanup() - daemon.logContainerEvent(container, "die") + daemon.LogContainerEvent(container, "die") } }() @@ -123,5 +124,19 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { mounts = append(mounts, container.ipcMounts()...) container.command.Mounts = mounts - return container.waitForStart() + return daemon.waitForStart(container) +} + +func (daemon *Daemon) waitForStart(container *Container) error { + container.monitor = daemon.newContainerMonitor(container, container.hostConfig.RestartPolicy) + + // block until we either receive an error from the initial start of the container's + // process or until the process is running in the container + select { + case <-container.monitor.startSignal: + case err := <-promise.Go(container.monitor.Start): + return err + } + + return nil } diff --git a/daemon/stop.go b/daemon/stop.go index 17629acdd6..c97781b8fa 100644 --- a/daemon/stop.go +++ b/daemon/stop.go @@ -55,6 +55,6 @@ func (daemon *Daemon) containerStop(container *Container, seconds int) error { } } - daemon.logContainerEvent(container, "stop") + daemon.LogContainerEvent(container, "stop") return nil } diff --git a/daemon/top_unix.go b/daemon/top_unix.go index 4112fbcbb1..36ace121e6 100644 --- a/daemon/top_unix.go +++ b/daemon/top_unix.go @@ -76,6 +76,6 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*types.Container } } } - container.logEvent("top") + daemon.LogContainerEvent(container, "top") return procList, nil } diff --git a/daemon/unpause.go b/daemon/unpause.go index 4112494fc4..3397f44b45 100644 --- a/daemon/unpause.go +++ b/daemon/unpause.go @@ -38,6 +38,6 @@ func (daemon *Daemon) containerUnpause(container *Container) error { } container.Paused = false - daemon.logContainerEvent(container, "unpause") + daemon.LogContainerEvent(container, "unpause") return nil } From 019c337b93d5e068fa7c701d8b70750052503188 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 3 Nov 2015 12:43:36 -0500 Subject: [PATCH 12/17] Decouple daemon and container to cleanup containers. Signed-off-by: David Calavera --- daemon/container.go | 18 ------------------ daemon/container_unix.go | 4 ++-- daemon/container_windows.go | 2 +- daemon/monitor.go | 4 +++- daemon/start.go | 21 ++++++++++++++++++++- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/daemon/container.go b/daemon/container.go index 8a5b0fd266..87b0e009da 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -236,24 +236,6 @@ func (streamConfig *streamConfig) StderrPipe() io.ReadCloser { return ioutils.NewBufReader(reader) } -// cleanup releases any network resources allocated to the container along with any rules -// around how containers are linked together. It also unmounts the container's root filesystem. -func (container *Container) cleanup() { - container.releaseNetwork() - - container.unmountIpcMounts(detachMounted) - - container.daemon.conditionalUnmountOnCleanup(container) - - for _, eConfig := range container.execCommands.s { - container.daemon.unregisterExecCommand(eConfig) - } - - if err := container.unmountVolumes(false); err != nil { - logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err) - } -} - // ExitOnNext signals to the monitor that it should not restart the container // after we send the kill signal. func (container *Container) ExitOnNext() { diff --git a/daemon/container_unix.go b/daemon/container_unix.go index 8cd8989f6d..640bf5bcb2 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -1153,7 +1153,7 @@ func (container *Container) getNetworkedContainer() (*Container, error) { } } -func (container *Container) releaseNetwork() { +func (daemon *Daemon) releaseNetwork(container *Container) { if container.hostConfig.NetworkMode.IsContainer() || container.Config.NetworkDisabled { return } @@ -1170,7 +1170,7 @@ func (container *Container) releaseNetwork() { return } - sb, err := container.daemon.netController.SandboxByID(sid) + sb, err := daemon.netController.SandboxByID(sid) if err != nil { logrus.Errorf("error locating sandbox id %s: %v", sid, err) return diff --git a/daemon/container_windows.go b/daemon/container_windows.go index f618cd8c23..0ad1fc69ad 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -162,7 +162,7 @@ func (container *Container) updateNetwork() error { return nil } -func (container *Container) releaseNetwork() { +func (daemon *Daemon) releaseNetwork(container *Container) { } // appendNetworkMounts appends any network mounts to the array of mount points passed in. diff --git a/daemon/monitor.go b/daemon/monitor.go index 4a24b7d94b..3512cbd1cd 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -21,6 +21,8 @@ const ( type containerSupervisor interface { // LogContainerEvent generates events related to a given container LogContainerEvent(*Container, string) + // Cleanup ensures that the container is properly unmounted + Cleanup(*Container) } // containerMonitor monitors the execution of a container's main process. @@ -96,7 +98,7 @@ func (m *containerMonitor) ExitOnNext() { // unmounts the contatiner's root filesystem func (m *containerMonitor) Close() error { // Cleanup networking and mounts - m.container.cleanup() + m.supervisor.Cleanup(m.container) // FIXME: here is race condition between two RUN instructions in Dockerfile // because they share same runconfig and change image. Must be fixed diff --git a/daemon/start.go b/daemon/start.go index de86b9e476..89a6020f41 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -3,6 +3,7 @@ package daemon import ( "runtime" + "github.com/Sirupsen/logrus" derr "github.com/docker/docker/errors" "github.com/docker/docker/pkg/promise" "github.com/docker/docker/runconfig" @@ -83,7 +84,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { container.ExitCode = 128 } container.toDisk() - container.cleanup() + daemon.Cleanup(container) daemon.LogContainerEvent(container, "die") } }() @@ -140,3 +141,21 @@ func (daemon *Daemon) waitForStart(container *Container) error { return nil } + +// Cleanup releases any network resources allocated to the container along with any rules +// around how containers are linked together. It also unmounts the container's root filesystem. +func (daemon *Daemon) Cleanup(container *Container) { + daemon.releaseNetwork(container) + + container.unmountIpcMounts(detachMounted) + + daemon.conditionalUnmountOnCleanup(container) + + for _, eConfig := range container.execCommands.s { + daemon.unregisterExecCommand(eConfig) + } + + if err := container.unmountVolumes(false); err != nil { + logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err) + } +} From 669949d6b436d2fc182b427a5e2b38b3bd77c743 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 3 Nov 2015 13:25:09 -0500 Subject: [PATCH 13/17] Decouple daemon and container to manage networks. Signed-off-by: David Calavera --- daemon/container_unix.go | 143 ++++++++++++++++------------------- daemon/container_windows.go | 32 ++++---- daemon/daemon.go | 2 +- daemon/delete.go | 2 +- daemon/network.go | 2 +- daemon/start.go | 8 +- runconfig/hostconfig_unix.go | 9 +++ 7 files changed, 97 insertions(+), 101 deletions(-) diff --git a/daemon/container_unix.go b/daemon/container_unix.go index 640bf5bcb2..d47647412b 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -77,11 +77,8 @@ func killProcessDirectly(container *Container) error { return nil } -func (container *Container) setupLinkedContainers() ([]string, error) { - var ( - env []string - daemon = container.daemon - ) +func (daemon *Daemon) setupLinkedContainers(container *Container) ([]string, error) { + var env []string children, err := daemon.children(container.Name) if err != nil { return nil, err @@ -185,17 +182,16 @@ func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs. return devs, derr.ErrorCodeDeviceInfo.WithArgs(deviceMapping.PathOnHost, err) } -func populateCommand(c *Container, env []string) error { +func (daemon *Daemon) populateCommand(c *Container, env []string) error { var en *execdriver.Network if !c.Config.NetworkDisabled { en = &execdriver.Network{} - if !c.daemon.execDriver.SupportsHooks() || c.hostConfig.NetworkMode.IsHost() { + if !daemon.execDriver.SupportsHooks() || c.hostConfig.NetworkMode.IsHost() { en.NamespacePath = c.NetworkSettings.SandboxKey } - parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2) - if parts[0] == "container" { - nc, err := c.getNetworkedContainer() + if c.hostConfig.NetworkMode.IsContainer() { + nc, err := daemon.getNetworkedContainer(c.ID, c.hostConfig.NetworkMode.ConnectedContainer()) if err != nil { return err } @@ -216,7 +212,7 @@ func populateCommand(c *Container, env []string) error { } if c.hostConfig.IpcMode.IsContainer() { - ic, err := c.getIpcContainer() + ic, err := daemon.getIpcContainer(c) if err != nil { return err } @@ -273,7 +269,7 @@ func populateCommand(c *Container, env []string) error { for _, ul := range ulimits { ulIdx[ul.Name] = ul } - for name, ul := range c.daemon.configStore.Ulimits { + for name, ul := range daemon.configStore.Ulimits { if _, exists := ulIdx[name]; !exists { ulimits = append(ulimits, ul) } @@ -321,12 +317,12 @@ func populateCommand(c *Container, env []string) error { processConfig.Env = env remappedRoot := &execdriver.User{} - rootUID, rootGID := c.daemon.GetRemappedUIDGID() + rootUID, rootGID := daemon.GetRemappedUIDGID() if rootUID != 0 { remappedRoot.UID = rootUID remappedRoot.GID = rootGID } - uidMap, gidMap := c.daemon.GetUIDGIDMaps() + uidMap, gidMap := daemon.GetUIDGIDMaps() c.command = &execdriver.Command{ CommonCommand: execdriver.CommonCommand{ @@ -442,7 +438,7 @@ func (container *Container) buildHostnameFile() error { return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) } -func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetwork.SandboxOption, error) { +func (daemon *Daemon) buildSandboxOptions(container *Container, n libnetwork.Network) ([]libnetwork.SandboxOption, error) { var ( sboxOptions []libnetwork.SandboxOption err error @@ -458,7 +454,7 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) - } else if container.daemon.execDriver.SupportsHooks() { + } else if daemon.execDriver.SupportsHooks() { // OptionUseExternalKey is mandatory for userns support. // But optional for non-userns support sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey()) @@ -478,8 +474,8 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw if len(container.hostConfig.DNS) > 0 { dns = container.hostConfig.DNS - } else if len(container.daemon.configStore.DNS) > 0 { - dns = container.daemon.configStore.DNS + } else if len(daemon.configStore.DNS) > 0 { + dns = daemon.configStore.DNS } for _, d := range dns { @@ -488,8 +484,8 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw if len(container.hostConfig.DNSSearch) > 0 { dnsSearch = container.hostConfig.DNSSearch - } else if len(container.daemon.configStore.DNSSearch) > 0 { - dnsSearch = container.daemon.configStore.DNSSearch + } else if len(daemon.configStore.DNSSearch) > 0 { + dnsSearch = daemon.configStore.DNSSearch } for _, ds := range dnsSearch { @@ -498,8 +494,8 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw if len(container.hostConfig.DNSOptions) > 0 { dnsOptions = container.hostConfig.DNSOptions - } else if len(container.daemon.configStore.DNSOptions) > 0 { - dnsOptions = container.daemon.configStore.DNSOptions + } else if len(daemon.configStore.DNSOptions) > 0 { + dnsOptions = daemon.configStore.DNSOptions } for _, ds := range dnsOptions { @@ -536,7 +532,7 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw var childEndpoints, parentEndpoints []string - children, err := container.daemon.children(container.Name) + children, err := daemon.children(container.Name) if err != nil { return nil, err } @@ -560,18 +556,18 @@ func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetw } bridgeSettings := container.NetworkSettings.Networks["bridge"] - refs := container.daemon.containerGraph().RefPaths(container.ID) + refs := daemon.containerGraph().RefPaths(container.ID) for _, ref := range refs { if ref.ParentID == "0" { continue } - c, err := container.daemon.Get(ref.ParentID) + c, err := daemon.Get(ref.ParentID) if err != nil { logrus.Error(err) } - if c != nil && !container.daemon.configStore.DisableBridge && container.hostConfig.NetworkMode.IsPrivate() { + if c != nil && !daemon.configStore.DisableBridge && container.hostConfig.NetworkMode.IsPrivate() { logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, bridgeSettings.IPAddress) sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(c.ID, ref.Name, bridgeSettings.IPAddress)) if ep.ID() != "" { @@ -719,13 +715,13 @@ func (container *Container) updateJoinInfo(n libnetwork.Network, ep libnetwork.E return nil } -func (container *Container) updateNetworkSettings(n libnetwork.Network) error { +func (daemon *Daemon) updateNetworkSettings(container *Container, n libnetwork.Network) error { if container.NetworkSettings == nil { container.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)} } for s := range container.NetworkSettings.Networks { - sn, err := container.daemon.FindNetwork(s) + sn, err := daemon.FindNetwork(s) if err != nil { continue } @@ -769,8 +765,8 @@ func (container *Container) updateSandboxNetworkSettings(sb libnetwork.Sandbox) // UpdateNetwork is used to update the container's network (e.g. when linked containers // get removed/unlinked). -func (container *Container) updateNetwork() error { - ctrl := container.daemon.netController +func (daemon *Daemon) updateNetwork(container *Container) error { + ctrl := daemon.netController sid := container.NetworkSettings.SandboxID sb, err := ctrl.SandboxByID(sid) @@ -781,7 +777,7 @@ func (container *Container) updateNetwork() error { // Find if container is connected to the default bridge network var n libnetwork.Network for name := range container.NetworkSettings.Networks { - sn, err := container.daemon.FindNetwork(name) + sn, err := daemon.FindNetwork(name) if err != nil { continue } @@ -796,7 +792,7 @@ func (container *Container) updateNetwork() error { return nil } - options, err := container.buildSandboxOptions(n) + options, err := daemon.buildSandboxOptions(container, n) if err != nil { return derr.ErrorCodeNetworkUpdate.WithArgs(err) } @@ -893,8 +889,8 @@ func (container *Container) buildCreateEndpointOptions(n libnetwork.Network) ([] return createOptions, nil } -func (container *Container) allocateNetwork() error { - controller := container.daemon.netController +func (daemon *Daemon) allocateNetwork(container *Container) error { + controller := daemon.netController // Cleanup any stale sandbox left over due to ungraceful daemon shutdown if err := controller.SandboxDestroy(container.ID); err != nil { @@ -913,7 +909,7 @@ func (container *Container) allocateNetwork() error { networkName = controller.Config().Daemon.DefaultNetwork } if mode.IsUserDefined() { - n, err := container.daemon.FindNetwork(networkName) + n, err := daemon.FindNetwork(networkName) if err != nil { return err } @@ -925,7 +921,7 @@ func (container *Container) allocateNetwork() error { } for n := range container.NetworkSettings.Networks { - if err := container.connectToNetwork(n, updateSettings); err != nil { + if err := daemon.connectToNetwork(container, n, updateSettings); err != nil { return err } } @@ -933,9 +929,9 @@ func (container *Container) allocateNetwork() error { return container.writeHostConfig() } -func (container *Container) getNetworkSandbox() libnetwork.Sandbox { +func (daemon *Daemon) getNetworkSandbox(container *Container) libnetwork.Sandbox { var sb libnetwork.Sandbox - container.daemon.netController.WalkSandboxes(func(s libnetwork.Sandbox) bool { + daemon.netController.WalkSandboxes(func(s libnetwork.Sandbox) bool { if s.ContainerID() == container.ID { sb = s return true @@ -946,11 +942,11 @@ func (container *Container) getNetworkSandbox() libnetwork.Sandbox { } // ConnectToNetwork connects a container to a netork -func (container *Container) ConnectToNetwork(idOrName string) error { +func (daemon *Daemon) ConnectToNetwork(container *Container, idOrName string) error { if !container.Running { return derr.ErrorCodeNotRunning.WithArgs(container.ID) } - if err := container.connectToNetwork(idOrName, true); err != nil { + if err := daemon.connectToNetwork(container, idOrName, true); err != nil { return err } if err := container.toDiskLocking(); err != nil { @@ -959,26 +955,26 @@ func (container *Container) ConnectToNetwork(idOrName string) error { return nil } -func (container *Container) connectToNetwork(idOrName string, updateSettings bool) (err error) { +func (daemon *Daemon) connectToNetwork(container *Container, idOrName string, updateSettings bool) (err error) { if container.hostConfig.NetworkMode.IsContainer() { return runconfig.ErrConflictSharedNetwork } if runconfig.NetworkMode(idOrName).IsBridge() && - container.daemon.configStore.DisableBridge { + daemon.configStore.DisableBridge { container.Config.NetworkDisabled = true return nil } - controller := container.daemon.netController + controller := daemon.netController - n, err := container.daemon.FindNetwork(idOrName) + n, err := daemon.FindNetwork(idOrName) if err != nil { return err } if updateSettings { - if err := container.updateNetworkSettings(n); err != nil { + if err := daemon.updateNetworkSettings(container, n); err != nil { return err } } @@ -1014,9 +1010,9 @@ func (container *Container) connectToNetwork(idOrName string, updateSettings boo return err } - sb := container.getNetworkSandbox() + sb := daemon.getNetworkSandbox(container) if sb == nil { - options, err := container.buildSandboxOptions(n) + options, err := daemon.buildSandboxOptions(container, n) if err != nil { return err } @@ -1039,12 +1035,12 @@ func (container *Container) connectToNetwork(idOrName string, updateSettings boo return nil } -func (container *Container) initializeNetworking() error { +func (daemon *Daemon) initializeNetworking(container *Container) error { var err error if container.hostConfig.NetworkMode.IsContainer() { // we need to get the hosts files from the container to join - nc, err := container.getNetworkedContainer() + nc, err := daemon.getNetworkedContainer(container.ID, container.hostConfig.NetworkMode.ConnectedContainer()) if err != nil { return err } @@ -1070,7 +1066,7 @@ func (container *Container) initializeNetworking() error { } - if err := container.allocateNetwork(); err != nil { + if err := daemon.allocateNetwork(container); err != nil { return err } @@ -1079,21 +1075,21 @@ func (container *Container) initializeNetworking() error { // called from the libcontainer pre-start hook to set the network // namespace configuration linkage to the libnetwork "sandbox" entity -func (container *Container) setNetworkNamespaceKey(pid int) error { +func (daemon *Daemon) setNetworkNamespaceKey(containerId string, pid int) error { path := fmt.Sprintf("/proc/%d/ns/net", pid) var sandbox libnetwork.Sandbox - search := libnetwork.SandboxContainerWalker(&sandbox, container.ID) - container.daemon.netController.WalkSandboxes(search) + search := libnetwork.SandboxContainerWalker(&sandbox, containerId) + daemon.netController.WalkSandboxes(search) if sandbox == nil { - return derr.ErrorCodeNoSandbox.WithArgs(container.ID) + return derr.ErrorCodeNoSandbox.WithArgs(containerId) } return sandbox.SetKey(path) } -func (container *Container) getIpcContainer() (*Container, error) { +func (daemon *Daemon) getIpcContainer(container *Container) (*Container, error) { containerID := container.hostConfig.IpcMode.Container() - c, err := container.daemon.Get(containerID) + c, err := daemon.Get(containerID) if err != nil { return nil, err } @@ -1130,27 +1126,18 @@ func (container *Container) setupWorkingDirectory() error { return nil } -func (container *Container) getNetworkedContainer() (*Container, error) { - parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2) - switch parts[0] { - case "container": - if len(parts) != 2 { - return nil, derr.ErrorCodeParseContainer - } - nc, err := container.daemon.Get(parts[1]) - if err != nil { - return nil, err - } - if container == nc { - return nil, derr.ErrorCodeJoinSelf - } - if !nc.IsRunning() { - return nil, derr.ErrorCodeJoinRunning.WithArgs(parts[1]) - } - return nc, nil - default: - return nil, derr.ErrorCodeModeNotContainer +func (daemon *Daemon) getNetworkedContainer(containerId, connectedContainerId string) (*Container, error) { + nc, err := daemon.Get(connectedContainerId) + if err != nil { + return nil, err } + if containerId == nc.ID { + return nil, derr.ErrorCodeJoinSelf + } + if !nc.IsRunning() { + return nil, derr.ErrorCodeJoinRunning.WithArgs(connectedContainerId) + } + return nc, nil } func (daemon *Daemon) releaseNetwork(container *Container) { @@ -1337,8 +1324,8 @@ func (container *Container) hasMountFor(path string) bool { return exists } -func (container *Container) setupIpcDirs() error { - rootUID, rootGID := container.daemon.GetRemappedUIDGID() +func (daemon *Daemon) setupIpcDirs(container *Container) error { + rootUID, rootGID := daemon.GetRemappedUIDGID() if !container.hasMountFor("/dev/shm") { shmPath, err := container.shmPath() if err != nil { diff --git a/daemon/container_windows.go b/daemon/container_windows.go index 0ad1fc69ad..4029848911 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -27,7 +27,7 @@ func killProcessDirectly(container *Container) error { return nil } -func (container *Container) setupLinkedContainers() ([]string, error) { +func (daemon *Daemon) setupLinkedContainers(container *Container) ([]string, error) { return nil, nil } @@ -36,12 +36,12 @@ func (container *Container) createDaemonEnvironment(linkedEnv []string) []string return container.Config.Env } -func (container *Container) initializeNetworking() error { +func (daemon *Daemon) initializeNetworking(container *Container) error { return nil } // ConnectToNetwork connects a container to the network -func (container *Container) ConnectToNetwork(idOrName string) error { +func (daemon *Daemon) ConnectToNetwork(container *Container, idOrName string) error { return nil } @@ -54,7 +54,7 @@ func (container *Container) setupWorkingDirectory() error { return nil } -func populateCommand(c *Container, env []string) error { +func (daemon *Daemon) populateCommand(c *Container, env []string) error { en := &execdriver.Network{ Interface: nil, } @@ -66,7 +66,7 @@ func populateCommand(c *Container, env []string) error { if !c.Config.NetworkDisabled { en.Interface = &execdriver.NetworkInterface{ MacAddress: c.Config.MacAddress, - Bridge: c.daemon.configStore.Bridge.VirtualSwitchName, + Bridge: daemon.configStore.Bridge.VirtualSwitchName, PortBindings: c.hostConfig.PortBindings, // TODO Windows. Include IPAddress. There already is a @@ -99,22 +99,22 @@ func populateCommand(c *Container, env []string) error { processConfig.Env = env var layerPaths []string - img, err := c.daemon.graph.Get(c.ImageID) + img, err := daemon.graph.Get(c.ImageID) if err != nil { return derr.ErrorCodeGetGraph.WithArgs(c.ImageID, err) } - for i := img; i != nil && err == nil; i, err = c.daemon.graph.GetParent(i) { - lp, err := c.daemon.driver.Get(i.ID, "") + for i := img; i != nil && err == nil; i, err = daemon.graph.GetParent(i) { + lp, err := daemon.driver.Get(i.ID, "") if err != nil { - return derr.ErrorCodeGetLayer.WithArgs(c.daemon.driver.String(), i.ID, err) + return derr.ErrorCodeGetLayer.WithArgs(daemon.driver.String(), i.ID, err) } layerPaths = append(layerPaths, lp) - err = c.daemon.driver.Put(i.ID) + err = daemon.driver.Put(i.ID) if err != nil { - return derr.ErrorCodePutLayer.WithArgs(c.daemon.driver.String(), i.ID, err) + return derr.ErrorCodePutLayer.WithArgs(daemon.driver.String(), i.ID, err) } } - m, err := c.daemon.driver.GetMetadata(c.ID) + m, err := daemon.driver.GetMetadata(c.ID) if err != nil { return derr.ErrorCodeGetLayerMetadata.WithArgs(err) } @@ -149,16 +149,16 @@ func (daemon *Daemon) getSize(container *Container) (int64, int64) { } // setNetworkNamespaceKey is a no-op on Windows. -func (container *Container) setNetworkNamespaceKey(pid int) error { +func (daemon *Daemon) setNetworkNamespaceKey(containerId string, pid int) error { return nil } // allocateNetwork is a no-op on Windows. -func (container *Container) allocateNetwork() error { +func (daemon *Daemon) allocateNetwork(container *Container) error { return nil } -func (container *Container) updateNetwork() error { +func (daemon *Daemon) updateNetwork(container *Container) error { return nil } @@ -172,7 +172,7 @@ func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) return volumeMounts, nil } -func (container *Container) setupIpcDirs() error { +func (daemon *Daemon) setupIpcDirs(container *Container) error { return nil } diff --git a/daemon/daemon.go b/daemon/daemon.go index 3f17027095..d902028163 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -957,7 +957,7 @@ func (daemon *Daemon) run(c *Container, pipes *execdriver.Pipes, startCallback e Start: startCallback, } hooks.PreStart = append(hooks.PreStart, func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error { - return c.setNetworkNamespaceKey(pid) + return daemon.setNetworkNamespaceKey(c.ID, pid) }) return daemon.execDriver.Run(c.command, pipes, hooks) } diff --git a/daemon/delete.go b/daemon/delete.go index 1de9ba9c1d..f62f71e972 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -45,7 +45,7 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error parentContainer, _ := daemon.Get(pe.ID()) if parentContainer != nil { - if err := parentContainer.updateNetwork(); err != nil { + if err := daemon.updateNetwork(parentContainer); err != nil { logrus.Debugf("Could not update network to remove link %s: %v", n, err) } } diff --git a/daemon/network.go b/daemon/network.go index 1184effc82..c1412cec89 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -134,7 +134,7 @@ func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName strin if err != nil { return err } - return container.ConnectToNetwork(networkName) + return daemon.ConnectToNetwork(container, networkName) } // DisconnectContainerFromNetwork disconnects the given container from diff --git a/daemon/start.go b/daemon/start.go index 89a6020f41..2943133b96 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -97,10 +97,10 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { // backwards API compatibility. container.hostConfig = runconfig.SetDefaultNetModeIfBlank(container.hostConfig) - if err := container.initializeNetworking(); err != nil { + if err := daemon.initializeNetworking(container); err != nil { return err } - linkedEnv, err := container.setupLinkedContainers() + linkedEnv, err := daemon.setupLinkedContainers(container) if err != nil { return err } @@ -108,12 +108,12 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { return err } env := container.createDaemonEnvironment(linkedEnv) - if err := populateCommand(container, env); err != nil { + if err := daemon.populateCommand(container, env); err != nil { return err } if !container.hostConfig.IpcMode.IsContainer() && !container.hostConfig.IpcMode.IsHost() { - if err := container.setupIpcDirs(); err != nil { + if err := daemon.setupIpcDirs(container); err != nil { return err } } diff --git a/runconfig/hostconfig_unix.go b/runconfig/hostconfig_unix.go index 0777d9ae0c..ed2bcf8e3e 100644 --- a/runconfig/hostconfig_unix.go +++ b/runconfig/hostconfig_unix.go @@ -66,6 +66,15 @@ func (n NetworkMode) IsNone() bool { return n == "none" } +// ConnectedContainer is the id of the container which network this container is connected to. +func (n NetworkMode) ConnectedContainer() string { + parts := strings.SplitN(string(n), ":", 2) + if len(parts) > 1 { + return parts[1] + } + return "" +} + // IsUserDefined indicates user-created network func (n NetworkMode) IsUserDefined() bool { return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() From c412300dd9f85301e2922ccc7c954eaa37fadcab Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 3 Nov 2015 13:45:12 -0500 Subject: [PATCH 14/17] Decouple daemon and container to configure logging drivers. Signed-off-by: David Calavera --- daemon/attach.go | 2 +- daemon/container.go | 38 ++++---------------------------------- daemon/logs.go | 42 +++++++++++++++++++++++++++++++++++++++++- daemon/monitor.go | 4 +++- 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/daemon/attach.go b/daemon/attach.go index 64a1223a4d..047828b17d 100644 --- a/daemon/attach.go +++ b/daemon/attach.go @@ -68,7 +68,7 @@ func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *Containe func (daemon *Daemon) attachWithLogs(container *Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool) error { if logs { - logDriver, err := container.getLogger() + logDriver, err := daemon.getLogger(container) if err != nil { return err } diff --git a/daemon/container.go b/daemon/container.go index 87b0e009da..f4c6b84d85 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -281,7 +281,7 @@ func (container *Container) exposes(p nat.Port) bool { return exists } -func (container *Container) getLogConfig() runconfig.LogConfig { +func (container *Container) getLogConfig(defaultConfig runconfig.LogConfig) runconfig.LogConfig { cfg := container.hostConfig.LogConfig if cfg.Type != "" || len(cfg.Config) > 0 { // container has log driver configured if cfg.Type == "" { @@ -290,17 +290,11 @@ func (container *Container) getLogConfig() runconfig.LogConfig { return cfg } // Use daemon's default log config for containers - return container.daemon.defaultLogConfig + return defaultConfig } -func (container *Container) getLogger() (logger.Logger, error) { - if container.logDriver != nil && container.IsRunning() { - return container.logDriver, nil - } - cfg := container.getLogConfig() - if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil { - return nil, err - } +// StartLogger starts a new logger driver for the container. +func (container *Container) StartLogger(cfg runconfig.LogConfig) (logger.Logger, error) { c, err := logger.GetLogDriver(cfg.Type) if err != nil { return nil, derr.ErrorCodeLoggingFactory.WithArgs(err) @@ -328,30 +322,6 @@ func (container *Container) getLogger() (logger.Logger, error) { return c(ctx) } -func (container *Container) startLogging() error { - cfg := container.getLogConfig() - if cfg.Type == "none" { - return nil // do not start logging routines - } - - l, err := container.getLogger() - if err != nil { - return derr.ErrorCodeInitLogger.WithArgs(err) - } - - copier := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l) - container.logCopier = copier - copier.Run() - container.logDriver = l - - // set LogPath field only for json-file logdriver - if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok { - container.LogPath = jl.LogPath() - } - - return nil -} - func (container *Container) getProcessLabel() string { // even if we have a process label return "" if we are running // in privileged mode diff --git a/daemon/logs.go b/daemon/logs.go index e4a4853c39..f3fa866a63 100644 --- a/daemon/logs.go +++ b/daemon/logs.go @@ -7,6 +7,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/logger" + "github.com/docker/docker/daemon/logger/jsonfilelog" derr "github.com/docker/docker/errors" "github.com/docker/docker/pkg/stdcopy" ) @@ -48,7 +49,7 @@ func (daemon *Daemon) ContainerLogs(containerName string, config *ContainerLogsC } config.OutStream = outStream - cLog, err := container.getLogger() + cLog, err := daemon.getLogger(container) if err != nil { return err } @@ -97,3 +98,42 @@ func (daemon *Daemon) ContainerLogs(containerName string, config *ContainerLogsC } } } + +func (daemon *Daemon) getLogger(container *Container) (logger.Logger, error) { + if container.logDriver != nil && container.IsRunning() { + return container.logDriver, nil + } + cfg := container.getLogConfig(daemon.defaultLogConfig) + if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil { + return nil, err + } + return container.StartLogger(cfg) +} + +// StartLogging initializes and starts the container logging stream. +func (daemon *Daemon) StartLogging(container *Container) error { + cfg := container.getLogConfig(daemon.defaultLogConfig) + if cfg.Type == "none" { + return nil // do not start logging routines + } + + if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil { + return err + } + l, err := container.StartLogger(cfg) + if err != nil { + return derr.ErrorCodeInitLogger.WithArgs(err) + } + + copier := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l) + container.logCopier = copier + copier.Run() + container.logDriver = l + + // set LogPath field only for json-file logdriver + if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok { + container.LogPath = jl.LogPath() + } + + return nil +} diff --git a/daemon/monitor.go b/daemon/monitor.go index 3512cbd1cd..4f37d0ef12 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -23,6 +23,8 @@ type containerSupervisor interface { LogContainerEvent(*Container, string) // Cleanup ensures that the container is properly unmounted Cleanup(*Container) + // StartLogging starts the logging driver for the container + StartLogging(*Container) error } // containerMonitor monitors the execution of a container's main process. @@ -142,7 +144,7 @@ func (m *containerMonitor) Start() error { for { m.container.RestartCount++ - if err := m.container.startLogging(); err != nil { + if err := m.supervisor.StartLogging(m.container); err != nil { m.resetContainer(false) return err From 5dc3a9a6da6ce6e3eee791251293cf045a4754f9 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 3 Nov 2015 14:06:16 -0500 Subject: [PATCH 15/17] Decouple daemon and container from the stats collector. Signed-off-by: David Calavera --- daemon/container.go | 4 --- daemon/daemon.go | 54 +++++++++++++++++++++++++++++++++- daemon/stats_collector_unix.go | 51 ++++++-------------------------- 3 files changed, 62 insertions(+), 47 deletions(-) diff --git a/daemon/container.go b/daemon/container.go index f4c6b84d85..95da662276 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -338,10 +338,6 @@ func (container *Container) getMountLabel() string { return container.MountLabel } -func (container *Container) stats() (*execdriver.ResourceStats, error) { - return container.daemon.stats(container) -} - func (container *Container) getExecIDs() []string { return container.execCommands.List() } diff --git a/daemon/daemon.go b/daemon/daemon.go index d902028163..de9a1157c2 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -56,6 +56,8 @@ import ( "github.com/docker/docker/volume/local" "github.com/docker/docker/volume/store" "github.com/docker/libnetwork" + lntypes "github.com/docker/libnetwork/types" + "github.com/opencontainers/runc/libcontainer" ) var ( @@ -816,7 +818,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo d.configStore = config d.sysInitPath = sysInitPath d.execDriver = ed - d.statsCollector = newStatsCollector(1 * time.Second) + d.statsCollector = d.newStatsCollector(1 * time.Second) d.defaultLogConfig = config.LogConfig d.RegistryService = registryService d.EventsService = eventsService @@ -1300,3 +1302,53 @@ func (daemon *Daemon) SearchRegistryForImages(term string, headers map[string][]string) (*registry.SearchResults, error) { return daemon.RegistryService.Search(term, authConfig, headers) } + +func (daemon *Daemon) GetContainerStats(container *Container) (*execdriver.ResourceStats, error) { + stats, err := daemon.stats(container) + if err != nil { + return nil, err + } + + // Retrieve the nw statistics from libnetwork and inject them in the Stats + var nwStats []*libcontainer.NetworkInterface + if nwStats, err = daemon.getNetworkStats(container); err != nil { + return nil, err + } + stats.Interfaces = nwStats + + return stats, nil +} + +func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) { + var list []*libcontainer.NetworkInterface + + sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID) + if err != nil { + return list, err + } + + stats, err := sb.Statistics() + if err != nil { + return list, err + } + + // Convert libnetwork nw stats into libcontainer nw stats + for ifName, ifStats := range stats { + list = append(list, convertLnNetworkStats(ifName, ifStats)) + } + + return list, nil +} + +func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface { + n := &libcontainer.NetworkInterface{Name: name} + n.RxBytes = stats.RxBytes + n.RxPackets = stats.RxPackets + n.RxErrors = stats.RxErrors + n.RxDropped = stats.RxDropped + n.TxBytes = stats.TxBytes + n.TxPackets = stats.TxPackets + n.TxErrors = stats.TxErrors + n.TxDropped = stats.TxDropped + return n +} diff --git a/daemon/stats_collector_unix.go b/daemon/stats_collector_unix.go index 827578b6f2..6fb32b9a22 100644 --- a/daemon/stats_collector_unix.go +++ b/daemon/stats_collector_unix.go @@ -14,18 +14,22 @@ import ( "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" "github.com/docker/docker/pkg/pubsub" - lntypes "github.com/docker/libnetwork/types" - "github.com/opencontainers/runc/libcontainer" "github.com/opencontainers/runc/libcontainer/system" ) +type statsSupervisor interface { + // GetContainerStats collects all the stats related to a container + GetContainerStats(container *Container) (*execdriver.ResourceStats, error) +} + // newStatsCollector returns a new statsCollector that collections // network and cgroup stats for a registered container at the specified // interval. The collector allows non-running containers to be added // and will start processing stats when they are started. -func newStatsCollector(interval time.Duration) *statsCollector { +func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector { s := &statsCollector{ interval: interval, + supervisor: daemon, publishers: make(map[*Container]*pubsub.Publisher), clockTicksPerSecond: uint64(system.GetClockTicks()), bufReader: bufio.NewReaderSize(nil, 128), @@ -37,6 +41,7 @@ func newStatsCollector(interval time.Duration) *statsCollector { // statsCollector manages and provides container resource stats type statsCollector struct { m sync.Mutex + supervisor statsSupervisor interval time.Duration clockTicksPerSecond uint64 publishers map[*Container]*pubsub.Publisher @@ -112,7 +117,7 @@ func (s *statsCollector) run() { } for _, pair := range pairs { - stats, err := pair.container.stats() + stats, err := s.supervisor.GetContainerStats(pair.container) if err != nil { if err != execdriver.ErrNotRunning { logrus.Errorf("collecting stats for %s: %v", pair.container.ID, err) @@ -121,10 +126,6 @@ func (s *statsCollector) run() { } stats.SystemUsage = systemUsage - // Retrieve the nw statistics from libnetwork and inject them in the Stats - if nwStats, err := s.getNetworkStats(pair.container); err == nil { - stats.Interfaces = nwStats - } pair.publisher.Publish(stats) } } @@ -177,37 +178,3 @@ func (s *statsCollector) getSystemCPUUsage() (uint64, error) { } return 0, derr.ErrorCodeBadStatFormat } - -func (s *statsCollector) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) { - var list []*libcontainer.NetworkInterface - - sb, err := c.daemon.netController.SandboxByID(c.NetworkSettings.SandboxID) - if err != nil { - return list, err - } - - stats, err := sb.Statistics() - if err != nil { - return list, err - } - - // Convert libnetwork nw stats into libcontainer nw stats - for ifName, ifStats := range stats { - list = append(list, convertLnNetworkStats(ifName, ifStats)) - } - - return list, nil -} - -func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface { - n := &libcontainer.NetworkInterface{Name: name} - n.RxBytes = stats.RxBytes - n.RxPackets = stats.RxPackets - n.RxErrors = stats.RxErrors - n.RxDropped = stats.RxDropped - n.TxBytes = stats.TxBytes - n.TxPackets = stats.TxPackets - n.TxErrors = stats.TxErrors - n.TxDropped = stats.TxDropped - return n -} From 444c82d19d3c8ee51f313c879d434ac5b8be6a6f Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 3 Nov 2015 14:07:47 -0500 Subject: [PATCH 16/17] Remove daemon field from container. Signed-off-by: David Calavera --- daemon/container.go | 1 - 1 file changed, 1 deletion(-) diff --git a/daemon/container.go b/daemon/container.go index 95da662276..b02f139473 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -75,7 +75,6 @@ type CommonContainer struct { command *execdriver.Command monitor *containerMonitor execCommands *execStore - daemon *Daemon // logDriver for closing logDriver logger.Logger logCopier *logger.Copier From 63efc12070b2aff0f062ad62cc577cf2ffb66ef6 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 3 Nov 2015 14:25:22 -0500 Subject: [PATCH 17/17] Remove further references to the daemon within containers. Signed-off-by: David Calavera --- daemon/archive.go | 8 ++++---- daemon/container.go | 4 ++-- daemon/container_unix.go | 20 ++++++++++---------- daemon/container_windows.go | 2 +- daemon/create.go | 2 +- daemon/create_unix.go | 4 ++-- daemon/create_windows.go | 4 ++-- daemon/daemon.go | 13 +++++++++---- daemon/exec.go | 8 ++++---- daemon/monitor.go | 8 ++++++-- daemon/mounts.go | 2 +- daemon/start.go | 2 +- daemon/stats_collector_windows.go | 2 +- daemon/volumes_unix.go | 4 ++-- daemon/volumes_windows.go | 5 +++-- 15 files changed, 49 insertions(+), 39 deletions(-) diff --git a/daemon/archive.go b/daemon/archive.go index 61196c91f5..f4a82478d9 100644 --- a/daemon/archive.go +++ b/daemon/archive.go @@ -142,7 +142,7 @@ func (daemon *Daemon) containerStatPath(container *Container, path string) (stat } defer daemon.Unmount(container) - err = container.mountVolumes() + err = daemon.mountVolumes(container) defer container.unmountVolumes(true) if err != nil { return nil, err @@ -184,7 +184,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c } }() - if err = container.mountVolumes(); err != nil { + if err = daemon.mountVolumes(container); err != nil { return nil, nil, err } @@ -239,7 +239,7 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n } defer daemon.Unmount(container) - err = container.mountVolumes() + err = daemon.mountVolumes(container) defer container.unmountVolumes(true) if err != nil { return err @@ -348,7 +348,7 @@ func (daemon *Daemon) containerCopy(container *Container, resource string) (rc i } }() - if err := container.mountVolumes(); err != nil { + if err := daemon.mountVolumes(container); err != nil { return nil, err } diff --git a/daemon/container.go b/daemon/container.go index b02f139473..bc132af2a3 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -498,8 +498,8 @@ func (container *Container) shouldRestart() bool { (container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0) } -func (container *Container) mountVolumes() error { - mounts, err := container.setupMounts() +func (daemon *Daemon) mountVolumes(container *Container) error { + mounts, err := daemon.setupMounts(container) if err != nil { return err } diff --git a/daemon/container_unix.go b/daemon/container_unix.go index d47647412b..a4b9cd5a6d 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -744,14 +744,14 @@ func (daemon *Daemon) updateNetworkSettings(container *Container, n libnetwork.N return nil } -func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error { +func (daemon *Daemon) updateEndpointNetworkSettings(container *Container, n libnetwork.Network, ep libnetwork.Endpoint) error { networkSettings, err := container.buildEndpointInfo(n, ep, container.NetworkSettings) if err != nil { return err } if container.hostConfig.NetworkMode == runconfig.NetworkMode("bridge") { - networkSettings.Bridge = container.daemon.configStore.Bridge.Iface + networkSettings.Bridge = daemon.configStore.Bridge.Iface } return nil @@ -1006,7 +1006,7 @@ func (daemon *Daemon) connectToNetwork(container *Container, idOrName string, up } }() - if err := container.updateEndpointNetworkSettings(n, ep); err != nil { + if err := daemon.updateEndpointNetworkSettings(container, n, ep); err != nil { return err } @@ -1075,13 +1075,13 @@ func (daemon *Daemon) initializeNetworking(container *Container) error { // called from the libcontainer pre-start hook to set the network // namespace configuration linkage to the libnetwork "sandbox" entity -func (daemon *Daemon) setNetworkNamespaceKey(containerId string, pid int) error { +func (daemon *Daemon) setNetworkNamespaceKey(containerID string, pid int) error { path := fmt.Sprintf("/proc/%d/ns/net", pid) var sandbox libnetwork.Sandbox - search := libnetwork.SandboxContainerWalker(&sandbox, containerId) + search := libnetwork.SandboxContainerWalker(&sandbox, containerID) daemon.netController.WalkSandboxes(search) if sandbox == nil { - return derr.ErrorCodeNoSandbox.WithArgs(containerId) + return derr.ErrorCodeNoSandbox.WithArgs(containerID) } return sandbox.SetKey(path) @@ -1126,16 +1126,16 @@ func (container *Container) setupWorkingDirectory() error { return nil } -func (daemon *Daemon) getNetworkedContainer(containerId, connectedContainerId string) (*Container, error) { - nc, err := daemon.Get(connectedContainerId) +func (daemon *Daemon) getNetworkedContainer(containerID, connectedContainerID string) (*Container, error) { + nc, err := daemon.Get(connectedContainerID) if err != nil { return nil, err } - if containerId == nc.ID { + if containerID == nc.ID { return nil, derr.ErrorCodeJoinSelf } if !nc.IsRunning() { - return nil, derr.ErrorCodeJoinRunning.WithArgs(connectedContainerId) + return nil, derr.ErrorCodeJoinRunning.WithArgs(connectedContainerID) } return nc, nil } diff --git a/daemon/container_windows.go b/daemon/container_windows.go index 4029848911..fd2dcb9bdf 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -149,7 +149,7 @@ func (daemon *Daemon) getSize(container *Container) (int64, int64) { } // setNetworkNamespaceKey is a no-op on Windows. -func (daemon *Daemon) setNetworkNamespaceKey(containerId string, pid int) error { +func (daemon *Daemon) setNetworkNamespaceKey(containerID string, pid int) error { return nil } diff --git a/daemon/create.go b/daemon/create.go index b27a20eed4..aaf0f1329b 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -119,7 +119,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re } defer daemon.Unmount(container) - if err := createContainerPlatformSpecificSettings(container, params.Config, params.HostConfig, img); err != nil { + if err := daemon.createContainerPlatformSpecificSettings(container, params.Config, params.HostConfig, img); err != nil { return nil, err } diff --git a/daemon/create_unix.go b/daemon/create_unix.go index 66138db967..4d84997e93 100644 --- a/daemon/create_unix.go +++ b/daemon/create_unix.go @@ -15,7 +15,7 @@ import ( ) // createContainerPlatformSpecificSettings performs platform specific container create functionality -func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error { +func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error { for spec := range config.Volumes { name := stringid.GenerateNonCryptoID() destination := filepath.Clean(spec) @@ -45,7 +45,7 @@ func createContainerPlatformSpecificSettings(container *Container, config *runco } } - v, err := container.daemon.createVolume(name, volumeDriver, nil) + v, err := daemon.createVolume(name, volumeDriver, nil) if err != nil { return err } diff --git a/daemon/create_windows.go b/daemon/create_windows.go index 1ea465f71c..a95e667b74 100644 --- a/daemon/create_windows.go +++ b/daemon/create_windows.go @@ -10,7 +10,7 @@ import ( ) // createContainerPlatformSpecificSettings performs platform specific container create functionality -func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error { +func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error { for spec := range config.Volumes { mp, err := volume.ParseMountSpec(spec, hostConfig.VolumeDriver) @@ -41,7 +41,7 @@ func createContainerPlatformSpecificSettings(container *Container, config *runco // Create the volume in the volume driver. If it doesn't exist, // a new one will be created. - v, err := container.daemon.createVolume(mp.Name, volumeDriver, nil) + v, err := daemon.createVolume(mp.Name, volumeDriver, nil) if err != nil { return err } diff --git a/daemon/daemon.go b/daemon/daemon.go index de9a1157c2..13e3f33103 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -194,7 +194,7 @@ func (daemon *Daemon) load(id string) (*Container, error) { // Register makes a container object usable by the daemon as func (daemon *Daemon) Register(container *Container) error { - if container.daemon != nil || daemon.Exists(container.ID) { + if daemon.Exists(container.ID) { return fmt.Errorf("Container is already loaded") } if err := validateID(container.ID); err != nil { @@ -204,8 +204,6 @@ func (daemon *Daemon) Register(container *Container) error { return err } - container.daemon = daemon - // Attach to stdout and stderr container.stderr = new(broadcaster.Unbuffered) container.stdout = new(broadcaster.Unbuffered) @@ -954,7 +952,8 @@ func (daemon *Daemon) Unmount(container *Container) error { return daemon.driver.Put(container.ID) } -func (daemon *Daemon) run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (execdriver.ExitStatus, error) { +// Run uses the execution driver to run a given container +func (daemon *Daemon) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (execdriver.ExitStatus, error) { hooks := execdriver.Hooks{ Start: startCallback, } @@ -1303,6 +1302,12 @@ func (daemon *Daemon) SearchRegistryForImages(term string, return daemon.RegistryService.Search(term, authConfig, headers) } +// IsShuttingDown tells whether the daemon is shutting down or not +func (daemon *Daemon) IsShuttingDown() bool { + return daemon.shutdown +} + +// GetContainerStats collects all the stats published by a container func (daemon *Daemon) GetContainerStats(container *Container) (*execdriver.ResourceStats, error) { stats, err := daemon.stats(container) if err != nil { diff --git a/daemon/exec.go b/daemon/exec.go index 6ebacd6643..bf0335ee49 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -331,7 +331,7 @@ func (d *Daemon) containerExecIds() map[string]struct{} { return ids } -func (daemon *Daemon) containerExec(container *Container, ec *ExecConfig) error { +func (d *Daemon) containerExec(container *Container, ec *ExecConfig) error { container.Lock() defer container.Unlock() @@ -350,7 +350,7 @@ func (daemon *Daemon) containerExec(container *Container, ec *ExecConfig) error // We use a callback here instead of a goroutine and an chan for // synchronization purposes - cErr := promise.Go(func() error { return daemon.monitorExec(container, ec, callback) }) + cErr := promise.Go(func() error { return d.monitorExec(container, ec, callback) }) // Exec should not return until the process is actually running select { @@ -362,13 +362,13 @@ func (daemon *Daemon) containerExec(container *Container, ec *ExecConfig) error return nil } -func (daemon *Daemon) monitorExec(container *Container, ExecConfig *ExecConfig, callback execdriver.DriverCallback) error { +func (d *Daemon) monitorExec(container *Container, ExecConfig *ExecConfig, callback execdriver.DriverCallback) error { var ( err error exitCode int ) pipes := execdriver.NewPipes(ExecConfig.streamConfig.stdin, ExecConfig.streamConfig.stdout, ExecConfig.streamConfig.stderr, ExecConfig.OpenStdin) - exitCode, err = daemon.Exec(container, ExecConfig, pipes, callback) + exitCode, err = d.Exec(container, ExecConfig, pipes, callback) if err != nil { logrus.Errorf("Error running command in existing container %s: %s", container.ID, err) } diff --git a/daemon/monitor.go b/daemon/monitor.go index 4f37d0ef12..fa89d1d6c7 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -25,6 +25,10 @@ type containerSupervisor interface { Cleanup(*Container) // StartLogging starts the logging driver for the container StartLogging(*Container) error + // Run starts a container + Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (execdriver.ExitStatus, error) + // IsShuttingDown tells whether the supervisor is shutting down or not + IsShuttingDown() bool } // containerMonitor monitors the execution of a container's main process. @@ -156,7 +160,7 @@ func (m *containerMonitor) Start() error { m.lastStartTime = time.Now() - if exitStatus, err = m.container.daemon.run(m.container, pipes, m.callback); err != nil { + if exitStatus, err = m.supervisor.Run(m.container, pipes, m.callback); err != nil { // if we receive an internal error from the initial start of a container then lets // return it instead of entering the restart loop if m.container.RestartCount == 0 { @@ -236,7 +240,7 @@ func (m *containerMonitor) shouldRestart(exitCode int) bool { // do not restart if the user or docker has requested that this container be stopped if m.shouldStop { - m.container.HasBeenManuallyStopped = !m.container.daemon.shutdown + m.container.HasBeenManuallyStopped = !m.supervisor.IsShuttingDown() return false } diff --git a/daemon/mounts.go b/daemon/mounts.go index 91d5c0953c..9d195fa8ae 100644 --- a/daemon/mounts.go +++ b/daemon/mounts.go @@ -26,7 +26,7 @@ func (daemon *Daemon) removeMountPoints(container *Container, rm bool) error { if m.Volume == nil { continue } - container.daemon.volumes.Decrement(m.Volume) + daemon.volumes.Decrement(m.Volume) if rm { err := daemon.volumes.Remove(m.Volume) // ErrVolumeInUse is ignored because having this diff --git a/daemon/start.go b/daemon/start.go index 2943133b96..8fe2fa84dd 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -118,7 +118,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { } } - mounts, err := container.setupMounts() + mounts, err := daemon.setupMounts(container) if err != nil { return err } diff --git a/daemon/stats_collector_windows.go b/daemon/stats_collector_windows.go index f16d7ee380..e63f37b0d6 100644 --- a/daemon/stats_collector_windows.go +++ b/daemon/stats_collector_windows.go @@ -6,7 +6,7 @@ import "time" // for a registered container at the specified interval. The collector allows // non-running containers to be added and will start processing stats when // they are started. -func newStatsCollector(interval time.Duration) *statsCollector { +func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector { return &statsCollector{} } diff --git a/daemon/volumes_unix.go b/daemon/volumes_unix.go index b2a3c27b67..a7ac991105 100644 --- a/daemon/volumes_unix.go +++ b/daemon/volumes_unix.go @@ -58,7 +58,7 @@ func copyOwnership(source, destination string) error { // setupMounts iterates through each of the mount points for a container and // calls Setup() on each. It also looks to see if is a network mount such as // /etc/resolv.conf, and if it is not, appends it to the array of mounts. -func (container *Container) setupMounts() ([]execdriver.Mount, error) { +func (daemon *Daemon) setupMounts(container *Container) ([]execdriver.Mount, error) { var mounts []execdriver.Mount for _, m := range container.MountPoints { path, err := m.Setup() @@ -79,7 +79,7 @@ func (container *Container) setupMounts() ([]execdriver.Mount, error) { // if we are going to mount any of the network files from container // metadata, the ownership must be set properly for potential container // remapped root (user namespaces) - rootUID, rootGID := container.daemon.GetRemappedUIDGID() + rootUID, rootGID := daemon.GetRemappedUIDGID() for _, mount := range netMounts { if err := os.Chown(mount.Source, rootUID, rootGID); err != nil { return nil, err diff --git a/daemon/volumes_windows.go b/daemon/volumes_windows.go index 5c6182da53..73ec171902 100644 --- a/daemon/volumes_windows.go +++ b/daemon/volumes_windows.go @@ -3,17 +3,18 @@ package daemon import ( + "sort" + "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" "github.com/docker/docker/volume" - "sort" ) // setupMounts configures the mount points for a container by appending each // of the configured mounts on the container to the execdriver mount structure // which will ultimately be passed into the exec driver during container creation. // It also ensures each of the mounts are lexographically sorted. -func (container *Container) setupMounts() ([]execdriver.Mount, error) { +func (daemon *Daemon) setupMounts(container *Container) ([]execdriver.Mount, error) { var mnts []execdriver.Mount for _, mount := range container.MountPoints { // type is volume.MountPoint // If there is no source, take it from the volume path