From 6bb0d1816acd8d4f7a542a6aac047da2b874f476 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 12 Nov 2015 11:55:17 -0800 Subject: [PATCH] Move Container to its own package. So other packages don't need to import the daemon package when they want to use this struct. Signed-off-by: David Calavera Signed-off-by: Tibor Vass --- builder/builder.go | 17 +- builder/dockerfile/internals.go | 9 +- container/archive.go | 69 + {daemon => container}/container.go | 177 +- container/container_unit_test.go | 36 + container/container_unix.go | 701 ++++++++ container/container_windows.go | 65 + {daemon => container}/monitor.go | 68 +- {daemon => container}/state.go | 56 +- {daemon => container}/state_test.go | 8 +- {daemon => container}/state_unix.go | 2 +- {daemon => container}/state_windows.go | 2 +- daemon/archive.go | 99 +- daemon/archive_unix.go | 4 +- daemon/archive_windows.go | 4 +- daemon/attach.go | 3 +- daemon/commit.go | 7 +- daemon/container_operations.go | 9 + daemon/container_operations_unix.go | 928 ++++++++++ ...ows.go => container_operations_windows.go} | 110 +- daemon/container_unit_test.go | 136 -- daemon/container_unix.go | 1545 ----------------- daemon/create.go | 9 +- daemon/create_unix.go | 9 +- daemon/create_windows.go | 9 +- daemon/daemon.go | 118 +- daemon/daemon_test.go | 134 +- daemon/daemon_unix.go | 19 +- daemon/daemon_windows.go | 13 +- daemon/daemonbuilder/builder.go | 15 +- daemon/delete.go | 13 +- daemon/delete_test.go | 11 +- daemon/events.go | 6 +- daemon/exec.go | 44 +- daemon/exec_unix.go | 3 +- daemon/exec_windows.go | 3 +- daemon/export.go | 5 +- daemon/history.go | 6 +- daemon/image_delete.go | 3 +- daemon/inspect.go | 9 +- daemon/inspect_unix.go | 15 +- daemon/inspect_windows.go | 9 +- daemon/kill.go | 7 +- daemon/list.go | 21 +- daemon/list_unix.go | 4 +- daemon/list_windows.go | 10 +- daemon/logs.go | 17 +- daemon/mounts.go | 5 +- daemon/pause.go | 5 +- daemon/rename.go | 12 +- daemon/restart.go | 3 +- daemon/start.go | 54 +- daemon/stats_collector_unix.go | 15 +- daemon/stats_collector_windows.go | 12 +- daemon/stop.go | 5 +- daemon/unpause.go | 5 +- daemon/volumes.go | 3 +- daemon/volumes_unix.go | 47 +- daemon/volumes_windows.go | 3 +- opts/opts_windows.go | 2 +- 60 files changed, 2454 insertions(+), 2284 deletions(-) create mode 100644 container/archive.go rename {daemon => container}/container.go (67%) create mode 100644 container/container_unit_test.go create mode 100644 container/container_unix.go create mode 100644 container/container_windows.go rename {daemon => container}/monitor.go (86%) rename {daemon => container}/state.go (75%) rename {daemon => container}/state_test.go (95%) rename {daemon => container}/state_unix.go (94%) rename {daemon => container}/state_windows.go (93%) create mode 100644 daemon/container_operations.go create mode 100644 daemon/container_operations_unix.go rename daemon/{container_windows.go => container_operations_windows.go} (53%) delete mode 100644 daemon/container_unit_test.go delete mode 100644 daemon/container_unix.go diff --git a/builder/builder.go b/builder/builder.go index e5d4d63f9b..ee1496c3dd 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -9,6 +9,7 @@ import ( "os" // TODO: remove dependency on daemon + "github.com/docker/docker/container" "github.com/docker/docker/daemon" "github.com/docker/docker/image" "github.com/docker/docker/runconfig" @@ -115,13 +116,11 @@ type Docker interface { // Pull tells Docker to pull image referenced by `name`. Pull(name string) (*image.Image, error) - // TODO: move daemon.Container to its own package - // Container looks up a Docker container referenced by `id`. - Container(id string) (*daemon.Container, error) + Container(id string) (*container.Container, error) // Create creates a new Docker container and returns potential warnings // TODO: put warnings in the error - Create(*runconfig.Config, *runconfig.HostConfig) (*daemon.Container, []string, error) + Create(*runconfig.Config, *runconfig.HostConfig) (*container.Container, []string, error) // Remove removes a container specified by `id`. Remove(id string, cfg *daemon.ContainerRmConfig) error // Commit creates a new Docker image from an existing Docker container. @@ -131,7 +130,7 @@ type Docker interface { // TODO: make an Extract method instead of passing `decompress` // TODO: do not pass a FileInfo, instead refactor the archive package to export a Walk function that can be used // with Context.Walk - Copy(c *daemon.Container, destPath string, src FileInfo, decompress bool) error + Copy(c *container.Container, destPath string, src FileInfo, decompress bool) error // Retain retains an image avoiding it to be removed or overwritten until a corresponding Release() call. // TODO: remove @@ -140,13 +139,13 @@ type Docker interface { // TODO: remove Release(sessionID string, activeImages []string) // Kill stops the container execution abruptly. - Kill(c *daemon.Container) error + Kill(c *container.Container) error // Mount mounts the root filesystem for the container. - Mount(c *daemon.Container) error + Mount(c *container.Container) error // Unmount unmounts the root filesystem for the container. - Unmount(c *daemon.Container) error + Unmount(c *container.Container) error // Start starts a new container - Start(c *daemon.Container) error + Start(c *container.Container) error } // ImageCache abstracts an image cache store. diff --git a/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index cdb86cdd17..e54c262e78 100644 --- a/builder/dockerfile/internals.go +++ b/builder/dockerfile/internals.go @@ -22,6 +22,7 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/builder" "github.com/docker/docker/builder/dockerfile/parser" + "github.com/docker/docker/container" "github.com/docker/docker/daemon" "github.com/docker/docker/image" "github.com/docker/docker/pkg/archive" @@ -419,8 +420,8 @@ func (b *Builder) processImageFrom(img *image.Image) error { } // The default path will be blank on Windows (set by HCS) - if len(b.runConfig.Env) == 0 && daemon.DefaultPathEnv != "" { - b.runConfig.Env = append(b.runConfig.Env, "PATH="+daemon.DefaultPathEnv) + if len(b.runConfig.Env) == 0 && container.DefaultPathEnv != "" { + b.runConfig.Env = append(b.runConfig.Env, "PATH="+container.DefaultPathEnv) } // Process ONBUILD triggers if they exist @@ -492,7 +493,7 @@ func (b *Builder) probeCache() (bool, error) { return true, nil } -func (b *Builder) create() (*daemon.Container, error) { +func (b *Builder) create() (*container.Container, error) { if b.image == "" && !b.noBaseImage { return nil, fmt.Errorf("Please provide a source image with `from` prior to run") } @@ -542,7 +543,7 @@ func (b *Builder) create() (*daemon.Container, error) { return c, nil } -func (b *Builder) run(c *daemon.Container) error { +func (b *Builder) run(c *container.Container) error { var errCh chan error if b.Verbose { errCh = c.Attach(nil, b.Stdout, b.Stderr) diff --git a/container/archive.go b/container/archive.go new file mode 100644 index 0000000000..a7dd87fb84 --- /dev/null +++ b/container/archive.go @@ -0,0 +1,69 @@ +package container + +import ( + "os" + "path/filepath" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/archive" +) + +// ResolvePath resolves the given path in the container to a resource on the +// host. Returns a resolved path (absolute path to the resource on the host), +// the absolute path to the resource relative to the container's rootfs, and +// a error if the path points to outside the container's rootfs. +func (container *Container) ResolvePath(path string) (resolvedPath, absPath string, err error) { + // Consider the given path as an absolute path in the container. + absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join(string(filepath.Separator), path), path) + + // Split the absPath into its Directory and Base components. We will + // resolve the dir in the scope of the container then append the base. + dirPath, basePath := filepath.Split(absPath) + + resolvedDirPath, err := container.GetResourcePath(dirPath) + if err != nil { + return "", "", err + } + + // resolvedDirPath will have been cleaned (no trailing path separators) so + // we can manually join it with the base path element. + resolvedPath = resolvedDirPath + string(filepath.Separator) + basePath + + return resolvedPath, absPath, nil +} + +// StatPath is the unexported version of StatPath. Locks and mounts should +// be acquired before calling this method and the given path should be fully +// resolved to a path on the host corresponding to the given absolute path +// inside the container. +func (container *Container) StatPath(resolvedPath, absPath string) (stat *types.ContainerPathStat, err error) { + lstat, err := os.Lstat(resolvedPath) + if err != nil { + return nil, err + } + + var linkTarget string + if lstat.Mode()&os.ModeSymlink != 0 { + // Fully evaluate the symlink in the scope of the container rootfs. + hostPath, err := container.GetResourcePath(absPath) + if err != nil { + return nil, err + } + + linkTarget, err = filepath.Rel(container.BaseFS, hostPath) + if err != nil { + return nil, err + } + + // Make it an absolute path. + linkTarget = filepath.Join(string(filepath.Separator), linkTarget) + } + + return &types.ContainerPathStat{ + Name: filepath.Base(absPath), + Size: lstat.Size(), + Mode: lstat.Mode(), + Mtime: lstat.ModTime(), + LinkTarget: linkTarget, + }, nil +} diff --git a/daemon/container.go b/container/container.go similarity index 67% rename from daemon/container.go rename to container/container.go index 3bdb5ccaa3..72744cbcba 100644 --- a/daemon/container.go +++ b/container/container.go @@ -1,8 +1,7 @@ -package daemon +package container import ( "encoding/json" - "errors" "fmt" "io" "os" @@ -11,8 +10,6 @@ import ( "syscall" "time" - "github.com/opencontainers/runc/libcontainer/label" - "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/exec" "github.com/docker/docker/daemon/execdriver" @@ -26,28 +23,22 @@ import ( "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/symlink" - "github.com/docker/docker/runconfig" "github.com/docker/docker/volume" + "github.com/opencontainers/runc/libcontainer/label" ) const configFileName = "config.v2.json" -var ( - // ErrRootFSReadOnly is returned when a container - // rootfs is marked readonly. - ErrRootFSReadOnly = errors.New("container rootfs is marked read-only") -) - // CommonContainer holds the fields for a container which are // applicable across all platforms supported by the daemon. type CommonContainer struct { *runconfig.StreamConfig // embed for Container to support states directly. *State `json:"State"` // Needed for remote api version <= 1.11 - root string // Path to the "home" of the container, including metadata. - basefs string // Path to the graphdriver mountpoint - rwlayer layer.RWLayer + Root string `json:"-"` // Path to the "home" of the container, including metadata. + BaseFS string `json:"-"` // Path to the graphdriver mountpoint + RWLayer layer.RWLayer `json:"-"` ID string Created time.Time Path string @@ -65,32 +56,33 @@ type CommonContainer struct { HasBeenStartedBefore bool HasBeenManuallyStopped bool // used for unless-stopped restart policy MountPoints map[string]*volume.MountPoint - hostConfig *runconfig.HostConfig - command *execdriver.Command + HostConfig *runconfig.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable + Command *execdriver.Command `json:"-"` monitor *containerMonitor - execCommands *exec.Store + ExecCommands *exec.Store `json:"-"` // logDriver for closing - logDriver logger.Logger - logCopier *logger.Copier + LogDriver logger.Logger `json:"-"` + LogCopier *logger.Copier `json:"-"` } -// newBaseContainer creates a new container with its +// NewBaseContainer creates a new container with its // basic configuration. -func newBaseContainer(id, root string) *Container { +func NewBaseContainer(id, root string) *Container { return &Container{ CommonContainer: CommonContainer{ ID: id, State: NewState(), - execCommands: exec.NewStore(), - root: root, + ExecCommands: exec.NewStore(), + Root: root, MountPoints: make(map[string]*volume.MountPoint), StreamConfig: runconfig.NewStreamConfig(), }, } } -func (container *Container) fromDisk() error { - pth, err := container.jsonPath() +// FromDisk loads the container configuration stored in the host. +func (container *Container) FromDisk() error { + pth, err := container.ConfigPath() if err != nil { return err } @@ -114,8 +106,9 @@ func (container *Container) fromDisk() error { return container.readHostConfig() } -func (container *Container) toDisk() error { - pth, err := container.jsonPath() +// ToDisk saves the container configuration on disk. +func (container *Container) ToDisk() error { + pth, err := container.ConfigPath() if err != nil { return err } @@ -133,22 +126,24 @@ func (container *Container) toDisk() error { return err } - return container.writeHostConfig() + return container.WriteHostConfig() } -func (container *Container) toDiskLocking() error { +// ToDiskLocking saves the container configuration on disk in a thread safe way. +func (container *Container) ToDiskLocking() error { container.Lock() - err := container.toDisk() + err := container.ToDisk() container.Unlock() return err } +// readHostConfig reads the host configuration from disk for the container. func (container *Container) readHostConfig() error { - container.hostConfig = &runconfig.HostConfig{} + container.HostConfig = &runconfig.HostConfig{} // If the hostconfig file does not exist, do not read it. - // (We still have to initialize container.hostConfig, + // (We still have to initialize container.HostConfig, // but that's OK, since we just did that above.) - pth, err := container.hostConfigPath() + pth, err := container.HostConfigPath() if err != nil { return err } @@ -162,17 +157,18 @@ func (container *Container) readHostConfig() error { } defer f.Close() - if err := json.NewDecoder(f).Decode(&container.hostConfig); err != nil { + if err := json.NewDecoder(f).Decode(&container.HostConfig); err != nil { return err } - initDNSHostConfig(container) + container.InitDNSHostConfig() return nil } -func (container *Container) writeHostConfig() error { - pth, err := container.hostConfigPath() +// WriteHostConfig saves the host configuration on disk for the container. +func (container *Container) WriteHostConfig() error { + pth, err := container.HostConfigPath() if err != nil { return err } @@ -183,19 +179,19 @@ func (container *Container) writeHostConfig() error { } defer f.Close() - return json.NewEncoder(f).Encode(&container.hostConfig) + return json.NewEncoder(f).Encode(&container.HostConfig) } -// 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 `/`. +// 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 `/`. // -// The basefs of a container is the host-facing path which is bind-mounted as +// The BaseFS of a container is the host-facing path which is bind-mounted as // `/` inside the container. This method is essentially used to access a // particular path inside the container as though you were a process in that // container. // -// NOTE: The returned path is *only* safely scoped inside the container's basefs +// NOTE: The returned path is *only* safely scoped inside the container's BaseFS // if no component of the returned path changes (such as a component // symlinking to a different path) between using this method and using the // path. See symlink.FollowSymlinkInScope for more details. @@ -203,11 +199,11 @@ func (container *Container) GetResourcePath(path string) (string, error) { // IMPORTANT - These are paths on the OS where the daemon is running, hence // any filepath operations must be done in an OS agnostic way. cleanPath := filepath.Join(string(os.PathSeparator), path) - r, e := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, cleanPath), container.basefs) + r, e := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, cleanPath), container.BaseFS) return r, e } -// Evaluates `path` in the scope of the container's root, with proper path +// GetRootResourcePath evaluates `path` in the scope of the container's root, with proper path // sanitisation. Symlinks are all scoped to the root of the container, as // though the container's root was `/`. // @@ -219,11 +215,11 @@ func (container *Container) GetResourcePath(path string) (string, error) { // if no component of the returned path changes (such as a component // symlinking to a different path) between using this method and using the // path. See symlink.FollowSymlinkInScope for more details. -func (container *Container) getRootResourcePath(path string) (string, error) { +func (container *Container) GetRootResourcePath(path string) (string, error) { // IMPORTANT - These are paths on the OS where the daemon is running, hence // any filepath operations must be done in an OS agnostic way. cleanPath := filepath.Join(string(os.PathSeparator), path) - return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root) + return symlink.FollowSymlinkInScope(filepath.Join(container.Root, cleanPath), container.Root) } // ExitOnNext signals to the monitor that it should not restart the container @@ -235,23 +231,20 @@ func (container *Container) ExitOnNext() { // 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 { - if err := container.command.ProcessConfig.Terminal.Resize(h, w); err != nil { + if err := container.Command.ProcessConfig.Terminal.Resize(h, w); err != nil { return err } return nil } -func (container *Container) hostConfigPath() (string, error) { - return container.getRootResourcePath("hostconfig.json") +// HostConfigPath returns the path to the container's JSON hostconfig +func (container *Container) HostConfigPath() (string, error) { + return container.GetRootResourcePath("hostconfig.json") } -func (container *Container) jsonPath() (string, error) { - return container.getRootResourcePath(configFileName) -} - -// This directory is only usable when the container is running -func (container *Container) rootfsPath() string { - return container.basefs +// ConfigPath returns the path to the container's JSON config +func (container *Container) ConfigPath() (string, error) { + return container.GetRootResourcePath(configFileName) } func validateID(id string) error { @@ -267,8 +260,9 @@ func (container *Container) exposes(p nat.Port) bool { return exists } -func (container *Container) getLogConfig(defaultConfig runconfig.LogConfig) runconfig.LogConfig { - cfg := container.hostConfig.LogConfig +// GetLogConfig returns the log configuration for the container. +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 == "" { cfg.Type = jsonfilelog.Name @@ -300,7 +294,7 @@ func (container *Container) StartLogger(cfg runconfig.LogConfig) (logger.Logger, // Set logging file for "json-logger" if cfg.Type == jsonfilelog.Name { - ctx.LogPath, err = container.getRootResourcePath(fmt.Sprintf("%s-json.log", container.ID)) + ctx.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID)) if err != nil { return nil, err } @@ -308,33 +302,39 @@ func (container *Container) StartLogger(cfg runconfig.LogConfig) (logger.Logger, return c(ctx) } -func (container *Container) getProcessLabel() string { +// GetProcessLabel returns the process label for the container. +func (container *Container) GetProcessLabel() string { // even if we have a process label return "" if we are running // in privileged mode - if container.hostConfig.Privileged { + if container.HostConfig.Privileged { return "" } return container.ProcessLabel } -func (container *Container) getMountLabel() string { - if container.hostConfig.Privileged { +// GetMountLabel returns the mounting label for the container. +// This label is empty if the container is privileged. +func (container *Container) GetMountLabel() string { + if container.HostConfig.Privileged { return "" } return container.MountLabel } -func (container *Container) getExecIDs() []string { - return container.execCommands.List() +// GetExecIDs returns the list of exec commands running on the container. +func (container *Container) GetExecIDs() []string { + return container.ExecCommands.List() } // 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 { - return attach(container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr) + return AttachStreams(container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr) } -func attach(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error { +// AttachStreams connects streams to a TTY. +// Used by exec too. Should this move somewhere else? +func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error { var ( cStdout, cStderr io.ReadCloser cStdin io.WriteCloser @@ -479,13 +479,16 @@ func copyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) return written, err } -func (container *Container) shouldRestart() bool { - return container.hostConfig.RestartPolicy.Name == "always" || - (container.hostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) || - (container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0) +// ShouldRestart decides whether the daemon should restart the container or not. +// This is based on the container's restart policy. +func (container *Container) ShouldRestart() bool { + return container.HostConfig.RestartPolicy.Name == "always" || + (container.HostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) || + (container.HostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0) } -func (container *Container) addBindMountPoint(name, source, destination string, rw bool) { +// AddBindMountPoint adds a new bind mount point configuration to the container. +func (container *Container) AddBindMountPoint(name, source, destination string, rw bool) { container.MountPoints[destination] = &volume.MountPoint{ Name: name, Source: source, @@ -494,7 +497,8 @@ func (container *Container) addBindMountPoint(name, source, destination string, } } -func (container *Container) addLocalMountPoint(name, destination string, rw bool) { +// AddLocalMountPoint adds a new local mount point configuration to the container. +func (container *Container) AddLocalMountPoint(name, destination string, rw bool) { container.MountPoints[destination] = &volume.MountPoint{ Name: name, Driver: volume.DefaultDriverName, @@ -503,7 +507,8 @@ func (container *Container) addLocalMountPoint(name, destination string, rw bool } } -func (container *Container) addMountPointWithVolume(destination string, vol volume.Volume, rw bool) { +// AddMountPointWithVolume adds a new mount point configured with a volume to the container. +func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) { container.MountPoints[destination] = &volume.MountPoint{ Name: vol.Name(), Driver: vol.DriverName(), @@ -513,11 +518,13 @@ func (container *Container) addMountPointWithVolume(destination string, vol volu } } -func (container *Container) isDestinationMounted(destination string) bool { +// IsDestinationMounted checkes whether a path is mounted on the container or not. +func (container *Container) IsDestinationMounted(destination string) bool { return container.MountPoints[destination] != nil } -func (container *Container) stopSignal() int { +// StopSignal returns the signal used to stop the container. +func (container *Container) StopSignal() int { var stopSignal syscall.Signal if container.Config.StopSignal != "" { stopSignal, _ = signal.ParseSignal(container.Config.StopSignal) @@ -529,7 +536,7 @@ func (container *Container) stopSignal() int { return int(stopSignal) } -// initDNSHostConfig ensures that the dns fields are never nil. +// InitDNSHostConfig ensures that the dns fields are never nil. // New containers don't ever have those fields nil, // but pre created containers can still have those nil values. // The non-recommended host configuration in the start api can @@ -537,16 +544,16 @@ func (container *Container) stopSignal() int { // we remove that behavior for good. // See https://github.com/docker/docker/pull/17779 // for a more detailed explanation on why we don't want that. -func initDNSHostConfig(container *Container) { - if container.hostConfig.DNS == nil { - container.hostConfig.DNS = make([]string, 0) +func (container *Container) InitDNSHostConfig() { + if container.HostConfig.DNS == nil { + container.HostConfig.DNS = make([]string, 0) } - if container.hostConfig.DNSSearch == nil { - container.hostConfig.DNSSearch = make([]string, 0) + if container.HostConfig.DNSSearch == nil { + container.HostConfig.DNSSearch = make([]string, 0) } - if container.hostConfig.DNSOptions == nil { - container.hostConfig.DNSOptions = make([]string, 0) + if container.HostConfig.DNSOptions == nil { + container.HostConfig.DNSOptions = make([]string, 0) } } diff --git a/container/container_unit_test.go b/container/container_unit_test.go new file mode 100644 index 0000000000..abc1033168 --- /dev/null +++ b/container/container_unit_test.go @@ -0,0 +1,36 @@ +package container + +import ( + "testing" + + "github.com/docker/docker/pkg/signal" + "github.com/docker/docker/runconfig" +) + +func TestContainerStopSignal(t *testing.T) { + c := &Container{ + CommonContainer: CommonContainer{ + Config: &runconfig.Config{}, + }, + } + + def, err := signal.ParseSignal(signal.DefaultStopSignal) + if err != nil { + t.Fatal(err) + } + + s := c.StopSignal() + if s != int(def) { + t.Fatalf("Expected %v, got %v", def, s) + } + + c = &Container{ + CommonContainer: CommonContainer{ + Config: &runconfig.Config{StopSignal: "SIGKILL"}, + }, + } + s = c.StopSignal() + if s != 9 { + t.Fatalf("Expected 9, got %v", s) + } +} diff --git a/container/container_unix.go b/container/container_unix.go new file mode 100644 index 0000000000..37af5064a8 --- /dev/null +++ b/container/container_unix.go @@ -0,0 +1,701 @@ +// +build linux freebsd + +package container + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/daemon/execdriver" + "github.com/docker/docker/daemon/network" + derr "github.com/docker/docker/errors" + "github.com/docker/docker/pkg/chrootarchive" + "github.com/docker/docker/pkg/nat" + "github.com/docker/docker/pkg/symlink" + "github.com/docker/docker/pkg/system" + "github.com/docker/docker/runconfig" + "github.com/docker/docker/utils" + "github.com/docker/docker/volume" + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/options" + "github.com/docker/libnetwork/types" + "github.com/opencontainers/runc/libcontainer/label" +) + +const ( + // DefaultPathEnv is unix style list of directories to search for + // executables. Each directory is separated from the next by a colon + // ':' character . + DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + + // DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container + DefaultSHMSize int64 = 67108864 +) + +// Container holds the fields specific to unixen implementations. See +// CommonContainer for standard fields common to all containers. +type Container struct { + CommonContainer + + // Fields below here are platform specific. + AppArmorProfile string + HostnamePath string + HostsPath string + ShmPath string + MqueuePath string + ResolvConfPath string +} + +// CreateDaemonEnvironment returns the list of all environment variables given the list of +// environment variables related to links. +// Sets PATH, HOSTNAME and if container.Config.Tty is set: TERM. +// The defaults set here do not override the values in container.Config.Env +func (container *Container) CreateDaemonEnvironment(linkedEnv []string) []string { + // if a domain name was specified, append it to the hostname (see #7851) + fullHostname := container.Config.Hostname + if container.Config.Domainname != "" { + fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) + } + // Setup environment + env := []string{ + "PATH=" + DefaultPathEnv, + "HOSTNAME=" + fullHostname, + // Note: we don't set HOME here because it'll get autoset intelligently + // based on the value of USER inside dockerinit, but only if it isn't + // set already (ie, that can be overridden by setting HOME via -e or ENV + // in a Dockerfile). + } + if container.Config.Tty { + env = append(env, "TERM=xterm") + } + env = append(env, linkedEnv...) + // because the env on the container can override certain default values + // we need to replace the 'env' keys where they match and append anything + // else. + env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env) + + return env +} + +// TrySetNetworkMount attempts to set the network mounts given a provided destination and +// the path to use for it; return true if the given destination was a network mount file +func (container *Container) TrySetNetworkMount(destination string, path string) bool { + if destination == "/etc/resolv.conf" { + container.ResolvConfPath = path + return true + } + if destination == "/etc/hostname" { + container.HostnamePath = path + return true + } + if destination == "/etc/hosts" { + container.HostsPath = path + return true + } + + return false +} + +// BuildHostnameFile writes the container's hostname file. +func (container *Container) BuildHostnameFile() error { + hostnamePath, err := container.GetRootResourcePath("hostname") + if err != nil { + return err + } + container.HostnamePath = hostnamePath + + if container.Config.Domainname != "" { + return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644) + } + return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) +} + +// GetEndpointInNetwork returns the container's endpoint to the provided network. +func (container *Container) GetEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) { + endpointName := strings.TrimPrefix(container.Name, "/") + return n.EndpointByName(endpointName) +} + +func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint) error { + if ep == nil { + return derr.ErrorCodeEmptyEndpoint + } + + networkSettings := container.NetworkSettings + if networkSettings == nil { + return derr.ErrorCodeEmptyNetwork + } + + driverInfo, err := ep.DriverInfo() + if err != nil { + return err + } + + if driverInfo == nil { + // It is not an error for epInfo to be nil + return nil + } + + if networkSettings.Ports == nil { + networkSettings.Ports = nat.PortMap{} + } + + if expData, ok := driverInfo[netlabel.ExposedPorts]; ok { + if exposedPorts, ok := expData.([]types.TransportPort); ok { + for _, tp := range exposedPorts { + natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port))) + if err != nil { + return derr.ErrorCodeParsingPort.WithArgs(tp.Port, err) + } + networkSettings.Ports[natPort] = nil + } + } + } + + mapData, ok := driverInfo[netlabel.PortMap] + if !ok { + return nil + } + + if portMapping, ok := mapData.([]types.PortBinding); ok { + for _, pp := range portMapping { + natPort, err := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) + if err != nil { + return err + } + natBndg := nat.PortBinding{HostIP: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))} + networkSettings.Ports[natPort] = append(networkSettings.Ports[natPort], natBndg) + } + } + + return nil +} + +// BuildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint. +func (container *Container) BuildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { + if ep == nil { + return derr.ErrorCodeEmptyEndpoint + } + + networkSettings := container.NetworkSettings + if networkSettings == nil { + return derr.ErrorCodeEmptyNetwork + } + + epInfo := ep.Info() + if epInfo == nil { + // It is not an error to get an empty endpoint info + return nil + } + + if _, ok := networkSettings.Networks[n.Name()]; !ok { + networkSettings.Networks[n.Name()] = new(network.EndpointSettings) + } + networkSettings.Networks[n.Name()].EndpointID = ep.ID() + + iface := epInfo.Iface() + if iface == nil { + return nil + } + + if iface.MacAddress() != nil { + networkSettings.Networks[n.Name()].MacAddress = iface.MacAddress().String() + } + + if iface.Address() != nil { + ones, _ := iface.Address().Mask.Size() + networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String() + networkSettings.Networks[n.Name()].IPPrefixLen = ones + } + + if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil { + onesv6, _ := iface.AddressIPv6().Mask.Size() + networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String() + networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6 + } + + return nil +} + +// UpdateJoinInfo updates network settings when container joins network n with endpoint ep. +func (container *Container) UpdateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { + if err := container.buildPortMapInfo(ep); err != nil { + return err + } + + epInfo := ep.Info() + if epInfo == nil { + // It is not an error to get an empty endpoint info + return nil + } + if epInfo.Gateway() != nil { + container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String() + } + if epInfo.GatewayIPv6().To16() != nil { + container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String() + } + + return nil +} + +// UpdateSandboxNetworkSettings updates the sandbox ID and Key. +func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox) error { + container.NetworkSettings.SandboxID = sb.ID() + container.NetworkSettings.SandboxKey = sb.Key() + return nil +} + +// BuildCreateEndpointOptions builds endpoint options from a given network. +func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) { + var ( + portSpecs = make(nat.PortSet) + bindings = make(nat.PortMap) + pbList []types.PortBinding + exposeList []types.TransportPort + createOptions []libnetwork.EndpointOption + ) + + if n.Name() == "bridge" || container.NetworkSettings.IsAnonymousEndpoint { + createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) + } + + // Other configs are applicable only for the endpoint in the network + // to which container was connected to on docker run. + if n.Name() != container.HostConfig.NetworkMode.NetworkName() && + !(n.Name() == "bridge" && container.HostConfig.NetworkMode.IsDefault()) { + return createOptions, nil + } + + if container.Config.ExposedPorts != nil { + portSpecs = container.Config.ExposedPorts + } + + if container.HostConfig.PortBindings != nil { + for p, b := range container.HostConfig.PortBindings { + bindings[p] = []nat.PortBinding{} + for _, bb := range b { + bindings[p] = append(bindings[p], nat.PortBinding{ + HostIP: bb.HostIP, + HostPort: bb.HostPort, + }) + } + } + } + + ports := make([]nat.Port, len(portSpecs)) + var i int + for p := range portSpecs { + ports[i] = p + i++ + } + nat.SortPortMap(ports, bindings) + for _, port := range ports { + expose := types.TransportPort{} + expose.Proto = types.ParseProtocol(port.Proto()) + expose.Port = uint16(port.Int()) + exposeList = append(exposeList, expose) + + pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto} + binding := bindings[port] + for i := 0; i < len(binding); i++ { + pbCopy := pb.GetCopy() + newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) + var portStart, portEnd int + if err == nil { + portStart, portEnd, err = newP.Range() + } + if err != nil { + return nil, derr.ErrorCodeHostPort.WithArgs(binding[i].HostPort, err) + } + pbCopy.HostPort = uint16(portStart) + pbCopy.HostPortEnd = uint16(portEnd) + pbCopy.HostIP = net.ParseIP(binding[i].HostIP) + pbList = append(pbList, pbCopy) + } + + if container.HostConfig.PublishAllPorts && len(binding) == 0 { + pbList = append(pbList, pb) + } + } + + createOptions = append(createOptions, + libnetwork.CreateOptionPortMapping(pbList), + libnetwork.CreateOptionExposedPorts(exposeList)) + + if container.Config.MacAddress != "" { + mac, err := net.ParseMAC(container.Config.MacAddress) + if err != nil { + return nil, err + } + + genericOption := options.Generic{ + netlabel.MacAddress: mac, + } + + createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) + } + + return createOptions, nil +} + +// SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir +func (container *Container) SetupWorkingDirectory() error { + if container.Config.WorkingDir == "" { + return nil + } + container.Config.WorkingDir = filepath.Clean(container.Config.WorkingDir) + + pth, err := container.GetResourcePath(container.Config.WorkingDir) + if err != nil { + return err + } + + pthInfo, err := os.Stat(pth) + if err != nil { + if !os.IsNotExist(err) { + return err + } + + if err := system.MkdirAll(pth, 0755); err != nil { + return err + } + } + if pthInfo != nil && !pthInfo.IsDir() { + return derr.ErrorCodeNotADir.WithArgs(container.Config.WorkingDir) + } + return nil +} + +// DisconnectFromNetwork disconnects a container from a network +func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error { + if !container.Running { + return derr.ErrorCodeNotRunning.WithArgs(container.ID) + } + + if container.HostConfig.NetworkMode.IsHost() && runconfig.NetworkMode(n.Type()).IsHost() { + return runconfig.ErrConflictHostNetwork + } + + if err := container.disconnectFromNetwork(n); err != nil { + return err + } + + if err := container.ToDiskLocking(); err != nil { + return fmt.Errorf("Error saving container to disk: %v", err) + } + return nil +} + +func (container *Container) disconnectFromNetwork(n libnetwork.Network) error { + var ( + ep libnetwork.Endpoint + sbox libnetwork.Sandbox + ) + + s := func(current libnetwork.Endpoint) bool { + epInfo := current.Info() + if epInfo == nil { + return false + } + if sb := epInfo.Sandbox(); sb != nil { + if sb.ContainerID() == container.ID { + ep = current + sbox = sb + return true + } + } + return false + } + n.WalkEndpoints(s) + + if ep == nil { + return fmt.Errorf("container %s is not connected to the network", container.ID) + } + + if err := ep.Leave(sbox); err != nil { + return fmt.Errorf("container %s failed to leave network %s: %v", container.ID, n.Name(), err) + } + + if err := ep.Delete(); err != nil { + return fmt.Errorf("endpoint delete failed for container %s on network %s: %v", container.ID, n.Name(), err) + } + + delete(container.NetworkSettings.Networks, n.Name()) + return nil +} + +// appendNetworkMounts appends any network mounts to the array of mount points passed in +func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) { + for _, mnt := range container.NetworkMounts() { + dest, err := container.GetResourcePath(mnt.Destination) + if err != nil { + return nil, err + } + volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest}) + } + return volumeMounts, nil +} + +// NetworkMounts returns the list of network mounts. +func (container *Container) NetworkMounts() []execdriver.Mount { + var mounts []execdriver.Mount + shared := container.HostConfig.NetworkMode.IsContainer() + if container.ResolvConfPath != "" { + if _, err := os.Stat(container.ResolvConfPath); err != nil { + logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err) + } else { + label.Relabel(container.ResolvConfPath, container.MountLabel, shared) + writable := !container.HostConfig.ReadonlyRootfs + if m, exists := container.MountPoints["/etc/resolv.conf"]; exists { + writable = m.RW + } + mounts = append(mounts, execdriver.Mount{ + Source: container.ResolvConfPath, + Destination: "/etc/resolv.conf", + Writable: writable, + Private: true, + }) + } + } + if container.HostnamePath != "" { + if _, err := os.Stat(container.HostnamePath); err != nil { + logrus.Warnf("HostnamePath set to %q, but can't stat this filename (err = %v); skipping", container.HostnamePath, err) + } else { + label.Relabel(container.HostnamePath, container.MountLabel, shared) + writable := !container.HostConfig.ReadonlyRootfs + if m, exists := container.MountPoints["/etc/hostname"]; exists { + writable = m.RW + } + mounts = append(mounts, execdriver.Mount{ + Source: container.HostnamePath, + Destination: "/etc/hostname", + Writable: writable, + Private: true, + }) + } + } + if container.HostsPath != "" { + if _, err := os.Stat(container.HostsPath); err != nil { + logrus.Warnf("HostsPath set to %q, but can't stat this filename (err = %v); skipping", container.HostsPath, err) + } else { + label.Relabel(container.HostsPath, container.MountLabel, shared) + writable := !container.HostConfig.ReadonlyRootfs + if m, exists := container.MountPoints["/etc/hosts"]; exists { + writable = m.RW + } + mounts = append(mounts, execdriver.Mount{ + Source: container.HostsPath, + Destination: "/etc/hosts", + Writable: writable, + Private: true, + }) + } + } + return mounts +} + +// CopyImagePathContent copies files in destination to the volume. +func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error { + rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, destination), container.BaseFS) + if err != nil { + return err + } + + if _, err = ioutil.ReadDir(rootfs); err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + + path, err := v.Mount() + if err != nil { + return err + } + + if err := copyExistingContents(rootfs, path); err != nil { + return err + } + + return v.Unmount() +} + +// ShmResourcePath returns path to shm +func (container *Container) ShmResourcePath() (string, error) { + return container.GetRootResourcePath("shm") +} + +// MqueueResourcePath returns path to mqueue +func (container *Container) MqueueResourcePath() (string, error) { + return container.GetRootResourcePath("mqueue") +} + +// HasMountFor checks if path is a mountpoint +func (container *Container) HasMountFor(path string) bool { + _, exists := container.MountPoints[path] + return exists +} + +// UnmountIpcMounts uses the provided unmount function to unmount shm and mqueue if they were mounted +func (container *Container) UnmountIpcMounts(unmount func(pth string) error) { + if container.HostConfig.IpcMode.IsContainer() || container.HostConfig.IpcMode.IsHost() { + return + } + + var warnings []string + + if !container.HasMountFor("/dev/shm") { + shmPath, err := container.ShmResourcePath() + if err != nil { + logrus.Error(err) + warnings = append(warnings, err.Error()) + } else if shmPath != "" { + if err := unmount(shmPath); err != nil { + warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", shmPath, err)) + } + + } + } + + if !container.HasMountFor("/dev/mqueue") { + mqueuePath, err := container.MqueueResourcePath() + if err != nil { + logrus.Error(err) + warnings = append(warnings, err.Error()) + } else if mqueuePath != "" { + if err := unmount(mqueuePath); err != nil { + warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", mqueuePath, err)) + } + } + } + + if len(warnings) > 0 { + logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n")) + } +} + +// IpcMounts returns the list of IPC mounts +func (container *Container) IpcMounts() []execdriver.Mount { + var mounts []execdriver.Mount + + if !container.HasMountFor("/dev/shm") { + label.SetFileLabel(container.ShmPath, container.MountLabel) + mounts = append(mounts, execdriver.Mount{ + Source: container.ShmPath, + Destination: "/dev/shm", + Writable: true, + Private: true, + }) + } + + if !container.HasMountFor("/dev/mqueue") { + label.SetFileLabel(container.MqueuePath, container.MountLabel) + mounts = append(mounts, execdriver.Mount{ + Source: container.MqueuePath, + Destination: "/dev/mqueue", + Writable: true, + Private: true, + }) + } + return mounts +} + +func detachMounted(path string) error { + return syscall.Unmount(path, syscall.MNT_DETACH) +} + +// UnmountVolumes unmounts all volumes +func (container *Container) UnmountVolumes(forceSyscall bool) error { + var ( + volumeMounts []volume.MountPoint + err error + ) + + for _, mntPoint := range container.MountPoints { + dest, err := container.GetResourcePath(mntPoint.Destination) + if err != nil { + return err + } + + volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest, Volume: mntPoint.Volume}) + } + + // Append any network mounts to the list (this is a no-op on Windows) + if volumeMounts, err = appendNetworkMounts(container, volumeMounts); err != nil { + return err + } + + for _, volumeMount := range volumeMounts { + if forceSyscall { + if err := detachMounted(volumeMount.Destination); err != nil { + logrus.Warnf("%s unmountVolumes: Failed to do lazy umount %v", container.ID, err) + } + } + + if volumeMount.Volume != nil { + if err := volumeMount.Volume.Unmount(); err != nil { + return err + } + } + } + + return nil +} + +// copyExistingContents copies from the source to the destination and +// ensures the ownership is appropriately set. +func copyExistingContents(source, destination string) error { + volList, err := ioutil.ReadDir(source) + if err != nil { + return err + } + if len(volList) > 0 { + srcList, err := ioutil.ReadDir(destination) + if err != nil { + return err + } + if len(srcList) == 0 { + // If the source volume is empty copy files from the root into the volume + if err := chrootarchive.CopyWithTar(source, destination); err != nil { + return err + } + } + } + return copyOwnership(source, destination) +} + +// copyOwnership copies the permissions and uid:gid of the source file +// to the destination file +func copyOwnership(source, destination string) error { + stat, err := system.Stat(source) + if err != nil { + return err + } + + if err := os.Chown(destination, int(stat.UID()), int(stat.GID())); err != nil { + return err + } + + return os.Chmod(destination, os.FileMode(stat.Mode())) +} + +// TmpfsMounts returns the list of tmpfs mounts +func (container *Container) TmpfsMounts() []execdriver.Mount { + var mounts []execdriver.Mount + for dest, data := range container.HostConfig.Tmpfs { + mounts = append(mounts, execdriver.Mount{ + Source: "tmpfs", + Destination: dest, + Data: data, + }) + } + return mounts +} diff --git a/container/container_windows.go b/container/container_windows.go new file mode 100644 index 0000000000..750ec6f02c --- /dev/null +++ b/container/container_windows.go @@ -0,0 +1,65 @@ +// +build windows + +package container + +import ( + "github.com/docker/docker/daemon/execdriver" + "github.com/docker/docker/volume" + "github.com/docker/libnetwork" +) + +// DefaultPathEnv is deliberately empty on Windows as the default path will be set by +// the container. Docker has no context of what the default path should be. +const DefaultPathEnv = "" + +// Container holds fields specific to the Windows implementation. See +// CommonContainer for standard fields common to all containers. +type Container struct { + CommonContainer + + // Fields below here are platform specific. +} + +// CreateDaemonEnvironment creates a new environment variable slice for this container. +func (container *Container) CreateDaemonEnvironment(linkedEnv []string) []string { + // On Windows, nothing to link. Just return the container environment. + return container.Config.Env +} + +// DisconnectFromNetwork disconnects a container from the network. +func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error { + return nil +} + +// SetupWorkingDirectory initializes the container working directory. +// This is a NOOP In windows. +func (container *Container) SetupWorkingDirectory() error { + return nil +} + +// UnmountIpcMounts unmount Ipc related mounts. +// This is a NOOP on windows. +func (container *Container) UnmountIpcMounts(unmount func(pth string) error) { +} + +// IpcMounts returns the list of Ipc related mounts. +func (container *Container) IpcMounts() []execdriver.Mount { + return nil +} + +// UnmountVolumes explicitely unmounts volumes from the container. +func (container *Container) UnmountVolumes(forceSyscall bool) error { + return nil +} + +// TmpfsMounts returns the list of tmpfs mounts +func (container *Container) TmpfsMounts() []execdriver.Mount { + return nil +} + +// appendNetworkMounts appends any network mounts to the array of mount points passed in. +// Windows does not support network mounts (not to be confused with SMB network mounts), so +// this is a no-op. +func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) { + return volumeMounts, nil +} diff --git a/daemon/monitor.go b/container/monitor.go similarity index 86% rename from daemon/monitor.go rename to container/monitor.go index a4d67cfde8..dd387aaa42 100644 --- a/daemon/monitor.go +++ b/container/monitor.go @@ -1,4 +1,4 @@ -package daemon +package container import ( "io" @@ -11,6 +11,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" + "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" @@ -21,8 +22,8 @@ const ( loggerCloseTimeout = 10 * time.Second ) -// containerSupervisor defines the interface that a supervisor must implement -type containerSupervisor interface { +// supervisor defines the interface that a supervisor must implement +type supervisor interface { // LogContainerEvent generates events related to a given container LogContainerEvent(*Container, string) // Cleanup ensures that the container is properly unmounted @@ -44,7 +45,7 @@ type containerMonitor struct { mux sync.Mutex // supervisor keeps track of the container and the events it generates - supervisor containerSupervisor + supervisor supervisor // container is the container being monitored container *Container @@ -76,17 +77,32 @@ type containerMonitor struct { lastStartTime time.Time } -// newContainerMonitor returns an initialized containerMonitor for the provided container -// honoring the provided restart policy -func (daemon *Daemon) newContainerMonitor(container *Container, policy runconfig.RestartPolicy) *containerMonitor { - return &containerMonitor{ - supervisor: daemon, +// StartMonitor initializes a containerMonitor for this container with the provided supervisor and restart policy +// and starts the container's process. +func (container *Container) StartMonitor(s supervisor, policy runconfig.RestartPolicy) error { + container.monitor = &containerMonitor{ + supervisor: s, container: container, restartPolicy: policy, timeIncrement: defaultTimeIncrement, stopChan: make(chan struct{}), startSignal: make(chan struct{}), } + + return container.monitor.wait() +} + +// wait starts the container and wait until +// we either receive an error from the initial start of the container's +// process or until the process is running in the container +func (m *containerMonitor) wait() error { + select { + case <-m.startSignal: + case err := <-promise.Go(m.start): + return err + } + + return nil } // Stop signals to the container monitor that it should stop monitoring the container @@ -113,7 +129,7 @@ func (m *containerMonitor) Close() error { // FIXME: here is race condition between two RUN instructions in Dockerfile // because they share same runconfig and change image. Must be fixed // in builder/builder.go - if err := m.container.toDisk(); err != nil { + if err := m.container.ToDisk(); err != nil { logrus.Errorf("Error dumping container %s state to disk: %s", m.container.ID, err) return err @@ -123,7 +139,7 @@ func (m *containerMonitor) Close() error { } // Start starts the containers process and monitors it according to the restart policy -func (m *containerMonitor) Start() error { +func (m *containerMonitor) start() error { var ( err error exitStatus execdriver.ExitStatus @@ -137,7 +153,7 @@ func (m *containerMonitor) Start() error { if afterRun { m.container.Lock() defer m.container.Unlock() - m.container.setStopped(&exitStatus) + m.container.SetStopped(&exitStatus) } m.Close() }() @@ -202,7 +218,7 @@ func (m *containerMonitor) Start() error { m.resetMonitor(err == nil && exitStatus.ExitCode == 0) if m.shouldRestart(exitStatus.ExitCode) { - m.container.setRestarting(&exitStatus) + m.container.SetRestarting(&exitStatus) m.logEvent("die") m.resetContainer(true) @@ -295,7 +311,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid }() if processConfig.Tty { - // The callback is called after the process Start() + // 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 { @@ -303,7 +319,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid } } - m.container.setRunning(pid) + m.container.SetRunning(pid) // signal that the process has started // close channel only if not closed @@ -313,7 +329,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid close(m.startSignal) } - if err := m.container.toDiskLocking(); err != nil { + if err := m.container.ToDiskLocking(); err != nil { logrus.Errorf("Error saving container to disk: %v", err) } return nil @@ -333,8 +349,8 @@ func (m *containerMonitor) resetContainer(lock bool) { logrus.Errorf("%s: %s", container.ID, err) } - if container.command != nil && container.command.ProcessConfig.Terminal != nil { - if err := container.command.ProcessConfig.Terminal.Close(); err != nil { + if container.Command != nil && container.Command.ProcessConfig.Terminal != nil { + if err := container.Command.ProcessConfig.Terminal.Close(); err != nil { logrus.Errorf("%s: Error closing terminal: %s", container.ID, err) } } @@ -344,11 +360,11 @@ func (m *containerMonitor) resetContainer(lock bool) { container.NewInputPipes() } - if container.logDriver != nil { - if container.logCopier != nil { + if container.LogDriver != nil { + if container.LogCopier != nil { exit := make(chan struct{}) go func() { - container.logCopier.Wait() + container.LogCopier.Wait() close(exit) }() select { @@ -357,14 +373,14 @@ func (m *containerMonitor) resetContainer(lock bool) { case <-exit: } } - container.logDriver.Close() - container.logCopier = nil - container.logDriver = nil + container.LogDriver.Close() + container.LogCopier = nil + container.LogDriver = nil } - c := container.command.ProcessConfig.Cmd + c := container.Command.ProcessConfig.Cmd - container.command.ProcessConfig.Cmd = exec.Cmd{ + container.Command.ProcessConfig.Cmd = exec.Cmd{ Stdin: c.Stdin, Stdout: c.Stdout, Stderr: c.Stderr, diff --git a/daemon/state.go b/container/state.go similarity index 75% rename from daemon/state.go rename to container/state.go index 8ff5effc63..1c8e8b6610 100644 --- a/daemon/state.go +++ b/container/state.go @@ -1,4 +1,4 @@ -package daemon +package container import ( "fmt" @@ -21,7 +21,7 @@ type State struct { Paused bool Restarting bool OOMKilled bool - removalInProgress bool // Not need for this to be persistent on disk. + RemovalInProgress bool // Not need for this to be persistent on disk. Dead bool Pid int ExitCode int @@ -51,7 +51,7 @@ func (s *State) String() string { return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) } - if s.removalInProgress { + if s.RemovalInProgress { return "Removal In Progress" } @@ -93,7 +93,8 @@ func (s *State) StateString() string { return "exited" } -func isValidStateString(s string) bool { +// IsValidStateString checks if the provided string is a valid container state or not. +func IsValidStateString(s string) bool { if s != "paused" && s != "restarting" && s != "running" && @@ -121,7 +122,7 @@ func wait(waitChan <-chan struct{}, timeout time.Duration) error { // waitRunning waits until state is running. If state is already // running it returns immediately. If you want wait forever you must // supply negative timeout. Returns pid, that was passed to -// setRunning. +// SetRunning. func (s *State) waitRunning(timeout time.Duration) (int, error) { s.Lock() if s.Running { @@ -139,7 +140,7 @@ func (s *State) waitRunning(timeout time.Duration) (int, error) { // WaitStop waits until state is stopped. If state already stopped it returns // immediately. If you want wait forever you must supply negative timeout. -// Returns exit code, that was passed to setStoppedLocking +// Returns exit code, that was passed to SetStoppedLocking func (s *State) WaitStop(timeout time.Duration) (int, error) { s.Lock() if !s.Running { @@ -178,7 +179,8 @@ func (s *State) getExitCode() int { return res } -func (s *State) setRunning(pid int) { +// SetRunning sets the state of the container to "running". +func (s *State) SetRunning(pid int) { s.Error = "" s.Running = true s.Paused = false @@ -190,13 +192,15 @@ func (s *State) setRunning(pid int) { s.waitChan = make(chan struct{}) } -func (s *State) setStoppedLocking(exitStatus *execdriver.ExitStatus) { +// SetStoppedLocking locks the container state is sets it to "stopped". +func (s *State) SetStoppedLocking(exitStatus *execdriver.ExitStatus) { s.Lock() - s.setStopped(exitStatus) + s.SetStopped(exitStatus) s.Unlock() } -func (s *State) setStopped(exitStatus *execdriver.ExitStatus) { +// SetStopped sets the container state to "stopped" without locking. +func (s *State) SetStopped(exitStatus *execdriver.ExitStatus) { s.Running = false s.Restarting = false s.Pid = 0 @@ -206,15 +210,17 @@ func (s *State) setStopped(exitStatus *execdriver.ExitStatus) { s.waitChan = make(chan struct{}) } -// setRestarting is when docker handles the auto restart of containers when they are +// SetRestartingLocking is when docker handles the auto restart of containers when they are // in the middle of a stop and being restarted again -func (s *State) setRestartingLocking(exitStatus *execdriver.ExitStatus) { +func (s *State) SetRestartingLocking(exitStatus *execdriver.ExitStatus) { s.Lock() - s.setRestarting(exitStatus) + s.SetRestarting(exitStatus) s.Unlock() } -func (s *State) setRestarting(exitStatus *execdriver.ExitStatus) { +// SetRestarting sets the container state to "restarting". +// It also sets the container PID to 0. +func (s *State) SetRestarting(exitStatus *execdriver.ExitStatus) { // we should consider the container running when it is restarting because of // all the checks in docker around rm/stop/etc s.Running = true @@ -226,37 +232,41 @@ func (s *State) setRestarting(exitStatus *execdriver.ExitStatus) { s.waitChan = make(chan struct{}) } -// setError sets the container's error state. This is useful when we want to +// SetError sets the container's error state. This is useful when we want to // know the error that occurred when container transits to another state // when inspecting it -func (s *State) setError(err error) { +func (s *State) SetError(err error) { s.Error = err.Error() } -func (s *State) isPaused() bool { +// IsPaused returns whether the container is paused or not. +func (s *State) IsPaused() bool { s.Lock() res := s.Paused s.Unlock() return res } -func (s *State) setRemovalInProgress() error { +// SetRemovalInProgress sets the container state as being removed. +func (s *State) SetRemovalInProgress() error { s.Lock() defer s.Unlock() - if s.removalInProgress { + if s.RemovalInProgress { return derr.ErrorCodeAlreadyRemoving } - s.removalInProgress = true + s.RemovalInProgress = true return nil } -func (s *State) resetRemovalInProgress() { +// ResetRemovalInProgress make the RemovalInProgress state to false. +func (s *State) ResetRemovalInProgress() { s.Lock() - s.removalInProgress = false + s.RemovalInProgress = false s.Unlock() } -func (s *State) setDead() { +// SetDead sets the container state to "dead" +func (s *State) SetDead() { s.Lock() s.Dead = true s.Unlock() diff --git a/daemon/state_test.go b/container/state_test.go similarity index 95% rename from daemon/state_test.go rename to container/state_test.go index c70dc4e1df..00c45f1324 100644 --- a/daemon/state_test.go +++ b/container/state_test.go @@ -1,4 +1,4 @@ -package daemon +package container import ( "sync/atomic" @@ -19,7 +19,7 @@ func TestStateRunStop(t *testing.T) { close(started) }() s.Lock() - s.setRunning(i + 100) + s.SetRunning(i + 100) s.Unlock() if !s.IsRunning() { @@ -52,7 +52,7 @@ func TestStateRunStop(t *testing.T) { atomic.StoreInt64(&exit, int64(exitCode)) close(stopped) }() - s.setStoppedLocking(&execdriver.ExitStatus{ExitCode: i}) + s.SetStoppedLocking(&execdriver.ExitStatus{ExitCode: i}) if s.IsRunning() { t.Fatal("State is running") } @@ -93,7 +93,7 @@ func TestStateTimeoutWait(t *testing.T) { } s.Lock() - s.setRunning(49) + s.SetRunning(49) s.Unlock() stopped := make(chan struct{}) diff --git a/daemon/state_unix.go b/container/state_unix.go similarity index 94% rename from daemon/state_unix.go rename to container/state_unix.go index e5f4db33fa..204b968b24 100644 --- a/daemon/state_unix.go +++ b/container/state_unix.go @@ -1,6 +1,6 @@ // +build linux freebsd -package daemon +package container import "github.com/docker/docker/daemon/execdriver" diff --git a/daemon/state_windows.go b/container/state_windows.go similarity index 93% rename from daemon/state_windows.go rename to container/state_windows.go index 223d4bc50b..645c9348c3 100644 --- a/daemon/state_windows.go +++ b/container/state_windows.go @@ -1,4 +1,4 @@ -package daemon +package container import "github.com/docker/docker/daemon/execdriver" diff --git a/daemon/archive.go b/daemon/archive.go index f4a82478d9..6257961b67 100644 --- a/daemon/archive.go +++ b/daemon/archive.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/docker/docker/api/types" + "github.com/docker/docker/container" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/ioutils" @@ -71,69 +72,9 @@ func (daemon *Daemon) ContainerExtractToDir(name, path string, noOverwriteDirNon return daemon.containerExtractToDir(container, path, noOverwriteDirNonDir, content) } -// resolvePath resolves the given path in the container to a resource on the -// host. Returns a resolved path (absolute path to the resource on the host), -// the absolute path to the resource relative to the container's rootfs, and -// a error if the path points to outside the container's rootfs. -func (container *Container) resolvePath(path string) (resolvedPath, absPath string, err error) { - // Consider the given path as an absolute path in the container. - absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join(string(filepath.Separator), path), path) - - // Split the absPath into its Directory and Base components. We will - // resolve the dir in the scope of the container then append the base. - dirPath, basePath := filepath.Split(absPath) - - resolvedDirPath, err := container.GetResourcePath(dirPath) - if err != nil { - return "", "", err - } - - // resolvedDirPath will have been cleaned (no trailing path separators) so - // we can manually join it with the base path element. - resolvedPath = resolvedDirPath + string(filepath.Separator) + basePath - - return resolvedPath, absPath, nil -} - -// statPath is the unexported version of StatPath. Locks and mounts should -// be acquired before calling this method and the given path should be fully -// resolved to a path on the host corresponding to the given absolute path -// inside the container. -func (container *Container) statPath(resolvedPath, absPath string) (stat *types.ContainerPathStat, err error) { - lstat, err := os.Lstat(resolvedPath) - if err != nil { - return nil, err - } - - var linkTarget string - if lstat.Mode()&os.ModeSymlink != 0 { - // Fully evaluate the symlink in the scope of the container rootfs. - hostPath, err := container.GetResourcePath(absPath) - if err != nil { - return nil, err - } - - linkTarget, err = filepath.Rel(container.basefs, hostPath) - if err != nil { - return nil, err - } - - // Make it an absolute path. - linkTarget = filepath.Join(string(filepath.Separator), linkTarget) - } - - return &types.ContainerPathStat{ - Name: filepath.Base(absPath), - Size: lstat.Size(), - Mode: lstat.Mode(), - Mtime: lstat.ModTime(), - LinkTarget: linkTarget, - }, nil -} - // containerStatPath stats the filesystem resource at the specified path in this // container. Returns stat info about the resource. -func (daemon *Daemon) containerStatPath(container *Container, path string) (stat *types.ContainerPathStat, err error) { +func (daemon *Daemon) containerStatPath(container *container.Container, path string) (stat *types.ContainerPathStat, err error) { container.Lock() defer container.Unlock() @@ -143,23 +84,23 @@ func (daemon *Daemon) containerStatPath(container *Container, path string) (stat defer daemon.Unmount(container) err = daemon.mountVolumes(container) - defer container.unmountVolumes(true) + defer container.UnmountVolumes(true) if err != nil { return nil, err } - resolvedPath, absPath, err := container.resolvePath(path) + resolvedPath, absPath, err := container.ResolvePath(path) if err != nil { return nil, err } - return container.statPath(resolvedPath, absPath) + return container.StatPath(resolvedPath, absPath) } // 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 (daemon *Daemon) containerArchivePath(container *Container, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) { +func (daemon *Daemon) containerArchivePath(container *container.Container, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) { container.Lock() defer func() { @@ -178,7 +119,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c defer func() { if err != nil { // unmount any volumes - container.unmountVolumes(true) + container.UnmountVolumes(true) // unmount the container's rootfs daemon.Unmount(container) } @@ -188,12 +129,12 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c return nil, nil, err } - resolvedPath, absPath, err := container.resolvePath(path) + resolvedPath, absPath, err := container.ResolvePath(path) if err != nil { return nil, nil, err } - stat, err = container.statPath(resolvedPath, absPath) + stat, err = container.StatPath(resolvedPath, absPath) if err != nil { return nil, nil, err } @@ -213,7 +154,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c content = ioutils.NewReadCloserWrapper(data, func() error { err := data.Close() - container.unmountVolumes(true) + container.UnmountVolumes(true) daemon.Unmount(container) container.Unlock() return err @@ -230,7 +171,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c // 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 (daemon *Daemon) containerExtractToDir(container *Container, path string, noOverwriteDirNonDir bool, content io.Reader) (err error) { +func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, noOverwriteDirNonDir bool, content io.Reader) (err error) { container.Lock() defer container.Unlock() @@ -240,14 +181,14 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n defer daemon.Unmount(container) err = daemon.mountVolumes(container) - defer container.unmountVolumes(true) + defer container.UnmountVolumes(true) if err != nil { return err } // The destination path needs to be resolved to a host path, with all // symbolic links followed in the scope of the container's rootfs. Note - // that we do not use `container.resolvePath(path)` here because we need + // that we do not use `container.ResolvePath(path)` here because we need // to also evaluate the last path element if it is a symlink. This is so // that you can extract an archive to a symlink that points to a directory. @@ -283,14 +224,14 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n // a volume file path. var baseRel string if strings.HasPrefix(resolvedPath, `\\?\Volume{`) { - if strings.HasPrefix(resolvedPath, container.basefs) { - baseRel = resolvedPath[len(container.basefs):] + if strings.HasPrefix(resolvedPath, container.BaseFS) { + baseRel = resolvedPath[len(container.BaseFS):] if baseRel[:1] == `\` { baseRel = baseRel[1:] } } } else { - baseRel, err = filepath.Rel(container.basefs, resolvedPath) + baseRel, err = filepath.Rel(container.BaseFS, resolvedPath) } if err != nil { return err @@ -303,7 +244,7 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n return err } - if !toVolume && container.hostConfig.ReadonlyRootfs { + if !toVolume && container.HostConfig.ReadonlyRootfs { return ErrRootFSReadOnly } @@ -323,7 +264,7 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n return nil } -func (daemon *Daemon) containerCopy(container *Container, resource string) (rc io.ReadCloser, err error) { +func (daemon *Daemon) containerCopy(container *container.Container, resource string) (rc io.ReadCloser, err error) { container.Lock() defer func() { @@ -342,7 +283,7 @@ func (daemon *Daemon) containerCopy(container *Container, resource string) (rc i defer func() { if err != nil { // unmount any volumes - container.unmountVolumes(true) + container.UnmountVolumes(true) // unmount the container's rootfs daemon.Unmount(container) } @@ -379,7 +320,7 @@ func (daemon *Daemon) containerCopy(container *Container, resource string) (rc i reader := ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() - container.unmountVolumes(true) + container.UnmountVolumes(true) daemon.Unmount(container) container.Unlock() return err diff --git a/daemon/archive_unix.go b/daemon/archive_unix.go index 7588d76d15..e51c9ebbcc 100644 --- a/daemon/archive_unix.go +++ b/daemon/archive_unix.go @@ -2,10 +2,12 @@ package daemon +import "github.com/docker/docker/container" + // checkIfPathIsInAVolume checks if the path is in a volume. If it is, it // cannot be in a read-only volume. If it is not in a volume, the container // cannot be configured with a read-only rootfs. -func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) { +func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) { var toVolume bool for _, mnt := range container.MountPoints { if toVolume = mnt.HasResource(absPath); toVolume { diff --git a/daemon/archive_windows.go b/daemon/archive_windows.go index e7359318e8..feebc84fe9 100644 --- a/daemon/archive_windows.go +++ b/daemon/archive_windows.go @@ -1,11 +1,13 @@ package daemon +import "github.com/docker/docker/container" + // checkIfPathIsInAVolume checks if the path is in a volume. If it is, it // cannot be in a read-only volume. If it is not in a volume, the container // cannot be configured with a read-only rootfs. // // This is a no-op on Windows which does not support read-only volumes, or // extracting to a mount point inside a volume. TODO Windows: FIXME Post-TP4 -func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) { +func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) { return false, nil } diff --git a/daemon/attach.go b/daemon/attach.go index 047828b17d..b024a063fd 100644 --- a/daemon/attach.go +++ b/daemon/attach.go @@ -5,6 +5,7 @@ import ( "time" "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/pkg/stdcopy" ) @@ -66,7 +67,7 @@ func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *Containe 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 { +func (daemon *Daemon) attachWithLogs(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool) error { if logs { logDriver, err := daemon.getLogger(container) if err != nil { diff --git a/daemon/commit.go b/daemon/commit.go index 4af06c3721..7029b3eaa7 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -8,6 +8,7 @@ import ( "time" "github.com/docker/distribution/reference" + "github.com/docker/docker/container" "github.com/docker/docker/dockerversion" "github.com/docker/docker/image" "github.com/docker/docker/layer" @@ -42,7 +43,7 @@ func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (string, err return "", fmt.Errorf("Windows does not support commit of a running container") } - if c.Pause && !container.isPaused() { + if c.Pause && !container.IsPaused() { daemon.containerPause(container) defer daemon.containerUnpause(container) } @@ -145,12 +146,12 @@ func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (string, err return id.String(), nil } -func (daemon *Daemon) exportContainerRw(container *Container) (archive.Archive, error) { +func (daemon *Daemon) exportContainerRw(container *container.Container) (archive.Archive, error) { if err := daemon.Mount(container); err != nil { return nil, err } - archive, err := container.rwlayer.TarStream() + archive, err := container.RWLayer.TarStream() if err != nil { return nil, err } diff --git a/daemon/container_operations.go b/daemon/container_operations.go new file mode 100644 index 0000000000..f36e580325 --- /dev/null +++ b/daemon/container_operations.go @@ -0,0 +1,9 @@ +package daemon + +import "errors" + +var ( + // ErrRootFSReadOnly is returned when a container + // rootfs is marked readonly. + ErrRootFSReadOnly = errors.New("container rootfs is marked read-only") +) diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go new file mode 100644 index 0000000000..30f1f62138 --- /dev/null +++ b/daemon/container_operations_unix.go @@ -0,0 +1,928 @@ +// +build linux freebsd + +package daemon + +import ( + "fmt" + "os" + "path" + "path/filepath" + "strconv" + "strings" + "syscall" + "time" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" + "github.com/docker/docker/daemon/execdriver" + "github.com/docker/docker/daemon/links" + "github.com/docker/docker/daemon/network" + derr "github.com/docker/docker/errors" + "github.com/docker/docker/pkg/fileutils" + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/mount" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/pkg/ulimit" + "github.com/docker/docker/runconfig" + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/options" + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/devices" + "github.com/opencontainers/runc/libcontainer/label" +) + +func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { + var env []string + children, err := daemon.children(container.Name) + if err != nil { + return nil, err + } + + bridgeSettings := container.NetworkSettings.Networks["bridge"] + if bridgeSettings == nil { + return nil, nil + } + + if len(children) > 0 { + for linkAlias, child := range children { + if !child.IsRunning() { + return nil, derr.ErrorCodeLinkNotRunning.WithArgs(child.Name, linkAlias) + } + + childBridgeSettings := child.NetworkSettings.Networks["bridge"] + if childBridgeSettings == nil { + return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID) + } + + link := links.NewLink( + bridgeSettings.IPAddress, + childBridgeSettings.IPAddress, + linkAlias, + child.Config.Env, + child.Config.ExposedPorts, + ) + + for _, envVar := range link.ToEnv() { + env = append(env, envVar) + } + } + } + return env, nil +} + +func (daemon *Daemon) populateCommand(c *container.Container, env []string) error { + var en *execdriver.Network + if !c.Config.NetworkDisabled { + en = &execdriver.Network{} + if !daemon.execDriver.SupportsHooks() || c.HostConfig.NetworkMode.IsHost() { + en.NamespacePath = c.NetworkSettings.SandboxKey + } + + if c.HostConfig.NetworkMode.IsContainer() { + nc, err := daemon.getNetworkedContainer(c.ID, c.HostConfig.NetworkMode.ConnectedContainer()) + if err != nil { + return err + } + en.ContainerID = nc.ID + } + } + + ipc := &execdriver.Ipc{} + var err error + c.ShmPath, err = c.ShmResourcePath() + if err != nil { + return err + } + + c.MqueuePath, err = c.MqueueResourcePath() + if err != nil { + return err + } + + if c.HostConfig.IpcMode.IsContainer() { + ic, err := daemon.getIpcContainer(c) + if err != nil { + return err + } + ipc.ContainerID = ic.ID + c.ShmPath = ic.ShmPath + c.MqueuePath = ic.MqueuePath + } else { + ipc.HostIpc = c.HostConfig.IpcMode.IsHost() + if ipc.HostIpc { + if _, err := os.Stat("/dev/shm"); err != nil { + return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host") + } + if _, err := os.Stat("/dev/mqueue"); err != nil { + return fmt.Errorf("/dev/mqueue is not mounted, but must be for --ipc=host") + } + c.ShmPath = "/dev/shm" + c.MqueuePath = "/dev/mqueue" + } + } + + pid := &execdriver.Pid{} + pid.HostPid = c.HostConfig.PidMode.IsHost() + + uts := &execdriver.UTS{ + HostUTS: c.HostConfig.UTSMode.IsHost(), + } + + // Build lists of devices allowed and created within the container. + var userSpecifiedDevices []*configs.Device + for _, deviceMapping := range c.HostConfig.Devices { + devs, err := getDevicesFromPath(deviceMapping) + if err != nil { + return err + } + + userSpecifiedDevices = append(userSpecifiedDevices, devs...) + } + + allowedDevices := mergeDevices(configs.DefaultAllowedDevices, userSpecifiedDevices) + + autoCreatedDevices := mergeDevices(configs.DefaultAutoCreatedDevices, userSpecifiedDevices) + + var rlimits []*ulimit.Rlimit + ulimits := c.HostConfig.Ulimits + + // Merge ulimits with daemon defaults + ulIdx := make(map[string]*ulimit.Ulimit) + for _, ul := range ulimits { + ulIdx[ul.Name] = ul + } + for name, ul := range daemon.configStore.Ulimits { + if _, exists := ulIdx[name]; !exists { + ulimits = append(ulimits, ul) + } + } + + weightDevices, err := getBlkioWeightDevices(c.HostConfig) + if err != nil { + return err + } + + for _, limit := range ulimits { + rl, err := limit.GetRlimit() + if err != nil { + return err + } + rlimits = append(rlimits, rl) + } + + resources := &execdriver.Resources{ + CommonResources: execdriver.CommonResources{ + Memory: c.HostConfig.Memory, + MemoryReservation: c.HostConfig.MemoryReservation, + CPUShares: c.HostConfig.CPUShares, + BlkioWeight: c.HostConfig.BlkioWeight, + }, + MemorySwap: c.HostConfig.MemorySwap, + KernelMemory: c.HostConfig.KernelMemory, + CpusetCpus: c.HostConfig.CpusetCpus, + CpusetMems: c.HostConfig.CpusetMems, + CPUPeriod: c.HostConfig.CPUPeriod, + CPUQuota: c.HostConfig.CPUQuota, + Rlimits: rlimits, + BlkioWeightDevice: weightDevices, + OomKillDisable: c.HostConfig.OomKillDisable, + MemorySwappiness: *c.HostConfig.MemorySwappiness, + } + + processConfig := execdriver.ProcessConfig{ + CommonProcessConfig: execdriver.CommonProcessConfig{ + Entrypoint: c.Path, + Arguments: c.Args, + Tty: c.Config.Tty, + }, + Privileged: c.HostConfig.Privileged, + User: c.Config.User, + } + + processConfig.SysProcAttr = &syscall.SysProcAttr{Setsid: true} + processConfig.Env = env + + remappedRoot := &execdriver.User{} + rootUID, rootGID := daemon.GetRemappedUIDGID() + if rootUID != 0 { + remappedRoot.UID = rootUID + remappedRoot.GID = rootGID + } + uidMap, gidMap := daemon.GetUIDGIDMaps() + + c.Command = &execdriver.Command{ + CommonCommand: execdriver.CommonCommand{ + ID: c.ID, + InitPath: "/.dockerinit", + MountLabel: c.GetMountLabel(), + Network: en, + ProcessConfig: processConfig, + ProcessLabel: c.GetProcessLabel(), + Rootfs: c.BaseFS, + Resources: resources, + WorkingDir: c.Config.WorkingDir, + }, + AllowedDevices: allowedDevices, + AppArmorProfile: c.AppArmorProfile, + AutoCreatedDevices: autoCreatedDevices, + CapAdd: c.HostConfig.CapAdd.Slice(), + CapDrop: c.HostConfig.CapDrop.Slice(), + CgroupParent: c.HostConfig.CgroupParent, + GIDMapping: gidMap, + GroupAdd: c.HostConfig.GroupAdd, + Ipc: ipc, + OomScoreAdj: c.HostConfig.OomScoreAdj, + Pid: pid, + ReadonlyRootfs: c.HostConfig.ReadonlyRootfs, + RemappedRoot: remappedRoot, + UIDMapping: uidMap, + UTS: uts, + } + + return nil +} + +// getSize returns the real size & virtual size of the container. +func (daemon *Daemon) getSize(container *container.Container) (int64, int64) { + var ( + sizeRw, sizeRootfs int64 + err error + ) + + 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 daemon.Unmount(container) + + sizeRw, err = container.RWLayer.Size() + if err != nil { + 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 + } + + if parent := container.RWLayer.Parent(); parent != nil { + sizeRootfs, err = parent.Size() + if err != nil { + sizeRootfs = -1 + } else if sizeRw != -1 { + sizeRootfs += sizeRw + } + } + return sizeRw, sizeRootfs +} + +func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libnetwork.Network) ([]libnetwork.SandboxOption, error) { + var ( + sboxOptions []libnetwork.SandboxOption + err error + dns []string + dnsSearch []string + dnsOptions []string + ) + + sboxOptions = append(sboxOptions, libnetwork.OptionHostname(container.Config.Hostname), + libnetwork.OptionDomainname(container.Config.Domainname)) + + if container.HostConfig.NetworkMode.IsHost() { + sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) + sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) + sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) + } else if daemon.execDriver.SupportsHooks() { + // OptionUseExternalKey is mandatory for userns support. + // But optional for non-userns support + sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey()) + } + + container.HostsPath, err = container.GetRootResourcePath("hosts") + if err != nil { + return nil, err + } + sboxOptions = append(sboxOptions, libnetwork.OptionHostsPath(container.HostsPath)) + + container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf") + if err != nil { + return nil, err + } + sboxOptions = append(sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath)) + + if len(container.HostConfig.DNS) > 0 { + dns = container.HostConfig.DNS + } else if len(daemon.configStore.DNS) > 0 { + dns = daemon.configStore.DNS + } + + for _, d := range dns { + sboxOptions = append(sboxOptions, libnetwork.OptionDNS(d)) + } + + if len(container.HostConfig.DNSSearch) > 0 { + dnsSearch = container.HostConfig.DNSSearch + } else if len(daemon.configStore.DNSSearch) > 0 { + dnsSearch = daemon.configStore.DNSSearch + } + + for _, ds := range dnsSearch { + sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(ds)) + } + + if len(container.HostConfig.DNSOptions) > 0 { + dnsOptions = container.HostConfig.DNSOptions + } else if len(daemon.configStore.DNSOptions) > 0 { + dnsOptions = daemon.configStore.DNSOptions + } + + for _, ds := range dnsOptions { + sboxOptions = append(sboxOptions, libnetwork.OptionDNSOptions(ds)) + } + + if container.NetworkSettings.SecondaryIPAddresses != nil { + name := container.Config.Hostname + if container.Config.Domainname != "" { + name = name + "." + container.Config.Domainname + } + + for _, a := range container.NetworkSettings.SecondaryIPAddresses { + sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(name, a.Addr)) + } + } + + for _, extraHost := range container.HostConfig.ExtraHosts { + // allow IPv6 addresses in extra hosts; only split on first ":" + parts := strings.SplitN(extraHost, ":", 2) + sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) + } + + // Link feature is supported only for the default bridge network. + // return if this call to build join options is not for default bridge network + if n.Name() != "bridge" { + return sboxOptions, nil + } + + ep, _ := container.GetEndpointInNetwork(n) + if ep == nil { + return sboxOptions, nil + } + + var childEndpoints, parentEndpoints []string + + children, err := daemon.children(container.Name) + if err != nil { + return nil, err + } + + for linkAlias, child := range children { + if !isLinkable(child) { + return nil, fmt.Errorf("Cannot link to %s, as it does not belong to the default network", child.Name) + } + _, alias := path.Split(linkAlias) + // allow access to the linked container via the alias, real name, and container hostname + aliasList := alias + " " + child.Config.Hostname + // only add the name if alias isn't equal to the name + if alias != child.Name[1:] { + aliasList = aliasList + " " + child.Name[1:] + } + sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.Networks["bridge"].IPAddress)) + cEndpoint, _ := child.GetEndpointInNetwork(n) + if cEndpoint != nil && cEndpoint.ID() != "" { + childEndpoints = append(childEndpoints, cEndpoint.ID()) + } + } + + bridgeSettings := container.NetworkSettings.Networks["bridge"] + refs := daemon.containerGraph().RefPaths(container.ID) + for _, ref := range refs { + if ref.ParentID == "0" { + continue + } + + c, err := daemon.Get(ref.ParentID) + if err != nil { + logrus.Error(err) + } + + 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() != "" { + parentEndpoints = append(parentEndpoints, ep.ID()) + } + } + } + + linkOptions := options.Generic{ + netlabel.GenericData: options.Generic{ + "ParentEndpoints": parentEndpoints, + "ChildEndpoints": childEndpoints, + }, + } + + sboxOptions = append(sboxOptions, libnetwork.OptionGeneric(linkOptions)) + + return sboxOptions, nil +} + +func (daemon *Daemon) updateNetworkSettings(container *container.Container, n libnetwork.Network) error { + if container.NetworkSettings == nil { + container.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)} + } + + if !container.HostConfig.NetworkMode.IsHost() && runconfig.NetworkMode(n.Type()).IsHost() { + return runconfig.ErrConflictHostNetwork + } + + for s := range container.NetworkSettings.Networks { + sn, err := daemon.FindNetwork(s) + if err != nil { + continue + } + + if sn.Name() == n.Name() { + // Avoid duplicate config + return nil + } + if !runconfig.NetworkMode(sn.Type()).IsPrivate() || + !runconfig.NetworkMode(n.Type()).IsPrivate() { + return runconfig.ErrConflictSharedNetwork + } + if runconfig.NetworkMode(sn.Name()).IsNone() || + runconfig.NetworkMode(n.Name()).IsNone() { + return runconfig.ErrConflictNoNetwork + } + } + container.NetworkSettings.Networks[n.Name()] = new(network.EndpointSettings) + + return nil +} + +func (daemon *Daemon) updateEndpointNetworkSettings(container *container.Container, n libnetwork.Network, ep libnetwork.Endpoint) error { + if err := container.BuildEndpointInfo(n, ep); err != nil { + return err + } + + if container.HostConfig.NetworkMode == runconfig.NetworkMode("bridge") { + container.NetworkSettings.Bridge = daemon.configStore.Bridge.Iface + } + + return nil +} + +// UpdateNetwork is used to update the container's network (e.g. when linked containers +// get removed/unlinked). +func (daemon *Daemon) updateNetwork(container *container.Container) error { + ctrl := daemon.netController + sid := container.NetworkSettings.SandboxID + + sb, err := ctrl.SandboxByID(sid) + if err != nil { + return derr.ErrorCodeNoSandbox.WithArgs(sid, err) + } + + // Find if container is connected to the default bridge network + var n libnetwork.Network + for name := range container.NetworkSettings.Networks { + sn, err := daemon.FindNetwork(name) + if err != nil { + continue + } + if sn.Name() == "bridge" { + n = sn + break + } + } + + if n == nil { + // Not connected to the default bridge network; Nothing to do + return nil + } + + options, err := daemon.buildSandboxOptions(container, n) + if err != nil { + return derr.ErrorCodeNetworkUpdate.WithArgs(err) + } + + if err := sb.Refresh(options...); err != nil { + return derr.ErrorCodeNetworkRefresh.WithArgs(sid, err) + } + + return nil +} + +func (daemon *Daemon) allocateNetwork(container *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 { + logrus.Errorf("failed to cleanup up stale network sandbox for container %s", container.ID) + } + + updateSettings := false + if len(container.NetworkSettings.Networks) == 0 { + mode := container.HostConfig.NetworkMode + if container.Config.NetworkDisabled || mode.IsContainer() { + return nil + } + + networkName := mode.NetworkName() + if mode.IsDefault() { + networkName = controller.Config().Daemon.DefaultNetwork + } + if mode.IsUserDefined() { + n, err := daemon.FindNetwork(networkName) + if err != nil { + return err + } + networkName = n.Name() + } + container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings) + container.NetworkSettings.Networks[networkName] = new(network.EndpointSettings) + updateSettings = true + } + + for n := range container.NetworkSettings.Networks { + if err := daemon.connectToNetwork(container, n, updateSettings); err != nil { + return err + } + } + + return container.WriteHostConfig() +} + +func (daemon *Daemon) getNetworkSandbox(container *container.Container) libnetwork.Sandbox { + var sb libnetwork.Sandbox + daemon.netController.WalkSandboxes(func(s libnetwork.Sandbox) bool { + if s.ContainerID() == container.ID { + sb = s + return true + } + return false + }) + return sb +} + +// ConnectToNetwork connects a container to a network +func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string) error { + if !container.Running { + return derr.ErrorCodeNotRunning.WithArgs(container.ID) + } + if err := daemon.connectToNetwork(container, idOrName, true); err != nil { + return err + } + if err := container.ToDiskLocking(); err != nil { + return fmt.Errorf("Error saving container to disk: %v", err) + } + return nil +} + +func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, updateSettings bool) (err error) { + if container.HostConfig.NetworkMode.IsContainer() { + return runconfig.ErrConflictSharedNetwork + } + + if runconfig.NetworkMode(idOrName).IsBridge() && + daemon.configStore.DisableBridge { + container.Config.NetworkDisabled = true + return nil + } + + controller := daemon.netController + + n, err := daemon.FindNetwork(idOrName) + if err != nil { + return err + } + + if updateSettings { + if err := daemon.updateNetworkSettings(container, n); err != nil { + return err + } + } + + ep, err := container.GetEndpointInNetwork(n) + if err == nil { + return fmt.Errorf("container already connected to network %s", idOrName) + } + + if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok { + return err + } + + createOptions, err := container.BuildCreateEndpointOptions(n) + if err != nil { + return err + } + + endpointName := strings.TrimPrefix(container.Name, "/") + ep, err = n.CreateEndpoint(endpointName, createOptions...) + if err != nil { + return err + } + defer func() { + if err != nil { + if e := ep.Delete(); e != nil { + logrus.Warnf("Could not rollback container connection to network %s", idOrName) + } + } + }() + + if err := daemon.updateEndpointNetworkSettings(container, n, ep); err != nil { + return err + } + + sb := daemon.getNetworkSandbox(container) + if sb == nil { + options, err := daemon.buildSandboxOptions(container, n) + if err != nil { + return err + } + sb, err = controller.NewSandbox(container.ID, options...) + if err != nil { + return err + } + + container.UpdateSandboxNetworkSettings(sb) + } + + if err := ep.Join(sb); err != nil { + return err + } + + if err := container.UpdateJoinInfo(n, ep); err != nil { + return derr.ErrorCodeJoinInfo.WithArgs(err) + } + + return nil +} + +func (daemon *Daemon) initializeNetworking(container *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 := daemon.getNetworkedContainer(container.ID, container.HostConfig.NetworkMode.ConnectedContainer()) + if err != nil { + return err + } + container.HostnamePath = nc.HostnamePath + container.HostsPath = nc.HostsPath + container.ResolvConfPath = nc.ResolvConfPath + container.Config.Hostname = nc.Config.Hostname + container.Config.Domainname = nc.Config.Domainname + return nil + } + + if container.HostConfig.NetworkMode.IsHost() { + container.Config.Hostname, err = os.Hostname() + if err != nil { + return err + } + + parts := strings.SplitN(container.Config.Hostname, ".", 2) + if len(parts) > 1 { + container.Config.Hostname = parts[0] + container.Config.Domainname = parts[1] + } + + } + + if err := daemon.allocateNetwork(container); err != nil { + return err + } + + return container.BuildHostnameFile() +} + +// 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 { + path := fmt.Sprintf("/proc/%d/ns/net", pid) + var sandbox libnetwork.Sandbox + search := libnetwork.SandboxContainerWalker(&sandbox, containerID) + daemon.netController.WalkSandboxes(search) + if sandbox == nil { + return derr.ErrorCodeNoSandbox.WithArgs(containerID, "no sandbox found") + } + + return sandbox.SetKey(path) +} + +func (daemon *Daemon) getIpcContainer(container *container.Container) (*container.Container, error) { + containerID := container.HostConfig.IpcMode.Container() + c, err := daemon.Get(containerID) + if err != nil { + return nil, err + } + if !c.IsRunning() { + return nil, derr.ErrorCodeIPCRunning + } + return c, nil +} + +func (daemon *Daemon) getNetworkedContainer(containerID, connectedContainerID string) (*container.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.Container) { + if container.HostConfig.NetworkMode.IsContainer() || container.Config.NetworkDisabled { + return + } + + sid := container.NetworkSettings.SandboxID + networks := container.NetworkSettings.Networks + for n := range networks { + networks[n] = &network.EndpointSettings{} + } + + container.NetworkSettings = &network.Settings{Networks: networks} + + if sid == "" || len(networks) == 0 { + return + } + + sb, err := daemon.netController.SandboxByID(sid) + if err != nil { + logrus.Errorf("error locating sandbox id %s: %v", sid, err) + return + } + + if err := sb.Delete(); err != nil { + logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err) + } +} + +func (daemon *Daemon) setupIpcDirs(c *container.Container) error { + rootUID, rootGID := daemon.GetRemappedUIDGID() + if !c.HasMountFor("/dev/shm") { + shmPath, err := c.ShmResourcePath() + if err != nil { + return err + } + + if err := idtools.MkdirAllAs(shmPath, 0700, rootUID, rootGID); err != nil { + return err + } + + shmSize := container.DefaultSHMSize + if c.HostConfig.ShmSize != nil { + shmSize = *c.HostConfig.ShmSize + } + shmproperty := "mode=1777,size=" + strconv.FormatInt(shmSize, 10) + if err := syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil { + return fmt.Errorf("mounting shm tmpfs: %s", err) + } + if err := os.Chown(shmPath, rootUID, rootGID); err != nil { + return err + } + } + + if !c.HasMountFor("/dev/mqueue") { + mqueuePath, err := c.MqueueResourcePath() + if err != nil { + return err + } + + if err := idtools.MkdirAllAs(mqueuePath, 0700, rootUID, rootGID); err != nil { + return err + } + + if err := syscall.Mount("mqueue", mqueuePath, "mqueue", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), ""); err != nil { + return fmt.Errorf("mounting mqueue mqueue : %s", err) + } + if err := os.Chown(mqueuePath, rootUID, rootGID); err != nil { + return err + } + } + + return nil +} + +func (daemon *Daemon) mountVolumes(container *container.Container) error { + mounts, err := daemon.setupMounts(container) + if err != nil { + return err + } + + for _, m := range mounts { + dest, err := container.GetResourcePath(m.Destination) + if err != nil { + return err + } + + var stat os.FileInfo + stat, err = os.Stat(m.Source) + if err != nil { + return err + } + if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { + return err + } + + opts := "rbind,ro" + if m.Writable { + opts = "rbind,rw" + } + + if err := mount.Mount(m.Source, dest, "bind", opts); err != nil { + return err + } + } + + return nil +} + +func killProcessDirectly(container *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 { + 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 { + return err + } + logrus.Debugf("Cannot kill process (pid=%d) with signal 9: no such process.", pid) + } + } + } + return nil +} + +func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs.Device, err error) { + device, err := devices.DeviceFromPath(deviceMapping.PathOnHost, deviceMapping.CgroupPermissions) + // if there was no error, return the device + if err == nil { + device.Path = deviceMapping.PathInContainer + return append(devs, device), nil + } + + // if the device is not a device node + // try to see if it's a directory holding many devices + if err == devices.ErrNotADevice { + + // check if it is a directory + if src, e := os.Stat(deviceMapping.PathOnHost); e == nil && src.IsDir() { + + // mount the internal devices recursively + filepath.Walk(deviceMapping.PathOnHost, func(dpath string, f os.FileInfo, e error) error { + childDevice, e := devices.DeviceFromPath(dpath, deviceMapping.CgroupPermissions) + if e != nil { + // ignore the device + return nil + } + + // add the device to userSpecified devices + childDevice.Path = strings.Replace(dpath, deviceMapping.PathOnHost, deviceMapping.PathInContainer, 1) + devs = append(devs, childDevice) + + return nil + }) + } + } + + if len(devs) > 0 { + return devs, nil + } + + return devs, derr.ErrorCodeDeviceInfo.WithArgs(deviceMapping.PathOnHost, err) +} + +func mergeDevices(defaultDevices, userDevices []*configs.Device) []*configs.Device { + if len(userDevices) == 0 { + return defaultDevices + } + + paths := map[string]*configs.Device{} + for _, d := range userDevices { + paths[d.Path] = d + } + + var devs []*configs.Device + for _, d := range defaultDevices { + if _, defined := paths[d.Path]; !defined { + devs = append(devs, d) + } + } + return append(devs, userDevices...) +} + +func detachMounted(path string) error { + return syscall.Unmount(path, syscall.MNT_DETACH) +} + +func isLinkable(child *container.Container) bool { + // A container is linkable only if it belongs to the default network + _, ok := child.NetworkSettings.Networks["bridge"] + return ok +} diff --git a/daemon/container_windows.go b/daemon/container_operations_windows.go similarity index 53% rename from daemon/container_windows.go rename to daemon/container_operations_windows.go index 2586273fa8..b4d148731c 100644 --- a/daemon/container_windows.go +++ b/daemon/container_operations_windows.go @@ -5,62 +5,31 @@ package daemon import ( "strings" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" "github.com/docker/docker/layer" - "github.com/docker/docker/volume" - "github.com/docker/libnetwork" ) -// DefaultPathEnv is deliberately empty on Windows as the default path will be set by -// the container. Docker has no context of what the default path should be. -const DefaultPathEnv = "" - -// Container holds fields specific to the Windows implementation. See -// CommonContainer for standard fields common to all containers. -type Container struct { - CommonContainer - - // Fields below here are platform specific. -} - -func killProcessDirectly(container *Container) error { - return nil -} - -func (daemon *Daemon) setupLinkedContainers(container *Container) ([]string, error) { +func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { return nil, nil } -func (container *Container) createDaemonEnvironment(linkedEnv []string) []string { - // On Windows, nothing to link. Just return the container environment. - return container.Config.Env -} - -func (daemon *Daemon) initializeNetworking(container *Container) error { +func (daemon *Daemon) initializeNetworking(container *container.Container) error { return nil } // ConnectToNetwork connects a container to the network -func (daemon *Daemon) ConnectToNetwork(container *Container, idOrName string) error { +func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string) error { return nil } -// DisconnectFromNetwork disconnects a container from, the network -func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error { - return nil -} - -func (container *Container) setupWorkingDirectory() error { - return nil -} - -func (daemon *Daemon) populateCommand(c *Container, env []string) error { +func (daemon *Daemon) populateCommand(c *container.Container, env []string) error { en := &execdriver.Network{ Interface: nil, } - parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2) + parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2) switch parts[0] { case "none": case "default", "": // empty string to support existing containers @@ -68,7 +37,7 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error { en.Interface = &execdriver.NetworkInterface{ MacAddress: c.Config.MacAddress, Bridge: daemon.configStore.Bridge.VirtualSwitchName, - PortBindings: c.hostConfig.PortBindings, + PortBindings: c.HostConfig.PortBindings, // TODO Windows. Include IPAddress. There already is a // property IPAddress on execDrive.CommonNetworkInterface, @@ -77,13 +46,13 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error { } } default: - return derr.ErrorCodeInvalidNetworkMode.WithArgs(c.hostConfig.NetworkMode) + return derr.ErrorCodeInvalidNetworkMode.WithArgs(c.HostConfig.NetworkMode) } // TODO Windows. More resource controls to be implemented later. resources := &execdriver.Resources{ CommonResources: execdriver.CommonResources{ - CPUShares: c.hostConfig.CPUShares, + CPUShares: c.HostConfig.CPUShares, }, } @@ -93,7 +62,7 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error { Arguments: c.Args, Tty: c.Config.Tty, }, - ConsoleSize: c.hostConfig.ConsoleSize, + ConsoleSize: c.HostConfig.ConsoleSize, } processConfig.Env = env @@ -123,23 +92,23 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error { } layerFolder := m["dir"] - c.command = &execdriver.Command{ + c.Command = &execdriver.Command{ CommonCommand: execdriver.CommonCommand{ ID: c.ID, - Rootfs: c.rootfsPath(), + Rootfs: c.BaseFS, InitPath: "/.dockerinit", WorkingDir: c.Config.WorkingDir, Network: en, - MountLabel: c.getMountLabel(), + MountLabel: c.GetMountLabel(), Resources: resources, ProcessConfig: processConfig, - ProcessLabel: c.getProcessLabel(), + ProcessLabel: c.GetProcessLabel(), }, FirstStart: !c.HasBeenStartedBefore, LayerFolder: layerFolder, LayerPaths: layerPaths, Hostname: c.Config.Hostname, - Isolation: c.hostConfig.Isolation, + Isolation: c.HostConfig.Isolation, ArgsEscaped: c.Config.ArgsEscaped, } @@ -147,7 +116,7 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error { } // getSize returns real size & virtual size -func (daemon *Daemon) getSize(container *Container) (int64, int64) { +func (daemon *Daemon) getSize(container *container.Container) (int64, int64) { // TODO Windows return 0, 0 } @@ -158,56 +127,39 @@ func (daemon *Daemon) setNetworkNamespaceKey(containerID string, pid int) error } // allocateNetwork is a no-op on Windows. -func (daemon *Daemon) allocateNetwork(container *Container) error { +func (daemon *Daemon) allocateNetwork(container *container.Container) error { return nil } -func (daemon *Daemon) updateNetwork(container *Container) error { +func (daemon *Daemon) updateNetwork(container *container.Container) error { return nil } -func (daemon *Daemon) releaseNetwork(container *Container) { +func (daemon *Daemon) releaseNetwork(container *container.Container) { } -// appendNetworkMounts appends any network mounts to the array of mount points passed in. -// Windows does not support network mounts (not to be confused with SMB network mounts), so -// this is a no-op. -func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) { - return volumeMounts, nil -} - -func (daemon *Daemon) setupIpcDirs(container *Container) error { +func (daemon *Daemon) setupIpcDirs(container *container.Container) error { return nil } -func (container *Container) unmountIpcMounts(unmount func(pth string) error) { -} - -func detachMounted(path string) error { - return nil -} - -func (container *Container) ipcMounts() []execdriver.Mount { - return nil -} - -func (container *Container) tmpfsMounts() []execdriver.Mount { - return nil -} - -func getDefaultRouteMtu() (int, error) { - return -1, errSystemNotSupported -} - // TODO Windows: Fix Post-TP4. This is a hack to allow docker cp to work // against containers which have volumes. You will still be able to cp // to somewhere on the container drive, but not to any mounted volumes // inside the container. Without this fix, docker cp is broken to any // container which has a volume, regardless of where the file is inside the // container. -func (daemon *Daemon) mountVolumes(container *Container) error { +func (daemon *Daemon) mountVolumes(container *container.Container) error { return nil } -func (container *Container) unmountVolumes(forceSyscall bool) error { + +func detachMounted(path string) error { + return nil +} + +func getDefaultRouteMtu() (int, error) { + return -1, errSystemNotSupported +} + +func killProcessDirectly(container *container.Container) error { return nil } diff --git a/daemon/container_unit_test.go b/daemon/container_unit_test.go deleted file mode 100644 index c42e7751f3..0000000000 --- a/daemon/container_unit_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package daemon - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/docker/docker/pkg/signal" - "github.com/docker/docker/runconfig" - "github.com/docker/docker/volume" - "github.com/docker/docker/volume/drivers" -) - -func TestGetFullName(t *testing.T) { - name, err := GetFullContainerName("testing") - if err != nil { - t.Fatal(err) - } - if name != "/testing" { - t.Fatalf("Expected /testing got %s", name) - } - if _, err := GetFullContainerName(""); err == nil { - t.Fatal("Error should not be nil") - } -} - -func TestValidContainerNames(t *testing.T) { - invalidNames := []string{"-rm", "&sdfsfd", "safd%sd"} - validNames := []string{"word-word", "word_word", "1weoid"} - - for _, name := range invalidNames { - if validContainerNamePattern.MatchString(name) { - t.Fatalf("%q is not a valid container name and was returned as valid.", name) - } - } - - for _, name := range validNames { - if !validContainerNamePattern.MatchString(name) { - t.Fatalf("%q is a valid container name and was returned as invalid.", name) - } - } -} - -func TestContainerStopSignal(t *testing.T) { - c := &Container{ - CommonContainer: CommonContainer{ - Config: &runconfig.Config{}, - }, - } - - def, err := signal.ParseSignal(signal.DefaultStopSignal) - if err != nil { - t.Fatal(err) - } - - s := c.stopSignal() - if s != int(def) { - t.Fatalf("Expected %v, got %v", def, s) - } - - c = &Container{ - CommonContainer: CommonContainer{ - Config: &runconfig.Config{StopSignal: "SIGKILL"}, - }, - } - s = c.stopSignal() - if s != 9 { - t.Fatalf("Expected 9, got %v", s) - } -} - -func TestContainerInitDNS(t *testing.T) { - tmp, err := ioutil.TempDir("", "docker-container-test-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - containerID := "d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e" - containerPath := filepath.Join(tmp, containerID) - if err := os.MkdirAll(containerPath, 0755); err != nil { - t.Fatal(err) - } - - config := `{"State":{"Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":2464,"ExitCode":0, -"Error":"","StartedAt":"2015-05-26T16:48:53.869308965Z","FinishedAt":"0001-01-01T00:00:00Z"}, -"ID":"d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e","Created":"2015-05-26T16:48:53.7987917Z","Path":"top", -"Args":[],"Config":{"Hostname":"d59df5276e7b","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"Cpuset":"", -"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":true,"OpenStdin":true, -"StdinOnce":false,"Env":null,"Cmd":["top"],"Image":"ubuntu:latest","Volumes":null,"WorkingDir":"","Entrypoint":null, -"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":{}},"Image":"07f8e8c5e66084bef8f848877857537ffe1c47edd01a93af27e7161672ad0e95", -"NetworkSettings":{"IPAddress":"172.17.0.1","IPPrefixLen":16,"MacAddress":"02:42:ac:11:00:01","LinkLocalIPv6Address":"fe80::42:acff:fe11:1", -"LinkLocalIPv6PrefixLen":64,"GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"Gateway":"172.17.42.1","IPv6Gateway":"","Bridge":"docker0","Ports":{}}, -"ResolvConfPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/resolv.conf", -"HostnamePath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hostname", -"HostsPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hosts", -"LogPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e-json.log", -"Name":"/ubuntu","Driver":"aufs","MountLabel":"","ProcessLabel":"","AppArmorProfile":"","RestartCount":0, -"UpdateDns":false,"Volumes":{},"VolumesRW":{},"AppliedVolumesFrom":null}` - - if err = ioutil.WriteFile(filepath.Join(containerPath, configFileName), []byte(config), 0644); err != nil { - t.Fatal(err) - } - - hostConfig := `{"Binds":[],"ContainerIDFile":"","Memory":0,"MemorySwap":0,"CpuShares":0,"CpusetCpus":"", -"Privileged":false,"PortBindings":{},"Links":null,"PublishAllPorts":false,"Dns":null,"DnsOptions":null,"DnsSearch":null,"ExtraHosts":null,"VolumesFrom":null, -"Devices":[],"NetworkMode":"bridge","IpcMode":"","PidMode":"","CapAdd":null,"CapDrop":null,"RestartPolicy":{"Name":"no","MaximumRetryCount":0}, -"SecurityOpt":null,"ReadonlyRootfs":false,"Ulimits":null,"LogConfig":{"Type":"","Config":null},"CgroupParent":""}` - if err = ioutil.WriteFile(filepath.Join(containerPath, "hostconfig.json"), []byte(hostConfig), 0644); err != nil { - t.Fatal(err) - } - - daemon, err := initDaemonWithVolumeStore(tmp) - if err != nil { - t.Fatal(err) - } - defer volumedrivers.Unregister(volume.DefaultDriverName) - - c, err := daemon.load(containerID) - if err != nil { - t.Fatal(err) - } - - if c.hostConfig.DNS == nil { - t.Fatal("Expected container DNS to not be nil") - } - - if c.hostConfig.DNSSearch == nil { - t.Fatal("Expected container DNSSearch to not be nil") - } - - if c.hostConfig.DNSOptions == nil { - t.Fatal("Expected container DNSOptions to not be nil") - } -} diff --git a/daemon/container_unix.go b/daemon/container_unix.go deleted file mode 100644 index cb716368cb..0000000000 --- a/daemon/container_unix.go +++ /dev/null @@ -1,1545 +0,0 @@ -// +build linux freebsd - -package daemon - -import ( - "fmt" - "io/ioutil" - "net" - "os" - "path" - "path/filepath" - "strconv" - "strings" - "syscall" - "time" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/daemon/execdriver" - "github.com/docker/docker/daemon/links" - "github.com/docker/docker/daemon/network" - derr "github.com/docker/docker/errors" - "github.com/docker/docker/pkg/fileutils" - "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/pkg/nat" - "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/pkg/symlink" - "github.com/docker/docker/pkg/system" - "github.com/docker/docker/pkg/ulimit" - "github.com/docker/docker/runconfig" - "github.com/docker/docker/utils" - "github.com/docker/docker/volume" - "github.com/docker/libnetwork" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/options" - "github.com/docker/libnetwork/types" - "github.com/opencontainers/runc/libcontainer/configs" - "github.com/opencontainers/runc/libcontainer/devices" - "github.com/opencontainers/runc/libcontainer/label" -) - -const ( - // DefaultPathEnv is unix style list of directories to search for - // executables. Each directory is separated from the next by a colon - // ':' character . - DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - - // DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container - DefaultSHMSize int64 = 67108864 -) - -// Container holds the fields specific to unixen implementations. See -// CommonContainer for standard fields common to all containers. -type Container struct { - CommonContainer - - // Fields below here are platform specific. - activeLinks map[string]*links.Link - AppArmorProfile string - HostnamePath string - HostsPath string - ShmPath string - MqueuePath string - ResolvConfPath string -} - -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 { - 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 { - return err - } - logrus.Debugf("Cannot kill process (pid=%d) with signal 9: no such process.", pid) - } - } - } - return nil -} - -func (daemon *Daemon) setupLinkedContainers(container *Container) ([]string, error) { - var env []string - children, err := daemon.children(container.Name) - if err != nil { - return nil, err - } - - bridgeSettings := container.NetworkSettings.Networks["bridge"] - if bridgeSettings == nil { - return nil, nil - } - - if len(children) > 0 { - for linkAlias, child := range children { - if !child.IsRunning() { - return nil, derr.ErrorCodeLinkNotRunning.WithArgs(child.Name, linkAlias) - } - - childBridgeSettings := child.NetworkSettings.Networks["bridge"] - if childBridgeSettings == nil { - return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID) - } - - link := links.NewLink( - bridgeSettings.IPAddress, - childBridgeSettings.IPAddress, - linkAlias, - child.Config.Env, - child.Config.ExposedPorts, - ) - - for _, envVar := range link.ToEnv() { - env = append(env, envVar) - } - } - } - return env, nil -} - -func (container *Container) createDaemonEnvironment(linkedEnv []string) []string { - // if a domain name was specified, append it to the hostname (see #7851) - fullHostname := container.Config.Hostname - if container.Config.Domainname != "" { - fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) - } - // Setup environment - env := []string{ - "PATH=" + DefaultPathEnv, - "HOSTNAME=" + fullHostname, - // Note: we don't set HOME here because it'll get autoset intelligently - // based on the value of USER inside dockerinit, but only if it isn't - // set already (ie, that can be overridden by setting HOME via -e or ENV - // in a Dockerfile). - } - if container.Config.Tty { - env = append(env, "TERM=xterm") - } - env = append(env, linkedEnv...) - // because the env on the container can override certain default values - // we need to replace the 'env' keys where they match and append anything - // else. - env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env) - - return env -} - -func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs.Device, err error) { - device, err := devices.DeviceFromPath(deviceMapping.PathOnHost, deviceMapping.CgroupPermissions) - // if there was no error, return the device - if err == nil { - device.Path = deviceMapping.PathInContainer - return append(devs, device), nil - } - - // if the device is not a device node - // try to see if it's a directory holding many devices - if err == devices.ErrNotADevice { - - // check if it is a directory - if src, e := os.Stat(deviceMapping.PathOnHost); e == nil && src.IsDir() { - - // mount the internal devices recursively - filepath.Walk(deviceMapping.PathOnHost, func(dpath string, f os.FileInfo, e error) error { - childDevice, e := devices.DeviceFromPath(dpath, deviceMapping.CgroupPermissions) - if e != nil { - // ignore the device - return nil - } - - // add the device to userSpecified devices - childDevice.Path = strings.Replace(dpath, deviceMapping.PathOnHost, deviceMapping.PathInContainer, 1) - devs = append(devs, childDevice) - - return nil - }) - } - } - - if len(devs) > 0 { - return devs, nil - } - - return devs, derr.ErrorCodeDeviceInfo.WithArgs(deviceMapping.PathOnHost, err) -} - -func (daemon *Daemon) populateCommand(c *Container, env []string) error { - var en *execdriver.Network - if !c.Config.NetworkDisabled { - en = &execdriver.Network{} - if !daemon.execDriver.SupportsHooks() || c.hostConfig.NetworkMode.IsHost() { - en.NamespacePath = c.NetworkSettings.SandboxKey - } - - if c.hostConfig.NetworkMode.IsContainer() { - nc, err := daemon.getNetworkedContainer(c.ID, c.hostConfig.NetworkMode.ConnectedContainer()) - if err != nil { - return err - } - en.ContainerID = nc.ID - } - } - - ipc := &execdriver.Ipc{} - var err error - c.ShmPath, err = c.shmPath() - if err != nil { - return err - } - - c.MqueuePath, err = c.mqueuePath() - if err != nil { - return err - } - - if c.hostConfig.IpcMode.IsContainer() { - ic, err := daemon.getIpcContainer(c) - if err != nil { - return err - } - ipc.ContainerID = ic.ID - c.ShmPath = ic.ShmPath - c.MqueuePath = ic.MqueuePath - } else { - ipc.HostIpc = c.hostConfig.IpcMode.IsHost() - if ipc.HostIpc { - if _, err := os.Stat("/dev/shm"); err != nil { - return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host") - } - if _, err := os.Stat("/dev/mqueue"); err != nil { - return fmt.Errorf("/dev/mqueue is not mounted, but must be for --ipc=host") - } - c.ShmPath = "/dev/shm" - c.MqueuePath = "/dev/mqueue" - } - } - - pid := &execdriver.Pid{} - pid.HostPid = c.hostConfig.PidMode.IsHost() - - uts := &execdriver.UTS{ - HostUTS: c.hostConfig.UTSMode.IsHost(), - } - - // Build lists of devices allowed and created within the container. - var userSpecifiedDevices []*configs.Device - for _, deviceMapping := range c.hostConfig.Devices { - devs, err := getDevicesFromPath(deviceMapping) - if err != nil { - return err - } - - userSpecifiedDevices = append(userSpecifiedDevices, devs...) - } - - allowedDevices := mergeDevices(configs.DefaultAllowedDevices, userSpecifiedDevices) - - autoCreatedDevices := mergeDevices(configs.DefaultAutoCreatedDevices, userSpecifiedDevices) - - var rlimits []*ulimit.Rlimit - ulimits := c.hostConfig.Ulimits - - // Merge ulimits with daemon defaults - ulIdx := make(map[string]*ulimit.Ulimit) - for _, ul := range ulimits { - ulIdx[ul.Name] = ul - } - for name, ul := range daemon.configStore.Ulimits { - if _, exists := ulIdx[name]; !exists { - ulimits = append(ulimits, ul) - } - } - - weightDevices, err := getBlkioWeightDevices(c.hostConfig) - if err != nil { - return err - } - - for _, limit := range ulimits { - rl, err := limit.GetRlimit() - if err != nil { - return err - } - rlimits = append(rlimits, rl) - } - - resources := &execdriver.Resources{ - CommonResources: execdriver.CommonResources{ - Memory: c.hostConfig.Memory, - MemoryReservation: c.hostConfig.MemoryReservation, - CPUShares: c.hostConfig.CPUShares, - BlkioWeight: c.hostConfig.BlkioWeight, - }, - MemorySwap: c.hostConfig.MemorySwap, - KernelMemory: c.hostConfig.KernelMemory, - CpusetCpus: c.hostConfig.CpusetCpus, - CpusetMems: c.hostConfig.CpusetMems, - CPUPeriod: c.hostConfig.CPUPeriod, - CPUQuota: c.hostConfig.CPUQuota, - Rlimits: rlimits, - BlkioWeightDevice: weightDevices, - OomKillDisable: c.hostConfig.OomKillDisable, - MemorySwappiness: *c.hostConfig.MemorySwappiness, - } - - processConfig := execdriver.ProcessConfig{ - CommonProcessConfig: execdriver.CommonProcessConfig{ - Entrypoint: c.Path, - Arguments: c.Args, - Tty: c.Config.Tty, - }, - Privileged: c.hostConfig.Privileged, - User: c.Config.User, - } - - processConfig.SysProcAttr = &syscall.SysProcAttr{Setsid: true} - processConfig.Env = env - - remappedRoot := &execdriver.User{} - rootUID, rootGID := daemon.GetRemappedUIDGID() - if rootUID != 0 { - remappedRoot.UID = rootUID - remappedRoot.GID = rootGID - } - uidMap, gidMap := daemon.GetUIDGIDMaps() - - c.command = &execdriver.Command{ - CommonCommand: execdriver.CommonCommand{ - ID: c.ID, - InitPath: "/.dockerinit", - MountLabel: c.getMountLabel(), - Network: en, - ProcessConfig: processConfig, - ProcessLabel: c.getProcessLabel(), - Rootfs: c.rootfsPath(), - Resources: resources, - WorkingDir: c.Config.WorkingDir, - }, - AllowedDevices: allowedDevices, - AppArmorProfile: c.AppArmorProfile, - AutoCreatedDevices: autoCreatedDevices, - CapAdd: c.hostConfig.CapAdd.Slice(), - CapDrop: c.hostConfig.CapDrop.Slice(), - CgroupParent: c.hostConfig.CgroupParent, - GIDMapping: gidMap, - GroupAdd: c.hostConfig.GroupAdd, - Ipc: ipc, - OomScoreAdj: c.hostConfig.OomScoreAdj, - Pid: pid, - ReadonlyRootfs: c.hostConfig.ReadonlyRootfs, - RemappedRoot: remappedRoot, - UIDMapping: uidMap, - UTS: uts, - } - - return nil -} - -func mergeDevices(defaultDevices, userDevices []*configs.Device) []*configs.Device { - if len(userDevices) == 0 { - return defaultDevices - } - - paths := map[string]*configs.Device{} - for _, d := range userDevices { - paths[d.Path] = d - } - - var devs []*configs.Device - for _, d := range defaultDevices { - if _, defined := paths[d.Path]; !defined { - devs = append(devs, d) - } - } - return append(devs, userDevices...) -} - -// getSize returns the real size & virtual size of the container. -func (daemon *Daemon) getSize(container *Container) (int64, int64) { - var ( - sizeRw, sizeRootfs int64 - err error - ) - - 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 daemon.Unmount(container) - - sizeRw, err = container.rwlayer.Size() - if err != nil { - 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 - } - - if parent := container.rwlayer.Parent(); parent != nil { - sizeRootfs, err = parent.Size() - if err != nil { - sizeRootfs = -1 - } else if sizeRw != -1 { - sizeRootfs += sizeRw - } - } - return sizeRw, sizeRootfs -} - -// Attempt to set the network mounts given a provided destination and -// the path to use for it; return true if the given destination was a -// network mount file -func (container *Container) trySetNetworkMount(destination string, path string) bool { - if destination == "/etc/resolv.conf" { - container.ResolvConfPath = path - return true - } - if destination == "/etc/hostname" { - container.HostnamePath = path - return true - } - if destination == "/etc/hosts" { - container.HostsPath = path - return true - } - - return false -} - -func (container *Container) buildHostnameFile() error { - hostnamePath, err := container.getRootResourcePath("hostname") - if err != nil { - return err - } - container.HostnamePath = hostnamePath - - if container.Config.Domainname != "" { - return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644) - } - return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) -} - -func (daemon *Daemon) buildSandboxOptions(container *Container, n libnetwork.Network) ([]libnetwork.SandboxOption, error) { - var ( - sboxOptions []libnetwork.SandboxOption - err error - dns []string - dnsSearch []string - dnsOptions []string - ) - - sboxOptions = append(sboxOptions, libnetwork.OptionHostname(container.Config.Hostname), - libnetwork.OptionDomainname(container.Config.Domainname)) - - if container.hostConfig.NetworkMode.IsHost() { - sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) - sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) - sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) - } else if daemon.execDriver.SupportsHooks() { - // OptionUseExternalKey is mandatory for userns support. - // But optional for non-userns support - sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey()) - } - - container.HostsPath, err = container.getRootResourcePath("hosts") - if err != nil { - return nil, err - } - sboxOptions = append(sboxOptions, libnetwork.OptionHostsPath(container.HostsPath)) - - container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf") - if err != nil { - return nil, err - } - sboxOptions = append(sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath)) - - if len(container.hostConfig.DNS) > 0 { - dns = container.hostConfig.DNS - } else if len(daemon.configStore.DNS) > 0 { - dns = daemon.configStore.DNS - } - - for _, d := range dns { - sboxOptions = append(sboxOptions, libnetwork.OptionDNS(d)) - } - - if len(container.hostConfig.DNSSearch) > 0 { - dnsSearch = container.hostConfig.DNSSearch - } else if len(daemon.configStore.DNSSearch) > 0 { - dnsSearch = daemon.configStore.DNSSearch - } - - for _, ds := range dnsSearch { - sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(ds)) - } - - if len(container.hostConfig.DNSOptions) > 0 { - dnsOptions = container.hostConfig.DNSOptions - } else if len(daemon.configStore.DNSOptions) > 0 { - dnsOptions = daemon.configStore.DNSOptions - } - - for _, ds := range dnsOptions { - sboxOptions = append(sboxOptions, libnetwork.OptionDNSOptions(ds)) - } - - if container.NetworkSettings.SecondaryIPAddresses != nil { - name := container.Config.Hostname - if container.Config.Domainname != "" { - name = name + "." + container.Config.Domainname - } - - for _, a := range container.NetworkSettings.SecondaryIPAddresses { - sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(name, a.Addr)) - } - } - - for _, extraHost := range container.hostConfig.ExtraHosts { - // allow IPv6 addresses in extra hosts; only split on first ":" - parts := strings.SplitN(extraHost, ":", 2) - sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) - } - - // Link feature is supported only for the default bridge network. - // return if this call to build join options is not for default bridge network - if n.Name() != "bridge" { - return sboxOptions, nil - } - - ep, _ := container.getEndpointInNetwork(n) - if ep == nil { - return sboxOptions, nil - } - - var childEndpoints, parentEndpoints []string - - children, err := daemon.children(container.Name) - if err != nil { - return nil, err - } - - for linkAlias, child := range children { - if !isLinkable(child) { - return nil, fmt.Errorf("Cannot link to %s, as it does not belong to the default network", child.Name) - } - _, alias := path.Split(linkAlias) - // allow access to the linked container via the alias, real name, and container hostname - aliasList := alias + " " + child.Config.Hostname - // only add the name if alias isn't equal to the name - if alias != child.Name[1:] { - aliasList = aliasList + " " + child.Name[1:] - } - sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.Networks["bridge"].IPAddress)) - cEndpoint, _ := child.getEndpointInNetwork(n) - if cEndpoint != nil && cEndpoint.ID() != "" { - childEndpoints = append(childEndpoints, cEndpoint.ID()) - } - } - - bridgeSettings := container.NetworkSettings.Networks["bridge"] - refs := daemon.containerGraph().RefPaths(container.ID) - for _, ref := range refs { - if ref.ParentID == "0" { - continue - } - - c, err := daemon.Get(ref.ParentID) - if err != nil { - logrus.Error(err) - } - - 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() != "" { - parentEndpoints = append(parentEndpoints, ep.ID()) - } - } - } - - linkOptions := options.Generic{ - netlabel.GenericData: options.Generic{ - "ParentEndpoints": parentEndpoints, - "ChildEndpoints": childEndpoints, - }, - } - - sboxOptions = append(sboxOptions, libnetwork.OptionGeneric(linkOptions)) - - return sboxOptions, nil -} - -func isLinkable(child *Container) bool { - // A container is linkable only if it belongs to the default network - _, ok := child.NetworkSettings.Networks["bridge"] - return ok -} - -func (container *Container) getEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) { - endpointName := strings.TrimPrefix(container.Name, "/") - return n.EndpointByName(endpointName) -} - -func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { - if ep == nil { - return nil, derr.ErrorCodeEmptyEndpoint - } - - if networkSettings == nil { - return nil, derr.ErrorCodeEmptyNetwork - } - - driverInfo, err := ep.DriverInfo() - if err != nil { - return nil, err - } - - if driverInfo == nil { - // It is not an error for epInfo to be nil - return networkSettings, nil - } - - if networkSettings.Ports == nil { - networkSettings.Ports = nat.PortMap{} - } - - if expData, ok := driverInfo[netlabel.ExposedPorts]; ok { - if exposedPorts, ok := expData.([]types.TransportPort); ok { - for _, tp := range exposedPorts { - natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port))) - if err != nil { - return nil, derr.ErrorCodeParsingPort.WithArgs(tp.Port, err) - } - networkSettings.Ports[natPort] = nil - } - } - } - - mapData, ok := driverInfo[netlabel.PortMap] - if !ok { - return networkSettings, nil - } - - if portMapping, ok := mapData.([]types.PortBinding); ok { - for _, pp := range portMapping { - natPort, err := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) - if err != nil { - return nil, err - } - natBndg := nat.PortBinding{HostIP: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))} - networkSettings.Ports[natPort] = append(networkSettings.Ports[natPort], natBndg) - } - } - - return networkSettings, nil -} - -func (container *Container) buildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { - if ep == nil { - return nil, derr.ErrorCodeEmptyEndpoint - } - - if networkSettings == nil { - return nil, derr.ErrorCodeEmptyNetwork - } - - epInfo := ep.Info() - if epInfo == nil { - // It is not an error to get an empty endpoint info - return networkSettings, nil - } - - if _, ok := networkSettings.Networks[n.Name()]; !ok { - networkSettings.Networks[n.Name()] = new(network.EndpointSettings) - } - networkSettings.Networks[n.Name()].EndpointID = ep.ID() - - iface := epInfo.Iface() - if iface == nil { - return networkSettings, nil - } - - if iface.MacAddress() != nil { - networkSettings.Networks[n.Name()].MacAddress = iface.MacAddress().String() - } - - if iface.Address() != nil { - ones, _ := iface.Address().Mask.Size() - networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String() - networkSettings.Networks[n.Name()].IPPrefixLen = ones - } - - if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil { - onesv6, _ := iface.AddressIPv6().Mask.Size() - networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String() - networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6 - } - - return networkSettings, nil -} - -func (container *Container) updateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { - if _, err := container.buildPortMapInfo(ep, container.NetworkSettings); err != nil { - return err - } - - epInfo := ep.Info() - if epInfo == nil { - // It is not an error to get an empty endpoint info - return nil - } - if epInfo.Gateway() != nil { - container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String() - } - if epInfo.GatewayIPv6().To16() != nil { - container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String() - } - - return nil -} - -func (daemon *Daemon) updateNetworkSettings(container *Container, n libnetwork.Network) error { - if container.NetworkSettings == nil { - container.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)} - } - - if !container.hostConfig.NetworkMode.IsHost() && runconfig.NetworkMode(n.Type()).IsHost() { - return runconfig.ErrConflictHostNetwork - } - - for s := range container.NetworkSettings.Networks { - sn, err := daemon.FindNetwork(s) - if err != nil { - continue - } - - if sn.Name() == n.Name() { - // Avoid duplicate config - return nil - } - if !runconfig.NetworkMode(sn.Type()).IsPrivate() || - !runconfig.NetworkMode(n.Type()).IsPrivate() { - return runconfig.ErrConflictSharedNetwork - } - if runconfig.NetworkMode(sn.Name()).IsNone() || - runconfig.NetworkMode(n.Name()).IsNone() { - return runconfig.ErrConflictNoNetwork - } - } - container.NetworkSettings.Networks[n.Name()] = new(network.EndpointSettings) - - return nil -} - -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 = daemon.configStore.Bridge.Iface - } - - return nil -} - -func (container *Container) updateSandboxNetworkSettings(sb libnetwork.Sandbox) error { - container.NetworkSettings.SandboxID = sb.ID() - container.NetworkSettings.SandboxKey = sb.Key() - return nil -} - -// UpdateNetwork is used to update the container's network (e.g. when linked containers -// get removed/unlinked). -func (daemon *Daemon) updateNetwork(container *Container) error { - ctrl := daemon.netController - sid := container.NetworkSettings.SandboxID - - sb, err := ctrl.SandboxByID(sid) - if err != nil { - return derr.ErrorCodeNoSandbox.WithArgs(sid, err) - } - - // Find if container is connected to the default bridge network - var n libnetwork.Network - for name := range container.NetworkSettings.Networks { - sn, err := daemon.FindNetwork(name) - if err != nil { - continue - } - if sn.Name() == "bridge" { - n = sn - break - } - } - - if n == nil { - // Not connected to the default bridge network; Nothing to do - return nil - } - - options, err := daemon.buildSandboxOptions(container, n) - if err != nil { - return derr.ErrorCodeNetworkUpdate.WithArgs(err) - } - - if err := sb.Refresh(options...); err != nil { - return derr.ErrorCodeNetworkRefresh.WithArgs(sid, err) - } - - return nil -} - -func (container *Container) buildCreateEndpointOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) { - var ( - portSpecs = make(nat.PortSet) - bindings = make(nat.PortMap) - pbList []types.PortBinding - exposeList []types.TransportPort - createOptions []libnetwork.EndpointOption - ) - - if n.Name() == "bridge" || container.NetworkSettings.IsAnonymousEndpoint { - createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) - } - - // Other configs are applicable only for the endpoint in the network - // to which container was connected to on docker run. - if n.Name() != container.hostConfig.NetworkMode.NetworkName() && - !(n.Name() == "bridge" && container.hostConfig.NetworkMode.IsDefault()) { - return createOptions, nil - } - - if container.Config.ExposedPorts != nil { - portSpecs = container.Config.ExposedPorts - } - - if container.hostConfig.PortBindings != nil { - for p, b := range container.hostConfig.PortBindings { - bindings[p] = []nat.PortBinding{} - for _, bb := range b { - bindings[p] = append(bindings[p], nat.PortBinding{ - HostIP: bb.HostIP, - HostPort: bb.HostPort, - }) - } - } - } - - ports := make([]nat.Port, len(portSpecs)) - var i int - for p := range portSpecs { - ports[i] = p - i++ - } - nat.SortPortMap(ports, bindings) - for _, port := range ports { - expose := types.TransportPort{} - expose.Proto = types.ParseProtocol(port.Proto()) - expose.Port = uint16(port.Int()) - exposeList = append(exposeList, expose) - - pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto} - binding := bindings[port] - for i := 0; i < len(binding); i++ { - pbCopy := pb.GetCopy() - newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) - var portStart, portEnd int - if err == nil { - portStart, portEnd, err = newP.Range() - } - if err != nil { - return nil, derr.ErrorCodeHostPort.WithArgs(binding[i].HostPort, err) - } - pbCopy.HostPort = uint16(portStart) - pbCopy.HostPortEnd = uint16(portEnd) - pbCopy.HostIP = net.ParseIP(binding[i].HostIP) - pbList = append(pbList, pbCopy) - } - - if container.hostConfig.PublishAllPorts && len(binding) == 0 { - pbList = append(pbList, pb) - } - } - - createOptions = append(createOptions, - libnetwork.CreateOptionPortMapping(pbList), - libnetwork.CreateOptionExposedPorts(exposeList)) - - if container.Config.MacAddress != "" { - mac, err := net.ParseMAC(container.Config.MacAddress) - if err != nil { - return nil, err - } - - genericOption := options.Generic{ - netlabel.MacAddress: mac, - } - - createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) - } - - return createOptions, nil -} - -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 { - logrus.Errorf("failed to cleanup up stale network sandbox for container %s", container.ID) - } - - updateSettings := false - if len(container.NetworkSettings.Networks) == 0 { - mode := container.hostConfig.NetworkMode - if container.Config.NetworkDisabled || mode.IsContainer() { - return nil - } - - networkName := mode.NetworkName() - if mode.IsDefault() { - networkName = controller.Config().Daemon.DefaultNetwork - } - if mode.IsUserDefined() { - n, err := daemon.FindNetwork(networkName) - if err != nil { - return err - } - networkName = n.Name() - } - container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings) - container.NetworkSettings.Networks[networkName] = new(network.EndpointSettings) - updateSettings = true - } - - for n := range container.NetworkSettings.Networks { - if err := daemon.connectToNetwork(container, n, updateSettings); err != nil { - return err - } - } - - return container.writeHostConfig() -} - -func (daemon *Daemon) getNetworkSandbox(container *Container) libnetwork.Sandbox { - var sb libnetwork.Sandbox - daemon.netController.WalkSandboxes(func(s libnetwork.Sandbox) bool { - if s.ContainerID() == container.ID { - sb = s - return true - } - return false - }) - return sb -} - -// ConnectToNetwork connects a container to a network -func (daemon *Daemon) ConnectToNetwork(container *Container, idOrName string) error { - if !container.Running { - return derr.ErrorCodeNotRunning.WithArgs(container.ID) - } - if err := daemon.connectToNetwork(container, idOrName, true); err != nil { - return err - } - if err := container.toDiskLocking(); err != nil { - return fmt.Errorf("Error saving container to disk: %v", err) - } - return nil -} - -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() && - daemon.configStore.DisableBridge { - container.Config.NetworkDisabled = true - return nil - } - - controller := daemon.netController - - n, err := daemon.FindNetwork(idOrName) - if err != nil { - return err - } - - if updateSettings { - if err := daemon.updateNetworkSettings(container, n); err != nil { - return err - } - } - - ep, err := container.getEndpointInNetwork(n) - if err == nil { - return fmt.Errorf("container already connected to network %s", idOrName) - } - - if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok { - return err - } - - createOptions, err := container.buildCreateEndpointOptions(n) - if err != nil { - return err - } - - endpointName := strings.TrimPrefix(container.Name, "/") - ep, err = n.CreateEndpoint(endpointName, createOptions...) - if err != nil { - return err - } - defer func() { - if err != nil { - if e := ep.Delete(); e != nil { - logrus.Warnf("Could not rollback container connection to network %s", idOrName) - } - } - }() - - if err := daemon.updateEndpointNetworkSettings(container, n, ep); err != nil { - return err - } - - sb := daemon.getNetworkSandbox(container) - if sb == nil { - options, err := daemon.buildSandboxOptions(container, n) - if err != nil { - return err - } - sb, err = controller.NewSandbox(container.ID, options...) - if err != nil { - return err - } - - container.updateSandboxNetworkSettings(sb) - } - - if err := ep.Join(sb); err != nil { - return err - } - - if err := container.updateJoinInfo(n, ep); err != nil { - return derr.ErrorCodeJoinInfo.WithArgs(err) - } - - return nil -} - -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 := daemon.getNetworkedContainer(container.ID, container.hostConfig.NetworkMode.ConnectedContainer()) - if err != nil { - return err - } - container.HostnamePath = nc.HostnamePath - container.HostsPath = nc.HostsPath - container.ResolvConfPath = nc.ResolvConfPath - container.Config.Hostname = nc.Config.Hostname - container.Config.Domainname = nc.Config.Domainname - return nil - } - - if container.hostConfig.NetworkMode.IsHost() { - container.Config.Hostname, err = os.Hostname() - if err != nil { - return err - } - - parts := strings.SplitN(container.Config.Hostname, ".", 2) - if len(parts) > 1 { - container.Config.Hostname = parts[0] - container.Config.Domainname = parts[1] - } - - } - - if err := daemon.allocateNetwork(container); err != nil { - return err - } - - return container.buildHostnameFile() -} - -// 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 { - path := fmt.Sprintf("/proc/%d/ns/net", pid) - var sandbox libnetwork.Sandbox - search := libnetwork.SandboxContainerWalker(&sandbox, containerID) - daemon.netController.WalkSandboxes(search) - if sandbox == nil { - return derr.ErrorCodeNoSandbox.WithArgs(containerID, "no sandbox found") - } - - return sandbox.SetKey(path) -} - -func (daemon *Daemon) getIpcContainer(container *Container) (*Container, error) { - containerID := container.hostConfig.IpcMode.Container() - c, err := daemon.Get(containerID) - if err != nil { - return nil, err - } - if !c.IsRunning() { - return nil, derr.ErrorCodeIPCRunning - } - return c, nil -} - -func (container *Container) setupWorkingDirectory() error { - if container.Config.WorkingDir == "" { - return nil - } - container.Config.WorkingDir = filepath.Clean(container.Config.WorkingDir) - - pth, err := container.GetResourcePath(container.Config.WorkingDir) - if err != nil { - return err - } - - pthInfo, err := os.Stat(pth) - if err != nil { - if !os.IsNotExist(err) { - return err - } - - if err := system.MkdirAll(pth, 0755); err != nil { - return err - } - } - if pthInfo != nil && !pthInfo.IsDir() { - return derr.ErrorCodeNotADir.WithArgs(container.Config.WorkingDir) - } - return nil -} - -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) { - if container.hostConfig.NetworkMode.IsContainer() || container.Config.NetworkDisabled { - return - } - - sid := container.NetworkSettings.SandboxID - networks := container.NetworkSettings.Networks - for n := range networks { - networks[n] = &network.EndpointSettings{} - } - - container.NetworkSettings = &network.Settings{Networks: networks} - - if sid == "" || len(networks) == 0 { - return - } - - sb, err := daemon.netController.SandboxByID(sid) - if err != nil { - logrus.Errorf("error locating sandbox id %s: %v", sid, err) - return - } - - if err := sb.Delete(); err != nil { - logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err) - } -} - -// DisconnectFromNetwork disconnects a container from a network -func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error { - if !container.Running { - return derr.ErrorCodeNotRunning.WithArgs(container.ID) - } - - if container.hostConfig.NetworkMode.IsHost() && runconfig.NetworkMode(n.Type()).IsHost() { - return runconfig.ErrConflictHostNetwork - } - - if err := container.disconnectFromNetwork(n); err != nil { - return err - } - - if err := container.toDiskLocking(); err != nil { - return fmt.Errorf("Error saving container to disk: %v", err) - } - return nil -} - -func (container *Container) disconnectFromNetwork(n libnetwork.Network) error { - var ( - ep libnetwork.Endpoint - sbox libnetwork.Sandbox - ) - - s := func(current libnetwork.Endpoint) bool { - epInfo := current.Info() - if epInfo == nil { - return false - } - if sb := epInfo.Sandbox(); sb != nil { - if sb.ContainerID() == container.ID { - ep = current - sbox = sb - return true - } - } - return false - } - n.WalkEndpoints(s) - - if ep == nil { - return fmt.Errorf("container %s is not connected to the network", container.ID) - } - - if err := ep.Leave(sbox); err != nil { - return fmt.Errorf("container %s failed to leave network %s: %v", container.ID, n.Name(), err) - } - - if err := ep.Delete(); err != nil { - return fmt.Errorf("endpoint delete failed for container %s on network %s: %v", container.ID, n.Name(), err) - } - - delete(container.NetworkSettings.Networks, n.Name()) - return nil -} - -// appendNetworkMounts appends any network mounts to the array of mount points passed in -func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) { - for _, mnt := range container.networkMounts() { - dest, err := container.GetResourcePath(mnt.Destination) - if err != nil { - return nil, err - } - volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest}) - } - return volumeMounts, nil -} - -func (container *Container) networkMounts() []execdriver.Mount { - var mounts []execdriver.Mount - shared := container.hostConfig.NetworkMode.IsContainer() - if container.ResolvConfPath != "" { - if _, err := os.Stat(container.ResolvConfPath); err != nil { - logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err) - } else { - label.Relabel(container.ResolvConfPath, container.MountLabel, shared) - writable := !container.hostConfig.ReadonlyRootfs - if m, exists := container.MountPoints["/etc/resolv.conf"]; exists { - writable = m.RW - } - mounts = append(mounts, execdriver.Mount{ - Source: container.ResolvConfPath, - Destination: "/etc/resolv.conf", - Writable: writable, - Private: true, - }) - } - } - if container.HostnamePath != "" { - if _, err := os.Stat(container.HostnamePath); err != nil { - logrus.Warnf("HostnamePath set to %q, but can't stat this filename (err = %v); skipping", container.HostnamePath, err) - } else { - label.Relabel(container.HostnamePath, container.MountLabel, shared) - writable := !container.hostConfig.ReadonlyRootfs - if m, exists := container.MountPoints["/etc/hostname"]; exists { - writable = m.RW - } - mounts = append(mounts, execdriver.Mount{ - Source: container.HostnamePath, - Destination: "/etc/hostname", - Writable: writable, - Private: true, - }) - } - } - if container.HostsPath != "" { - if _, err := os.Stat(container.HostsPath); err != nil { - logrus.Warnf("HostsPath set to %q, but can't stat this filename (err = %v); skipping", container.HostsPath, err) - } else { - label.Relabel(container.HostsPath, container.MountLabel, shared) - writable := !container.hostConfig.ReadonlyRootfs - if m, exists := container.MountPoints["/etc/hosts"]; exists { - writable = m.RW - } - mounts = append(mounts, execdriver.Mount{ - Source: container.HostsPath, - Destination: "/etc/hosts", - Writable: writable, - Private: true, - }) - } - } - return mounts -} - -func (container *Container) copyImagePathContent(v volume.Volume, destination string) error { - rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, destination), container.basefs) - if err != nil { - return err - } - - if _, err = ioutil.ReadDir(rootfs); err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - - path, err := v.Mount() - if err != nil { - return err - } - - if err := copyExistingContents(rootfs, path); err != nil { - return err - } - - return v.Unmount() -} - -func (container *Container) shmPath() (string, error) { - return container.getRootResourcePath("shm") -} -func (container *Container) mqueuePath() (string, error) { - return container.getRootResourcePath("mqueue") -} - -func (container *Container) hasMountFor(path string) bool { - _, exists := container.MountPoints[path] - return exists -} - -func (daemon *Daemon) setupIpcDirs(container *Container) error { - rootUID, rootGID := daemon.GetRemappedUIDGID() - if !container.hasMountFor("/dev/shm") { - shmPath, err := container.shmPath() - if err != nil { - return err - } - - if err := idtools.MkdirAllAs(shmPath, 0700, rootUID, rootGID); err != nil { - return err - } - - shmSize := DefaultSHMSize - if container.hostConfig.ShmSize != nil { - shmSize = *container.hostConfig.ShmSize - } - - shmproperty := "mode=1777,size=" + strconv.FormatInt(shmSize, 10) - if err := syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel(shmproperty, container.getMountLabel())); err != nil { - return fmt.Errorf("mounting shm tmpfs: %s", err) - } - if err := os.Chown(shmPath, rootUID, rootGID); err != nil { - return err - } - } - - if !container.hasMountFor("/dev/mqueue") { - mqueuePath, err := container.mqueuePath() - if err != nil { - return err - } - - if err := idtools.MkdirAllAs(mqueuePath, 0700, rootUID, rootGID); err != nil { - return err - } - - if err := syscall.Mount("mqueue", mqueuePath, "mqueue", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), ""); err != nil { - return fmt.Errorf("mounting mqueue mqueue : %s", err) - } - if err := os.Chown(mqueuePath, rootUID, rootGID); err != nil { - return err - } - } - - return nil -} - -func (container *Container) unmountIpcMounts(unmount func(pth string) error) { - if container.hostConfig.IpcMode.IsContainer() || container.hostConfig.IpcMode.IsHost() { - return - } - - var warnings []string - - if !container.hasMountFor("/dev/shm") { - shmPath, err := container.shmPath() - if err != nil { - logrus.Error(err) - warnings = append(warnings, err.Error()) - } else if shmPath != "" { - if err := unmount(shmPath); err != nil { - warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", shmPath, err)) - } - - } - } - - if !container.hasMountFor("/dev/mqueue") { - mqueuePath, err := container.mqueuePath() - if err != nil { - logrus.Error(err) - warnings = append(warnings, err.Error()) - } else if mqueuePath != "" { - if err := unmount(mqueuePath); err != nil { - warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", mqueuePath, err)) - } - } - } - - if len(warnings) > 0 { - logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n")) - } -} - -func (container *Container) ipcMounts() []execdriver.Mount { - var mounts []execdriver.Mount - - if !container.hasMountFor("/dev/shm") { - label.SetFileLabel(container.ShmPath, container.MountLabel) - mounts = append(mounts, execdriver.Mount{ - Source: container.ShmPath, - Destination: "/dev/shm", - Writable: true, - Private: true, - }) - } - - if !container.hasMountFor("/dev/mqueue") { - label.SetFileLabel(container.MqueuePath, container.MountLabel) - mounts = append(mounts, execdriver.Mount{ - Source: container.MqueuePath, - Destination: "/dev/mqueue", - Writable: true, - Private: true, - }) - } - return mounts -} - -func detachMounted(path string) error { - return syscall.Unmount(path, syscall.MNT_DETACH) -} - -func (daemon *Daemon) mountVolumes(container *Container) error { - mounts, err := daemon.setupMounts(container) - if err != nil { - return err - } - - for _, m := range mounts { - dest, err := container.GetResourcePath(m.Destination) - if err != nil { - return err - } - - var stat os.FileInfo - stat, err = os.Stat(m.Source) - if err != nil { - return err - } - if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil { - return err - } - - opts := "rbind,ro" - if m.Writable { - opts = "rbind,rw" - } - - if err := mount.Mount(m.Source, dest, "bind", opts); err != nil { - return err - } - } - - return nil -} - -func (container *Container) unmountVolumes(forceSyscall bool) error { - var ( - volumeMounts []volume.MountPoint - err error - ) - - for _, mntPoint := range container.MountPoints { - dest, err := container.GetResourcePath(mntPoint.Destination) - if err != nil { - return err - } - - volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest, Volume: mntPoint.Volume}) - } - - // Append any network mounts to the list (this is a no-op on Windows) - if volumeMounts, err = appendNetworkMounts(container, volumeMounts); err != nil { - return err - } - - for _, volumeMount := range volumeMounts { - if forceSyscall { - if err := detachMounted(volumeMount.Destination); err != nil { - logrus.Warnf("%s unmountVolumes: Failed to do lazy umount %v", container.ID, err) - } - } - - if volumeMount.Volume != nil { - if err := volumeMount.Volume.Unmount(); err != nil { - return err - } - } - } - - return nil -} - -func (container *Container) tmpfsMounts() []execdriver.Mount { - var mounts []execdriver.Mount - for dest, data := range container.hostConfig.Tmpfs { - mounts = append(mounts, execdriver.Mount{ - Source: "tmpfs", - Destination: dest, - Data: data, - }) - } - return mounts -} diff --git a/daemon/create.go b/daemon/create.go index 423bbf7f76..fa59ea3dcc 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -3,6 +3,7 @@ package daemon import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" "github.com/docker/docker/image" "github.com/docker/docker/pkg/idtools" @@ -48,9 +49,9 @@ func (daemon *Daemon) ContainerCreate(params *ContainerCreateConfig) (types.Cont } // Create creates a new container from the given configuration with a given name. -func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, retErr error) { +func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *container.Container, retErr error) { var ( - container *Container + container *container.Container img *image.Image imgID image.ID err error @@ -86,7 +87,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re if err != nil { return nil, err } - if err := idtools.MkdirAs(container.root, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAs(container.Root, 0700, rootUID, rootGID); err != nil { return nil, err } @@ -105,7 +106,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re return nil, err } - if err := container.toDiskLocking(); err != nil { + if err := container.ToDiskLocking(); err != nil { logrus.Errorf("Error saving new container to disk: %v", err) return nil, err } diff --git a/daemon/create_unix.go b/daemon/create_unix.go index 4975c350a5..741df132b7 100644 --- a/daemon/create_unix.go +++ b/daemon/create_unix.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" "github.com/docker/docker/image" "github.com/docker/docker/pkg/stringid" @@ -15,7 +16,7 @@ import ( ) // createContainerPlatformSpecificSettings performs platform specific container create functionality -func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error { +func (daemon *Daemon) createContainerPlatformSpecificSettings(container *container.Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error { if err := daemon.Mount(container); err != nil { return err } @@ -27,7 +28,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Contain // Skip volumes for which we already have something mounted on that // destination because of a --volume-from. - if container.isDestinationMounted(destination) { + if container.IsDestinationMounted(destination) { continue } path, err := container.GetResourcePath(destination) @@ -61,12 +62,12 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Contain // never attempt to copy existing content in a container FS to a shared volume if v.DriverName() == volume.DefaultDriverName { - if err := container.copyImagePathContent(v, destination); err != nil { + if err := container.CopyImagePathContent(v, destination); err != nil { return err } } - container.addMountPointWithVolume(destination, v, true) + container.AddMountPointWithVolume(destination, v, true) } return nil } diff --git a/daemon/create_windows.go b/daemon/create_windows.go index a95e667b74..5affc91d2f 100644 --- a/daemon/create_windows.go +++ b/daemon/create_windows.go @@ -3,6 +3,7 @@ package daemon import ( "fmt" + "github.com/docker/docker/container" "github.com/docker/docker/image" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/runconfig" @@ -10,7 +11,7 @@ import ( ) // createContainerPlatformSpecificSettings performs platform specific container create functionality -func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error { +func (daemon *Daemon) createContainerPlatformSpecificSettings(container *container.Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error { for spec := range config.Volumes { mp, err := volume.ParseMountSpec(spec, hostConfig.VolumeDriver) @@ -25,7 +26,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Contain // Skip volumes for which we already have something mounted on that // destination because of a --volume-from. - if container.isDestinationMounted(mp.Destination) { + if container.IsDestinationMounted(mp.Destination) { continue } @@ -71,13 +72,13 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Contain // // // never attempt to copy existing content in a container FS to a shared volume // if v.DriverName() == volume.DefaultDriverName { - // if err := container.copyImagePathContent(v, mp.Destination); err != nil { + // if err := container.CopyImagePathContent(v, mp.Destination); err != nil { // return err // } // } // Add it to container.MountPoints - container.addMountPointWithVolume(mp.Destination, v, mp.RW) + container.AddMountPointWithVolume(mp.Destination, v, mp.RW) } return nil } diff --git a/daemon/daemon.go b/daemon/daemon.go index ccc19bd211..dbaa708179 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -23,6 +23,7 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/cliconfig" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/events" "github.com/docker/docker/daemon/exec" "github.com/docker/docker/daemon/execdriver" @@ -84,17 +85,17 @@ func (e ErrImageDoesNotExist) Error() string { } type contStore struct { - s map[string]*Container + s map[string]*container.Container sync.Mutex } -func (c *contStore) Add(id string, cont *Container) { +func (c *contStore) Add(id string, cont *container.Container) { c.Lock() c.s[id] = cont c.Unlock() } -func (c *contStore) Get(id string) *Container { +func (c *contStore) Get(id string) *container.Container { c.Lock() res := c.s[id] c.Unlock() @@ -107,7 +108,7 @@ func (c *contStore) Delete(id string) { c.Unlock() } -func (c *contStore) List() []*Container { +func (c *contStore) List() []*container.Container { containers := new(History) c.Lock() for _, cont := range c.s { @@ -155,7 +156,7 @@ type Daemon struct { // - A partial container ID prefix (e.g. short ID) of any length that is // unique enough to only return a single container object // If none of these searches succeed, an error is returned -func (daemon *Daemon) Get(prefixOrName string) (*Container, error) { +func (daemon *Daemon) Get(prefixOrName string) (*container.Container, error) { if containerByID := daemon.containers.Get(prefixOrName); containerByID != nil { // prefix is an exact match to a full container ID return containerByID, nil @@ -188,7 +189,7 @@ func (daemon *Daemon) Exists(id string) bool { // IsPaused returns a bool indicating if the specified container is paused. func (daemon *Daemon) IsPaused(id string) bool { c, _ := daemon.Get(id) - return c.State.isPaused() + return c.State.IsPaused() } func (daemon *Daemon) containerRoot(id string) string { @@ -197,10 +198,10 @@ func (daemon *Daemon) containerRoot(id string) string { // Load reads the contents of a container from disk // This is typically done at startup. -func (daemon *Daemon) load(id string) (*Container, error) { +func (daemon *Daemon) load(id string) (*container.Container, error) { container := daemon.newBaseContainer(id) - if err := container.fromDisk(); err != nil { + if err := container.FromDisk(); err != nil { return nil, err } @@ -212,7 +213,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 { +func (daemon *Daemon) Register(container *container.Container) error { if daemon.Exists(container.ID) { return fmt.Errorf("Container is already loaded") } @@ -238,7 +239,7 @@ func (daemon *Daemon) Register(container *Container) error { if container.IsRunning() { logrus.Debugf("killing old running container %s", container.ID) // Set exit code to 128 + SIGKILL (9) to properly represent unsuccessful exit - container.setStoppedLocking(&execdriver.ExitStatus{ExitCode: 137}) + container.SetStoppedLocking(&execdriver.ExitStatus{ExitCode: 137}) // use the current driver and ensure that the container is dead x.x cmd := &execdriver.Command{ CommonCommand: execdriver.CommonCommand{ @@ -247,10 +248,10 @@ func (daemon *Daemon) Register(container *Container) error { } daemon.execDriver.Terminate(cmd) - container.unmountIpcMounts(mount.Unmount) + container.UnmountIpcMounts(mount.Unmount) daemon.Unmount(container) - if err := container.toDiskLocking(); err != nil { + if err := container.ToDiskLocking(); err != nil { logrus.Errorf("Error saving stopped state to disk: %v", err) } } @@ -262,7 +263,7 @@ func (daemon *Daemon) Register(container *Container) error { return nil } -func (daemon *Daemon) ensureName(container *Container) error { +func (daemon *Daemon) ensureName(container *container.Container) error { if container.Name == "" { name, err := daemon.generateNewName(container.ID) if err != nil { @@ -270,7 +271,7 @@ func (daemon *Daemon) ensureName(container *Container) error { } container.Name = name - if err := container.toDiskLocking(); err != nil { + if err := container.ToDiskLocking(); err != nil { logrus.Errorf("Error saving container name to disk: %v", err) } } @@ -279,7 +280,7 @@ func (daemon *Daemon) ensureName(container *Container) error { func (daemon *Daemon) restore() error { type cr struct { - container *Container + container *container.Container registered bool } @@ -336,7 +337,7 @@ func (daemon *Daemon) restore() error { for _, c := range containers { group.Add(1) - go func(container *Container, registered bool) { + go func(container *container.Container, registered bool) { defer group.Done() if !registered { @@ -355,7 +356,7 @@ func (daemon *Daemon) restore() error { // check the restart policy on the containers and restart any container with // the restart policy of "always" - if daemon.configStore.AutoRestart && container.shouldRestart() { + if daemon.configStore.AutoRestart && container.ShouldRestart() { logrus.Debugf("Starting container %s", container.ID) if err := daemon.containerStart(container); err != nil { @@ -474,7 +475,7 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *stringutils.StrSlic return cmdSlice[0], cmdSlice[1:] } -func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID image.ID) (*Container, error) { +func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID image.ID) (*container.Container, error) { var ( id string err error @@ -493,7 +494,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID base.Path = entrypoint base.Args = args //FIXME: de-duplicate from config base.Config = config - base.hostConfig = &runconfig.HostConfig{} + base.HostConfig = &runconfig.HostConfig{} base.ImageID = imgID base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} base.Name = name @@ -516,7 +517,7 @@ func GetFullContainerName(name string) (string, error) { } // GetByName returns a container given a name. -func (daemon *Daemon) GetByName(name string) (*Container, error) { +func (daemon *Daemon) GetByName(name string) (*container.Container, error) { fullName, err := GetFullContainerName(name) if err != nil { return nil, err @@ -576,12 +577,12 @@ func (daemon *Daemon) GetLabels(id string) map[string]string { // children returns all child containers of the container with the // given name. The containers are returned as a map from the container // name to a pointer to Container. -func (daemon *Daemon) children(name string) (map[string]*Container, error) { +func (daemon *Daemon) children(name string) (map[string]*container.Container, error) { name, err := GetFullContainerName(name) if err != nil { return nil, err } - children := make(map[string]*Container) + children := make(map[string]*container.Container) err = daemon.containerGraphDB.Walk(name, func(p string, e *graphdb.Entity) error { c, err := daemon.Get(e.ID()) @@ -609,7 +610,7 @@ func (daemon *Daemon) parents(name string) ([]string, error) { return daemon.containerGraphDB.Parents(name) } -func (daemon *Daemon) registerLink(parent, child *Container, alias string) error { +func (daemon *Daemon) registerLink(parent, child *container.Container, alias string) error { fullName := filepath.Join(parent.Name, alias) if !daemon.containerGraphDB.Exists(fullName) { _, err := daemon.containerGraphDB.Set(fullName, child.ID) @@ -830,7 +831,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo d.ID = trustKey.PublicKey().KeyID() d.repository = daemonRepo - d.containers = &contStore{s: make(map[string]*Container)} + d.containers = &contStore{s: make(map[string]*container.Container)} d.execCommands = exec.NewStore() d.tagStore = tagStore d.distributionPool = distributionPool @@ -861,9 +862,9 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo return d, nil } -func (daemon *Daemon) shutdownContainer(c *Container) error { +func (daemon *Daemon) shutdownContainer(c *container.Container) error { // TODO(windows): Handle docker restart with paused containers - if c.isPaused() { + if c.IsPaused() { // To terminate a process in freezer cgroup, we should send // SIGTERM to this process then unfreeze it, and the process will // force to terminate immediately. @@ -906,20 +907,20 @@ func (daemon *Daemon) Shutdown() error { if daemon.containers != nil { group := sync.WaitGroup{} logrus.Debug("starting clean shutdown of all containers...") - for _, container := range daemon.List() { - if !container.IsRunning() { + for _, cont := range daemon.List() { + if !cont.IsRunning() { continue } - logrus.Debugf("stopping %s", container.ID) + logrus.Debugf("stopping %s", cont.ID) group.Add(1) - go func(c *Container) { + go func(c *container.Container) { defer group.Done() if err := daemon.shutdownContainer(c); err != nil { logrus.Errorf("Stop container error: %v", err) return } logrus.Debugf("container stopped %s", c.ID) - }(container) + }(cont) } group.Wait() } @@ -948,9 +949,9 @@ func (daemon *Daemon) Shutdown() error { return nil } -// Mount sets container.basefs +// Mount sets container.BaseFS // (is it not set coming in? why is it unset?) -func (daemon *Daemon) Mount(container *Container) error { +func (daemon *Daemon) Mount(container *container.Container) error { var layerID layer.ChainID if container.ImageID != "" { img, err := daemon.imageStore.Get(container.ImageID) @@ -959,7 +960,7 @@ func (daemon *Daemon) Mount(container *Container) error { } layerID = img.RootFS.ChainID() } - rwlayer, err := daemon.layerStore.Mount(container.ID, layerID, container.getMountLabel(), daemon.setupInitLayer) + rwlayer, err := daemon.layerStore.Mount(container.ID, layerID, container.GetMountLabel(), daemon.setupInitLayer) if err != nil { return err } @@ -969,56 +970,56 @@ func (daemon *Daemon) Mount(container *Container) error { } logrus.Debugf("container mounted via layerStore: %v", dir) - if container.basefs != dir { + if container.BaseFS != dir { // The mount path reported by the graph driver should always be trusted on Windows, since the // volume path for a given mounted layer may change over time. This should only be an error // on non-Windows operating systems. - if container.basefs != "" && runtime.GOOS != "windows" { + if container.BaseFS != "" && runtime.GOOS != "windows" { daemon.Unmount(container) return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')", - daemon.driver, container.ID, container.basefs, dir) + daemon.driver, container.ID, container.BaseFS, dir) } } - container.basefs = dir // TODO: combine these fields - container.rwlayer = rwlayer + container.BaseFS = dir // TODO: combine these fields + container.RWLayer = rwlayer return nil } // Unmount unsets the container base filesystem -func (daemon *Daemon) Unmount(container *Container) { +func (daemon *Daemon) Unmount(container *container.Container) { if err := daemon.layerStore.Unmount(container.ID); err != nil { logrus.Errorf("Error unmounting container %s: %s", container.ID, err) } } // 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) { +func (daemon *Daemon) Run(c *container.Container, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (execdriver.ExitStatus, error) { hooks := execdriver.Hooks{ Start: startCallback, } hooks.PreStart = append(hooks.PreStart, func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error { return daemon.setNetworkNamespaceKey(c.ID, pid) }) - return daemon.execDriver.Run(c.command, pipes, hooks) + return daemon.execDriver.Run(c.Command, pipes, hooks) } -func (daemon *Daemon) kill(c *Container, sig int) error { - return daemon.execDriver.Kill(c.command, sig) +func (daemon *Daemon) kill(c *container.Container, sig int) error { + return daemon.execDriver.Kill(c.Command, sig) } -func (daemon *Daemon) stats(c *Container) (*execdriver.ResourceStats, error) { +func (daemon *Daemon) stats(c *container.Container) (*execdriver.ResourceStats, error) { return daemon.execDriver.Stats(c.ID) } -func (daemon *Daemon) subscribeToContainerStats(c *Container) chan interface{} { +func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} { return daemon.statsCollector.collect(c) } -func (daemon *Daemon) unsubscribeToContainerStats(c *Container, ch chan interface{}) { +func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) { daemon.statsCollector.unsubscribe(c, ch) } -func (daemon *Daemon) changes(container *Container) ([]archive.Change, error) { +func (daemon *Daemon) changes(container *container.Container) ([]archive.Change, error) { return daemon.layerStore.Changes(container.ID) } @@ -1347,7 +1348,7 @@ func tempDir(rootDir string, rootUID, rootGID int) (string, error) { return tmpDir, idtools.MkdirAllAs(tmpDir, 0700, rootUID, rootGID) } -func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.HostConfig) error { +func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *runconfig.HostConfig) error { container.Lock() if err := parseSecurityOpt(container, hostConfig); err != nil { container.Unlock() @@ -1369,8 +1370,8 @@ func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig. return err } - container.hostConfig = hostConfig - container.toDisk() + container.HostConfig = hostConfig + container.ToDisk() return nil } @@ -1466,7 +1467,7 @@ func (daemon *Daemon) IsShuttingDown() bool { } // GetContainerStats collects all the stats published by a container -func (daemon *Daemon) GetContainerStats(container *Container) (*execdriver.ResourceStats, error) { +func (daemon *Daemon) GetContainerStats(container *container.Container) (*execdriver.ResourceStats, error) { stats, err := daemon.stats(container) if err != nil { return nil, err @@ -1482,7 +1483,7 @@ func (daemon *Daemon) GetContainerStats(container *Container) (*execdriver.Resou return stats, nil } -func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) { +func (daemon *Daemon) getNetworkStats(c *container.Container) ([]*libcontainer.NetworkInterface, error) { var list []*libcontainer.NetworkInterface sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID) @@ -1505,8 +1506,8 @@ func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInte // newBaseContainer creates a new container with its initial // configuration based on the root storage from the daemon. -func (daemon *Daemon) newBaseContainer(id string) *Container { - return newBaseContainer(id, daemon.containerRoot(id)) +func (daemon *Daemon) newBaseContainer(id string) *container.Container { + return container.NewBaseContainer(id, daemon.containerRoot(id)) } func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface { @@ -1521,3 +1522,10 @@ func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *lib n.TxDropped = stats.TxDropped return n } + +func validateID(id string) error { + if id == "" { + return derr.ErrorCodeEmptyID + } + return nil +} diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go index 68334b2202..de59c224fb 100644 --- a/daemon/daemon_test.go +++ b/daemon/daemon_test.go @@ -1,13 +1,17 @@ package daemon import ( + "io/ioutil" "os" "path" + "path/filepath" "testing" + "github.com/docker/docker/container" "github.com/docker/docker/pkg/graphdb" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/runconfig" + "github.com/docker/docker/volume" volumedrivers "github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/local" "github.com/docker/docker/volume/store" @@ -18,43 +22,43 @@ import ( // func TestGet(t *testing.T) { - c1 := &Container{ - CommonContainer: CommonContainer{ + c1 := &container.Container{ + CommonContainer: container.CommonContainer{ ID: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57", Name: "tender_bardeen", }, } - c2 := &Container{ - CommonContainer: CommonContainer{ + c2 := &container.Container{ + CommonContainer: container.CommonContainer{ ID: "3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de", Name: "drunk_hawking", }, } - c3 := &Container{ - CommonContainer: CommonContainer{ + c3 := &container.Container{ + CommonContainer: container.CommonContainer{ ID: "3cdbd1aa394fd68559fd1441d6eff2abfafdcba06e72d2febdba229008b0bf57", Name: "3cdbd1aa", }, } - c4 := &Container{ - CommonContainer: CommonContainer{ + c4 := &container.Container{ + CommonContainer: container.CommonContainer{ ID: "75fb0b800922abdbef2d27e60abcdfaf7fb0698b2a96d22d3354da361a6ff4a5", Name: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57", }, } - c5 := &Container{ - CommonContainer: CommonContainer{ + c5 := &container.Container{ + CommonContainer: container.CommonContainer{ ID: "d22d69a2b8960bf7fafdcba06e72d2febdba960bf7fafdcba06e72d2f9008b060b", Name: "d22d69a2b896", }, } store := &contStore{ - s: map[string]*Container{ + s: map[string]*container.Container{ c1.ID: c1, c2.ID: c2, c3.ID: c3, @@ -136,7 +140,7 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) { } func TestParseSecurityOpt(t *testing.T) { - container := &Container{} + container := &container.Container{} config := &runconfig.HostConfig{} // test apparmor @@ -190,3 +194,109 @@ func TestNetworkOptions(t *testing.T) { t.Fatalf("Expected networkOptions error, got nil") } } + +func TestGetFullName(t *testing.T) { + name, err := GetFullContainerName("testing") + if err != nil { + t.Fatal(err) + } + if name != "/testing" { + t.Fatalf("Expected /testing got %s", name) + } + if _, err := GetFullContainerName(""); err == nil { + t.Fatal("Error should not be nil") + } +} + +func TestValidContainerNames(t *testing.T) { + invalidNames := []string{"-rm", "&sdfsfd", "safd%sd"} + validNames := []string{"word-word", "word_word", "1weoid"} + + for _, name := range invalidNames { + if validContainerNamePattern.MatchString(name) { + t.Fatalf("%q is not a valid container name and was returned as valid.", name) + } + } + + for _, name := range validNames { + if !validContainerNamePattern.MatchString(name) { + t.Fatalf("%q is a valid container name and was returned as invalid.", name) + } + } +} + +func TestContainerInitDNS(t *testing.T) { + tmp, err := ioutil.TempDir("", "docker-container-test-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmp) + + containerID := "d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e" + containerPath := filepath.Join(tmp, containerID) + if err := os.MkdirAll(containerPath, 0755); err != nil { + t.Fatal(err) + } + + config := `{"State":{"Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":2464,"ExitCode":0, +"Error":"","StartedAt":"2015-05-26T16:48:53.869308965Z","FinishedAt":"0001-01-01T00:00:00Z"}, +"ID":"d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e","Created":"2015-05-26T16:48:53.7987917Z","Path":"top", +"Args":[],"Config":{"Hostname":"d59df5276e7b","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"Cpuset":"", +"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":true,"OpenStdin":true, +"StdinOnce":false,"Env":null,"Cmd":["top"],"Image":"ubuntu:latest","Volumes":null,"WorkingDir":"","Entrypoint":null, +"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":{}},"Image":"07f8e8c5e66084bef8f848877857537ffe1c47edd01a93af27e7161672ad0e95", +"NetworkSettings":{"IPAddress":"172.17.0.1","IPPrefixLen":16,"MacAddress":"02:42:ac:11:00:01","LinkLocalIPv6Address":"fe80::42:acff:fe11:1", +"LinkLocalIPv6PrefixLen":64,"GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"Gateway":"172.17.42.1","IPv6Gateway":"","Bridge":"docker0","Ports":{}}, +"ResolvConfPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/resolv.conf", +"HostnamePath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hostname", +"HostsPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hosts", +"LogPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e-json.log", +"Name":"/ubuntu","Driver":"aufs","MountLabel":"","ProcessLabel":"","AppArmorProfile":"","RestartCount":0, +"UpdateDns":false,"Volumes":{},"VolumesRW":{},"AppliedVolumesFrom":null}` + + // Container struct only used to retrieve path to config file + container := &container.Container{CommonContainer: container.CommonContainer{Root: containerPath}} + configPath, err := container.ConfigPath() + if err != nil { + t.Fatal(err) + } + if err = ioutil.WriteFile(configPath, []byte(config), 0644); err != nil { + t.Fatal(err) + } + + hostConfig := `{"Binds":[],"ContainerIDFile":"","Memory":0,"MemorySwap":0,"CpuShares":0,"CpusetCpus":"", +"Privileged":false,"PortBindings":{},"Links":null,"PublishAllPorts":false,"Dns":null,"DnsOptions":null,"DnsSearch":null,"ExtraHosts":null,"VolumesFrom":null, +"Devices":[],"NetworkMode":"bridge","IpcMode":"","PidMode":"","CapAdd":null,"CapDrop":null,"RestartPolicy":{"Name":"no","MaximumRetryCount":0}, +"SecurityOpt":null,"ReadonlyRootfs":false,"Ulimits":null,"LogConfig":{"Type":"","Config":null},"CgroupParent":""}` + + hostConfigPath, err := container.HostConfigPath() + if err != nil { + t.Fatal(err) + } + if err = ioutil.WriteFile(hostConfigPath, []byte(hostConfig), 0644); err != nil { + t.Fatal(err) + } + + daemon, err := initDaemonWithVolumeStore(tmp) + if err != nil { + t.Fatal(err) + } + defer volumedrivers.Unregister(volume.DefaultDriverName) + + c, err := daemon.load(containerID) + if err != nil { + t.Fatal(err) + } + + if c.HostConfig.DNS == nil { + t.Fatal("Expected container DNS to not be nil") + } + + if c.HostConfig.DNSSearch == nil { + t.Fatal("Expected container DNSSearch to not be nil") + } + + if c.HostConfig.DNSOptions == nil { + t.Fatal("Expected container DNSOptions to not be nil") + } +} diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index ce41bd03cf..202cdb22db 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -12,6 +12,7 @@ import ( "syscall" "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/graphdriver" derr "github.com/docker/docker/errors" "github.com/docker/docker/image" @@ -57,7 +58,7 @@ func getBlkioWeightDevices(config *runconfig.HostConfig) ([]*blkiodev.WeightDevi return BlkioWeightDevices, nil } -func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error { +func parseSecurityOpt(container *container.Container, config *runconfig.HostConfig) error { var ( labelOpts []string err error @@ -128,7 +129,7 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *runconfig.HostConfig, a hostConfig.MemorySwap = hostConfig.Memory * 2 } if hostConfig.ShmSize == nil { - shmSize := DefaultSHMSize + shmSize := container.DefaultSHMSize hostConfig.ShmSize = &shmSize } var err error @@ -575,7 +576,7 @@ func setupInitLayer(initLayer string, rootUID, rootGID int) error { } // registerLinks writes the links to a file. -func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig.HostConfig) error { +func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *runconfig.HostConfig) error { if hostConfig == nil || hostConfig.Links == nil { return nil } @@ -590,14 +591,14 @@ func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig. //An error from daemon.Get() means this name could not be found return fmt.Errorf("Could not get container for %s", name) } - for child.hostConfig.NetworkMode.IsContainer() { - parts := strings.SplitN(string(child.hostConfig.NetworkMode), ":", 2) + for child.HostConfig.NetworkMode.IsContainer() { + parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2) child, err = daemon.Get(parts[1]) if err != nil { return fmt.Errorf("Could not get container for %s", parts[1]) } } - if child.hostConfig.NetworkMode.IsHost() { + if child.HostConfig.NetworkMode.IsHost() { return runconfig.ErrConflictHostNetworkAndLinks } if err := daemon.registerLink(container, child, alias); err != nil { @@ -608,7 +609,7 @@ func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig. // After we load all the links into the daemon // set them to nil on the hostconfig hostConfig.Links = nil - if err := container.writeHostConfig(); err != nil { + if err := container.WriteHostConfig(); err != nil { return err } @@ -617,13 +618,13 @@ func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig. // conditionalMountOnStart is a platform specific helper function during the // container start to call mount. -func (daemon *Daemon) conditionalMountOnStart(container *Container) error { +func (daemon *Daemon) conditionalMountOnStart(container *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) { +func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) { daemon.Unmount(container) } diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index d4a894d5fa..c2de6c8e99 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -10,6 +10,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/reference" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/dockerversion" "github.com/docker/docker/image" @@ -34,7 +35,7 @@ func getBlkioWeightDevices(config *runconfig.HostConfig) ([]*blkiodev.WeightDevi return nil, nil } -func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error { +func parseSecurityOpt(container *container.Container, config *runconfig.HostConfig) error { return nil } @@ -115,7 +116,7 @@ func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkC // registerLinks sets up links between containers and writes the // configuration out for persistence. As of Windows TP4, links are not supported. -func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig.HostConfig) error { +func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *runconfig.HostConfig) error { return nil } @@ -125,9 +126,9 @@ func (daemon *Daemon) cleanupMounts() error { // conditionalMountOnStart is a platform specific helper function during the // container start to call mount. -func (daemon *Daemon) conditionalMountOnStart(container *Container) error { +func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { // We do not mount if a Hyper-V container - if !container.hostConfig.Isolation.IsHyperV() { + if !container.HostConfig.Isolation.IsHyperV() { if err := daemon.Mount(container); err != nil { return err } @@ -137,9 +138,9 @@ func (daemon *Daemon) conditionalMountOnStart(container *Container) error { // conditionalUnmountOnCleanup is a platform specific helper function called // during the cleanup of a container to unmount. -func (daemon *Daemon) conditionalUnmountOnCleanup(container *Container) { +func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) { // We do not unmount if a Hyper-V container - if !container.hostConfig.Isolation.IsHyperV() { + if !container.HostConfig.Isolation.IsHyperV() { daemon.Unmount(container) } } diff --git a/daemon/daemonbuilder/builder.go b/daemon/daemonbuilder/builder.go index 4a94d9489e..d36036ea05 100644 --- a/daemon/daemonbuilder/builder.go +++ b/daemon/daemonbuilder/builder.go @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/builder" "github.com/docker/docker/cliconfig" + "github.com/docker/docker/container" "github.com/docker/docker/daemon" "github.com/docker/docker/image" "github.com/docker/docker/pkg/archive" @@ -80,12 +81,12 @@ func (d Docker) Pull(name string) (*image.Image, error) { } // Container looks up a Docker container referenced by `id`. -func (d Docker) Container(id string) (*daemon.Container, error) { +func (d Docker) Container(id string) (*container.Container, error) { return d.Daemon.Get(id) } // Create creates a new Docker container and returns potential warnings -func (d Docker) Create(cfg *runconfig.Config, hostCfg *runconfig.HostConfig) (*daemon.Container, []string, error) { +func (d Docker) Create(cfg *runconfig.Config, hostCfg *runconfig.HostConfig) (*container.Container, []string, error) { ccr, err := d.Daemon.ContainerCreate(&daemon.ContainerCreateConfig{ Name: "", Config: cfg, @@ -129,7 +130,7 @@ func (d Docker) Release(sessionID string, activeImages []string) { // specified by a container object. // TODO: make sure callers don't unnecessarily convert destPath with filepath.FromSlash (Copy does it already). // Copy should take in abstract paths (with slashes) and the implementation should convert it to OS-specific paths. -func (d Docker) Copy(c *daemon.Container, destPath string, src builder.FileInfo, decompress bool) error { +func (d Docker) Copy(c *container.Container, destPath string, src builder.FileInfo, decompress bool) error { srcPath := src.Path() destExists := true rootUID, rootGID := d.Daemon.GetRemappedUIDGID() @@ -212,23 +213,23 @@ func (d Docker) GetCachedImage(imgID string, cfg *runconfig.Config) (string, err } // Kill stops the container execution abruptly. -func (d Docker) Kill(container *daemon.Container) error { +func (d Docker) Kill(container *container.Container) error { return d.Daemon.Kill(container) } // Mount mounts the root filesystem for the container. -func (d Docker) Mount(c *daemon.Container) error { +func (d Docker) Mount(c *container.Container) error { return d.Daemon.Mount(c) } // Unmount unmounts the root filesystem for the container. -func (d Docker) Unmount(c *daemon.Container) error { +func (d Docker) Unmount(c *container.Container) error { d.Daemon.Unmount(c) return nil } // Start starts a container -func (d Docker) Start(c *daemon.Container) error { +func (d Docker) Start(c *container.Container) error { return d.Daemon.Start(c) } diff --git a/daemon/delete.go b/daemon/delete.go index a2e46acd4b..4ff5627ab7 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -5,6 +5,7 @@ import ( "path" "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" "github.com/docker/docker/layer" volumestore "github.com/docker/docker/volume/store" @@ -26,14 +27,14 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error } // Container state RemovalInProgress should be used to avoid races. - if err = container.setRemovalInProgress(); err != nil { + if err = container.SetRemovalInProgress(); err != nil { if err == derr.ErrorCodeAlreadyRemoving { // do not fail when the removal is in progress started by other request. return nil } return derr.ErrorCodeRmState.WithArgs(err) } - defer container.resetRemovalInProgress() + defer container.ResetRemovalInProgress() // check if container wasn't deregistered by previous rm since Get if c := daemon.containers.Get(container.ID); c == nil { @@ -87,7 +88,7 @@ func (daemon *Daemon) rmLink(name string) error { // cleanupContainer unregisters a container from the daemon, stops stats // collection and cleanly removes contents and metadata from the filesystem. -func (daemon *Daemon) cleanupContainer(container *Container, forceRemove bool) (err error) { +func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemove bool) (err error) { if container.IsRunning() { if !forceRemove { return derr.ErrorCodeRmRunning @@ -106,12 +107,12 @@ func (daemon *Daemon) cleanupContainer(container *Container, forceRemove bool) ( } // Mark container dead. We don't want anybody to be restarting it. - container.setDead() + container.SetDead() // Save container state to disk. So that if error happens before // container meta file got removed from disk, then a restart of // docker should not make a dead container alive. - if err := container.toDiskLocking(); err != nil { + if err := container.ToDiskLocking(); err != nil { logrus.Errorf("Error saving dying container to disk: %v", err) } @@ -129,7 +130,7 @@ func (daemon *Daemon) cleanupContainer(container *Container, forceRemove bool) ( } }() - if err = os.RemoveAll(container.root); err != nil { + if err = os.RemoveAll(container.Root); err != nil { return derr.ErrorCodeRmFS.WithArgs(container.ID, err) } diff --git a/daemon/delete_test.go b/daemon/delete_test.go index d2fd765a3e..dcfd81af28 100644 --- a/daemon/delete_test.go +++ b/daemon/delete_test.go @@ -5,6 +5,7 @@ import ( "os" "testing" + "github.com/docker/docker/container" "github.com/docker/docker/runconfig" ) @@ -18,19 +19,19 @@ func TestContainerDoubleDelete(t *testing.T) { repository: tmp, root: tmp, } - daemon.containers = &contStore{s: make(map[string]*Container)} + daemon.containers = &contStore{s: make(map[string]*container.Container)} - container := &Container{ - CommonContainer: CommonContainer{ + container := &container.Container{ + CommonContainer: container.CommonContainer{ ID: "test", - State: NewState(), + State: container.NewState(), Config: &runconfig.Config{}, }, } daemon.containers.Add(container.ID, container) // Mark the container as having a delete in progress - if err := container.setRemovalInProgress(); err != nil { + if err := container.SetRemovalInProgress(); err != nil { t.Fatal(err) } diff --git a/daemon/events.go b/daemon/events.go index 85ac25c0e6..969e1d3964 100644 --- a/daemon/events.go +++ b/daemon/events.go @@ -1,7 +1,11 @@ package daemon +import ( + "github.com/docker/docker/container" +) + // LogContainerEvent generates an event related to a container. -func (daemon *Daemon) LogContainerEvent(container *Container, action string) { +func (daemon *Daemon) LogContainerEvent(container *container.Container, action string) { daemon.EventsService.Log( action, container.ID, diff --git a/daemon/exec.go b/daemon/exec.go index 01a3435826..c3f2ef4c42 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -6,6 +6,7 @@ import ( "time" "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/exec" "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" @@ -15,15 +16,15 @@ import ( "github.com/docker/docker/runconfig" ) -func (d *Daemon) registerExecCommand(container *Container, config *exec.Config) { +func (d *Daemon) registerExecCommand(container *container.Container, config *exec.Config) { // Storing execs in container in order to kill them gracefully whenever the container is stopped or removed. - container.execCommands.Add(config.ID, config) + container.ExecCommands.Add(config.ID, config) // Storing execs in daemon for easy access via remote API. d.execCommands.Add(config.ID, config) } // ExecExists looks up the exec instance and returns a bool if it exists or not. -// It will also return the error produced by `getExecConfig` +// It will also return the error produced by `getConfig` func (d *Daemon) ExecExists(name string) (bool, error) { if _, err := d.getExecConfig(name); err != nil { return false, err @@ -47,7 +48,7 @@ func (d *Daemon) getExecConfig(name string) (*exec.Config, error) { if !container.IsRunning() { return nil, derr.ErrorCodeContainerNotRunning.WithArgs(container.ID, container.State.String()) } - if container.isPaused() { + if container.IsPaused() { return nil, derr.ErrorCodeExecPaused.WithArgs(container.ID) } return ec, nil @@ -57,12 +58,12 @@ func (d *Daemon) getExecConfig(name string) (*exec.Config, error) { return nil, derr.ErrorCodeNoExecID.WithArgs(name) } -func (d *Daemon) unregisterExecCommand(container *Container, execConfig *exec.Config) { - container.execCommands.Delete(execConfig.ID) +func (d *Daemon) unregisterExecCommand(container *container.Container, execConfig *exec.Config) { + container.ExecCommands.Delete(execConfig.ID) d.execCommands.Delete(execConfig.ID) } -func (d *Daemon) getActiveContainer(name string) (*Container, error) { +func (d *Daemon) getActiveContainer(name string) (*container.Container, error) { container, err := d.Get(name) if err != nil { return nil, err @@ -71,7 +72,7 @@ func (d *Daemon) getActiveContainer(name string) (*Container, error) { if !container.IsRunning() { return nil, derr.ErrorCodeNotRunning.WithArgs(name) } - if container.isPaused() { + if container.IsPaused() { return nil, derr.ErrorCodeExecPaused.WithArgs(name) } return container, nil @@ -131,9 +132,9 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io. ec.Running = true ec.Unlock() - container := d.containers.Get(ec.ContainerID) - logrus.Debugf("starting exec command %s in container %s", ec.ID, container.ID) - d.LogContainerEvent(container, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " ")) + c := d.containers.Get(ec.ContainerID) + logrus.Debugf("starting exec command %s in container %s", ec.ID, c.ID) + d.LogContainerEvent(c, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " ")) if ec.OpenStdin { r, w := io.Pipe() @@ -157,8 +158,7 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io. ec.NewNopInputPipe() } - attachErr := attach(ec.StreamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr) - + attachErr := container.AttachStreams(ec.StreamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr) execErr := make(chan error) // Note, the ExecConfig data will be removed when the container @@ -166,7 +166,7 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io. // the exitStatus) even after the cmd is done running. go func() { - execErr <- d.containerExec(container, ec) + execErr <- d.containerExec(c, ec) }() select { @@ -184,19 +184,19 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io. } // Maybe the container stopped while we were trying to exec - if !container.IsRunning() { + if !c.IsRunning() { return derr.ErrorCodeExecContainerStopped } - return derr.ErrorCodeExecCantRun.WithArgs(ec.ID, container.ID, err) + return derr.ErrorCodeExecCantRun.WithArgs(ec.ID, c.ID, err) } } // Exec calls the underlying exec driver to run -func (d *Daemon) Exec(c *Container, execConfig *exec.Config, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) { +func (d *Daemon) Exec(c *container.Container, execConfig *exec.Config, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) { hooks := execdriver.Hooks{ Start: startCallback, } - exitStatus, err := d.execDriver.Exec(c.command, execConfig.ProcessConfig, pipes, hooks) + exitStatus, err := d.execDriver.Exec(c.Command, execConfig.ProcessConfig, pipes, hooks) // On err, make sure we don't leave ExitCode at zero if err != nil && exitStatus == 0 { @@ -238,14 +238,14 @@ func (d *Daemon) execCommandGC() { func (d *Daemon) containerExecIds() map[string]struct{} { ids := map[string]struct{}{} for _, c := range d.containers.List() { - for _, id := range c.execCommands.List() { + for _, id := range c.ExecCommands.List() { ids[id] = struct{}{} } } return ids } -func (d *Daemon) containerExec(container *Container, ec *exec.Config) error { +func (d *Daemon) containerExec(container *container.Container, ec *exec.Config) error { container.Lock() defer container.Unlock() @@ -268,7 +268,7 @@ func (d *Daemon) containerExec(container *Container, ec *exec.Config) error { return ec.Wait(cErr) } -func (d *Daemon) monitorExec(container *Container, execConfig *exec.Config, callback execdriver.DriverCallback) error { +func (d *Daemon) monitorExec(container *container.Container, execConfig *exec.Config, callback execdriver.DriverCallback) error { pipes := execdriver.NewPipes(execConfig.Stdin(), execConfig.Stdout(), execConfig.Stderr(), execConfig.OpenStdin) exitCode, err := d.Exec(container, execConfig, pipes, callback) if err != nil { @@ -287,6 +287,6 @@ func (d *Daemon) monitorExec(container *Container, execConfig *exec.Config, call } // 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) + container.ExecCommands.Delete(execConfig.ID) return err } diff --git a/daemon/exec_unix.go b/daemon/exec_unix.go index 985112fe9d..4ac49927e9 100644 --- a/daemon/exec_unix.go +++ b/daemon/exec_unix.go @@ -3,13 +3,14 @@ package daemon import ( + "github.com/docker/docker/container" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/runconfig" ) // setPlatformSpecificExecProcessConfig sets platform-specific fields in the // ProcessConfig structure. -func setPlatformSpecificExecProcessConfig(config *runconfig.ExecConfig, container *Container, pc *execdriver.ProcessConfig) { +func setPlatformSpecificExecProcessConfig(config *runconfig.ExecConfig, container *container.Container, pc *execdriver.ProcessConfig) { user := config.User if len(user) == 0 { user = container.Config.User diff --git a/daemon/exec_windows.go b/daemon/exec_windows.go index bdab49b194..d6e887acad 100644 --- a/daemon/exec_windows.go +++ b/daemon/exec_windows.go @@ -1,11 +1,12 @@ package daemon import ( + "github.com/docker/docker/container" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/runconfig" ) // setPlatformSpecificExecProcessConfig sets platform-specific fields in the // ProcessConfig structure. This is a no-op on Windows -func setPlatformSpecificExecProcessConfig(config *runconfig.ExecConfig, container *Container, pc *execdriver.ProcessConfig) { +func setPlatformSpecificExecProcessConfig(config *runconfig.ExecConfig, container *container.Container, pc *execdriver.ProcessConfig) { } diff --git a/daemon/export.go b/daemon/export.go index 0a246ed5af..4446caf0b1 100644 --- a/daemon/export.go +++ b/daemon/export.go @@ -3,6 +3,7 @@ package daemon import ( "io" + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/ioutils" @@ -29,13 +30,13 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error { return nil } -func (daemon *Daemon) containerExport(container *Container) (archive.Archive, error) { +func (daemon *Daemon) containerExport(container *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{ + archive, err := archive.TarWithOptions(container.BaseFS, &archive.TarOptions{ Compression: archive.Uncompressed, UIDMaps: uidMaps, GIDMaps: gidMaps, diff --git a/daemon/history.go b/daemon/history.go index 3fc10da036..451f7b17e6 100644 --- a/daemon/history.go +++ b/daemon/history.go @@ -2,11 +2,13 @@ package daemon import ( "sort" + + "github.com/docker/docker/container" ) // History is a convenience type for storing a list of containers, // ordered by creation date. -type History []*Container +type History []*container.Container func (history *History) Len() int { return len(*history) @@ -23,7 +25,7 @@ func (history *History) Swap(i, j int) { } // Add the given container to history. -func (history *History) Add(container *Container) { +func (history *History) Add(container *container.Container) { *history = append(*history, container) } diff --git a/daemon/image_delete.go b/daemon/image_delete.go index e2e8dd1513..e2503c01d6 100644 --- a/daemon/image_delete.go +++ b/daemon/image_delete.go @@ -6,6 +6,7 @@ import ( "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" "github.com/docker/docker/image" "github.com/docker/docker/pkg/stringid" @@ -133,7 +134,7 @@ func isImageIDPrefix(imageID, possiblePrefix string) bool { // getContainerUsingImage returns a container that was created using the given // imageID. Returns nil if there is no such container. -func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *Container { +func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Container { for _, container := range daemon.List() { if container.ImageID == imageID { return container diff --git a/daemon/inspect.go b/daemon/inspect.go index dc27d7abb4..004a4ad5f8 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -6,6 +6,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions/v1p20" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/exec" "github.com/docker/docker/daemon/network" "github.com/docker/docker/layer" @@ -85,7 +86,7 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er MacAddress: container.Config.MacAddress, NetworkDisabled: container.Config.NetworkDisabled, ExposedPorts: container.Config.ExposedPorts, - VolumeDriver: container.hostConfig.VolumeDriver, + VolumeDriver: container.HostConfig.VolumeDriver, } networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings) @@ -97,9 +98,9 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er }, nil } -func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.ContainerJSONBase, error) { +func (daemon *Daemon) getInspectData(container *container.Container, size bool) (*types.ContainerJSONBase, error) { // make a copy to play with - hostConfig := *container.hostConfig + hostConfig := *container.HostConfig if children, err := daemon.children(container.Name); err == nil { for linkAlias, child := range children { @@ -143,7 +144,7 @@ func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.Co Driver: container.Driver, MountLabel: container.MountLabel, ProcessLabel: container.ProcessLabel, - ExecIDs: container.getExecIDs(), + ExecIDs: container.GetExecIDs(), HostConfig: &hostConfig, } diff --git a/daemon/inspect_unix.go b/daemon/inspect_unix.go index 91f1cb75f2..b72e09b338 100644 --- a/daemon/inspect_unix.go +++ b/daemon/inspect_unix.go @@ -5,10 +5,11 @@ package daemon import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions/v1p19" + "github.com/docker/docker/container" ) // This sets platform-specific fields -func setPlatformSpecificContainerFields(container *Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase { +func setPlatformSpecificContainerFields(container *container.Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase { contJSONBase.AppArmorProfile = container.AppArmorProfile contJSONBase.ResolvConfPath = container.ResolvConfPath contJSONBase.HostnamePath = container.HostnamePath @@ -44,11 +45,11 @@ func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON, MacAddress: container.Config.MacAddress, NetworkDisabled: container.Config.NetworkDisabled, ExposedPorts: container.Config.ExposedPorts, - VolumeDriver: container.hostConfig.VolumeDriver, - Memory: container.hostConfig.Memory, - MemorySwap: container.hostConfig.MemorySwap, - CPUShares: container.hostConfig.CPUShares, - CPUSet: container.hostConfig.CpusetCpus, + VolumeDriver: container.HostConfig.VolumeDriver, + Memory: container.HostConfig.Memory, + MemorySwap: container.HostConfig.MemorySwap, + CPUShares: container.HostConfig.CPUShares, + CPUSet: container.HostConfig.CpusetCpus, } networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings) @@ -61,7 +62,7 @@ func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON, }, nil } -func addMountPoints(container *Container) []types.MountPoint { +func addMountPoints(container *container.Container) []types.MountPoint { mountPoints := make([]types.MountPoint, 0, len(container.MountPoints)) for _, m := range container.MountPoints { mountPoints = append(mountPoints, types.MountPoint{ diff --git a/daemon/inspect_windows.go b/daemon/inspect_windows.go index f571619ac7..330bc901dc 100644 --- a/daemon/inspect_windows.go +++ b/daemon/inspect_windows.go @@ -1,13 +1,16 @@ package daemon -import "github.com/docker/docker/api/types" +import ( + "github.com/docker/docker/api/types" + "github.com/docker/docker/container" +) // This sets platform-specific fields -func setPlatformSpecificContainerFields(container *Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase { +func setPlatformSpecificContainerFields(container *container.Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase { return contJSONBase } -func addMountPoints(container *Container) []types.MountPoint { +func addMountPoints(container *container.Container) []types.MountPoint { mountPoints := make([]types.MountPoint, 0, len(container.MountPoints)) for _, m := range container.MountPoints { mountPoints = append(mountPoints, types.MountPoint{ diff --git a/daemon/kill.go b/daemon/kill.go index 6ed3a82fee..2bead8ba1e 100644 --- a/daemon/kill.go +++ b/daemon/kill.go @@ -7,6 +7,7 @@ import ( "time" "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" "github.com/docker/docker/pkg/signal" ) @@ -37,7 +38,7 @@ func (daemon *Daemon) ContainerKill(name string, sig uint64) error { // 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 { +func (daemon *Daemon) killWithSignal(container *container.Container, sig int) error { logrus.Debugf("Sending %d to %s", sig, container.ID) container.Lock() defer container.Unlock() @@ -69,7 +70,7 @@ func (daemon *Daemon) killWithSignal(container *Container, sig int) error { } // Kill forcefully terminates a container. -func (daemon *Daemon) Kill(container *Container) error { +func (daemon *Daemon) Kill(container *container.Container) error { if !container.IsRunning() { return derr.ErrorCodeNotRunning.WithArgs(container.ID) } @@ -105,7 +106,7 @@ func (daemon *Daemon) Kill(container *Container) error { } // killPossibleDeadProcess is a wrapper aroung killSig() suppressing "no such process" error. -func (daemon *Daemon) killPossiblyDeadProcess(container *Container, sig int) error { +func (daemon *Daemon) killPossiblyDeadProcess(container *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) diff --git a/daemon/list.go b/daemon/list.go index f289f2a66a..356515b99a 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -8,6 +8,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" + "github.com/docker/docker/container" "github.com/docker/docker/image" "github.com/docker/docker/pkg/graphdb" "github.com/docker/docker/pkg/nat" @@ -19,7 +20,7 @@ type iterationAction int // containerReducer represents a reducer for a container. // Returns the object to serialize by the api. -type containerReducer func(*Container, *listContext) (*types.Container, error) +type containerReducer func(*container.Container, *listContext) (*types.Container, error) const ( // includeContainer is the action to include a container in the reducer. @@ -34,7 +35,7 @@ const ( var errStopIteration = errors.New("container list iteration stopped") // List returns an array of all containers registered in the daemon. -func (daemon *Daemon) List() []*Container { +func (daemon *Daemon) List() []*container.Container { return daemon.containers.List() } @@ -71,10 +72,10 @@ type listContext struct { exitAllowed []int // beforeFilter is a filter to ignore containers that appear before the one given // this is used for --filter=before= and --before=, the latter is deprecated. - beforeFilter *Container + beforeFilter *container.Container // sinceFilter is a filter to stop the filtering when the iterator arrive to the given container // this is used for --filter=since= and --since=, the latter is deprecated. - sinceFilter *Container + sinceFilter *container.Container // ContainersConfig is the filters set by the user *ContainersConfig } @@ -110,7 +111,7 @@ func (daemon *Daemon) reduceContainers(config *ContainersConfig, reducer contain } // reducePsContainer is the basic representation for a container as expected by the ps command. -func (daemon *Daemon) reducePsContainer(container *Container, ctx *listContext, reducer containerReducer) (*types.Container, error) { +func (daemon *Daemon) reducePsContainer(container *container.Container, ctx *listContext, reducer containerReducer) (*types.Container, error) { container.Lock() defer container.Unlock() @@ -148,7 +149,7 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error) } err = psFilters.WalkValues("status", func(value string) error { - if !isValidStateString(value) { + if !container.IsValidStateString(value) { return fmt.Errorf("Unrecognised filter value for status: %s", value) } @@ -159,7 +160,7 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error) return nil, err } - var beforeContFilter, sinceContFilter *Container + var beforeContFilter, sinceContFilter *container.Container err = psFilters.WalkValues("before", func(value string) error { beforeContFilter, err = daemon.Get(value) return err @@ -230,7 +231,7 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error) // includeContainerInList decides whether a containers should be include in the output or not based in the filter. // It also decides if the iteration should be stopped or not. -func includeContainerInList(container *Container, ctx *listContext) iterationAction { +func includeContainerInList(container *container.Container, ctx *listContext) iterationAction { // Do not include container if it's stopped and we're not filters if !container.Running && !ctx.All && ctx.Limit <= 0 && ctx.beforeFilter == nil && ctx.sinceFilter == nil { return excludeContainer @@ -309,7 +310,7 @@ func includeContainerInList(container *Container, ctx *listContext) iterationAct } // transformContainer generates the container type expected by the docker ps command. -func (daemon *Daemon) transformContainer(container *Container, ctx *listContext) (*types.Container, error) { +func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) { newC := &types.Container{ ID: container.ID, Names: ctx.names[container.ID], @@ -349,7 +350,7 @@ func (daemon *Daemon) transformContainer(container *Container, ctx *listContext) } newC.Created = container.Created.Unix() newC.Status = container.State.String() - newC.HostConfig.NetworkMode = string(container.hostConfig.NetworkMode) + newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode) newC.Ports = []types.Port{} for port, bindings := range container.NetworkSettings.Ports { diff --git a/daemon/list_unix.go b/daemon/list_unix.go index 156e12d9a5..8dccbe4e89 100644 --- a/daemon/list_unix.go +++ b/daemon/list_unix.go @@ -2,8 +2,10 @@ package daemon +import "github.com/docker/docker/container" + // excludeByIsolation is a platform specific helper function to support PS // filtering by Isolation. This is a Windows-only concept, so is a no-op on Unix. -func excludeByIsolation(container *Container, ctx *listContext) iterationAction { +func excludeByIsolation(container *container.Container, ctx *listContext) iterationAction { return includeContainer } diff --git a/daemon/list_windows.go b/daemon/list_windows.go index d14bf8ca72..7fbcd3af26 100644 --- a/daemon/list_windows.go +++ b/daemon/list_windows.go @@ -1,11 +1,15 @@ package daemon -import "strings" +import ( + "strings" + + "github.com/docker/docker/container" +) // excludeByIsolation is a platform specific helper function to support PS // filtering by Isolation. This is a Windows-only concept, so is a no-op on Unix. -func excludeByIsolation(container *Container, ctx *listContext) iterationAction { - i := strings.ToLower(string(container.hostConfig.Isolation)) +func excludeByIsolation(container *container.Container, ctx *listContext) iterationAction { + i := strings.ToLower(string(container.HostConfig.Isolation)) if i == "" { i = "default" } diff --git a/daemon/logs.go b/daemon/logs.go index f3fa866a63..057bdda73e 100644 --- a/daemon/logs.go +++ b/daemon/logs.go @@ -6,6 +6,7 @@ import ( "time" "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/jsonfilelog" derr "github.com/docker/docker/errors" @@ -99,11 +100,11 @@ 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 +func (daemon *Daemon) getLogger(container *container.Container) (logger.Logger, error) { + if container.LogDriver != nil && container.IsRunning() { + return container.LogDriver, nil } - cfg := container.getLogConfig(daemon.defaultLogConfig) + cfg := container.GetLogConfig(daemon.defaultLogConfig) if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil { return nil, err } @@ -111,8 +112,8 @@ func (daemon *Daemon) getLogger(container *Container) (logger.Logger, error) { } // StartLogging initializes and starts the container logging stream. -func (daemon *Daemon) StartLogging(container *Container) error { - cfg := container.getLogConfig(daemon.defaultLogConfig) +func (daemon *Daemon) StartLogging(container *container.Container) error { + cfg := container.GetLogConfig(daemon.defaultLogConfig) if cfg.Type == "none" { return nil // do not start logging routines } @@ -126,9 +127,9 @@ func (daemon *Daemon) StartLogging(container *Container) error { } copier := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l) - container.logCopier = copier + container.LogCopier = copier copier.Run() - container.logDriver = l + container.LogDriver = l // set LogPath field only for json-file logdriver if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok { diff --git a/daemon/mounts.go b/daemon/mounts.go index e9df648c8a..6b47575697 100644 --- a/daemon/mounts.go +++ b/daemon/mounts.go @@ -3,11 +3,12 @@ package daemon import ( "strings" + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" volumestore "github.com/docker/docker/volume/store" ) -func (daemon *Daemon) prepareMountPoints(container *Container) error { +func (daemon *Daemon) prepareMountPoints(container *container.Container) error { for _, config := range container.MountPoints { if len(config.Driver) > 0 { v, err := daemon.createVolume(config.Name, config.Driver, nil) @@ -20,7 +21,7 @@ func (daemon *Daemon) prepareMountPoints(container *Container) error { return nil } -func (daemon *Daemon) removeMountPoints(container *Container, rm bool) error { +func (daemon *Daemon) removeMountPoints(container *container.Container, rm bool) error { var rmErrors []string for _, m := range container.MountPoints { if m.Volume == nil { diff --git a/daemon/pause.go b/daemon/pause.go index be7f63394b..c602cda8ef 100644 --- a/daemon/pause.go +++ b/daemon/pause.go @@ -1,6 +1,7 @@ package daemon import ( + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" ) @@ -20,7 +21,7 @@ func (daemon *Daemon) ContainerPause(name string) error { // containerPause pauses the container execution without stopping the process. // The execution can be resumed by calling containerUnpause. -func (daemon *Daemon) containerPause(container *Container) error { +func (daemon *Daemon) containerPause(container *container.Container) error { container.Lock() defer container.Unlock() @@ -34,7 +35,7 @@ func (daemon *Daemon) containerPause(container *Container) error { return derr.ErrorCodeAlreadyPaused.WithArgs(container.ID) } - if err := daemon.execDriver.Pause(container.command); err != nil { + if err := daemon.execDriver.Pause(container.Command); err != nil { return err } container.Paused = true diff --git a/daemon/rename.go b/daemon/rename.go index 36421dcd7d..5fd0e73df4 100644 --- a/daemon/rename.go +++ b/daemon/rename.go @@ -13,17 +13,15 @@ import ( // reserved. func (daemon *Daemon) ContainerRename(oldName, newName string) error { var ( - err error - sid string - sb libnetwork.Sandbox - container *Container + sid string + sb libnetwork.Sandbox ) if oldName == "" || newName == "" { return derr.ErrorCodeEmptyRename } - container, err = daemon.Get(oldName) + container, err := daemon.Get(oldName) if err != nil { return err } @@ -50,7 +48,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error { return derr.ErrorCodeRenameDelete.WithArgs(oldName, err) } - if err = container.toDisk(); err != nil { + if err = container.ToDisk(); err != nil { return err } @@ -62,7 +60,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error { defer func() { if err != nil { container.Name = oldName - if e := container.toDisk(); e != nil { + if e := container.ToDisk(); e != nil { logrus.Errorf("%s: Failed in writing to Disk on rename failure: %v", container.ID, e) } } diff --git a/daemon/restart.go b/daemon/restart.go index fcfe6128fa..846ea2440e 100644 --- a/daemon/restart.go +++ b/daemon/restart.go @@ -1,6 +1,7 @@ package daemon import ( + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" ) @@ -25,7 +26,7 @@ func (daemon *Daemon) ContainerRestart(name string, seconds int) error { // 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 { +func (daemon *Daemon) containerRestart(container *container.Container, seconds int) error { // Avoid unnecessarily unmounting and then directly mounting // the container when the container stops and then starts // again diff --git a/daemon/start.go b/daemon/start.go index 2b301dc858..54cadeb305 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -4,8 +4,8 @@ import ( "runtime" "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" - "github.com/docker/docker/pkg/promise" "github.com/docker/docker/runconfig" ) @@ -16,7 +16,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf return err } - if container.isPaused() { + if container.IsPaused() { return derr.ErrorCodeStartPaused } @@ -42,7 +42,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf if err := daemon.setHostConfig(container, hostConfig); err != nil { return err } - initDNSHostConfig(container) + container.InitDNSHostConfig() } } else { if hostConfig != nil { @@ -52,7 +52,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf // check if hostConfig is in line with the current system settings. // It may happen cgroups are umounted or the like. - if _, err = daemon.verifyContainerSettings(container.hostConfig, nil); err != nil { + if _, err = daemon.verifyContainerSettings(container.HostConfig, nil); err != nil { return err } @@ -64,7 +64,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf } // Start starts a container -func (daemon *Daemon) Start(container *Container) error { +func (daemon *Daemon) Start(container *container.Container) error { return daemon.containerStart(container) } @@ -72,7 +72,7 @@ func (daemon *Daemon) Start(container *Container) error { // 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) { +func (daemon *Daemon) containerStart(container *container.Container) (err error) { container.Lock() defer container.Unlock() @@ -80,7 +80,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { return nil } - if container.removalInProgress || container.Dead { + if container.RemovalInProgress || container.Dead { return derr.ErrorCodeContainerBeingRemoved } @@ -88,12 +88,12 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { // setup has been cleaned up properly defer func() { if err != nil { - container.setError(err) + 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.ToDisk() daemon.Cleanup(container) daemon.LogContainerEvent(container, "die") } @@ -105,7 +105,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { // Make sure NetworkMode has an acceptable value. We do this to ensure // backwards API compatibility. - container.hostConfig = runconfig.SetDefaultNetModeIfBlank(container.hostConfig) + container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig) if err := daemon.initializeNetworking(container); err != nil { return err @@ -114,15 +114,15 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { if err != nil { return err } - if err := container.setupWorkingDirectory(); err != nil { + if err := container.SetupWorkingDirectory(); err != nil { return err } - env := container.createDaemonEnvironment(linkedEnv) + env := container.CreateDaemonEnvironment(linkedEnv) if err := daemon.populateCommand(container, env); err != nil { return err } - if !container.hostConfig.IpcMode.IsContainer() && !container.hostConfig.IpcMode.IsHost() { + if !container.HostConfig.IpcMode.IsContainer() && !container.HostConfig.IpcMode.IsHost() { if err := daemon.setupIpcDirs(container); err != nil { return err } @@ -132,10 +132,10 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { if err != nil { return err } - mounts = append(mounts, container.ipcMounts()...) - mounts = append(mounts, container.tmpfsMounts()...) + mounts = append(mounts, container.IpcMounts()...) + mounts = append(mounts, container.TmpfsMounts()...) - container.command.Mounts = mounts + container.Command.Mounts = mounts if err := daemon.waitForStart(container); err != nil { return err } @@ -143,34 +143,24 @@ func (daemon *Daemon) containerStart(container *Container) (err error) { return nil } -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 +func (daemon *Daemon) waitForStart(container *container.Container) error { + return container.StartMonitor(daemon, container.HostConfig.RestartPolicy) } // 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) { +func (daemon *Daemon) Cleanup(container *container.Container) { daemon.releaseNetwork(container) - container.unmountIpcMounts(detachMounted) + container.UnmountIpcMounts(detachMounted) daemon.conditionalUnmountOnCleanup(container) - for _, eConfig := range container.execCommands.Commands() { + for _, eConfig := range container.ExecCommands.Commands() { daemon.unregisterExecCommand(container, eConfig) } - if err := container.unmountVolumes(false); err != nil { + if err := container.UnmountVolumes(false); err != nil { logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err) } } diff --git a/daemon/stats_collector_unix.go b/daemon/stats_collector_unix.go index 6fb32b9a22..2fd368cd34 100644 --- a/daemon/stats_collector_unix.go +++ b/daemon/stats_collector_unix.go @@ -11,6 +11,7 @@ import ( "time" "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" "github.com/docker/docker/pkg/pubsub" @@ -19,7 +20,7 @@ import ( type statsSupervisor interface { // GetContainerStats collects all the stats related to a container - GetContainerStats(container *Container) (*execdriver.ResourceStats, error) + GetContainerStats(container *container.Container) (*execdriver.ResourceStats, error) } // newStatsCollector returns a new statsCollector that collections @@ -30,7 +31,7 @@ func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector s := &statsCollector{ interval: interval, supervisor: daemon, - publishers: make(map[*Container]*pubsub.Publisher), + publishers: make(map[*container.Container]*pubsub.Publisher), clockTicksPerSecond: uint64(system.GetClockTicks()), bufReader: bufio.NewReaderSize(nil, 128), } @@ -44,14 +45,14 @@ type statsCollector struct { supervisor statsSupervisor interval time.Duration clockTicksPerSecond uint64 - publishers map[*Container]*pubsub.Publisher + publishers map[*container.Container]*pubsub.Publisher bufReader *bufio.Reader } // collect registers the container with the collector and adds it to // the event loop for collection on the specified interval returning // a channel for the subscriber to receive on. -func (s *statsCollector) collect(c *Container) chan interface{} { +func (s *statsCollector) collect(c *container.Container) chan interface{} { s.m.Lock() defer s.m.Unlock() publisher, exists := s.publishers[c] @@ -64,7 +65,7 @@ func (s *statsCollector) collect(c *Container) chan interface{} { // stopCollection closes the channels for all subscribers and removes // the container from metrics collection. -func (s *statsCollector) stopCollection(c *Container) { +func (s *statsCollector) stopCollection(c *container.Container) { s.m.Lock() if publisher, exists := s.publishers[c]; exists { publisher.Close() @@ -74,7 +75,7 @@ func (s *statsCollector) stopCollection(c *Container) { } // unsubscribe removes a specific subscriber from receiving updates for a container's stats. -func (s *statsCollector) unsubscribe(c *Container, ch chan interface{}) { +func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) { s.m.Lock() publisher := s.publishers[c] if publisher != nil { @@ -88,7 +89,7 @@ func (s *statsCollector) unsubscribe(c *Container, ch chan interface{}) { func (s *statsCollector) run() { type publishersPair struct { - container *Container + container *container.Container publisher *pubsub.Publisher } // we cannot determine the capacity here. diff --git a/daemon/stats_collector_windows.go b/daemon/stats_collector_windows.go index e63f37b0d6..b6cb24cd7c 100644 --- a/daemon/stats_collector_windows.go +++ b/daemon/stats_collector_windows.go @@ -1,6 +1,10 @@ package daemon -import "time" +import ( + "time" + + "github.com/docker/docker/container" +) // newStatsCollector returns a new statsCollector for collection stats // for a registered container at the specified interval. The collector allows @@ -17,15 +21,15 @@ type statsCollector struct { // collect registers the container with the collector and adds it to // the event loop for collection on the specified interval returning // a channel for the subscriber to receive on. -func (s *statsCollector) collect(c *Container) chan interface{} { +func (s *statsCollector) collect(c *container.Container) chan interface{} { return nil } // stopCollection closes the channels for all subscribers and removes // the container from metrics collection. -func (s *statsCollector) stopCollection(c *Container) { +func (s *statsCollector) stopCollection(c *container.Container) { } // unsubscribe removes a specific subscriber from receiving updates for a container's stats. -func (s *statsCollector) unsubscribe(c *Container, ch chan interface{}) { +func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) { } diff --git a/daemon/stop.go b/daemon/stop.go index c97781b8fa..14e9ea8689 100644 --- a/daemon/stop.go +++ b/daemon/stop.go @@ -4,6 +4,7 @@ import ( "time" "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" ) @@ -32,13 +33,13 @@ func (daemon *Daemon) ContainerStop(name string, seconds int) error { // 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 { +func (daemon *Daemon) containerStop(container *container.Container, seconds int) error { if !container.IsRunning() { return nil } // 1. Send a SIGTERM - if err := daemon.killPossiblyDeadProcess(container, container.stopSignal()); err != nil { + 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 diff --git a/daemon/unpause.go b/daemon/unpause.go index 3397f44b45..33a980e8e0 100644 --- a/daemon/unpause.go +++ b/daemon/unpause.go @@ -1,6 +1,7 @@ package daemon import ( + "github.com/docker/docker/container" derr "github.com/docker/docker/errors" ) @@ -19,7 +20,7 @@ func (daemon *Daemon) ContainerUnpause(name string) error { } // containerUnpause resumes the container execution after the container is paused. -func (daemon *Daemon) containerUnpause(container *Container) error { +func (daemon *Daemon) containerUnpause(container *container.Container) error { container.Lock() defer container.Unlock() @@ -33,7 +34,7 @@ func (daemon *Daemon) containerUnpause(container *Container) error { return derr.ErrorCodeNotPaused.WithArgs(container.ID) } - if err := daemon.execDriver.Unpause(container.command); err != nil { + if err := daemon.execDriver.Unpause(container.Command); err != nil { return err } diff --git a/daemon/volumes.go b/daemon/volumes.go index 028affac65..7a09327ed1 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/docker/docker/api/types" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" "github.com/docker/docker/runconfig" @@ -70,7 +71,7 @@ func (m mounts) parts(i int) int { // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination. // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations. // 4. Cleanup old volumes that are about to be reasigned. -func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error { +func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *runconfig.HostConfig) error { binds := map[string]bool{} mountPoints := map[string]*volume.MountPoint{} diff --git a/daemon/volumes_unix.go b/daemon/volumes_unix.go index 7bd0a9b1b0..f0f38e9c90 100644 --- a/daemon/volumes_unix.go +++ b/daemon/volumes_unix.go @@ -3,66 +3,27 @@ package daemon import ( - "io/ioutil" "os" "sort" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/execdriver" - "github.com/docker/docker/pkg/chrootarchive" - "github.com/docker/docker/pkg/system" "github.com/docker/docker/volume" volumedrivers "github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/local" ) -// copyExistingContents copies from the source to the destination and -// ensures the ownership is appropriately set. -func copyExistingContents(source, destination string) error { - volList, err := ioutil.ReadDir(source) - if err != nil { - return err - } - if len(volList) > 0 { - srcList, err := ioutil.ReadDir(destination) - if err != nil { - return err - } - if len(srcList) == 0 { - // If the source volume is empty copy files from the root into the volume - if err := chrootarchive.CopyWithTar(source, destination); err != nil { - return err - } - } - } - return copyOwnership(source, destination) -} - -// copyOwnership copies the permissions and uid:gid of the source file -// to the destination file -func copyOwnership(source, destination string) error { - stat, err := system.Stat(source) - if err != nil { - return err - } - - if err := os.Chown(destination, int(stat.UID()), int(stat.GID())); err != nil { - return err - } - - return os.Chmod(destination, os.FileMode(stat.Mode())) -} - // 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 (daemon *Daemon) setupMounts(container *Container) ([]execdriver.Mount, error) { +func (daemon *Daemon) setupMounts(container *container.Container) ([]execdriver.Mount, error) { var mounts []execdriver.Mount for _, m := range container.MountPoints { path, err := m.Setup() if err != nil { return nil, err } - if !container.trySetNetworkMount(m.Destination, path) { + if !container.TrySetNetworkMount(m.Destination, path) { mounts = append(mounts, execdriver.Mount{ Source: path, Destination: m.Destination, @@ -72,7 +33,7 @@ func (daemon *Daemon) setupMounts(container *Container) ([]execdriver.Mount, err } mounts = sortMounts(mounts) - netMounts := container.networkMounts() + netMounts := container.NetworkMounts() // 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) diff --git a/daemon/volumes_windows.go b/daemon/volumes_windows.go index 8cc0294482..dd4a9c9f2c 100644 --- a/daemon/volumes_windows.go +++ b/daemon/volumes_windows.go @@ -5,6 +5,7 @@ package daemon import ( "sort" + "github.com/docker/docker/container" "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" "github.com/docker/docker/volume" @@ -14,7 +15,7 @@ import ( // 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 (daemon *Daemon) setupMounts(container *Container) ([]execdriver.Mount, error) { +func (daemon *Daemon) setupMounts(container *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 diff --git a/opts/opts_windows.go b/opts/opts_windows.go index b9ff2bae64..2a9e2be744 100644 --- a/opts/opts_windows.go +++ b/opts/opts_windows.go @@ -22,7 +22,7 @@ package opts // time="2015-11-06T13:38:37.259627400-08:00" level=debug msg="After createRootfs" // time="2015-11-06T13:38:37.263626300-08:00" level=debug msg="After setHostConfig" // time="2015-11-06T13:38:37.267631200-08:00" level=debug msg="before createContainerPl...." -// time="2015-11-06T13:38:37.271629500-08:00" level=debug msg=toDiskLocking.... +// time="2015-11-06T13:38:37.271629500-08:00" level=debug msg=ToDiskLocking.... // time="2015-11-06T13:38:37.275643200-08:00" level=debug msg="loggin event...." // time="2015-11-06T13:38:37.277627600-08:00" level=debug msg="logged event...." // time="2015-11-06T13:38:37.279631800-08:00" level=debug msg="In defer func"