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 <david.calavera@gmail.com>
This commit is contained in:
David Calavera 2015-11-02 20:06:09 -05:00
parent 1c94f5f53a
commit 3a49765046
17 changed files with 188 additions and 176 deletions

View file

@ -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.

View file

@ -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 {

View file

@ -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
}

View file

@ -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)

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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)
}
}
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)

View file

@ -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)
}
}
}

View file

@ -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

View file

@ -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")

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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()
}