mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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 <david.calavera@gmail.com> Signed-off-by: Tibor Vass <tibor@docker.com>
This commit is contained in:
parent
33ab2bb52c
commit
6bb0d1816a
60 changed files with 2454 additions and 2284 deletions
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
// TODO: remove dependency on daemon
|
// TODO: remove dependency on daemon
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
|
|
@ -115,13 +116,11 @@ type Docker interface {
|
||||||
// Pull tells Docker to pull image referenced by `name`.
|
// Pull tells Docker to pull image referenced by `name`.
|
||||||
Pull(name string) (*image.Image, error)
|
Pull(name string) (*image.Image, error)
|
||||||
|
|
||||||
// TODO: move daemon.Container to its own package
|
|
||||||
|
|
||||||
// Container looks up a Docker container referenced by `id`.
|
// 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
|
// Create creates a new Docker container and returns potential warnings
|
||||||
// TODO: put warnings in the error
|
// 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 removes a container specified by `id`.
|
||||||
Remove(id string, cfg *daemon.ContainerRmConfig) error
|
Remove(id string, cfg *daemon.ContainerRmConfig) error
|
||||||
// Commit creates a new Docker image from an existing Docker container.
|
// 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: 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
|
// TODO: do not pass a FileInfo, instead refactor the archive package to export a Walk function that can be used
|
||||||
// with Context.Walk
|
// 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.
|
// Retain retains an image avoiding it to be removed or overwritten until a corresponding Release() call.
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
|
|
@ -140,13 +139,13 @@ type Docker interface {
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
Release(sessionID string, activeImages []string)
|
Release(sessionID string, activeImages []string)
|
||||||
// Kill stops the container execution abruptly.
|
// 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 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 unmounts the root filesystem for the container.
|
||||||
Unmount(c *daemon.Container) error
|
Unmount(c *container.Container) error
|
||||||
// Start starts a new container
|
// Start starts a new container
|
||||||
Start(c *daemon.Container) error
|
Start(c *container.Container) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageCache abstracts an image cache store.
|
// ImageCache abstracts an image cache store.
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/docker/docker/api"
|
"github.com/docker/docker/api"
|
||||||
"github.com/docker/docker/builder"
|
"github.com/docker/docker/builder"
|
||||||
"github.com/docker/docker/builder/dockerfile/parser"
|
"github.com/docker/docker/builder/dockerfile/parser"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"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)
|
// The default path will be blank on Windows (set by HCS)
|
||||||
if len(b.runConfig.Env) == 0 && daemon.DefaultPathEnv != "" {
|
if len(b.runConfig.Env) == 0 && container.DefaultPathEnv != "" {
|
||||||
b.runConfig.Env = append(b.runConfig.Env, "PATH="+daemon.DefaultPathEnv)
|
b.runConfig.Env = append(b.runConfig.Env, "PATH="+container.DefaultPathEnv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process ONBUILD triggers if they exist
|
// Process ONBUILD triggers if they exist
|
||||||
|
|
@ -492,7 +493,7 @@ func (b *Builder) probeCache() (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) create() (*daemon.Container, error) {
|
func (b *Builder) create() (*container.Container, error) {
|
||||||
if b.image == "" && !b.noBaseImage {
|
if b.image == "" && !b.noBaseImage {
|
||||||
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
|
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
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) run(c *daemon.Container) error {
|
func (b *Builder) run(c *container.Container) error {
|
||||||
var errCh chan error
|
var errCh chan error
|
||||||
if b.Verbose {
|
if b.Verbose {
|
||||||
errCh = c.Attach(nil, b.Stdout, b.Stderr)
|
errCh = c.Attach(nil, b.Stdout, b.Stderr)
|
||||||
|
|
|
||||||
69
container/archive.go
Normal file
69
container/archive.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
package daemon
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -11,8 +10,6 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/label"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/daemon/exec"
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
|
|
@ -26,28 +23,22 @@ import (
|
||||||
"github.com/docker/docker/pkg/promise"
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
"github.com/docker/docker/pkg/symlink"
|
"github.com/docker/docker/pkg/symlink"
|
||||||
|
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/docker/volume"
|
"github.com/docker/docker/volume"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/label"
|
||||||
)
|
)
|
||||||
|
|
||||||
const configFileName = "config.v2.json"
|
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
|
// CommonContainer holds the fields for a container which are
|
||||||
// applicable across all platforms supported by the daemon.
|
// applicable across all platforms supported by the daemon.
|
||||||
type CommonContainer struct {
|
type CommonContainer struct {
|
||||||
*runconfig.StreamConfig
|
*runconfig.StreamConfig
|
||||||
// embed for Container to support states directly.
|
// embed for Container to support states directly.
|
||||||
*State `json:"State"` // Needed for remote api version <= 1.11
|
*State `json:"State"` // Needed for remote api version <= 1.11
|
||||||
root string // Path to the "home" of the container, including metadata.
|
Root string `json:"-"` // Path to the "home" of the container, including metadata.
|
||||||
basefs string // Path to the graphdriver mountpoint
|
BaseFS string `json:"-"` // Path to the graphdriver mountpoint
|
||||||
rwlayer layer.RWLayer
|
RWLayer layer.RWLayer `json:"-"`
|
||||||
ID string
|
ID string
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Path string
|
Path string
|
||||||
|
|
@ -65,32 +56,33 @@ type CommonContainer struct {
|
||||||
HasBeenStartedBefore bool
|
HasBeenStartedBefore bool
|
||||||
HasBeenManuallyStopped bool // used for unless-stopped restart policy
|
HasBeenManuallyStopped bool // used for unless-stopped restart policy
|
||||||
MountPoints map[string]*volume.MountPoint
|
MountPoints map[string]*volume.MountPoint
|
||||||
hostConfig *runconfig.HostConfig
|
HostConfig *runconfig.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
|
||||||
command *execdriver.Command
|
Command *execdriver.Command `json:"-"`
|
||||||
monitor *containerMonitor
|
monitor *containerMonitor
|
||||||
execCommands *exec.Store
|
ExecCommands *exec.Store `json:"-"`
|
||||||
// logDriver for closing
|
// logDriver for closing
|
||||||
logDriver logger.Logger
|
LogDriver logger.Logger `json:"-"`
|
||||||
logCopier *logger.Copier
|
LogCopier *logger.Copier `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBaseContainer creates a new container with its
|
// NewBaseContainer creates a new container with its
|
||||||
// basic configuration.
|
// basic configuration.
|
||||||
func newBaseContainer(id, root string) *Container {
|
func NewBaseContainer(id, root string) *Container {
|
||||||
return &Container{
|
return &Container{
|
||||||
CommonContainer: CommonContainer{
|
CommonContainer: CommonContainer{
|
||||||
ID: id,
|
ID: id,
|
||||||
State: NewState(),
|
State: NewState(),
|
||||||
execCommands: exec.NewStore(),
|
ExecCommands: exec.NewStore(),
|
||||||
root: root,
|
Root: root,
|
||||||
MountPoints: make(map[string]*volume.MountPoint),
|
MountPoints: make(map[string]*volume.MountPoint),
|
||||||
StreamConfig: runconfig.NewStreamConfig(),
|
StreamConfig: runconfig.NewStreamConfig(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) fromDisk() error {
|
// FromDisk loads the container configuration stored in the host.
|
||||||
pth, err := container.jsonPath()
|
func (container *Container) FromDisk() error {
|
||||||
|
pth, err := container.ConfigPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -114,8 +106,9 @@ func (container *Container) fromDisk() error {
|
||||||
return container.readHostConfig()
|
return container.readHostConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) toDisk() error {
|
// ToDisk saves the container configuration on disk.
|
||||||
pth, err := container.jsonPath()
|
func (container *Container) ToDisk() error {
|
||||||
|
pth, err := container.ConfigPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -133,22 +126,24 @@ func (container *Container) toDisk() error {
|
||||||
return err
|
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()
|
container.Lock()
|
||||||
err := container.toDisk()
|
err := container.ToDisk()
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readHostConfig reads the host configuration from disk for the container.
|
||||||
func (container *Container) readHostConfig() error {
|
func (container *Container) readHostConfig() error {
|
||||||
container.hostConfig = &runconfig.HostConfig{}
|
container.HostConfig = &runconfig.HostConfig{}
|
||||||
// If the hostconfig file does not exist, do not read it.
|
// 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.)
|
// but that's OK, since we just did that above.)
|
||||||
pth, err := container.hostConfigPath()
|
pth, err := container.HostConfigPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -162,17 +157,18 @@ func (container *Container) readHostConfig() error {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
initDNSHostConfig(container)
|
container.InitDNSHostConfig()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) writeHostConfig() error {
|
// WriteHostConfig saves the host configuration on disk for the container.
|
||||||
pth, err := container.hostConfigPath()
|
func (container *Container) WriteHostConfig() error {
|
||||||
|
pth, err := container.HostConfigPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -183,19 +179,19 @@ func (container *Container) writeHostConfig() error {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
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
|
// 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
|
// sanitisation. Symlinks are all scoped to the BaseFS of the container, as
|
||||||
// though the container's basefs was `/`.
|
// 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
|
// `/` inside the container. This method is essentially used to access a
|
||||||
// particular path inside the container as though you were a process in that
|
// particular path inside the container as though you were a process in that
|
||||||
// container.
|
// 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
|
// if no component of the returned path changes (such as a component
|
||||||
// symlinking to a different path) between using this method and using the
|
// symlinking to a different path) between using this method and using the
|
||||||
// path. See symlink.FollowSymlinkInScope for more details.
|
// 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
|
// IMPORTANT - These are paths on the OS where the daemon is running, hence
|
||||||
// any filepath operations must be done in an OS agnostic way.
|
// any filepath operations must be done in an OS agnostic way.
|
||||||
cleanPath := filepath.Join(string(os.PathSeparator), path)
|
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
|
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
|
// sanitisation. Symlinks are all scoped to the root of the container, as
|
||||||
// though the container's root was `/`.
|
// 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
|
// if no component of the returned path changes (such as a component
|
||||||
// symlinking to a different path) between using this method and using the
|
// symlinking to a different path) between using this method and using the
|
||||||
// path. See symlink.FollowSymlinkInScope for more details.
|
// 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
|
// IMPORTANT - These are paths on the OS where the daemon is running, hence
|
||||||
// any filepath operations must be done in an OS agnostic way.
|
// any filepath operations must be done in an OS agnostic way.
|
||||||
cleanPath := filepath.Join(string(os.PathSeparator), path)
|
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
|
// 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
|
// Resize changes the TTY of the process running inside the container
|
||||||
// to the given height and width. The container must be running.
|
// to the given height and width. The container must be running.
|
||||||
func (container *Container) Resize(h, w int) error {
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) hostConfigPath() (string, error) {
|
// HostConfigPath returns the path to the container's JSON hostconfig
|
||||||
return container.getRootResourcePath("hostconfig.json")
|
func (container *Container) HostConfigPath() (string, error) {
|
||||||
|
return container.GetRootResourcePath("hostconfig.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) jsonPath() (string, error) {
|
// ConfigPath returns the path to the container's JSON config
|
||||||
return container.getRootResourcePath(configFileName)
|
func (container *Container) ConfigPath() (string, error) {
|
||||||
}
|
return container.GetRootResourcePath(configFileName)
|
||||||
|
|
||||||
// This directory is only usable when the container is running
|
|
||||||
func (container *Container) rootfsPath() string {
|
|
||||||
return container.basefs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateID(id string) error {
|
func validateID(id string) error {
|
||||||
|
|
@ -267,8 +260,9 @@ func (container *Container) exposes(p nat.Port) bool {
|
||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) getLogConfig(defaultConfig runconfig.LogConfig) runconfig.LogConfig {
|
// GetLogConfig returns the log configuration for the container.
|
||||||
cfg := container.hostConfig.LogConfig
|
func (container *Container) GetLogConfig(defaultConfig runconfig.LogConfig) runconfig.LogConfig {
|
||||||
|
cfg := container.HostConfig.LogConfig
|
||||||
if cfg.Type != "" || len(cfg.Config) > 0 { // container has log driver configured
|
if cfg.Type != "" || len(cfg.Config) > 0 { // container has log driver configured
|
||||||
if cfg.Type == "" {
|
if cfg.Type == "" {
|
||||||
cfg.Type = jsonfilelog.Name
|
cfg.Type = jsonfilelog.Name
|
||||||
|
|
@ -300,7 +294,7 @@ func (container *Container) StartLogger(cfg runconfig.LogConfig) (logger.Logger,
|
||||||
|
|
||||||
// Set logging file for "json-logger"
|
// Set logging file for "json-logger"
|
||||||
if cfg.Type == jsonfilelog.Name {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -308,33 +302,39 @@ func (container *Container) StartLogger(cfg runconfig.LogConfig) (logger.Logger,
|
||||||
return c(ctx)
|
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
|
// even if we have a process label return "" if we are running
|
||||||
// in privileged mode
|
// in privileged mode
|
||||||
if container.hostConfig.Privileged {
|
if container.HostConfig.Privileged {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return container.ProcessLabel
|
return container.ProcessLabel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) getMountLabel() string {
|
// GetMountLabel returns the mounting label for the container.
|
||||||
if container.hostConfig.Privileged {
|
// This label is empty if the container is privileged.
|
||||||
|
func (container *Container) GetMountLabel() string {
|
||||||
|
if container.HostConfig.Privileged {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return container.MountLabel
|
return container.MountLabel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) getExecIDs() []string {
|
// GetExecIDs returns the list of exec commands running on the container.
|
||||||
return container.execCommands.List()
|
func (container *Container) GetExecIDs() []string {
|
||||||
|
return container.ExecCommands.List()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach connects to the container's TTY, delegating to standard
|
// Attach connects to the container's TTY, delegating to standard
|
||||||
// streams or websockets depending on the configuration.
|
// streams or websockets depending on the configuration.
|
||||||
func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error {
|
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 (
|
var (
|
||||||
cStdout, cStderr io.ReadCloser
|
cStdout, cStderr io.ReadCloser
|
||||||
cStdin io.WriteCloser
|
cStdin io.WriteCloser
|
||||||
|
|
@ -479,13 +479,16 @@ func copyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
|
||||||
return written, err
|
return written, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) shouldRestart() bool {
|
// ShouldRestart decides whether the daemon should restart the container or not.
|
||||||
return container.hostConfig.RestartPolicy.Name == "always" ||
|
// This is based on the container's restart policy.
|
||||||
(container.hostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) ||
|
func (container *Container) ShouldRestart() bool {
|
||||||
(container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
|
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{
|
container.MountPoints[destination] = &volume.MountPoint{
|
||||||
Name: name,
|
Name: name,
|
||||||
Source: source,
|
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{
|
container.MountPoints[destination] = &volume.MountPoint{
|
||||||
Name: name,
|
Name: name,
|
||||||
Driver: volume.DefaultDriverName,
|
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{
|
container.MountPoints[destination] = &volume.MountPoint{
|
||||||
Name: vol.Name(),
|
Name: vol.Name(),
|
||||||
Driver: vol.DriverName(),
|
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
|
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
|
var stopSignal syscall.Signal
|
||||||
if container.Config.StopSignal != "" {
|
if container.Config.StopSignal != "" {
|
||||||
stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
|
stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
|
||||||
|
|
@ -529,7 +536,7 @@ func (container *Container) stopSignal() int {
|
||||||
return int(stopSignal)
|
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,
|
// New containers don't ever have those fields nil,
|
||||||
// but pre created containers can still have those nil values.
|
// but pre created containers can still have those nil values.
|
||||||
// The non-recommended host configuration in the start api can
|
// 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.
|
// we remove that behavior for good.
|
||||||
// See https://github.com/docker/docker/pull/17779
|
// See https://github.com/docker/docker/pull/17779
|
||||||
// for a more detailed explanation on why we don't want that.
|
// for a more detailed explanation on why we don't want that.
|
||||||
func initDNSHostConfig(container *Container) {
|
func (container *Container) InitDNSHostConfig() {
|
||||||
if container.hostConfig.DNS == nil {
|
if container.HostConfig.DNS == nil {
|
||||||
container.hostConfig.DNS = make([]string, 0)
|
container.HostConfig.DNS = make([]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.hostConfig.DNSSearch == nil {
|
if container.HostConfig.DNSSearch == nil {
|
||||||
container.hostConfig.DNSSearch = make([]string, 0)
|
container.HostConfig.DNSSearch = make([]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.hostConfig.DNSOptions == nil {
|
if container.HostConfig.DNSOptions == nil {
|
||||||
container.hostConfig.DNSOptions = make([]string, 0)
|
container.HostConfig.DNSOptions = make([]string, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
36
container/container_unit_test.go
Normal file
36
container/container_unit_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
701
container/container_unix.go
Normal file
701
container/container_unix.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
65
container/container_windows.go
Normal file
65
container/container_windows.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package daemon
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
|
|
@ -21,8 +22,8 @@ const (
|
||||||
loggerCloseTimeout = 10 * time.Second
|
loggerCloseTimeout = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// containerSupervisor defines the interface that a supervisor must implement
|
// supervisor defines the interface that a supervisor must implement
|
||||||
type containerSupervisor interface {
|
type supervisor interface {
|
||||||
// LogContainerEvent generates events related to a given container
|
// LogContainerEvent generates events related to a given container
|
||||||
LogContainerEvent(*Container, string)
|
LogContainerEvent(*Container, string)
|
||||||
// Cleanup ensures that the container is properly unmounted
|
// Cleanup ensures that the container is properly unmounted
|
||||||
|
|
@ -44,7 +45,7 @@ type containerMonitor struct {
|
||||||
mux sync.Mutex
|
mux sync.Mutex
|
||||||
|
|
||||||
// supervisor keeps track of the container and the events it generates
|
// supervisor keeps track of the container and the events it generates
|
||||||
supervisor containerSupervisor
|
supervisor supervisor
|
||||||
|
|
||||||
// container is the container being monitored
|
// container is the container being monitored
|
||||||
container *Container
|
container *Container
|
||||||
|
|
@ -76,17 +77,32 @@ type containerMonitor struct {
|
||||||
lastStartTime time.Time
|
lastStartTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// newContainerMonitor returns an initialized containerMonitor for the provided container
|
// StartMonitor initializes a containerMonitor for this container with the provided supervisor and restart policy
|
||||||
// honoring the provided restart policy
|
// and starts the container's process.
|
||||||
func (daemon *Daemon) newContainerMonitor(container *Container, policy runconfig.RestartPolicy) *containerMonitor {
|
func (container *Container) StartMonitor(s supervisor, policy runconfig.RestartPolicy) error {
|
||||||
return &containerMonitor{
|
container.monitor = &containerMonitor{
|
||||||
supervisor: daemon,
|
supervisor: s,
|
||||||
container: container,
|
container: container,
|
||||||
restartPolicy: policy,
|
restartPolicy: policy,
|
||||||
timeIncrement: defaultTimeIncrement,
|
timeIncrement: defaultTimeIncrement,
|
||||||
stopChan: make(chan struct{}),
|
stopChan: make(chan struct{}),
|
||||||
startSignal: 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
|
// 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
|
// FIXME: here is race condition between two RUN instructions in Dockerfile
|
||||||
// because they share same runconfig and change image. Must be fixed
|
// because they share same runconfig and change image. Must be fixed
|
||||||
// in builder/builder.go
|
// 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)
|
logrus.Errorf("Error dumping container %s state to disk: %s", m.container.ID, err)
|
||||||
|
|
||||||
return 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
|
// Start starts the containers process and monitors it according to the restart policy
|
||||||
func (m *containerMonitor) Start() error {
|
func (m *containerMonitor) start() error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
exitStatus execdriver.ExitStatus
|
exitStatus execdriver.ExitStatus
|
||||||
|
|
@ -137,7 +153,7 @@ func (m *containerMonitor) Start() error {
|
||||||
if afterRun {
|
if afterRun {
|
||||||
m.container.Lock()
|
m.container.Lock()
|
||||||
defer m.container.Unlock()
|
defer m.container.Unlock()
|
||||||
m.container.setStopped(&exitStatus)
|
m.container.SetStopped(&exitStatus)
|
||||||
}
|
}
|
||||||
m.Close()
|
m.Close()
|
||||||
}()
|
}()
|
||||||
|
|
@ -202,7 +218,7 @@ func (m *containerMonitor) Start() error {
|
||||||
m.resetMonitor(err == nil && exitStatus.ExitCode == 0)
|
m.resetMonitor(err == nil && exitStatus.ExitCode == 0)
|
||||||
|
|
||||||
if m.shouldRestart(exitStatus.ExitCode) {
|
if m.shouldRestart(exitStatus.ExitCode) {
|
||||||
m.container.setRestarting(&exitStatus)
|
m.container.SetRestarting(&exitStatus)
|
||||||
m.logEvent("die")
|
m.logEvent("die")
|
||||||
m.resetContainer(true)
|
m.resetContainer(true)
|
||||||
|
|
||||||
|
|
@ -295,7 +311,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if processConfig.Tty {
|
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
|
// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave
|
||||||
// which we close here.
|
// which we close here.
|
||||||
if c, ok := processConfig.Stdout.(io.Closer); ok {
|
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
|
// signal that the process has started
|
||||||
// close channel only if not closed
|
// close channel only if not closed
|
||||||
|
|
@ -313,7 +329,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid
|
||||||
close(m.startSignal)
|
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)
|
logrus.Errorf("Error saving container to disk: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -333,8 +349,8 @@ func (m *containerMonitor) resetContainer(lock bool) {
|
||||||
logrus.Errorf("%s: %s", container.ID, err)
|
logrus.Errorf("%s: %s", container.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.command != nil && container.command.ProcessConfig.Terminal != nil {
|
if container.Command != nil && container.Command.ProcessConfig.Terminal != nil {
|
||||||
if err := container.command.ProcessConfig.Terminal.Close(); err != nil {
|
if err := container.Command.ProcessConfig.Terminal.Close(); err != nil {
|
||||||
logrus.Errorf("%s: Error closing terminal: %s", container.ID, err)
|
logrus.Errorf("%s: Error closing terminal: %s", container.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -344,11 +360,11 @@ func (m *containerMonitor) resetContainer(lock bool) {
|
||||||
container.NewInputPipes()
|
container.NewInputPipes()
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.logDriver != nil {
|
if container.LogDriver != nil {
|
||||||
if container.logCopier != nil {
|
if container.LogCopier != nil {
|
||||||
exit := make(chan struct{})
|
exit := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
container.logCopier.Wait()
|
container.LogCopier.Wait()
|
||||||
close(exit)
|
close(exit)
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
|
|
@ -357,14 +373,14 @@ func (m *containerMonitor) resetContainer(lock bool) {
|
||||||
case <-exit:
|
case <-exit:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
container.logDriver.Close()
|
container.LogDriver.Close()
|
||||||
container.logCopier = nil
|
container.LogCopier = nil
|
||||||
container.logDriver = 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,
|
Stdin: c.Stdin,
|
||||||
Stdout: c.Stdout,
|
Stdout: c.Stdout,
|
||||||
Stderr: c.Stderr,
|
Stderr: c.Stderr,
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package daemon
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -21,7 +21,7 @@ type State struct {
|
||||||
Paused bool
|
Paused bool
|
||||||
Restarting bool
|
Restarting bool
|
||||||
OOMKilled 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
|
Dead bool
|
||||||
Pid int
|
Pid int
|
||||||
ExitCode 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)))
|
return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.removalInProgress {
|
if s.RemovalInProgress {
|
||||||
return "Removal In Progress"
|
return "Removal In Progress"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +93,8 @@ func (s *State) StateString() string {
|
||||||
return "exited"
|
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" &&
|
if s != "paused" &&
|
||||||
s != "restarting" &&
|
s != "restarting" &&
|
||||||
s != "running" &&
|
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
|
// waitRunning waits until state is running. If state is already
|
||||||
// running it returns immediately. If you want wait forever you must
|
// running it returns immediately. If you want wait forever you must
|
||||||
// supply negative timeout. Returns pid, that was passed to
|
// supply negative timeout. Returns pid, that was passed to
|
||||||
// setRunning.
|
// SetRunning.
|
||||||
func (s *State) waitRunning(timeout time.Duration) (int, error) {
|
func (s *State) waitRunning(timeout time.Duration) (int, error) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
if s.Running {
|
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
|
// WaitStop waits until state is stopped. If state already stopped it returns
|
||||||
// immediately. If you want wait forever you must supply negative timeout.
|
// 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) {
|
func (s *State) WaitStop(timeout time.Duration) (int, error) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
if !s.Running {
|
if !s.Running {
|
||||||
|
|
@ -178,7 +179,8 @@ func (s *State) getExitCode() int {
|
||||||
return res
|
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.Error = ""
|
||||||
s.Running = true
|
s.Running = true
|
||||||
s.Paused = false
|
s.Paused = false
|
||||||
|
|
@ -190,13 +192,15 @@ func (s *State) setRunning(pid int) {
|
||||||
s.waitChan = make(chan struct{})
|
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.Lock()
|
||||||
s.setStopped(exitStatus)
|
s.SetStopped(exitStatus)
|
||||||
s.Unlock()
|
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.Running = false
|
||||||
s.Restarting = false
|
s.Restarting = false
|
||||||
s.Pid = 0
|
s.Pid = 0
|
||||||
|
|
@ -206,15 +210,17 @@ func (s *State) setStopped(exitStatus *execdriver.ExitStatus) {
|
||||||
s.waitChan = make(chan struct{})
|
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
|
// 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.Lock()
|
||||||
s.setRestarting(exitStatus)
|
s.SetRestarting(exitStatus)
|
||||||
s.Unlock()
|
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
|
// we should consider the container running when it is restarting because of
|
||||||
// all the checks in docker around rm/stop/etc
|
// all the checks in docker around rm/stop/etc
|
||||||
s.Running = true
|
s.Running = true
|
||||||
|
|
@ -226,37 +232,41 @@ func (s *State) setRestarting(exitStatus *execdriver.ExitStatus) {
|
||||||
s.waitChan = make(chan struct{})
|
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
|
// know the error that occurred when container transits to another state
|
||||||
// when inspecting it
|
// when inspecting it
|
||||||
func (s *State) setError(err error) {
|
func (s *State) SetError(err error) {
|
||||||
s.Error = 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()
|
s.Lock()
|
||||||
res := s.Paused
|
res := s.Paused
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) setRemovalInProgress() error {
|
// SetRemovalInProgress sets the container state as being removed.
|
||||||
|
func (s *State) SetRemovalInProgress() error {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
if s.removalInProgress {
|
if s.RemovalInProgress {
|
||||||
return derr.ErrorCodeAlreadyRemoving
|
return derr.ErrorCodeAlreadyRemoving
|
||||||
}
|
}
|
||||||
s.removalInProgress = true
|
s.RemovalInProgress = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) resetRemovalInProgress() {
|
// ResetRemovalInProgress make the RemovalInProgress state to false.
|
||||||
|
func (s *State) ResetRemovalInProgress() {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
s.removalInProgress = false
|
s.RemovalInProgress = false
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) setDead() {
|
// SetDead sets the container state to "dead"
|
||||||
|
func (s *State) SetDead() {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
s.Dead = true
|
s.Dead = true
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package daemon
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -19,7 +19,7 @@ func TestStateRunStop(t *testing.T) {
|
||||||
close(started)
|
close(started)
|
||||||
}()
|
}()
|
||||||
s.Lock()
|
s.Lock()
|
||||||
s.setRunning(i + 100)
|
s.SetRunning(i + 100)
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
|
||||||
if !s.IsRunning() {
|
if !s.IsRunning() {
|
||||||
|
|
@ -52,7 +52,7 @@ func TestStateRunStop(t *testing.T) {
|
||||||
atomic.StoreInt64(&exit, int64(exitCode))
|
atomic.StoreInt64(&exit, int64(exitCode))
|
||||||
close(stopped)
|
close(stopped)
|
||||||
}()
|
}()
|
||||||
s.setStoppedLocking(&execdriver.ExitStatus{ExitCode: i})
|
s.SetStoppedLocking(&execdriver.ExitStatus{ExitCode: i})
|
||||||
if s.IsRunning() {
|
if s.IsRunning() {
|
||||||
t.Fatal("State is running")
|
t.Fatal("State is running")
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +93,7 @@ func TestStateTimeoutWait(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Lock()
|
s.Lock()
|
||||||
s.setRunning(49)
|
s.SetRunning(49)
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
|
||||||
stopped := make(chan struct{})
|
stopped := make(chan struct{})
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// +build linux freebsd
|
// +build linux freebsd
|
||||||
|
|
||||||
package daemon
|
package container
|
||||||
|
|
||||||
import "github.com/docker/docker/daemon/execdriver"
|
import "github.com/docker/docker/daemon/execdriver"
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package daemon
|
package container
|
||||||
|
|
||||||
import "github.com/docker/docker/daemon/execdriver"
|
import "github.com/docker/docker/daemon/execdriver"
|
||||||
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/pkg/chrootarchive"
|
"github.com/docker/docker/pkg/chrootarchive"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"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)
|
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
|
// containerStatPath stats the filesystem resource at the specified path in this
|
||||||
// container. Returns stat info about the resource.
|
// 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()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
|
@ -143,23 +84,23 @@ func (daemon *Daemon) containerStatPath(container *Container, path string) (stat
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
|
|
||||||
err = daemon.mountVolumes(container)
|
err = daemon.mountVolumes(container)
|
||||||
defer container.unmountVolumes(true)
|
defer container.UnmountVolumes(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedPath, absPath, err := container.resolvePath(path)
|
resolvedPath, absPath, err := container.ResolvePath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return container.statPath(resolvedPath, absPath)
|
return container.StatPath(resolvedPath, absPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// containerArchivePath creates an archive of the filesystem resource at the specified
|
// containerArchivePath creates an archive of the filesystem resource at the specified
|
||||||
// path in this container. Returns a tar archive of the resource and stat info
|
// path in this container. Returns a tar archive of the resource and stat info
|
||||||
// about the resource.
|
// 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()
|
container.Lock()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -178,7 +119,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// unmount any volumes
|
// unmount any volumes
|
||||||
container.unmountVolumes(true)
|
container.UnmountVolumes(true)
|
||||||
// unmount the container's rootfs
|
// unmount the container's rootfs
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
}
|
}
|
||||||
|
|
@ -188,12 +129,12 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedPath, absPath, err := container.resolvePath(path)
|
resolvedPath, absPath, err := container.ResolvePath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stat, err = container.statPath(resolvedPath, absPath)
|
stat, err = container.StatPath(resolvedPath, absPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -213,7 +154,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c
|
||||||
|
|
||||||
content = ioutils.NewReadCloserWrapper(data, func() error {
|
content = ioutils.NewReadCloserWrapper(data, func() error {
|
||||||
err := data.Close()
|
err := data.Close()
|
||||||
container.unmountVolumes(true)
|
container.UnmountVolumes(true)
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
return err
|
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
|
// 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-
|
// given content would cause an existing directory to be replaced with a non-
|
||||||
// directory and vice versa.
|
// 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()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
|
@ -240,14 +181,14 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
|
|
||||||
err = daemon.mountVolumes(container)
|
err = daemon.mountVolumes(container)
|
||||||
defer container.unmountVolumes(true)
|
defer container.UnmountVolumes(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The destination path needs to be resolved to a host path, with all
|
// 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
|
// 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
|
// 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.
|
// 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.
|
// a volume file path.
|
||||||
var baseRel string
|
var baseRel string
|
||||||
if strings.HasPrefix(resolvedPath, `\\?\Volume{`) {
|
if strings.HasPrefix(resolvedPath, `\\?\Volume{`) {
|
||||||
if strings.HasPrefix(resolvedPath, container.basefs) {
|
if strings.HasPrefix(resolvedPath, container.BaseFS) {
|
||||||
baseRel = resolvedPath[len(container.basefs):]
|
baseRel = resolvedPath[len(container.BaseFS):]
|
||||||
if baseRel[:1] == `\` {
|
if baseRel[:1] == `\` {
|
||||||
baseRel = baseRel[1:]
|
baseRel = baseRel[1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
baseRel, err = filepath.Rel(container.basefs, resolvedPath)
|
baseRel, err = filepath.Rel(container.BaseFS, resolvedPath)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -303,7 +244,7 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !toVolume && container.hostConfig.ReadonlyRootfs {
|
if !toVolume && container.HostConfig.ReadonlyRootfs {
|
||||||
return ErrRootFSReadOnly
|
return ErrRootFSReadOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,7 +264,7 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n
|
||||||
return nil
|
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()
|
container.Lock()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -342,7 +283,7 @@ func (daemon *Daemon) containerCopy(container *Container, resource string) (rc i
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// unmount any volumes
|
// unmount any volumes
|
||||||
container.unmountVolumes(true)
|
container.UnmountVolumes(true)
|
||||||
// unmount the container's rootfs
|
// unmount the container's rootfs
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
}
|
}
|
||||||
|
|
@ -379,7 +320,7 @@ func (daemon *Daemon) containerCopy(container *Container, resource string) (rc i
|
||||||
|
|
||||||
reader := ioutils.NewReadCloserWrapper(archive, func() error {
|
reader := ioutils.NewReadCloserWrapper(archive, func() error {
|
||||||
err := archive.Close()
|
err := archive.Close()
|
||||||
container.unmountVolumes(true)
|
container.UnmountVolumes(true)
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
|
import "github.com/docker/docker/container"
|
||||||
|
|
||||||
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
// 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 in a read-only volume. If it is not in a volume, the container
|
||||||
// cannot be configured with a read-only rootfs.
|
// 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
|
var toVolume bool
|
||||||
for _, mnt := range container.MountPoints {
|
for _, mnt := range container.MountPoints {
|
||||||
if toVolume = mnt.HasResource(absPath); toVolume {
|
if toVolume = mnt.HasResource(absPath); toVolume {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
|
import "github.com/docker/docker/container"
|
||||||
|
|
||||||
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
// 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 in a read-only volume. If it is not in a volume, the container
|
||||||
// cannot be configured with a read-only rootfs.
|
// cannot be configured with a read-only rootfs.
|
||||||
//
|
//
|
||||||
// This is a no-op on Windows which does not support read-only volumes, or
|
// 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
|
// 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
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/logger"
|
"github.com/docker/docker/daemon/logger"
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"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)
|
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 {
|
if logs {
|
||||||
logDriver, err := daemon.getLogger(container)
|
logDriver, err := daemon.getLogger(container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/dockerversion"
|
"github.com/docker/docker/dockerversion"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/layer"
|
"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")
|
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)
|
daemon.containerPause(container)
|
||||||
defer daemon.containerUnpause(container)
|
defer daemon.containerUnpause(container)
|
||||||
}
|
}
|
||||||
|
|
@ -145,12 +146,12 @@ func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (string, err
|
||||||
return id.String(), nil
|
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 {
|
if err := daemon.Mount(container); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
archive, err := container.rwlayer.TarStream()
|
archive, err := container.RWLayer.TarStream()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
daemon/container_operations.go
Normal file
9
daemon/container_operations.go
Normal file
|
|
@ -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")
|
||||||
|
)
|
||||||
928
daemon/container_operations_unix.go
Normal file
928
daemon/container_operations_unix.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -5,62 +5,31 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/layer"
|
"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
|
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
|
||||||
// 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) {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) createDaemonEnvironment(linkedEnv []string) []string {
|
func (daemon *Daemon) initializeNetworking(container *container.Container) error {
|
||||||
// On Windows, nothing to link. Just return the container environment.
|
|
||||||
return container.Config.Env
|
|
||||||
}
|
|
||||||
|
|
||||||
func (daemon *Daemon) initializeNetworking(container *Container) error {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectToNetwork connects a container to the network
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisconnectFromNetwork disconnects a container from, the network
|
func (daemon *Daemon) populateCommand(c *container.Container, env []string) error {
|
||||||
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 {
|
|
||||||
en := &execdriver.Network{
|
en := &execdriver.Network{
|
||||||
Interface: nil,
|
Interface: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2)
|
parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2)
|
||||||
switch parts[0] {
|
switch parts[0] {
|
||||||
case "none":
|
case "none":
|
||||||
case "default", "": // empty string to support existing containers
|
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{
|
en.Interface = &execdriver.NetworkInterface{
|
||||||
MacAddress: c.Config.MacAddress,
|
MacAddress: c.Config.MacAddress,
|
||||||
Bridge: daemon.configStore.Bridge.VirtualSwitchName,
|
Bridge: daemon.configStore.Bridge.VirtualSwitchName,
|
||||||
PortBindings: c.hostConfig.PortBindings,
|
PortBindings: c.HostConfig.PortBindings,
|
||||||
|
|
||||||
// TODO Windows. Include IPAddress. There already is a
|
// TODO Windows. Include IPAddress. There already is a
|
||||||
// property IPAddress on execDrive.CommonNetworkInterface,
|
// property IPAddress on execDrive.CommonNetworkInterface,
|
||||||
|
|
@ -77,13 +46,13 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return derr.ErrorCodeInvalidNetworkMode.WithArgs(c.hostConfig.NetworkMode)
|
return derr.ErrorCodeInvalidNetworkMode.WithArgs(c.HostConfig.NetworkMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Windows. More resource controls to be implemented later.
|
// TODO Windows. More resource controls to be implemented later.
|
||||||
resources := &execdriver.Resources{
|
resources := &execdriver.Resources{
|
||||||
CommonResources: execdriver.CommonResources{
|
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,
|
Arguments: c.Args,
|
||||||
Tty: c.Config.Tty,
|
Tty: c.Config.Tty,
|
||||||
},
|
},
|
||||||
ConsoleSize: c.hostConfig.ConsoleSize,
|
ConsoleSize: c.HostConfig.ConsoleSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
processConfig.Env = env
|
processConfig.Env = env
|
||||||
|
|
@ -123,23 +92,23 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
|
||||||
}
|
}
|
||||||
layerFolder := m["dir"]
|
layerFolder := m["dir"]
|
||||||
|
|
||||||
c.command = &execdriver.Command{
|
c.Command = &execdriver.Command{
|
||||||
CommonCommand: execdriver.CommonCommand{
|
CommonCommand: execdriver.CommonCommand{
|
||||||
ID: c.ID,
|
ID: c.ID,
|
||||||
Rootfs: c.rootfsPath(),
|
Rootfs: c.BaseFS,
|
||||||
InitPath: "/.dockerinit",
|
InitPath: "/.dockerinit",
|
||||||
WorkingDir: c.Config.WorkingDir,
|
WorkingDir: c.Config.WorkingDir,
|
||||||
Network: en,
|
Network: en,
|
||||||
MountLabel: c.getMountLabel(),
|
MountLabel: c.GetMountLabel(),
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
ProcessConfig: processConfig,
|
ProcessConfig: processConfig,
|
||||||
ProcessLabel: c.getProcessLabel(),
|
ProcessLabel: c.GetProcessLabel(),
|
||||||
},
|
},
|
||||||
FirstStart: !c.HasBeenStartedBefore,
|
FirstStart: !c.HasBeenStartedBefore,
|
||||||
LayerFolder: layerFolder,
|
LayerFolder: layerFolder,
|
||||||
LayerPaths: layerPaths,
|
LayerPaths: layerPaths,
|
||||||
Hostname: c.Config.Hostname,
|
Hostname: c.Config.Hostname,
|
||||||
Isolation: c.hostConfig.Isolation,
|
Isolation: c.HostConfig.Isolation,
|
||||||
ArgsEscaped: c.Config.ArgsEscaped,
|
ArgsEscaped: c.Config.ArgsEscaped,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +116,7 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSize returns real size & virtual size
|
// 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
|
// TODO Windows
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
@ -158,56 +127,39 @@ func (daemon *Daemon) setNetworkNamespaceKey(containerID string, pid int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocateNetwork is a no-op on Windows.
|
// allocateNetwork is a no-op on Windows.
|
||||||
func (daemon *Daemon) allocateNetwork(container *Container) error {
|
func (daemon *Daemon) allocateNetwork(container *container.Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) updateNetwork(container *Container) error {
|
func (daemon *Daemon) updateNetwork(container *container.Container) error {
|
||||||
return nil
|
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.
|
func (daemon *Daemon) setupIpcDirs(container *container.Container) error {
|
||||||
// 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 {
|
|
||||||
return nil
|
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
|
// 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
|
// against containers which have volumes. You will still be able to cp
|
||||||
// to somewhere on the container drive, but not to any mounted volumes
|
// to somewhere on the container drive, but not to any mounted volumes
|
||||||
// inside the container. Without this fix, docker cp is broken to any
|
// 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 which has a volume, regardless of where the file is inside the
|
||||||
// container.
|
// container.
|
||||||
func (daemon *Daemon) mountVolumes(container *Container) error {
|
func (daemon *Daemon) mountVolumes(container *container.Container) error {
|
||||||
return nil
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,7 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"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.
|
// 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 (
|
var (
|
||||||
container *Container
|
container *container.Container
|
||||||
img *image.Image
|
img *image.Image
|
||||||
imgID image.ID
|
imgID image.ID
|
||||||
err error
|
err error
|
||||||
|
|
@ -86,7 +87,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,7 +106,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re
|
||||||
return nil, err
|
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)
|
logrus.Errorf("Error saving new container to disk: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
|
@ -15,7 +16,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// createContainerPlatformSpecificSettings performs platform specific container create functionality
|
// 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 {
|
if err := daemon.Mount(container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +28,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Contain
|
||||||
|
|
||||||
// Skip volumes for which we already have something mounted on that
|
// Skip volumes for which we already have something mounted on that
|
||||||
// destination because of a --volume-from.
|
// destination because of a --volume-from.
|
||||||
if container.isDestinationMounted(destination) {
|
if container.IsDestinationMounted(destination) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
path, err := container.GetResourcePath(destination)
|
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
|
// never attempt to copy existing content in a container FS to a shared volume
|
||||||
if v.DriverName() == volume.DefaultDriverName {
|
if v.DriverName() == volume.DefaultDriverName {
|
||||||
if err := container.copyImagePathContent(v, destination); err != nil {
|
if err := container.CopyImagePathContent(v, destination); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
container.addMountPointWithVolume(destination, v, true)
|
container.AddMountPointWithVolume(destination, v, true)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
|
|
@ -10,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// createContainerPlatformSpecificSettings performs platform specific container create functionality
|
// 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 {
|
for spec := range config.Volumes {
|
||||||
|
|
||||||
mp, err := volume.ParseMountSpec(spec, hostConfig.VolumeDriver)
|
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
|
// Skip volumes for which we already have something mounted on that
|
||||||
// destination because of a --volume-from.
|
// destination because of a --volume-from.
|
||||||
if container.isDestinationMounted(mp.Destination) {
|
if container.IsDestinationMounted(mp.Destination) {
|
||||||
continue
|
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
|
// // never attempt to copy existing content in a container FS to a shared volume
|
||||||
// if v.DriverName() == volume.DefaultDriverName {
|
// 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
|
// return err
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Add it to container.MountPoints
|
// Add it to container.MountPoints
|
||||||
container.addMountPointWithVolume(mp.Destination, v, mp.RW)
|
container.AddMountPointWithVolume(mp.Destination, v, mp.RW)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
118
daemon/daemon.go
118
daemon/daemon.go
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/docker/docker/api"
|
"github.com/docker/docker/api"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/cliconfig"
|
"github.com/docker/docker/cliconfig"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/events"
|
"github.com/docker/docker/daemon/events"
|
||||||
"github.com/docker/docker/daemon/exec"
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
|
|
@ -84,17 +85,17 @@ func (e ErrImageDoesNotExist) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type contStore struct {
|
type contStore struct {
|
||||||
s map[string]*Container
|
s map[string]*container.Container
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *contStore) Add(id string, cont *Container) {
|
func (c *contStore) Add(id string, cont *container.Container) {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
c.s[id] = cont
|
c.s[id] = cont
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *contStore) Get(id string) *Container {
|
func (c *contStore) Get(id string) *container.Container {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
res := c.s[id]
|
res := c.s[id]
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
|
|
@ -107,7 +108,7 @@ func (c *contStore) Delete(id string) {
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *contStore) List() []*Container {
|
func (c *contStore) List() []*container.Container {
|
||||||
containers := new(History)
|
containers := new(History)
|
||||||
c.Lock()
|
c.Lock()
|
||||||
for _, cont := range c.s {
|
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
|
// - A partial container ID prefix (e.g. short ID) of any length that is
|
||||||
// unique enough to only return a single container object
|
// unique enough to only return a single container object
|
||||||
// If none of these searches succeed, an error is returned
|
// 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 {
|
if containerByID := daemon.containers.Get(prefixOrName); containerByID != nil {
|
||||||
// prefix is an exact match to a full container ID
|
// prefix is an exact match to a full container ID
|
||||||
return containerByID, nil
|
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.
|
// IsPaused returns a bool indicating if the specified container is paused.
|
||||||
func (daemon *Daemon) IsPaused(id string) bool {
|
func (daemon *Daemon) IsPaused(id string) bool {
|
||||||
c, _ := daemon.Get(id)
|
c, _ := daemon.Get(id)
|
||||||
return c.State.isPaused()
|
return c.State.IsPaused()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) containerRoot(id string) string {
|
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
|
// Load reads the contents of a container from disk
|
||||||
// This is typically done at startup.
|
// 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)
|
container := daemon.newBaseContainer(id)
|
||||||
|
|
||||||
if err := container.fromDisk(); err != nil {
|
if err := container.FromDisk(); err != nil {
|
||||||
return nil, err
|
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 <container.ID>
|
// Register makes a container object usable by the daemon as <container.ID>
|
||||||
func (daemon *Daemon) Register(container *Container) error {
|
func (daemon *Daemon) Register(container *container.Container) error {
|
||||||
if daemon.Exists(container.ID) {
|
if daemon.Exists(container.ID) {
|
||||||
return fmt.Errorf("Container is already loaded")
|
return fmt.Errorf("Container is already loaded")
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +239,7 @@ func (daemon *Daemon) Register(container *Container) error {
|
||||||
if container.IsRunning() {
|
if container.IsRunning() {
|
||||||
logrus.Debugf("killing old running container %s", container.ID)
|
logrus.Debugf("killing old running container %s", container.ID)
|
||||||
// Set exit code to 128 + SIGKILL (9) to properly represent unsuccessful exit
|
// 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
|
// use the current driver and ensure that the container is dead x.x
|
||||||
cmd := &execdriver.Command{
|
cmd := &execdriver.Command{
|
||||||
CommonCommand: execdriver.CommonCommand{
|
CommonCommand: execdriver.CommonCommand{
|
||||||
|
|
@ -247,10 +248,10 @@ func (daemon *Daemon) Register(container *Container) error {
|
||||||
}
|
}
|
||||||
daemon.execDriver.Terminate(cmd)
|
daemon.execDriver.Terminate(cmd)
|
||||||
|
|
||||||
container.unmountIpcMounts(mount.Unmount)
|
container.UnmountIpcMounts(mount.Unmount)
|
||||||
|
|
||||||
daemon.Unmount(container)
|
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)
|
logrus.Errorf("Error saving stopped state to disk: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -262,7 +263,7 @@ func (daemon *Daemon) Register(container *Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) ensureName(container *Container) error {
|
func (daemon *Daemon) ensureName(container *container.Container) error {
|
||||||
if container.Name == "" {
|
if container.Name == "" {
|
||||||
name, err := daemon.generateNewName(container.ID)
|
name, err := daemon.generateNewName(container.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -270,7 +271,7 @@ func (daemon *Daemon) ensureName(container *Container) error {
|
||||||
}
|
}
|
||||||
container.Name = name
|
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)
|
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 {
|
func (daemon *Daemon) restore() error {
|
||||||
type cr struct {
|
type cr struct {
|
||||||
container *Container
|
container *container.Container
|
||||||
registered bool
|
registered bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -336,7 +337,7 @@ func (daemon *Daemon) restore() error {
|
||||||
for _, c := range containers {
|
for _, c := range containers {
|
||||||
group.Add(1)
|
group.Add(1)
|
||||||
|
|
||||||
go func(container *Container, registered bool) {
|
go func(container *container.Container, registered bool) {
|
||||||
defer group.Done()
|
defer group.Done()
|
||||||
|
|
||||||
if !registered {
|
if !registered {
|
||||||
|
|
@ -355,7 +356,7 @@ func (daemon *Daemon) restore() error {
|
||||||
|
|
||||||
// check the restart policy on the containers and restart any container with
|
// check the restart policy on the containers and restart any container with
|
||||||
// the restart policy of "always"
|
// the restart policy of "always"
|
||||||
if daemon.configStore.AutoRestart && container.shouldRestart() {
|
if daemon.configStore.AutoRestart && container.ShouldRestart() {
|
||||||
logrus.Debugf("Starting container %s", container.ID)
|
logrus.Debugf("Starting container %s", container.ID)
|
||||||
|
|
||||||
if err := daemon.containerStart(container); err != nil {
|
if err := daemon.containerStart(container); err != nil {
|
||||||
|
|
@ -474,7 +475,7 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *stringutils.StrSlic
|
||||||
return cmdSlice[0], cmdSlice[1:]
|
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 (
|
var (
|
||||||
id string
|
id string
|
||||||
err error
|
err error
|
||||||
|
|
@ -493,7 +494,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID
|
||||||
base.Path = entrypoint
|
base.Path = entrypoint
|
||||||
base.Args = args //FIXME: de-duplicate from config
|
base.Args = args //FIXME: de-duplicate from config
|
||||||
base.Config = config
|
base.Config = config
|
||||||
base.hostConfig = &runconfig.HostConfig{}
|
base.HostConfig = &runconfig.HostConfig{}
|
||||||
base.ImageID = imgID
|
base.ImageID = imgID
|
||||||
base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName}
|
base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName}
|
||||||
base.Name = name
|
base.Name = name
|
||||||
|
|
@ -516,7 +517,7 @@ func GetFullContainerName(name string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByName returns a container given a name.
|
// 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)
|
fullName, err := GetFullContainerName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// children returns all child containers of the container with the
|
||||||
// given name. The containers are returned as a map from the container
|
// given name. The containers are returned as a map from the container
|
||||||
// name to a pointer to 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)
|
name, err := GetFullContainerName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
err = daemon.containerGraphDB.Walk(name, func(p string, e *graphdb.Entity) error {
|
||||||
c, err := daemon.Get(e.ID())
|
c, err := daemon.Get(e.ID())
|
||||||
|
|
@ -609,7 +610,7 @@ func (daemon *Daemon) parents(name string) ([]string, error) {
|
||||||
return daemon.containerGraphDB.Parents(name)
|
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)
|
fullName := filepath.Join(parent.Name, alias)
|
||||||
if !daemon.containerGraphDB.Exists(fullName) {
|
if !daemon.containerGraphDB.Exists(fullName) {
|
||||||
_, err := daemon.containerGraphDB.Set(fullName, child.ID)
|
_, 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.ID = trustKey.PublicKey().KeyID()
|
||||||
d.repository = daemonRepo
|
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.execCommands = exec.NewStore()
|
||||||
d.tagStore = tagStore
|
d.tagStore = tagStore
|
||||||
d.distributionPool = distributionPool
|
d.distributionPool = distributionPool
|
||||||
|
|
@ -861,9 +862,9 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
||||||
return d, nil
|
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
|
// TODO(windows): Handle docker restart with paused containers
|
||||||
if c.isPaused() {
|
if c.IsPaused() {
|
||||||
// To terminate a process in freezer cgroup, we should send
|
// To terminate a process in freezer cgroup, we should send
|
||||||
// SIGTERM to this process then unfreeze it, and the process will
|
// SIGTERM to this process then unfreeze it, and the process will
|
||||||
// force to terminate immediately.
|
// force to terminate immediately.
|
||||||
|
|
@ -906,20 +907,20 @@ func (daemon *Daemon) Shutdown() error {
|
||||||
if daemon.containers != nil {
|
if daemon.containers != nil {
|
||||||
group := sync.WaitGroup{}
|
group := sync.WaitGroup{}
|
||||||
logrus.Debug("starting clean shutdown of all containers...")
|
logrus.Debug("starting clean shutdown of all containers...")
|
||||||
for _, container := range daemon.List() {
|
for _, cont := range daemon.List() {
|
||||||
if !container.IsRunning() {
|
if !cont.IsRunning() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
logrus.Debugf("stopping %s", container.ID)
|
logrus.Debugf("stopping %s", cont.ID)
|
||||||
group.Add(1)
|
group.Add(1)
|
||||||
go func(c *Container) {
|
go func(c *container.Container) {
|
||||||
defer group.Done()
|
defer group.Done()
|
||||||
if err := daemon.shutdownContainer(c); err != nil {
|
if err := daemon.shutdownContainer(c); err != nil {
|
||||||
logrus.Errorf("Stop container error: %v", err)
|
logrus.Errorf("Stop container error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logrus.Debugf("container stopped %s", c.ID)
|
logrus.Debugf("container stopped %s", c.ID)
|
||||||
}(container)
|
}(cont)
|
||||||
}
|
}
|
||||||
group.Wait()
|
group.Wait()
|
||||||
}
|
}
|
||||||
|
|
@ -948,9 +949,9 @@ func (daemon *Daemon) Shutdown() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount sets container.basefs
|
// Mount sets container.BaseFS
|
||||||
// (is it not set coming in? why is it unset?)
|
// (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
|
var layerID layer.ChainID
|
||||||
if container.ImageID != "" {
|
if container.ImageID != "" {
|
||||||
img, err := daemon.imageStore.Get(container.ImageID)
|
img, err := daemon.imageStore.Get(container.ImageID)
|
||||||
|
|
@ -959,7 +960,7 @@ func (daemon *Daemon) Mount(container *Container) error {
|
||||||
}
|
}
|
||||||
layerID = img.RootFS.ChainID()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -969,56 +970,56 @@ func (daemon *Daemon) Mount(container *Container) error {
|
||||||
}
|
}
|
||||||
logrus.Debugf("container mounted via layerStore: %v", dir)
|
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
|
// 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
|
// volume path for a given mounted layer may change over time. This should only be an error
|
||||||
// on non-Windows operating systems.
|
// on non-Windows operating systems.
|
||||||
if container.basefs != "" && runtime.GOOS != "windows" {
|
if container.BaseFS != "" && runtime.GOOS != "windows" {
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
|
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.BaseFS = dir // TODO: combine these fields
|
||||||
container.rwlayer = rwlayer
|
container.RWLayer = rwlayer
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmount unsets the container base filesystem
|
// 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 {
|
if err := daemon.layerStore.Unmount(container.ID); err != nil {
|
||||||
logrus.Errorf("Error unmounting container %s: %s", container.ID, err)
|
logrus.Errorf("Error unmounting container %s: %s", container.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run uses the execution driver to run a given container
|
// 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{
|
hooks := execdriver.Hooks{
|
||||||
Start: startCallback,
|
Start: startCallback,
|
||||||
}
|
}
|
||||||
hooks.PreStart = append(hooks.PreStart, func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error {
|
hooks.PreStart = append(hooks.PreStart, func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error {
|
||||||
return daemon.setNetworkNamespaceKey(c.ID, pid)
|
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 {
|
func (daemon *Daemon) kill(c *container.Container, sig int) error {
|
||||||
return daemon.execDriver.Kill(c.command, sig)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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()
|
container.Lock()
|
||||||
if err := parseSecurityOpt(container, hostConfig); err != nil {
|
if err := parseSecurityOpt(container, hostConfig); err != nil {
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
|
|
@ -1369,8 +1370,8 @@ func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
container.hostConfig = hostConfig
|
container.HostConfig = hostConfig
|
||||||
container.toDisk()
|
container.ToDisk()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1466,7 +1467,7 @@ func (daemon *Daemon) IsShuttingDown() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContainerStats collects all the stats published by a container
|
// 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)
|
stats, err := daemon.stats(container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1482,7 +1483,7 @@ func (daemon *Daemon) GetContainerStats(container *Container) (*execdriver.Resou
|
||||||
return stats, nil
|
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
|
var list []*libcontainer.NetworkInterface
|
||||||
|
|
||||||
sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
|
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
|
// newBaseContainer creates a new container with its initial
|
||||||
// configuration based on the root storage from the daemon.
|
// configuration based on the root storage from the daemon.
|
||||||
func (daemon *Daemon) newBaseContainer(id string) *Container {
|
func (daemon *Daemon) newBaseContainer(id string) *container.Container {
|
||||||
return newBaseContainer(id, daemon.containerRoot(id))
|
return container.NewBaseContainer(id, daemon.containerRoot(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface {
|
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
|
n.TxDropped = stats.TxDropped
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateID(id string) error {
|
||||||
|
if id == "" {
|
||||||
|
return derr.ErrorCodeEmptyID
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/pkg/graphdb"
|
"github.com/docker/docker/pkg/graphdb"
|
||||||
"github.com/docker/docker/pkg/truncindex"
|
"github.com/docker/docker/pkg/truncindex"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
|
"github.com/docker/docker/volume"
|
||||||
volumedrivers "github.com/docker/docker/volume/drivers"
|
volumedrivers "github.com/docker/docker/volume/drivers"
|
||||||
"github.com/docker/docker/volume/local"
|
"github.com/docker/docker/volume/local"
|
||||||
"github.com/docker/docker/volume/store"
|
"github.com/docker/docker/volume/store"
|
||||||
|
|
@ -18,43 +22,43 @@ import (
|
||||||
//
|
//
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
c1 := &Container{
|
c1 := &container.Container{
|
||||||
CommonContainer: CommonContainer{
|
CommonContainer: container.CommonContainer{
|
||||||
ID: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
|
ID: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
|
||||||
Name: "tender_bardeen",
|
Name: "tender_bardeen",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c2 := &Container{
|
c2 := &container.Container{
|
||||||
CommonContainer: CommonContainer{
|
CommonContainer: container.CommonContainer{
|
||||||
ID: "3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de",
|
ID: "3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de",
|
||||||
Name: "drunk_hawking",
|
Name: "drunk_hawking",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c3 := &Container{
|
c3 := &container.Container{
|
||||||
CommonContainer: CommonContainer{
|
CommonContainer: container.CommonContainer{
|
||||||
ID: "3cdbd1aa394fd68559fd1441d6eff2abfafdcba06e72d2febdba229008b0bf57",
|
ID: "3cdbd1aa394fd68559fd1441d6eff2abfafdcba06e72d2febdba229008b0bf57",
|
||||||
Name: "3cdbd1aa",
|
Name: "3cdbd1aa",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c4 := &Container{
|
c4 := &container.Container{
|
||||||
CommonContainer: CommonContainer{
|
CommonContainer: container.CommonContainer{
|
||||||
ID: "75fb0b800922abdbef2d27e60abcdfaf7fb0698b2a96d22d3354da361a6ff4a5",
|
ID: "75fb0b800922abdbef2d27e60abcdfaf7fb0698b2a96d22d3354da361a6ff4a5",
|
||||||
Name: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
|
Name: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c5 := &Container{
|
c5 := &container.Container{
|
||||||
CommonContainer: CommonContainer{
|
CommonContainer: container.CommonContainer{
|
||||||
ID: "d22d69a2b8960bf7fafdcba06e72d2febdba960bf7fafdcba06e72d2f9008b060b",
|
ID: "d22d69a2b8960bf7fafdcba06e72d2febdba960bf7fafdcba06e72d2f9008b060b",
|
||||||
Name: "d22d69a2b896",
|
Name: "d22d69a2b896",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
store := &contStore{
|
store := &contStore{
|
||||||
s: map[string]*Container{
|
s: map[string]*container.Container{
|
||||||
c1.ID: c1,
|
c1.ID: c1,
|
||||||
c2.ID: c2,
|
c2.ID: c2,
|
||||||
c3.ID: c3,
|
c3.ID: c3,
|
||||||
|
|
@ -136,7 +140,7 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseSecurityOpt(t *testing.T) {
|
func TestParseSecurityOpt(t *testing.T) {
|
||||||
container := &Container{}
|
container := &container.Container{}
|
||||||
config := &runconfig.HostConfig{}
|
config := &runconfig.HostConfig{}
|
||||||
|
|
||||||
// test apparmor
|
// test apparmor
|
||||||
|
|
@ -190,3 +194,109 @@ func TestNetworkOptions(t *testing.T) {
|
||||||
t.Fatalf("Expected networkOptions error, got nil")
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/graphdriver"
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
|
|
@ -57,7 +58,7 @@ func getBlkioWeightDevices(config *runconfig.HostConfig) ([]*blkiodev.WeightDevi
|
||||||
return BlkioWeightDevices, nil
|
return BlkioWeightDevices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error {
|
func parseSecurityOpt(container *container.Container, config *runconfig.HostConfig) error {
|
||||||
var (
|
var (
|
||||||
labelOpts []string
|
labelOpts []string
|
||||||
err error
|
err error
|
||||||
|
|
@ -128,7 +129,7 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *runconfig.HostConfig, a
|
||||||
hostConfig.MemorySwap = hostConfig.Memory * 2
|
hostConfig.MemorySwap = hostConfig.Memory * 2
|
||||||
}
|
}
|
||||||
if hostConfig.ShmSize == nil {
|
if hostConfig.ShmSize == nil {
|
||||||
shmSize := DefaultSHMSize
|
shmSize := container.DefaultSHMSize
|
||||||
hostConfig.ShmSize = &shmSize
|
hostConfig.ShmSize = &shmSize
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
|
|
@ -575,7 +576,7 @@ func setupInitLayer(initLayer string, rootUID, rootGID int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerLinks writes the links to a file.
|
// 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 {
|
if hostConfig == nil || hostConfig.Links == nil {
|
||||||
return 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
|
//An error from daemon.Get() means this name could not be found
|
||||||
return fmt.Errorf("Could not get container for %s", name)
|
return fmt.Errorf("Could not get container for %s", name)
|
||||||
}
|
}
|
||||||
for child.hostConfig.NetworkMode.IsContainer() {
|
for child.HostConfig.NetworkMode.IsContainer() {
|
||||||
parts := strings.SplitN(string(child.hostConfig.NetworkMode), ":", 2)
|
parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2)
|
||||||
child, err = daemon.Get(parts[1])
|
child, err = daemon.Get(parts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Could not get container for %s", parts[1])
|
return fmt.Errorf("Could not get container for %s", parts[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if child.hostConfig.NetworkMode.IsHost() {
|
if child.HostConfig.NetworkMode.IsHost() {
|
||||||
return runconfig.ErrConflictHostNetworkAndLinks
|
return runconfig.ErrConflictHostNetworkAndLinks
|
||||||
}
|
}
|
||||||
if err := daemon.registerLink(container, child, alias); err != nil {
|
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
|
// After we load all the links into the daemon
|
||||||
// set them to nil on the hostconfig
|
// set them to nil on the hostconfig
|
||||||
hostConfig.Links = nil
|
hostConfig.Links = nil
|
||||||
if err := container.writeHostConfig(); err != nil {
|
if err := container.WriteHostConfig(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -617,13 +618,13 @@ func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig.
|
||||||
|
|
||||||
// conditionalMountOnStart is a platform specific helper function during the
|
// conditionalMountOnStart is a platform specific helper function during the
|
||||||
// container start to call mount.
|
// container start to call mount.
|
||||||
func (daemon *Daemon) conditionalMountOnStart(container *Container) error {
|
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
|
||||||
return daemon.Mount(container)
|
return daemon.Mount(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
// conditionalUnmountOnCleanup is a platform specific helper function called
|
// conditionalUnmountOnCleanup is a platform specific helper function called
|
||||||
// during the cleanup of a container to unmount.
|
// during the cleanup of a container to unmount.
|
||||||
func (daemon *Daemon) conditionalUnmountOnCleanup(container *Container) {
|
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) {
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/graphdriver"
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
"github.com/docker/docker/dockerversion"
|
"github.com/docker/docker/dockerversion"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
|
|
@ -34,7 +35,7 @@ func getBlkioWeightDevices(config *runconfig.HostConfig) ([]*blkiodev.WeightDevi
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error {
|
func parseSecurityOpt(container *container.Container, config *runconfig.HostConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,7 +116,7 @@ func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkC
|
||||||
|
|
||||||
// registerLinks sets up links between containers and writes the
|
// registerLinks sets up links between containers and writes the
|
||||||
// configuration out for persistence. As of Windows TP4, links are not supported.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,9 +126,9 @@ func (daemon *Daemon) cleanupMounts() error {
|
||||||
|
|
||||||
// conditionalMountOnStart is a platform specific helper function during the
|
// conditionalMountOnStart is a platform specific helper function during the
|
||||||
// container start to call mount.
|
// 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
|
// 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 {
|
if err := daemon.Mount(container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -137,9 +138,9 @@ func (daemon *Daemon) conditionalMountOnStart(container *Container) error {
|
||||||
|
|
||||||
// conditionalUnmountOnCleanup is a platform specific helper function called
|
// conditionalUnmountOnCleanup is a platform specific helper function called
|
||||||
// during the cleanup of a container to unmount.
|
// 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
|
// We do not unmount if a Hyper-V container
|
||||||
if !container.hostConfig.Isolation.IsHyperV() {
|
if !container.HostConfig.Isolation.IsHyperV() {
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/docker/docker/api"
|
"github.com/docker/docker/api"
|
||||||
"github.com/docker/docker/builder"
|
"github.com/docker/docker/builder"
|
||||||
"github.com/docker/docker/cliconfig"
|
"github.com/docker/docker/cliconfig"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"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`.
|
// 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)
|
return d.Daemon.Get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new Docker container and returns potential warnings
|
// 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{
|
ccr, err := d.Daemon.ContainerCreate(&daemon.ContainerCreateConfig{
|
||||||
Name: "",
|
Name: "",
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
|
|
@ -129,7 +130,7 @@ func (d Docker) Release(sessionID string, activeImages []string) {
|
||||||
// specified by a container object.
|
// specified by a container object.
|
||||||
// TODO: make sure callers don't unnecessarily convert destPath with filepath.FromSlash (Copy does it already).
|
// 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.
|
// 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()
|
srcPath := src.Path()
|
||||||
destExists := true
|
destExists := true
|
||||||
rootUID, rootGID := d.Daemon.GetRemappedUIDGID()
|
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.
|
// 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)
|
return d.Daemon.Kill(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount mounts the root filesystem for the 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)
|
return d.Daemon.Mount(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmount unmounts the root filesystem for the container.
|
// 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)
|
d.Daemon.Unmount(c)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts a container
|
// 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)
|
return d.Daemon.Start(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/layer"
|
"github.com/docker/docker/layer"
|
||||||
volumestore "github.com/docker/docker/volume/store"
|
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.
|
// 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 {
|
if err == derr.ErrorCodeAlreadyRemoving {
|
||||||
// do not fail when the removal is in progress started by other request.
|
// do not fail when the removal is in progress started by other request.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return derr.ErrorCodeRmState.WithArgs(err)
|
return derr.ErrorCodeRmState.WithArgs(err)
|
||||||
}
|
}
|
||||||
defer container.resetRemovalInProgress()
|
defer container.ResetRemovalInProgress()
|
||||||
|
|
||||||
// check if container wasn't deregistered by previous rm since Get
|
// check if container wasn't deregistered by previous rm since Get
|
||||||
if c := daemon.containers.Get(container.ID); c == nil {
|
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
|
// cleanupContainer unregisters a container from the daemon, stops stats
|
||||||
// collection and cleanly removes contents and metadata from the filesystem.
|
// 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 container.IsRunning() {
|
||||||
if !forceRemove {
|
if !forceRemove {
|
||||||
return derr.ErrorCodeRmRunning
|
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.
|
// 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
|
// Save container state to disk. So that if error happens before
|
||||||
// container meta file got removed from disk, then a restart of
|
// container meta file got removed from disk, then a restart of
|
||||||
// docker should not make a dead container alive.
|
// 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)
|
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)
|
return derr.ErrorCodeRmFS.WithArgs(container.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -18,19 +19,19 @@ func TestContainerDoubleDelete(t *testing.T) {
|
||||||
repository: tmp,
|
repository: tmp,
|
||||||
root: tmp,
|
root: tmp,
|
||||||
}
|
}
|
||||||
daemon.containers = &contStore{s: make(map[string]*Container)}
|
daemon.containers = &contStore{s: make(map[string]*container.Container)}
|
||||||
|
|
||||||
container := &Container{
|
container := &container.Container{
|
||||||
CommonContainer: CommonContainer{
|
CommonContainer: container.CommonContainer{
|
||||||
ID: "test",
|
ID: "test",
|
||||||
State: NewState(),
|
State: container.NewState(),
|
||||||
Config: &runconfig.Config{},
|
Config: &runconfig.Config{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
daemon.containers.Add(container.ID, container)
|
daemon.containers.Add(container.ID, container)
|
||||||
|
|
||||||
// Mark the container as having a delete in progress
|
// Mark the container as having a delete in progress
|
||||||
if err := container.setRemovalInProgress(); err != nil {
|
if err := container.SetRemovalInProgress(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/container"
|
||||||
|
)
|
||||||
|
|
||||||
// LogContainerEvent generates an event related to a 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(
|
daemon.EventsService.Log(
|
||||||
action,
|
action,
|
||||||
container.ID,
|
container.ID,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/exec"
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
|
|
@ -15,15 +16,15 @@ import (
|
||||||
"github.com/docker/docker/runconfig"
|
"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.
|
// 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.
|
// Storing execs in daemon for easy access via remote API.
|
||||||
d.execCommands.Add(config.ID, config)
|
d.execCommands.Add(config.ID, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecExists looks up the exec instance and returns a bool if it exists or not.
|
// 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) {
|
func (d *Daemon) ExecExists(name string) (bool, error) {
|
||||||
if _, err := d.getExecConfig(name); err != nil {
|
if _, err := d.getExecConfig(name); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
@ -47,7 +48,7 @@ func (d *Daemon) getExecConfig(name string) (*exec.Config, error) {
|
||||||
if !container.IsRunning() {
|
if !container.IsRunning() {
|
||||||
return nil, derr.ErrorCodeContainerNotRunning.WithArgs(container.ID, container.State.String())
|
return nil, derr.ErrorCodeContainerNotRunning.WithArgs(container.ID, container.State.String())
|
||||||
}
|
}
|
||||||
if container.isPaused() {
|
if container.IsPaused() {
|
||||||
return nil, derr.ErrorCodeExecPaused.WithArgs(container.ID)
|
return nil, derr.ErrorCodeExecPaused.WithArgs(container.ID)
|
||||||
}
|
}
|
||||||
return ec, nil
|
return ec, nil
|
||||||
|
|
@ -57,12 +58,12 @@ func (d *Daemon) getExecConfig(name string) (*exec.Config, error) {
|
||||||
return nil, derr.ErrorCodeNoExecID.WithArgs(name)
|
return nil, derr.ErrorCodeNoExecID.WithArgs(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Daemon) unregisterExecCommand(container *Container, execConfig *exec.Config) {
|
func (d *Daemon) unregisterExecCommand(container *container.Container, execConfig *exec.Config) {
|
||||||
container.execCommands.Delete(execConfig.ID)
|
container.ExecCommands.Delete(execConfig.ID)
|
||||||
d.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)
|
container, err := d.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -71,7 +72,7 @@ func (d *Daemon) getActiveContainer(name string) (*Container, error) {
|
||||||
if !container.IsRunning() {
|
if !container.IsRunning() {
|
||||||
return nil, derr.ErrorCodeNotRunning.WithArgs(name)
|
return nil, derr.ErrorCodeNotRunning.WithArgs(name)
|
||||||
}
|
}
|
||||||
if container.isPaused() {
|
if container.IsPaused() {
|
||||||
return nil, derr.ErrorCodeExecPaused.WithArgs(name)
|
return nil, derr.ErrorCodeExecPaused.WithArgs(name)
|
||||||
}
|
}
|
||||||
return container, nil
|
return container, nil
|
||||||
|
|
@ -131,9 +132,9 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
|
||||||
ec.Running = true
|
ec.Running = true
|
||||||
ec.Unlock()
|
ec.Unlock()
|
||||||
|
|
||||||
container := d.containers.Get(ec.ContainerID)
|
c := d.containers.Get(ec.ContainerID)
|
||||||
logrus.Debugf("starting exec command %s in container %s", ec.ID, container.ID)
|
logrus.Debugf("starting exec command %s in container %s", ec.ID, c.ID)
|
||||||
d.LogContainerEvent(container, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " "))
|
d.LogContainerEvent(c, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " "))
|
||||||
|
|
||||||
if ec.OpenStdin {
|
if ec.OpenStdin {
|
||||||
r, w := io.Pipe()
|
r, w := io.Pipe()
|
||||||
|
|
@ -157,8 +158,7 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
|
||||||
ec.NewNopInputPipe()
|
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)
|
execErr := make(chan error)
|
||||||
|
|
||||||
// Note, the ExecConfig data will be removed when the container
|
// 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.
|
// the exitStatus) even after the cmd is done running.
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
execErr <- d.containerExec(container, ec)
|
execErr <- d.containerExec(c, ec)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
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
|
// Maybe the container stopped while we were trying to exec
|
||||||
if !container.IsRunning() {
|
if !c.IsRunning() {
|
||||||
return derr.ErrorCodeExecContainerStopped
|
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
|
// 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{
|
hooks := execdriver.Hooks{
|
||||||
Start: startCallback,
|
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
|
// On err, make sure we don't leave ExitCode at zero
|
||||||
if err != nil && exitStatus == 0 {
|
if err != nil && exitStatus == 0 {
|
||||||
|
|
@ -238,14 +238,14 @@ func (d *Daemon) execCommandGC() {
|
||||||
func (d *Daemon) containerExecIds() map[string]struct{} {
|
func (d *Daemon) containerExecIds() map[string]struct{} {
|
||||||
ids := map[string]struct{}{}
|
ids := map[string]struct{}{}
|
||||||
for _, c := range d.containers.List() {
|
for _, c := range d.containers.List() {
|
||||||
for _, id := range c.execCommands.List() {
|
for _, id := range c.ExecCommands.List() {
|
||||||
ids[id] = struct{}{}
|
ids[id] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ids
|
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()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
|
@ -268,7 +268,7 @@ func (d *Daemon) containerExec(container *Container, ec *exec.Config) error {
|
||||||
return ec.Wait(cErr)
|
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)
|
pipes := execdriver.NewPipes(execConfig.Stdin(), execConfig.Stdout(), execConfig.Stderr(), execConfig.OpenStdin)
|
||||||
exitCode, err := d.Exec(container, execConfig, pipes, callback)
|
exitCode, err := d.Exec(container, execConfig, pipes, callback)
|
||||||
if err != nil {
|
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
|
// remove the exec command from the container's store only and not the
|
||||||
// daemon's store so that the exec command can be inspected.
|
// daemon's store so that the exec command can be inspected.
|
||||||
container.execCommands.Delete(execConfig.ID)
|
container.ExecCommands.Delete(execConfig.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,14 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
// setPlatformSpecificExecProcessConfig sets platform-specific fields in the
|
// setPlatformSpecificExecProcessConfig sets platform-specific fields in the
|
||||||
// ProcessConfig structure.
|
// 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
|
user := config.User
|
||||||
if len(user) == 0 {
|
if len(user) == 0 {
|
||||||
user = container.Config.User
|
user = container.Config.User
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
// setPlatformSpecificExecProcessConfig sets platform-specific fields in the
|
// setPlatformSpecificExecProcessConfig sets platform-specific fields in the
|
||||||
// ProcessConfig structure. This is a no-op on Windows
|
// 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) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
|
@ -29,13 +30,13 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error {
|
||||||
return nil
|
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 {
|
if err := daemon.Mount(container); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
uidMaps, gidMaps := daemon.GetUIDGIDMaps()
|
uidMaps, gidMaps := daemon.GetUIDGIDMaps()
|
||||||
archive, err := archive.TarWithOptions(container.basefs, &archive.TarOptions{
|
archive, err := archive.TarWithOptions(container.BaseFS, &archive.TarOptions{
|
||||||
Compression: archive.Uncompressed,
|
Compression: archive.Uncompressed,
|
||||||
UIDMaps: uidMaps,
|
UIDMaps: uidMaps,
|
||||||
GIDMaps: gidMaps,
|
GIDMaps: gidMaps,
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
// History is a convenience type for storing a list of containers,
|
// History is a convenience type for storing a list of containers,
|
||||||
// ordered by creation date.
|
// ordered by creation date.
|
||||||
type History []*Container
|
type History []*container.Container
|
||||||
|
|
||||||
func (history *History) Len() int {
|
func (history *History) Len() int {
|
||||||
return len(*history)
|
return len(*history)
|
||||||
|
|
@ -23,7 +25,7 @@ func (history *History) Swap(i, j int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the given container to history.
|
// Add the given container to history.
|
||||||
func (history *History) Add(container *Container) {
|
func (history *History) Add(container *container.Container) {
|
||||||
*history = append(*history, container)
|
*history = append(*history, container)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"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
|
// getContainerUsingImage returns a container that was created using the given
|
||||||
// imageID. Returns nil if there is no such container.
|
// 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() {
|
for _, container := range daemon.List() {
|
||||||
if container.ImageID == imageID {
|
if container.ImageID == imageID {
|
||||||
return container
|
return container
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/versions/v1p20"
|
"github.com/docker/docker/api/types/versions/v1p20"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/exec"
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/docker/docker/daemon/network"
|
"github.com/docker/docker/daemon/network"
|
||||||
"github.com/docker/docker/layer"
|
"github.com/docker/docker/layer"
|
||||||
|
|
@ -85,7 +86,7 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er
|
||||||
MacAddress: container.Config.MacAddress,
|
MacAddress: container.Config.MacAddress,
|
||||||
NetworkDisabled: container.Config.NetworkDisabled,
|
NetworkDisabled: container.Config.NetworkDisabled,
|
||||||
ExposedPorts: container.Config.ExposedPorts,
|
ExposedPorts: container.Config.ExposedPorts,
|
||||||
VolumeDriver: container.hostConfig.VolumeDriver,
|
VolumeDriver: container.HostConfig.VolumeDriver,
|
||||||
}
|
}
|
||||||
networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
|
networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
|
||||||
|
|
||||||
|
|
@ -97,9 +98,9 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er
|
||||||
}, nil
|
}, 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
|
// make a copy to play with
|
||||||
hostConfig := *container.hostConfig
|
hostConfig := *container.HostConfig
|
||||||
|
|
||||||
if children, err := daemon.children(container.Name); err == nil {
|
if children, err := daemon.children(container.Name); err == nil {
|
||||||
for linkAlias, child := range children {
|
for linkAlias, child := range children {
|
||||||
|
|
@ -143,7 +144,7 @@ func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.Co
|
||||||
Driver: container.Driver,
|
Driver: container.Driver,
|
||||||
MountLabel: container.MountLabel,
|
MountLabel: container.MountLabel,
|
||||||
ProcessLabel: container.ProcessLabel,
|
ProcessLabel: container.ProcessLabel,
|
||||||
ExecIDs: container.getExecIDs(),
|
ExecIDs: container.GetExecIDs(),
|
||||||
HostConfig: &hostConfig,
|
HostConfig: &hostConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,11 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/versions/v1p19"
|
"github.com/docker/docker/api/types/versions/v1p19"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This sets platform-specific fields
|
// 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.AppArmorProfile = container.AppArmorProfile
|
||||||
contJSONBase.ResolvConfPath = container.ResolvConfPath
|
contJSONBase.ResolvConfPath = container.ResolvConfPath
|
||||||
contJSONBase.HostnamePath = container.HostnamePath
|
contJSONBase.HostnamePath = container.HostnamePath
|
||||||
|
|
@ -44,11 +45,11 @@ func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON,
|
||||||
MacAddress: container.Config.MacAddress,
|
MacAddress: container.Config.MacAddress,
|
||||||
NetworkDisabled: container.Config.NetworkDisabled,
|
NetworkDisabled: container.Config.NetworkDisabled,
|
||||||
ExposedPorts: container.Config.ExposedPorts,
|
ExposedPorts: container.Config.ExposedPorts,
|
||||||
VolumeDriver: container.hostConfig.VolumeDriver,
|
VolumeDriver: container.HostConfig.VolumeDriver,
|
||||||
Memory: container.hostConfig.Memory,
|
Memory: container.HostConfig.Memory,
|
||||||
MemorySwap: container.hostConfig.MemorySwap,
|
MemorySwap: container.HostConfig.MemorySwap,
|
||||||
CPUShares: container.hostConfig.CPUShares,
|
CPUShares: container.HostConfig.CPUShares,
|
||||||
CPUSet: container.hostConfig.CpusetCpus,
|
CPUSet: container.HostConfig.CpusetCpus,
|
||||||
}
|
}
|
||||||
networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
|
networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
|
||||||
|
|
||||||
|
|
@ -61,7 +62,7 @@ func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addMountPoints(container *Container) []types.MountPoint {
|
func addMountPoints(container *container.Container) []types.MountPoint {
|
||||||
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
|
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
|
||||||
for _, m := range container.MountPoints {
|
for _, m := range container.MountPoints {
|
||||||
mountPoints = append(mountPoints, types.MountPoint{
|
mountPoints = append(mountPoints, types.MountPoint{
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
package daemon
|
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
|
// 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
|
return contJSONBase
|
||||||
}
|
}
|
||||||
|
|
||||||
func addMountPoints(container *Container) []types.MountPoint {
|
func addMountPoints(container *container.Container) []types.MountPoint {
|
||||||
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
|
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
|
||||||
for _, m := range container.MountPoints {
|
for _, m := range container.MountPoints {
|
||||||
mountPoints = append(mountPoints, types.MountPoint{
|
mountPoints = append(mountPoints, types.MountPoint{
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"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
|
// 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
|
// or not running, or if there is a problem returned from the
|
||||||
// underlying kill command.
|
// 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)
|
logrus.Debugf("Sending %d to %s", sig, container.ID)
|
||||||
container.Lock()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
@ -69,7 +70,7 @@ func (daemon *Daemon) killWithSignal(container *Container, sig int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kill forcefully terminates a container.
|
// Kill forcefully terminates a container.
|
||||||
func (daemon *Daemon) Kill(container *Container) error {
|
func (daemon *Daemon) Kill(container *container.Container) error {
|
||||||
if !container.IsRunning() {
|
if !container.IsRunning() {
|
||||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
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.
|
// 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)
|
err := daemon.killWithSignal(container, sig)
|
||||||
if err == syscall.ESRCH {
|
if err == syscall.ESRCH {
|
||||||
logrus.Debugf("Cannot kill process (pid=%d) with signal %d: no such process.", container.GetPID(), sig)
|
logrus.Debugf("Cannot kill process (pid=%d) with signal %d: no such process.", container.GetPID(), sig)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/image"
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/pkg/graphdb"
|
"github.com/docker/docker/pkg/graphdb"
|
||||||
"github.com/docker/docker/pkg/nat"
|
"github.com/docker/docker/pkg/nat"
|
||||||
|
|
@ -19,7 +20,7 @@ type iterationAction int
|
||||||
|
|
||||||
// containerReducer represents a reducer for a container.
|
// containerReducer represents a reducer for a container.
|
||||||
// Returns the object to serialize by the api.
|
// 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 (
|
const (
|
||||||
// includeContainer is the action to include a container in the reducer.
|
// includeContainer is the action to include a container in the reducer.
|
||||||
|
|
@ -34,7 +35,7 @@ const (
|
||||||
var errStopIteration = errors.New("container list iteration stopped")
|
var errStopIteration = errors.New("container list iteration stopped")
|
||||||
|
|
||||||
// List returns an array of all containers registered in the daemon.
|
// 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()
|
return daemon.containers.List()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,10 +72,10 @@ type listContext struct {
|
||||||
exitAllowed []int
|
exitAllowed []int
|
||||||
// beforeFilter is a filter to ignore containers that appear before the one given
|
// 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.
|
// 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
|
// 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.
|
// 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 is the filters set by the user
|
||||||
*ContainersConfig
|
*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.
|
// 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()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
|
@ -148,7 +149,7 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = psFilters.WalkValues("status", func(value string) 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)
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var beforeContFilter, sinceContFilter *Container
|
var beforeContFilter, sinceContFilter *container.Container
|
||||||
err = psFilters.WalkValues("before", func(value string) error {
|
err = psFilters.WalkValues("before", func(value string) error {
|
||||||
beforeContFilter, err = daemon.Get(value)
|
beforeContFilter, err = daemon.Get(value)
|
||||||
return err
|
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.
|
// 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.
|
// 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
|
// 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 {
|
if !container.Running && !ctx.All && ctx.Limit <= 0 && ctx.beforeFilter == nil && ctx.sinceFilter == nil {
|
||||||
return excludeContainer
|
return excludeContainer
|
||||||
|
|
@ -309,7 +310,7 @@ func includeContainerInList(container *Container, ctx *listContext) iterationAct
|
||||||
}
|
}
|
||||||
|
|
||||||
// transformContainer generates the container type expected by the docker ps command.
|
// 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{
|
newC := &types.Container{
|
||||||
ID: container.ID,
|
ID: container.ID,
|
||||||
Names: ctx.names[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.Created = container.Created.Unix()
|
||||||
newC.Status = container.State.String()
|
newC.Status = container.State.String()
|
||||||
newC.HostConfig.NetworkMode = string(container.hostConfig.NetworkMode)
|
newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
|
||||||
|
|
||||||
newC.Ports = []types.Port{}
|
newC.Ports = []types.Port{}
|
||||||
for port, bindings := range container.NetworkSettings.Ports {
|
for port, bindings := range container.NetworkSettings.Ports {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
|
import "github.com/docker/docker/container"
|
||||||
|
|
||||||
// excludeByIsolation is a platform specific helper function to support PS
|
// 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.
|
// 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
|
return includeContainer
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
|
)
|
||||||
|
|
||||||
// excludeByIsolation is a platform specific helper function to support PS
|
// 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.
|
// 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 {
|
||||||
i := strings.ToLower(string(container.hostConfig.Isolation))
|
i := strings.ToLower(string(container.HostConfig.Isolation))
|
||||||
if i == "" {
|
if i == "" {
|
||||||
i = "default"
|
i = "default"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/logger"
|
"github.com/docker/docker/daemon/logger"
|
||||||
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
||||||
derr "github.com/docker/docker/errors"
|
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) {
|
func (daemon *Daemon) getLogger(container *container.Container) (logger.Logger, error) {
|
||||||
if container.logDriver != nil && container.IsRunning() {
|
if container.LogDriver != nil && container.IsRunning() {
|
||||||
return container.logDriver, nil
|
return container.LogDriver, nil
|
||||||
}
|
}
|
||||||
cfg := container.getLogConfig(daemon.defaultLogConfig)
|
cfg := container.GetLogConfig(daemon.defaultLogConfig)
|
||||||
if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil {
|
if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -111,8 +112,8 @@ func (daemon *Daemon) getLogger(container *Container) (logger.Logger, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartLogging initializes and starts the container logging stream.
|
// StartLogging initializes and starts the container logging stream.
|
||||||
func (daemon *Daemon) StartLogging(container *Container) error {
|
func (daemon *Daemon) StartLogging(container *container.Container) error {
|
||||||
cfg := container.getLogConfig(daemon.defaultLogConfig)
|
cfg := container.GetLogConfig(daemon.defaultLogConfig)
|
||||||
if cfg.Type == "none" {
|
if cfg.Type == "none" {
|
||||||
return nil // do not start logging routines
|
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)
|
copier := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
|
||||||
container.logCopier = copier
|
container.LogCopier = copier
|
||||||
copier.Run()
|
copier.Run()
|
||||||
container.logDriver = l
|
container.LogDriver = l
|
||||||
|
|
||||||
// set LogPath field only for json-file logdriver
|
// set LogPath field only for json-file logdriver
|
||||||
if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok {
|
if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
volumestore "github.com/docker/docker/volume/store"
|
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 {
|
for _, config := range container.MountPoints {
|
||||||
if len(config.Driver) > 0 {
|
if len(config.Driver) > 0 {
|
||||||
v, err := daemon.createVolume(config.Name, config.Driver, nil)
|
v, err := daemon.createVolume(config.Name, config.Driver, nil)
|
||||||
|
|
@ -20,7 +21,7 @@ func (daemon *Daemon) prepareMountPoints(container *Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) removeMountPoints(container *Container, rm bool) error {
|
func (daemon *Daemon) removeMountPoints(container *container.Container, rm bool) error {
|
||||||
var rmErrors []string
|
var rmErrors []string
|
||||||
for _, m := range container.MountPoints {
|
for _, m := range container.MountPoints {
|
||||||
if m.Volume == nil {
|
if m.Volume == nil {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
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.
|
// containerPause pauses the container execution without stopping the process.
|
||||||
// The execution can be resumed by calling containerUnpause.
|
// 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()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
|
@ -34,7 +35,7 @@ func (daemon *Daemon) containerPause(container *Container) error {
|
||||||
return derr.ErrorCodeAlreadyPaused.WithArgs(container.ID)
|
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
|
return err
|
||||||
}
|
}
|
||||||
container.Paused = true
|
container.Paused = true
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,15 @@ import (
|
||||||
// reserved.
|
// reserved.
|
||||||
func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
||||||
var (
|
var (
|
||||||
err error
|
sid string
|
||||||
sid string
|
sb libnetwork.Sandbox
|
||||||
sb libnetwork.Sandbox
|
|
||||||
container *Container
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if oldName == "" || newName == "" {
|
if oldName == "" || newName == "" {
|
||||||
return derr.ErrorCodeEmptyRename
|
return derr.ErrorCodeEmptyRename
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err = daemon.Get(oldName)
|
container, err := daemon.Get(oldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +48,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
||||||
return derr.ErrorCodeRenameDelete.WithArgs(oldName, err)
|
return derr.ErrorCodeRenameDelete.WithArgs(oldName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = container.toDisk(); err != nil {
|
if err = container.ToDisk(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +60,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
container.Name = oldName
|
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)
|
logrus.Errorf("%s: Failed in writing to Disk on rename failure: %v", container.ID, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
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
|
// container. When stopping, wait for the given duration in seconds to
|
||||||
// gracefully stop, before forcefully terminating the container. If
|
// gracefully stop, before forcefully terminating the container. If
|
||||||
// given a negative duration, wait forever for a graceful stop.
|
// 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
|
// Avoid unnecessarily unmounting and then directly mounting
|
||||||
// the container when the container stops and then starts
|
// the container when the container stops and then starts
|
||||||
// again
|
// again
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/pkg/promise"
|
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.isPaused() {
|
if container.IsPaused() {
|
||||||
return derr.ErrorCodeStartPaused
|
return derr.ErrorCodeStartPaused
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf
|
||||||
if err := daemon.setHostConfig(container, hostConfig); err != nil {
|
if err := daemon.setHostConfig(container, hostConfig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
initDNSHostConfig(container)
|
container.InitDNSHostConfig()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if hostConfig != nil {
|
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.
|
// check if hostConfig is in line with the current system settings.
|
||||||
// It may happen cgroups are umounted or the like.
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts a container
|
// Start starts a container
|
||||||
func (daemon *Daemon) Start(container *Container) error {
|
func (daemon *Daemon) Start(container *container.Container) error {
|
||||||
return daemon.containerStart(container)
|
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
|
// container needs, such as storage and networking, as well as links
|
||||||
// between containers. The container is left waiting for a signal to
|
// between containers. The container is left waiting for a signal to
|
||||||
// begin running.
|
// begin running.
|
||||||
func (daemon *Daemon) containerStart(container *Container) (err error) {
|
func (daemon *Daemon) containerStart(container *container.Container) (err error) {
|
||||||
container.Lock()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
|
@ -80,7 +80,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.removalInProgress || container.Dead {
|
if container.RemovalInProgress || container.Dead {
|
||||||
return derr.ErrorCodeContainerBeingRemoved
|
return derr.ErrorCodeContainerBeingRemoved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,12 +88,12 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
|
||||||
// setup has been cleaned up properly
|
// setup has been cleaned up properly
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
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 no one else has set it, make sure we don't leave it at zero
|
||||||
if container.ExitCode == 0 {
|
if container.ExitCode == 0 {
|
||||||
container.ExitCode = 128
|
container.ExitCode = 128
|
||||||
}
|
}
|
||||||
container.toDisk()
|
container.ToDisk()
|
||||||
daemon.Cleanup(container)
|
daemon.Cleanup(container)
|
||||||
daemon.LogContainerEvent(container, "die")
|
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
|
// Make sure NetworkMode has an acceptable value. We do this to ensure
|
||||||
// backwards API compatibility.
|
// backwards API compatibility.
|
||||||
container.hostConfig = runconfig.SetDefaultNetModeIfBlank(container.hostConfig)
|
container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig)
|
||||||
|
|
||||||
if err := daemon.initializeNetworking(container); err != nil {
|
if err := daemon.initializeNetworking(container); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -114,15 +114,15 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := container.setupWorkingDirectory(); err != nil {
|
if err := container.SetupWorkingDirectory(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
env := container.createDaemonEnvironment(linkedEnv)
|
env := container.CreateDaemonEnvironment(linkedEnv)
|
||||||
if err := daemon.populateCommand(container, env); err != nil {
|
if err := daemon.populateCommand(container, env); err != nil {
|
||||||
return err
|
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 {
|
if err := daemon.setupIpcDirs(container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -132,10 +132,10 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mounts = append(mounts, container.ipcMounts()...)
|
mounts = append(mounts, container.IpcMounts()...)
|
||||||
mounts = append(mounts, container.tmpfsMounts()...)
|
mounts = append(mounts, container.TmpfsMounts()...)
|
||||||
|
|
||||||
container.command.Mounts = mounts
|
container.Command.Mounts = mounts
|
||||||
if err := daemon.waitForStart(container); err != nil {
|
if err := daemon.waitForStart(container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -143,34 +143,24 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) waitForStart(container *Container) error {
|
func (daemon *Daemon) waitForStart(container *container.Container) error {
|
||||||
container.monitor = daemon.newContainerMonitor(container, container.hostConfig.RestartPolicy)
|
return container.StartMonitor(daemon, 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup releases any network resources allocated to the container along with any rules
|
// 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.
|
// 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)
|
daemon.releaseNetwork(container)
|
||||||
|
|
||||||
container.unmountIpcMounts(detachMounted)
|
container.UnmountIpcMounts(detachMounted)
|
||||||
|
|
||||||
daemon.conditionalUnmountOnCleanup(container)
|
daemon.conditionalUnmountOnCleanup(container)
|
||||||
|
|
||||||
for _, eConfig := range container.execCommands.Commands() {
|
for _, eConfig := range container.ExecCommands.Commands() {
|
||||||
daemon.unregisterExecCommand(container, eConfig)
|
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)
|
logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/pkg/pubsub"
|
"github.com/docker/docker/pkg/pubsub"
|
||||||
|
|
@ -19,7 +20,7 @@ import (
|
||||||
|
|
||||||
type statsSupervisor interface {
|
type statsSupervisor interface {
|
||||||
// GetContainerStats collects all the stats related to a container
|
// 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
|
// newStatsCollector returns a new statsCollector that collections
|
||||||
|
|
@ -30,7 +31,7 @@ func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector
|
||||||
s := &statsCollector{
|
s := &statsCollector{
|
||||||
interval: interval,
|
interval: interval,
|
||||||
supervisor: daemon,
|
supervisor: daemon,
|
||||||
publishers: make(map[*Container]*pubsub.Publisher),
|
publishers: make(map[*container.Container]*pubsub.Publisher),
|
||||||
clockTicksPerSecond: uint64(system.GetClockTicks()),
|
clockTicksPerSecond: uint64(system.GetClockTicks()),
|
||||||
bufReader: bufio.NewReaderSize(nil, 128),
|
bufReader: bufio.NewReaderSize(nil, 128),
|
||||||
}
|
}
|
||||||
|
|
@ -44,14 +45,14 @@ type statsCollector struct {
|
||||||
supervisor statsSupervisor
|
supervisor statsSupervisor
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
clockTicksPerSecond uint64
|
clockTicksPerSecond uint64
|
||||||
publishers map[*Container]*pubsub.Publisher
|
publishers map[*container.Container]*pubsub.Publisher
|
||||||
bufReader *bufio.Reader
|
bufReader *bufio.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect registers the container with the collector and adds it to
|
// collect registers the container with the collector and adds it to
|
||||||
// the event loop for collection on the specified interval returning
|
// the event loop for collection on the specified interval returning
|
||||||
// a channel for the subscriber to receive on.
|
// 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()
|
s.m.Lock()
|
||||||
defer s.m.Unlock()
|
defer s.m.Unlock()
|
||||||
publisher, exists := s.publishers[c]
|
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
|
// stopCollection closes the channels for all subscribers and removes
|
||||||
// the container from metrics collection.
|
// the container from metrics collection.
|
||||||
func (s *statsCollector) stopCollection(c *Container) {
|
func (s *statsCollector) stopCollection(c *container.Container) {
|
||||||
s.m.Lock()
|
s.m.Lock()
|
||||||
if publisher, exists := s.publishers[c]; exists {
|
if publisher, exists := s.publishers[c]; exists {
|
||||||
publisher.Close()
|
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.
|
// 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()
|
s.m.Lock()
|
||||||
publisher := s.publishers[c]
|
publisher := s.publishers[c]
|
||||||
if publisher != nil {
|
if publisher != nil {
|
||||||
|
|
@ -88,7 +89,7 @@ func (s *statsCollector) unsubscribe(c *Container, ch chan interface{}) {
|
||||||
|
|
||||||
func (s *statsCollector) run() {
|
func (s *statsCollector) run() {
|
||||||
type publishersPair struct {
|
type publishersPair struct {
|
||||||
container *Container
|
container *container.Container
|
||||||
publisher *pubsub.Publisher
|
publisher *pubsub.Publisher
|
||||||
}
|
}
|
||||||
// we cannot determine the capacity here.
|
// we cannot determine the capacity here.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
|
)
|
||||||
|
|
||||||
// newStatsCollector returns a new statsCollector for collection stats
|
// newStatsCollector returns a new statsCollector for collection stats
|
||||||
// for a registered container at the specified interval. The collector allows
|
// 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
|
// collect registers the container with the collector and adds it to
|
||||||
// the event loop for collection on the specified interval returning
|
// the event loop for collection on the specified interval returning
|
||||||
// a channel for the subscriber to receive on.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// stopCollection closes the channels for all subscribers and removes
|
// stopCollection closes the channels for all subscribers and removes
|
||||||
// the container from metrics collection.
|
// 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.
|
// 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{}) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
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
|
// 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
|
// for the initial signal forever. If the container is not running Stop returns
|
||||||
// immediately.
|
// immediately.
|
||||||
func (daemon *Daemon) containerStop(container *Container, seconds int) error {
|
func (daemon *Daemon) containerStop(container *container.Container, seconds int) error {
|
||||||
if !container.IsRunning() {
|
if !container.IsRunning() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Send a SIGTERM
|
// 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")
|
logrus.Infof("Failed to send SIGTERM to the process, force killing")
|
||||||
if err := daemon.killPossiblyDeadProcess(container, 9); err != nil {
|
if err := daemon.killPossiblyDeadProcess(container, 9); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/docker/docker/container"
|
||||||
derr "github.com/docker/docker/errors"
|
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.
|
// 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()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
|
@ -33,7 +34,7 @@ func (daemon *Daemon) containerUnpause(container *Container) error {
|
||||||
return derr.ErrorCodeNotPaused.WithArgs(container.ID)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/runconfig"
|
"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.
|
// 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.
|
// 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.
|
// 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{}
|
binds := map[string]bool{}
|
||||||
mountPoints := map[string]*volume.MountPoint{}
|
mountPoints := map[string]*volume.MountPoint{}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,66 +3,27 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
"github.com/docker/docker/pkg/chrootarchive"
|
|
||||||
"github.com/docker/docker/pkg/system"
|
|
||||||
"github.com/docker/docker/volume"
|
"github.com/docker/docker/volume"
|
||||||
volumedrivers "github.com/docker/docker/volume/drivers"
|
volumedrivers "github.com/docker/docker/volume/drivers"
|
||||||
"github.com/docker/docker/volume/local"
|
"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
|
// 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
|
// 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.
|
// /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
|
var mounts []execdriver.Mount
|
||||||
for _, m := range container.MountPoints {
|
for _, m := range container.MountPoints {
|
||||||
path, err := m.Setup()
|
path, err := m.Setup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !container.trySetNetworkMount(m.Destination, path) {
|
if !container.TrySetNetworkMount(m.Destination, path) {
|
||||||
mounts = append(mounts, execdriver.Mount{
|
mounts = append(mounts, execdriver.Mount{
|
||||||
Source: path,
|
Source: path,
|
||||||
Destination: m.Destination,
|
Destination: m.Destination,
|
||||||
|
|
@ -72,7 +33,7 @@ func (daemon *Daemon) setupMounts(container *Container) ([]execdriver.Mount, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mounts = sortMounts(mounts)
|
mounts = sortMounts(mounts)
|
||||||
netMounts := container.networkMounts()
|
netMounts := container.NetworkMounts()
|
||||||
// if we are going to mount any of the network files from container
|
// if we are going to mount any of the network files from container
|
||||||
// metadata, the ownership must be set properly for potential container
|
// metadata, the ownership must be set properly for potential container
|
||||||
// remapped root (user namespaces)
|
// remapped root (user namespaces)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/volume"
|
"github.com/docker/docker/volume"
|
||||||
|
|
@ -14,7 +15,7 @@ import (
|
||||||
// of the configured mounts on the container to the execdriver mount structure
|
// of the configured mounts on the container to the execdriver mount structure
|
||||||
// which will ultimately be passed into the exec driver during container creation.
|
// which will ultimately be passed into the exec driver during container creation.
|
||||||
// It also ensures each of the mounts are lexographically sorted.
|
// 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
|
var mnts []execdriver.Mount
|
||||||
for _, mount := range container.MountPoints { // type is volume.MountPoint
|
for _, mount := range container.MountPoints { // type is volume.MountPoint
|
||||||
// If there is no source, take it from the volume path
|
// If there is no source, take it from the volume path
|
||||||
|
|
|
||||||
|
|
@ -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.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.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.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.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.277627600-08:00" level=debug msg="logged event...."
|
||||||
// time="2015-11-06T13:38:37.279631800-08:00" level=debug msg="In defer func"
|
// time="2015-11-06T13:38:37.279631800-08:00" level=debug msg="In defer func"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue