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:
David Calavera 2015-11-12 11:55:17 -08:00 committed by Tibor Vass
parent 33ab2bb52c
commit 6bb0d1816a
60 changed files with 2454 additions and 2284 deletions

View File

@ -9,6 +9,7 @@ import (
"os"
// TODO: remove dependency on daemon
"github.com/docker/docker/container"
"github.com/docker/docker/daemon"
"github.com/docker/docker/image"
"github.com/docker/docker/runconfig"
@ -115,13 +116,11 @@ type Docker interface {
// Pull tells Docker to pull image referenced by `name`.
Pull(name string) (*image.Image, error)
// TODO: move daemon.Container to its own package
// Container looks up a Docker container referenced by `id`.
Container(id string) (*daemon.Container, error)
Container(id string) (*container.Container, error)
// Create creates a new Docker container and returns potential warnings
// TODO: put warnings in the error
Create(*runconfig.Config, *runconfig.HostConfig) (*daemon.Container, []string, error)
Create(*runconfig.Config, *runconfig.HostConfig) (*container.Container, []string, error)
// Remove removes a container specified by `id`.
Remove(id string, cfg *daemon.ContainerRmConfig) error
// Commit creates a new Docker image from an existing Docker container.
@ -131,7 +130,7 @@ type Docker interface {
// TODO: make an Extract method instead of passing `decompress`
// TODO: do not pass a FileInfo, instead refactor the archive package to export a Walk function that can be used
// with Context.Walk
Copy(c *daemon.Container, destPath string, src FileInfo, decompress bool) error
Copy(c *container.Container, destPath string, src FileInfo, decompress bool) error
// Retain retains an image avoiding it to be removed or overwritten until a corresponding Release() call.
// TODO: remove
@ -140,13 +139,13 @@ type Docker interface {
// TODO: remove
Release(sessionID string, activeImages []string)
// Kill stops the container execution abruptly.
Kill(c *daemon.Container) error
Kill(c *container.Container) error
// Mount mounts the root filesystem for the container.
Mount(c *daemon.Container) error
Mount(c *container.Container) error
// Unmount unmounts the root filesystem for the container.
Unmount(c *daemon.Container) error
Unmount(c *container.Container) error
// Start starts a new container
Start(c *daemon.Container) error
Start(c *container.Container) error
}
// ImageCache abstracts an image cache store.

View File

@ -22,6 +22,7 @@ import (
"github.com/docker/docker/api"
"github.com/docker/docker/builder"
"github.com/docker/docker/builder/dockerfile/parser"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/archive"
@ -419,8 +420,8 @@ func (b *Builder) processImageFrom(img *image.Image) error {
}
// The default path will be blank on Windows (set by HCS)
if len(b.runConfig.Env) == 0 && daemon.DefaultPathEnv != "" {
b.runConfig.Env = append(b.runConfig.Env, "PATH="+daemon.DefaultPathEnv)
if len(b.runConfig.Env) == 0 && container.DefaultPathEnv != "" {
b.runConfig.Env = append(b.runConfig.Env, "PATH="+container.DefaultPathEnv)
}
// Process ONBUILD triggers if they exist
@ -492,7 +493,7 @@ func (b *Builder) probeCache() (bool, error) {
return true, nil
}
func (b *Builder) create() (*daemon.Container, error) {
func (b *Builder) create() (*container.Container, error) {
if b.image == "" && !b.noBaseImage {
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
}
@ -542,7 +543,7 @@ func (b *Builder) create() (*daemon.Container, error) {
return c, nil
}
func (b *Builder) run(c *daemon.Container) error {
func (b *Builder) run(c *container.Container) error {
var errCh chan error
if b.Verbose {
errCh = c.Attach(nil, b.Stdout, b.Stderr)

69
container/archive.go Normal file
View 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
}

View File

@ -1,8 +1,7 @@
package daemon
package container
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
@ -11,8 +10,6 @@ import (
"syscall"
"time"
"github.com/opencontainers/runc/libcontainer/label"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/exec"
"github.com/docker/docker/daemon/execdriver"
@ -26,28 +23,22 @@ import (
"github.com/docker/docker/pkg/promise"
"github.com/docker/docker/pkg/signal"
"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/volume"
"github.com/opencontainers/runc/libcontainer/label"
)
const configFileName = "config.v2.json"
var (
// ErrRootFSReadOnly is returned when a container
// rootfs is marked readonly.
ErrRootFSReadOnly = errors.New("container rootfs is marked read-only")
)
// CommonContainer holds the fields for a container which are
// applicable across all platforms supported by the daemon.
type CommonContainer struct {
*runconfig.StreamConfig
// embed for Container to support states directly.
*State `json:"State"` // Needed for remote api version <= 1.11
root string // Path to the "home" of the container, including metadata.
basefs string // Path to the graphdriver mountpoint
rwlayer layer.RWLayer
Root string `json:"-"` // Path to the "home" of the container, including metadata.
BaseFS string `json:"-"` // Path to the graphdriver mountpoint
RWLayer layer.RWLayer `json:"-"`
ID string
Created time.Time
Path string
@ -65,32 +56,33 @@ type CommonContainer struct {
HasBeenStartedBefore bool
HasBeenManuallyStopped bool // used for unless-stopped restart policy
MountPoints map[string]*volume.MountPoint
hostConfig *runconfig.HostConfig
command *execdriver.Command
HostConfig *runconfig.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
Command *execdriver.Command `json:"-"`
monitor *containerMonitor
execCommands *exec.Store
ExecCommands *exec.Store `json:"-"`
// logDriver for closing
logDriver logger.Logger
logCopier *logger.Copier
LogDriver logger.Logger `json:"-"`
LogCopier *logger.Copier `json:"-"`
}
// newBaseContainer creates a new container with its
// NewBaseContainer creates a new container with its
// basic configuration.
func newBaseContainer(id, root string) *Container {
func NewBaseContainer(id, root string) *Container {
return &Container{
CommonContainer: CommonContainer{
ID: id,
State: NewState(),
execCommands: exec.NewStore(),
root: root,
ExecCommands: exec.NewStore(),
Root: root,
MountPoints: make(map[string]*volume.MountPoint),
StreamConfig: runconfig.NewStreamConfig(),
},
}
}
func (container *Container) fromDisk() error {
pth, err := container.jsonPath()
// FromDisk loads the container configuration stored in the host.
func (container *Container) FromDisk() error {
pth, err := container.ConfigPath()
if err != nil {
return err
}
@ -114,8 +106,9 @@ func (container *Container) fromDisk() error {
return container.readHostConfig()
}
func (container *Container) toDisk() error {
pth, err := container.jsonPath()
// ToDisk saves the container configuration on disk.
func (container *Container) ToDisk() error {
pth, err := container.ConfigPath()
if err != nil {
return err
}
@ -133,22 +126,24 @@ func (container *Container) toDisk() error {
return err
}
return container.writeHostConfig()
return container.WriteHostConfig()
}
func (container *Container) toDiskLocking() error {
// ToDiskLocking saves the container configuration on disk in a thread safe way.
func (container *Container) ToDiskLocking() error {
container.Lock()
err := container.toDisk()
err := container.ToDisk()
container.Unlock()
return err
}
// readHostConfig reads the host configuration from disk for the container.
func (container *Container) readHostConfig() error {
container.hostConfig = &runconfig.HostConfig{}
container.HostConfig = &runconfig.HostConfig{}
// If the hostconfig file does not exist, do not read it.
// (We still have to initialize container.hostConfig,
// (We still have to initialize container.HostConfig,
// but that's OK, since we just did that above.)
pth, err := container.hostConfigPath()
pth, err := container.HostConfigPath()
if err != nil {
return err
}
@ -162,17 +157,18 @@ func (container *Container) readHostConfig() error {
}
defer f.Close()
if err := json.NewDecoder(f).Decode(&container.hostConfig); err != nil {
if err := json.NewDecoder(f).Decode(&container.HostConfig); err != nil {
return err
}
initDNSHostConfig(container)
container.InitDNSHostConfig()
return nil
}
func (container *Container) writeHostConfig() error {
pth, err := container.hostConfigPath()
// WriteHostConfig saves the host configuration on disk for the container.
func (container *Container) WriteHostConfig() error {
pth, err := container.HostConfigPath()
if err != nil {
return err
}
@ -183,19 +179,19 @@ func (container *Container) writeHostConfig() error {
}
defer f.Close()
return json.NewEncoder(f).Encode(&container.hostConfig)
return json.NewEncoder(f).Encode(&container.HostConfig)
}
// GetResourcePath evaluates `path` in the scope of the container's basefs, with proper path
// sanitisation. Symlinks are all scoped to the basefs of the container, as
// though the container's basefs was `/`.
// GetResourcePath evaluates `path` in the scope of the container's BaseFS, with proper path
// sanitisation. Symlinks are all scoped to the BaseFS of the container, as
// though the container's BaseFS was `/`.
//
// The basefs of a container is the host-facing path which is bind-mounted as
// The BaseFS of a container is the host-facing path which is bind-mounted as
// `/` inside the container. This method is essentially used to access a
// particular path inside the container as though you were a process in that
// container.
//
// NOTE: The returned path is *only* safely scoped inside the container's basefs
// NOTE: The returned path is *only* safely scoped inside the container's BaseFS
// if no component of the returned path changes (such as a component
// symlinking to a different path) between using this method and using the
// path. See symlink.FollowSymlinkInScope for more details.
@ -203,11 +199,11 @@ func (container *Container) GetResourcePath(path string) (string, error) {
// IMPORTANT - These are paths on the OS where the daemon is running, hence
// any filepath operations must be done in an OS agnostic way.
cleanPath := filepath.Join(string(os.PathSeparator), path)
r, e := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, cleanPath), container.basefs)
r, e := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, cleanPath), container.BaseFS)
return r, e
}
// Evaluates `path` in the scope of the container's root, with proper path
// GetRootResourcePath evaluates `path` in the scope of the container's root, with proper path
// sanitisation. Symlinks are all scoped to the root of the container, as
// though the container's root was `/`.
//
@ -219,11 +215,11 @@ func (container *Container) GetResourcePath(path string) (string, error) {
// if no component of the returned path changes (such as a component
// symlinking to a different path) between using this method and using the
// path. See symlink.FollowSymlinkInScope for more details.
func (container *Container) getRootResourcePath(path string) (string, error) {
func (container *Container) GetRootResourcePath(path string) (string, error) {
// IMPORTANT - These are paths on the OS where the daemon is running, hence
// any filepath operations must be done in an OS agnostic way.
cleanPath := filepath.Join(string(os.PathSeparator), path)
return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root)
return symlink.FollowSymlinkInScope(filepath.Join(container.Root, cleanPath), container.Root)
}
// ExitOnNext signals to the monitor that it should not restart the container
@ -235,23 +231,20 @@ func (container *Container) ExitOnNext() {
// Resize changes the TTY of the process running inside the container
// to the given height and width. The container must be running.
func (container *Container) Resize(h, w int) error {
if err := container.command.ProcessConfig.Terminal.Resize(h, w); err != nil {
if err := container.Command.ProcessConfig.Terminal.Resize(h, w); err != nil {
return err
}
return nil
}
func (container *Container) hostConfigPath() (string, error) {
return container.getRootResourcePath("hostconfig.json")
// HostConfigPath returns the path to the container's JSON hostconfig
func (container *Container) HostConfigPath() (string, error) {
return container.GetRootResourcePath("hostconfig.json")
}
func (container *Container) jsonPath() (string, error) {
return container.getRootResourcePath(configFileName)
}
// This directory is only usable when the container is running
func (container *Container) rootfsPath() string {
return container.basefs
// ConfigPath returns the path to the container's JSON config
func (container *Container) ConfigPath() (string, error) {
return container.GetRootResourcePath(configFileName)
}
func validateID(id string) error {
@ -267,8 +260,9 @@ func (container *Container) exposes(p nat.Port) bool {
return exists
}
func (container *Container) getLogConfig(defaultConfig runconfig.LogConfig) runconfig.LogConfig {
cfg := container.hostConfig.LogConfig
// GetLogConfig returns the log configuration for the container.
func (container *Container) GetLogConfig(defaultConfig runconfig.LogConfig) runconfig.LogConfig {
cfg := container.HostConfig.LogConfig
if cfg.Type != "" || len(cfg.Config) > 0 { // container has log driver configured
if cfg.Type == "" {
cfg.Type = jsonfilelog.Name
@ -300,7 +294,7 @@ func (container *Container) StartLogger(cfg runconfig.LogConfig) (logger.Logger,
// Set logging file for "json-logger"
if cfg.Type == jsonfilelog.Name {
ctx.LogPath, err = container.getRootResourcePath(fmt.Sprintf("%s-json.log", container.ID))
ctx.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID))
if err != nil {
return nil, err
}
@ -308,33 +302,39 @@ func (container *Container) StartLogger(cfg runconfig.LogConfig) (logger.Logger,
return c(ctx)
}
func (container *Container) getProcessLabel() string {
// GetProcessLabel returns the process label for the container.
func (container *Container) GetProcessLabel() string {
// even if we have a process label return "" if we are running
// in privileged mode
if container.hostConfig.Privileged {
if container.HostConfig.Privileged {
return ""
}
return container.ProcessLabel
}
func (container *Container) getMountLabel() string {
if container.hostConfig.Privileged {
// GetMountLabel returns the mounting label for the container.
// This label is empty if the container is privileged.
func (container *Container) GetMountLabel() string {
if container.HostConfig.Privileged {
return ""
}
return container.MountLabel
}
func (container *Container) getExecIDs() []string {
return container.execCommands.List()
// GetExecIDs returns the list of exec commands running on the container.
func (container *Container) GetExecIDs() []string {
return container.ExecCommands.List()
}
// Attach connects to the container's TTY, delegating to standard
// streams or websockets depending on the configuration.
func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error {
return attach(container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr)
return AttachStreams(container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr)
}
func attach(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error {
// AttachStreams connects streams to a TTY.
// Used by exec too. Should this move somewhere else?
func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error {
var (
cStdout, cStderr io.ReadCloser
cStdin io.WriteCloser
@ -479,13 +479,16 @@ func copyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
return written, err
}
func (container *Container) shouldRestart() bool {
return container.hostConfig.RestartPolicy.Name == "always" ||
(container.hostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) ||
(container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
// ShouldRestart decides whether the daemon should restart the container or not.
// This is based on the container's restart policy.
func (container *Container) ShouldRestart() bool {
return container.HostConfig.RestartPolicy.Name == "always" ||
(container.HostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) ||
(container.HostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
}
func (container *Container) addBindMountPoint(name, source, destination string, rw bool) {
// AddBindMountPoint adds a new bind mount point configuration to the container.
func (container *Container) AddBindMountPoint(name, source, destination string, rw bool) {
container.MountPoints[destination] = &volume.MountPoint{
Name: name,
Source: source,
@ -494,7 +497,8 @@ func (container *Container) addBindMountPoint(name, source, destination string,
}
}
func (container *Container) addLocalMountPoint(name, destination string, rw bool) {
// AddLocalMountPoint adds a new local mount point configuration to the container.
func (container *Container) AddLocalMountPoint(name, destination string, rw bool) {
container.MountPoints[destination] = &volume.MountPoint{
Name: name,
Driver: volume.DefaultDriverName,
@ -503,7 +507,8 @@ func (container *Container) addLocalMountPoint(name, destination string, rw bool
}
}
func (container *Container) addMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
// AddMountPointWithVolume adds a new mount point configured with a volume to the container.
func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
container.MountPoints[destination] = &volume.MountPoint{
Name: vol.Name(),
Driver: vol.DriverName(),
@ -513,11 +518,13 @@ func (container *Container) addMountPointWithVolume(destination string, vol volu
}
}
func (container *Container) isDestinationMounted(destination string) bool {
// IsDestinationMounted checkes whether a path is mounted on the container or not.
func (container *Container) IsDestinationMounted(destination string) bool {
return container.MountPoints[destination] != nil
}
func (container *Container) stopSignal() int {
// StopSignal returns the signal used to stop the container.
func (container *Container) StopSignal() int {
var stopSignal syscall.Signal
if container.Config.StopSignal != "" {
stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
@ -529,7 +536,7 @@ func (container *Container) stopSignal() int {
return int(stopSignal)
}
// initDNSHostConfig ensures that the dns fields are never nil.
// InitDNSHostConfig ensures that the dns fields are never nil.
// New containers don't ever have those fields nil,
// but pre created containers can still have those nil values.
// The non-recommended host configuration in the start api can
@ -537,16 +544,16 @@ func (container *Container) stopSignal() int {
// we remove that behavior for good.
// See https://github.com/docker/docker/pull/17779
// for a more detailed explanation on why we don't want that.
func initDNSHostConfig(container *Container) {
if container.hostConfig.DNS == nil {
container.hostConfig.DNS = make([]string, 0)
func (container *Container) InitDNSHostConfig() {
if container.HostConfig.DNS == nil {
container.HostConfig.DNS = make([]string, 0)
}
if container.hostConfig.DNSSearch == nil {
container.hostConfig.DNSSearch = make([]string, 0)
if container.HostConfig.DNSSearch == nil {
container.HostConfig.DNSSearch = make([]string, 0)
}
if container.hostConfig.DNSOptions == nil {
container.hostConfig.DNSOptions = make([]string, 0)
if container.HostConfig.DNSOptions == nil {
container.HostConfig.DNSOptions = make([]string, 0)
}
}

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

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

View File

@ -1,4 +1,4 @@
package daemon
package container
import (
"io"
@ -11,6 +11,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/execdriver"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/promise"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
@ -21,8 +22,8 @@ const (
loggerCloseTimeout = 10 * time.Second
)
// containerSupervisor defines the interface that a supervisor must implement
type containerSupervisor interface {
// supervisor defines the interface that a supervisor must implement
type supervisor interface {
// LogContainerEvent generates events related to a given container
LogContainerEvent(*Container, string)
// Cleanup ensures that the container is properly unmounted
@ -44,7 +45,7 @@ type containerMonitor struct {
mux sync.Mutex
// supervisor keeps track of the container and the events it generates
supervisor containerSupervisor
supervisor supervisor
// container is the container being monitored
container *Container
@ -76,17 +77,32 @@ type containerMonitor struct {
lastStartTime time.Time
}
// newContainerMonitor returns an initialized containerMonitor for the provided container
// honoring the provided restart policy
func (daemon *Daemon) newContainerMonitor(container *Container, policy runconfig.RestartPolicy) *containerMonitor {
return &containerMonitor{
supervisor: daemon,
// StartMonitor initializes a containerMonitor for this container with the provided supervisor and restart policy
// and starts the container's process.
func (container *Container) StartMonitor(s supervisor, policy runconfig.RestartPolicy) error {
container.monitor = &containerMonitor{
supervisor: s,
container: container,
restartPolicy: policy,
timeIncrement: defaultTimeIncrement,
stopChan: make(chan struct{}),
startSignal: make(chan struct{}),
}
return container.monitor.wait()
}
// wait starts the container and wait until
// we either receive an error from the initial start of the container's
// process or until the process is running in the container
func (m *containerMonitor) wait() error {
select {
case <-m.startSignal:
case err := <-promise.Go(m.start):
return err
}
return nil
}
// Stop signals to the container monitor that it should stop monitoring the container
@ -113,7 +129,7 @@ func (m *containerMonitor) Close() error {
// FIXME: here is race condition between two RUN instructions in Dockerfile
// because they share same runconfig and change image. Must be fixed
// in builder/builder.go
if err := m.container.toDisk(); err != nil {
if err := m.container.ToDisk(); err != nil {
logrus.Errorf("Error dumping container %s state to disk: %s", m.container.ID, err)
return err
@ -123,7 +139,7 @@ func (m *containerMonitor) Close() error {
}
// Start starts the containers process and monitors it according to the restart policy
func (m *containerMonitor) Start() error {
func (m *containerMonitor) start() error {
var (
err error
exitStatus execdriver.ExitStatus
@ -137,7 +153,7 @@ func (m *containerMonitor) Start() error {
if afterRun {
m.container.Lock()
defer m.container.Unlock()
m.container.setStopped(&exitStatus)
m.container.SetStopped(&exitStatus)
}
m.Close()
}()
@ -202,7 +218,7 @@ func (m *containerMonitor) Start() error {
m.resetMonitor(err == nil && exitStatus.ExitCode == 0)
if m.shouldRestart(exitStatus.ExitCode) {
m.container.setRestarting(&exitStatus)
m.container.SetRestarting(&exitStatus)
m.logEvent("die")
m.resetContainer(true)
@ -295,7 +311,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid
}()
if processConfig.Tty {
// The callback is called after the process Start()
// The callback is called after the process start()
// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave
// which we close here.
if c, ok := processConfig.Stdout.(io.Closer); ok {
@ -303,7 +319,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid
}
}
m.container.setRunning(pid)
m.container.SetRunning(pid)
// signal that the process has started
// close channel only if not closed
@ -313,7 +329,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid
close(m.startSignal)
}
if err := m.container.toDiskLocking(); err != nil {
if err := m.container.ToDiskLocking(); err != nil {
logrus.Errorf("Error saving container to disk: %v", err)
}
return nil
@ -333,8 +349,8 @@ func (m *containerMonitor) resetContainer(lock bool) {
logrus.Errorf("%s: %s", container.ID, err)
}
if container.command != nil && container.command.ProcessConfig.Terminal != nil {
if err := container.command.ProcessConfig.Terminal.Close(); err != nil {
if container.Command != nil && container.Command.ProcessConfig.Terminal != nil {
if err := container.Command.ProcessConfig.Terminal.Close(); err != nil {
logrus.Errorf("%s: Error closing terminal: %s", container.ID, err)
}
}
@ -344,11 +360,11 @@ func (m *containerMonitor) resetContainer(lock bool) {
container.NewInputPipes()
}
if container.logDriver != nil {
if container.logCopier != nil {
if container.LogDriver != nil {
if container.LogCopier != nil {
exit := make(chan struct{})
go func() {
container.logCopier.Wait()
container.LogCopier.Wait()
close(exit)
}()
select {
@ -357,14 +373,14 @@ func (m *containerMonitor) resetContainer(lock bool) {
case <-exit:
}
}
container.logDriver.Close()
container.logCopier = nil
container.logDriver = nil
container.LogDriver.Close()
container.LogCopier = nil
container.LogDriver = nil
}
c := container.command.ProcessConfig.Cmd
c := container.Command.ProcessConfig.Cmd
container.command.ProcessConfig.Cmd = exec.Cmd{
container.Command.ProcessConfig.Cmd = exec.Cmd{
Stdin: c.Stdin,
Stdout: c.Stdout,
Stderr: c.Stderr,

View File

@ -1,4 +1,4 @@
package daemon
package container
import (
"fmt"
@ -21,7 +21,7 @@ type State struct {
Paused bool
Restarting bool
OOMKilled bool
removalInProgress bool // Not need for this to be persistent on disk.
RemovalInProgress bool // Not need for this to be persistent on disk.
Dead bool
Pid int
ExitCode int
@ -51,7 +51,7 @@ func (s *State) String() string {
return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
}
if s.removalInProgress {
if s.RemovalInProgress {
return "Removal In Progress"
}
@ -93,7 +93,8 @@ func (s *State) StateString() string {
return "exited"
}
func isValidStateString(s string) bool {
// IsValidStateString checks if the provided string is a valid container state or not.
func IsValidStateString(s string) bool {
if s != "paused" &&
s != "restarting" &&
s != "running" &&
@ -121,7 +122,7 @@ func wait(waitChan <-chan struct{}, timeout time.Duration) error {
// waitRunning waits until state is running. If state is already
// running it returns immediately. If you want wait forever you must
// supply negative timeout. Returns pid, that was passed to
// setRunning.
// SetRunning.
func (s *State) waitRunning(timeout time.Duration) (int, error) {
s.Lock()
if s.Running {
@ -139,7 +140,7 @@ func (s *State) waitRunning(timeout time.Duration) (int, error) {
// WaitStop waits until state is stopped. If state already stopped it returns
// immediately. If you want wait forever you must supply negative timeout.
// Returns exit code, that was passed to setStoppedLocking
// Returns exit code, that was passed to SetStoppedLocking
func (s *State) WaitStop(timeout time.Duration) (int, error) {
s.Lock()
if !s.Running {
@ -178,7 +179,8 @@ func (s *State) getExitCode() int {
return res
}
func (s *State) setRunning(pid int) {
// SetRunning sets the state of the container to "running".
func (s *State) SetRunning(pid int) {
s.Error = ""
s.Running = true
s.Paused = false
@ -190,13 +192,15 @@ func (s *State) setRunning(pid int) {
s.waitChan = make(chan struct{})
}
func (s *State) setStoppedLocking(exitStatus *execdriver.ExitStatus) {
// SetStoppedLocking locks the container state is sets it to "stopped".
func (s *State) SetStoppedLocking(exitStatus *execdriver.ExitStatus) {
s.Lock()
s.setStopped(exitStatus)
s.SetStopped(exitStatus)
s.Unlock()
}
func (s *State) setStopped(exitStatus *execdriver.ExitStatus) {
// SetStopped sets the container state to "stopped" without locking.
func (s *State) SetStopped(exitStatus *execdriver.ExitStatus) {
s.Running = false
s.Restarting = false
s.Pid = 0
@ -206,15 +210,17 @@ func (s *State) setStopped(exitStatus *execdriver.ExitStatus) {
s.waitChan = make(chan struct{})
}
// setRestarting is when docker handles the auto restart of containers when they are
// SetRestartingLocking is when docker handles the auto restart of containers when they are
// in the middle of a stop and being restarted again
func (s *State) setRestartingLocking(exitStatus *execdriver.ExitStatus) {
func (s *State) SetRestartingLocking(exitStatus *execdriver.ExitStatus) {
s.Lock()
s.setRestarting(exitStatus)
s.SetRestarting(exitStatus)
s.Unlock()
}
func (s *State) setRestarting(exitStatus *execdriver.ExitStatus) {
// SetRestarting sets the container state to "restarting".
// It also sets the container PID to 0.
func (s *State) SetRestarting(exitStatus *execdriver.ExitStatus) {
// we should consider the container running when it is restarting because of
// all the checks in docker around rm/stop/etc
s.Running = true
@ -226,37 +232,41 @@ func (s *State) setRestarting(exitStatus *execdriver.ExitStatus) {
s.waitChan = make(chan struct{})
}
// setError sets the container's error state. This is useful when we want to
// SetError sets the container's error state. This is useful when we want to
// know the error that occurred when container transits to another state
// when inspecting it
func (s *State) setError(err error) {
func (s *State) SetError(err error) {
s.Error = err.Error()
}
func (s *State) isPaused() bool {
// IsPaused returns whether the container is paused or not.
func (s *State) IsPaused() bool {
s.Lock()
res := s.Paused
s.Unlock()
return res
}
func (s *State) setRemovalInProgress() error {
// SetRemovalInProgress sets the container state as being removed.
func (s *State) SetRemovalInProgress() error {
s.Lock()
defer s.Unlock()
if s.removalInProgress {
if s.RemovalInProgress {
return derr.ErrorCodeAlreadyRemoving
}
s.removalInProgress = true
s.RemovalInProgress = true
return nil
}
func (s *State) resetRemovalInProgress() {
// ResetRemovalInProgress make the RemovalInProgress state to false.
func (s *State) ResetRemovalInProgress() {
s.Lock()
s.removalInProgress = false
s.RemovalInProgress = false
s.Unlock()
}
func (s *State) setDead() {
// SetDead sets the container state to "dead"
func (s *State) SetDead() {
s.Lock()
s.Dead = true
s.Unlock()

View File

@ -1,4 +1,4 @@
package daemon
package container
import (
"sync/atomic"
@ -19,7 +19,7 @@ func TestStateRunStop(t *testing.T) {
close(started)
}()
s.Lock()
s.setRunning(i + 100)
s.SetRunning(i + 100)
s.Unlock()
if !s.IsRunning() {
@ -52,7 +52,7 @@ func TestStateRunStop(t *testing.T) {
atomic.StoreInt64(&exit, int64(exitCode))
close(stopped)
}()
s.setStoppedLocking(&execdriver.ExitStatus{ExitCode: i})
s.SetStoppedLocking(&execdriver.ExitStatus{ExitCode: i})
if s.IsRunning() {
t.Fatal("State is running")
}
@ -93,7 +93,7 @@ func TestStateTimeoutWait(t *testing.T) {
}
s.Lock()
s.setRunning(49)
s.SetRunning(49)
s.Unlock()
stopped := make(chan struct{})

View File

@ -1,6 +1,6 @@
// +build linux freebsd
package daemon
package container
import "github.com/docker/docker/daemon/execdriver"

View File

@ -1,4 +1,4 @@
package daemon
package container
import "github.com/docker/docker/daemon/execdriver"

View File

@ -8,6 +8,7 @@ import (
"strings"
"github.com/docker/docker/api/types"
"github.com/docker/docker/container"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/ioutils"
@ -71,69 +72,9 @@ func (daemon *Daemon) ContainerExtractToDir(name, path string, noOverwriteDirNon
return daemon.containerExtractToDir(container, path, noOverwriteDirNonDir, content)
}
// resolvePath resolves the given path in the container to a resource on the
// host. Returns a resolved path (absolute path to the resource on the host),
// the absolute path to the resource relative to the container's rootfs, and
// a error if the path points to outside the container's rootfs.
func (container *Container) resolvePath(path string) (resolvedPath, absPath string, err error) {
// Consider the given path as an absolute path in the container.
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join(string(filepath.Separator), path), path)
// Split the absPath into its Directory and Base components. We will
// resolve the dir in the scope of the container then append the base.
dirPath, basePath := filepath.Split(absPath)
resolvedDirPath, err := container.GetResourcePath(dirPath)
if err != nil {
return "", "", err
}
// resolvedDirPath will have been cleaned (no trailing path separators) so
// we can manually join it with the base path element.
resolvedPath = resolvedDirPath + string(filepath.Separator) + basePath
return resolvedPath, absPath, nil
}
// statPath is the unexported version of StatPath. Locks and mounts should
// be acquired before calling this method and the given path should be fully
// resolved to a path on the host corresponding to the given absolute path
// inside the container.
func (container *Container) statPath(resolvedPath, absPath string) (stat *types.ContainerPathStat, err error) {
lstat, err := os.Lstat(resolvedPath)
if err != nil {
return nil, err
}
var linkTarget string
if lstat.Mode()&os.ModeSymlink != 0 {
// Fully evaluate the symlink in the scope of the container rootfs.
hostPath, err := container.GetResourcePath(absPath)
if err != nil {
return nil, err
}
linkTarget, err = filepath.Rel(container.basefs, hostPath)
if err != nil {
return nil, err
}
// Make it an absolute path.
linkTarget = filepath.Join(string(filepath.Separator), linkTarget)
}
return &types.ContainerPathStat{
Name: filepath.Base(absPath),
Size: lstat.Size(),
Mode: lstat.Mode(),
Mtime: lstat.ModTime(),
LinkTarget: linkTarget,
}, nil
}
// containerStatPath stats the filesystem resource at the specified path in this
// container. Returns stat info about the resource.
func (daemon *Daemon) containerStatPath(container *Container, path string) (stat *types.ContainerPathStat, err error) {
func (daemon *Daemon) containerStatPath(container *container.Container, path string) (stat *types.ContainerPathStat, err error) {
container.Lock()
defer container.Unlock()
@ -143,23 +84,23 @@ func (daemon *Daemon) containerStatPath(container *Container, path string) (stat
defer daemon.Unmount(container)
err = daemon.mountVolumes(container)
defer container.unmountVolumes(true)
defer container.UnmountVolumes(true)
if err != nil {
return nil, err
}
resolvedPath, absPath, err := container.resolvePath(path)
resolvedPath, absPath, err := container.ResolvePath(path)
if err != nil {
return nil, err
}
return container.statPath(resolvedPath, absPath)
return container.StatPath(resolvedPath, absPath)
}
// containerArchivePath creates an archive of the filesystem resource at the specified
// path in this container. Returns a tar archive of the resource and stat info
// about the resource.
func (daemon *Daemon) containerArchivePath(container *Container, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) {
func (daemon *Daemon) containerArchivePath(container *container.Container, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) {
container.Lock()
defer func() {
@ -178,7 +119,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c
defer func() {
if err != nil {
// unmount any volumes
container.unmountVolumes(true)
container.UnmountVolumes(true)
// unmount the container's rootfs
daemon.Unmount(container)
}
@ -188,12 +129,12 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c
return nil, nil, err
}
resolvedPath, absPath, err := container.resolvePath(path)
resolvedPath, absPath, err := container.ResolvePath(path)
if err != nil {
return nil, nil, err
}
stat, err = container.statPath(resolvedPath, absPath)
stat, err = container.StatPath(resolvedPath, absPath)
if err != nil {
return nil, nil, err
}
@ -213,7 +154,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c
content = ioutils.NewReadCloserWrapper(data, func() error {
err := data.Close()
container.unmountVolumes(true)
container.UnmountVolumes(true)
daemon.Unmount(container)
container.Unlock()
return err
@ -230,7 +171,7 @@ func (daemon *Daemon) containerArchivePath(container *Container, path string) (c
// noOverwriteDirNonDir is true then it will be an error if unpacking the
// given content would cause an existing directory to be replaced with a non-
// directory and vice versa.
func (daemon *Daemon) containerExtractToDir(container *Container, path string, noOverwriteDirNonDir bool, content io.Reader) (err error) {
func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, noOverwriteDirNonDir bool, content io.Reader) (err error) {
container.Lock()
defer container.Unlock()
@ -240,14 +181,14 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n
defer daemon.Unmount(container)
err = daemon.mountVolumes(container)
defer container.unmountVolumes(true)
defer container.UnmountVolumes(true)
if err != nil {
return err
}
// The destination path needs to be resolved to a host path, with all
// symbolic links followed in the scope of the container's rootfs. Note
// that we do not use `container.resolvePath(path)` here because we need
// that we do not use `container.ResolvePath(path)` here because we need
// to also evaluate the last path element if it is a symlink. This is so
// that you can extract an archive to a symlink that points to a directory.
@ -283,14 +224,14 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n
// a volume file path.
var baseRel string
if strings.HasPrefix(resolvedPath, `\\?\Volume{`) {
if strings.HasPrefix(resolvedPath, container.basefs) {
baseRel = resolvedPath[len(container.basefs):]
if strings.HasPrefix(resolvedPath, container.BaseFS) {
baseRel = resolvedPath[len(container.BaseFS):]
if baseRel[:1] == `\` {
baseRel = baseRel[1:]
}
}
} else {
baseRel, err = filepath.Rel(container.basefs, resolvedPath)
baseRel, err = filepath.Rel(container.BaseFS, resolvedPath)
}
if err != nil {
return err
@ -303,7 +244,7 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n
return err
}
if !toVolume && container.hostConfig.ReadonlyRootfs {
if !toVolume && container.HostConfig.ReadonlyRootfs {
return ErrRootFSReadOnly
}
@ -323,7 +264,7 @@ func (daemon *Daemon) containerExtractToDir(container *Container, path string, n
return nil
}
func (daemon *Daemon) containerCopy(container *Container, resource string) (rc io.ReadCloser, err error) {
func (daemon *Daemon) containerCopy(container *container.Container, resource string) (rc io.ReadCloser, err error) {
container.Lock()
defer func() {
@ -342,7 +283,7 @@ func (daemon *Daemon) containerCopy(container *Container, resource string) (rc i
defer func() {
if err != nil {
// unmount any volumes
container.unmountVolumes(true)
container.UnmountVolumes(true)
// unmount the container's rootfs
daemon.Unmount(container)
}
@ -379,7 +320,7 @@ func (daemon *Daemon) containerCopy(container *Container, resource string) (rc i
reader := ioutils.NewReadCloserWrapper(archive, func() error {
err := archive.Close()
container.unmountVolumes(true)
container.UnmountVolumes(true)
daemon.Unmount(container)
container.Unlock()
return err

View File

@ -2,10 +2,12 @@
package daemon
import "github.com/docker/docker/container"
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
// cannot be in a read-only volume. If it is not in a volume, the container
// cannot be configured with a read-only rootfs.
func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) {
func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) {
var toVolume bool
for _, mnt := range container.MountPoints {
if toVolume = mnt.HasResource(absPath); toVolume {

View File

@ -1,11 +1,13 @@
package daemon
import "github.com/docker/docker/container"
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
// cannot be in a read-only volume. If it is not in a volume, the container
// cannot be configured with a read-only rootfs.
//
// This is a no-op on Windows which does not support read-only volumes, or
// extracting to a mount point inside a volume. TODO Windows: FIXME Post-TP4
func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) {
func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) {
return false, nil
}

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/logger"
"github.com/docker/docker/pkg/stdcopy"
)
@ -66,7 +67,7 @@ func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *Containe
return daemon.attachWithLogs(container, c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream)
}
func (daemon *Daemon) attachWithLogs(container *Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool) error {
func (daemon *Daemon) attachWithLogs(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool) error {
if logs {
logDriver, err := daemon.getLogger(container)
if err != nil {

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/docker/distribution/reference"
"github.com/docker/docker/container"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
@ -42,7 +43,7 @@ func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (string, err
return "", fmt.Errorf("Windows does not support commit of a running container")
}
if c.Pause && !container.isPaused() {
if c.Pause && !container.IsPaused() {
daemon.containerPause(container)
defer daemon.containerUnpause(container)
}
@ -145,12 +146,12 @@ func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (string, err
return id.String(), nil
}
func (daemon *Daemon) exportContainerRw(container *Container) (archive.Archive, error) {
func (daemon *Daemon) exportContainerRw(container *container.Container) (archive.Archive, error) {
if err := daemon.Mount(container); err != nil {
return nil, err
}
archive, err := container.rwlayer.TarStream()
archive, err := container.RWLayer.TarStream()
if err != nil {
return nil, err
}

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

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

View File

@ -5,62 +5,31 @@ package daemon
import (
"strings"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/execdriver"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/layer"
"github.com/docker/docker/volume"
"github.com/docker/libnetwork"
)
// DefaultPathEnv is deliberately empty on Windows as the default path will be set by
// the container. Docker has no context of what the default path should be.
const DefaultPathEnv = ""
// Container holds fields specific to the Windows implementation. See
// CommonContainer for standard fields common to all containers.
type Container struct {
CommonContainer
// Fields below here are platform specific.
}
func killProcessDirectly(container *Container) error {
return nil
}
func (daemon *Daemon) setupLinkedContainers(container *Container) ([]string, error) {
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
return nil, nil
}
func (container *Container) createDaemonEnvironment(linkedEnv []string) []string {
// On Windows, nothing to link. Just return the container environment.
return container.Config.Env
}
func (daemon *Daemon) initializeNetworking(container *Container) error {
func (daemon *Daemon) initializeNetworking(container *container.Container) error {
return nil
}
// ConnectToNetwork connects a container to the network
func (daemon *Daemon) ConnectToNetwork(container *Container, idOrName string) error {
func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string) error {
return nil
}
// DisconnectFromNetwork disconnects a container from, the network
func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error {
return nil
}
func (container *Container) setupWorkingDirectory() error {
return nil
}
func (daemon *Daemon) populateCommand(c *Container, env []string) error {
func (daemon *Daemon) populateCommand(c *container.Container, env []string) error {
en := &execdriver.Network{
Interface: nil,
}
parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2)
parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2)
switch parts[0] {
case "none":
case "default", "": // empty string to support existing containers
@ -68,7 +37,7 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
en.Interface = &execdriver.NetworkInterface{
MacAddress: c.Config.MacAddress,
Bridge: daemon.configStore.Bridge.VirtualSwitchName,
PortBindings: c.hostConfig.PortBindings,
PortBindings: c.HostConfig.PortBindings,
// TODO Windows. Include IPAddress. There already is a
// property IPAddress on execDrive.CommonNetworkInterface,
@ -77,13 +46,13 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
}
}
default:
return derr.ErrorCodeInvalidNetworkMode.WithArgs(c.hostConfig.NetworkMode)
return derr.ErrorCodeInvalidNetworkMode.WithArgs(c.HostConfig.NetworkMode)
}
// TODO Windows. More resource controls to be implemented later.
resources := &execdriver.Resources{
CommonResources: execdriver.CommonResources{
CPUShares: c.hostConfig.CPUShares,
CPUShares: c.HostConfig.CPUShares,
},
}
@ -93,7 +62,7 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
Arguments: c.Args,
Tty: c.Config.Tty,
},
ConsoleSize: c.hostConfig.ConsoleSize,
ConsoleSize: c.HostConfig.ConsoleSize,
}
processConfig.Env = env
@ -123,23 +92,23 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
}
layerFolder := m["dir"]
c.command = &execdriver.Command{
c.Command = &execdriver.Command{
CommonCommand: execdriver.CommonCommand{
ID: c.ID,
Rootfs: c.rootfsPath(),
Rootfs: c.BaseFS,
InitPath: "/.dockerinit",
WorkingDir: c.Config.WorkingDir,
Network: en,
MountLabel: c.getMountLabel(),
MountLabel: c.GetMountLabel(),
Resources: resources,
ProcessConfig: processConfig,
ProcessLabel: c.getProcessLabel(),
ProcessLabel: c.GetProcessLabel(),
},
FirstStart: !c.HasBeenStartedBefore,
LayerFolder: layerFolder,
LayerPaths: layerPaths,
Hostname: c.Config.Hostname,
Isolation: c.hostConfig.Isolation,
Isolation: c.HostConfig.Isolation,
ArgsEscaped: c.Config.ArgsEscaped,
}
@ -147,7 +116,7 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error {
}
// getSize returns real size & virtual size
func (daemon *Daemon) getSize(container *Container) (int64, int64) {
func (daemon *Daemon) getSize(container *container.Container) (int64, int64) {
// TODO Windows
return 0, 0
}
@ -158,56 +127,39 @@ func (daemon *Daemon) setNetworkNamespaceKey(containerID string, pid int) error
}
// allocateNetwork is a no-op on Windows.
func (daemon *Daemon) allocateNetwork(container *Container) error {
func (daemon *Daemon) allocateNetwork(container *container.Container) error {
return nil
}
func (daemon *Daemon) updateNetwork(container *Container) error {
func (daemon *Daemon) updateNetwork(container *container.Container) error {
return nil
}
func (daemon *Daemon) releaseNetwork(container *Container) {
func (daemon *Daemon) releaseNetwork(container *container.Container) {
}
// appendNetworkMounts appends any network mounts to the array of mount points passed in.
// Windows does not support network mounts (not to be confused with SMB network mounts), so
// this is a no-op.
func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) {
return volumeMounts, nil
}
func (daemon *Daemon) setupIpcDirs(container *Container) error {
func (daemon *Daemon) setupIpcDirs(container *container.Container) error {
return nil
}
func (container *Container) unmountIpcMounts(unmount func(pth string) error) {
}
func detachMounted(path string) error {
return nil
}
func (container *Container) ipcMounts() []execdriver.Mount {
return nil
}
func (container *Container) tmpfsMounts() []execdriver.Mount {
return nil
}
func getDefaultRouteMtu() (int, error) {
return -1, errSystemNotSupported
}
// TODO Windows: Fix Post-TP4. This is a hack to allow docker cp to work
// against containers which have volumes. You will still be able to cp
// to somewhere on the container drive, but not to any mounted volumes
// inside the container. Without this fix, docker cp is broken to any
// container which has a volume, regardless of where the file is inside the
// container.
func (daemon *Daemon) mountVolumes(container *Container) error {
func (daemon *Daemon) mountVolumes(container *container.Container) error {
return nil
}
func (container *Container) unmountVolumes(forceSyscall bool) error {
func detachMounted(path string) error {
return nil
}
func getDefaultRouteMtu() (int, error) {
return -1, errSystemNotSupported
}
func killProcessDirectly(container *container.Container) error {
return nil
}

View File

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

View File

@ -3,6 +3,7 @@ package daemon
import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types"
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/idtools"
@ -48,9 +49,9 @@ func (daemon *Daemon) ContainerCreate(params *ContainerCreateConfig) (types.Cont
}
// Create creates a new container from the given configuration with a given name.
func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, retErr error) {
func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *container.Container, retErr error) {
var (
container *Container
container *container.Container
img *image.Image
imgID image.ID
err error
@ -86,7 +87,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re
if err != nil {
return nil, err
}
if err := idtools.MkdirAs(container.root, 0700, rootUID, rootGID); err != nil {
if err := idtools.MkdirAs(container.Root, 0700, rootUID, rootGID); err != nil {
return nil, err
}
@ -105,7 +106,7 @@ func (daemon *Daemon) create(params *ContainerCreateConfig) (retC *Container, re
return nil, err
}
if err := container.toDiskLocking(); err != nil {
if err := container.ToDiskLocking(); err != nil {
logrus.Errorf("Error saving new container to disk: %v", err)
return nil, err
}

View File

@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/stringid"
@ -15,7 +16,7 @@ import (
)
// createContainerPlatformSpecificSettings performs platform specific container create functionality
func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
func (daemon *Daemon) createContainerPlatformSpecificSettings(container *container.Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
if err := daemon.Mount(container); err != nil {
return err
}
@ -27,7 +28,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Contain
// Skip volumes for which we already have something mounted on that
// destination because of a --volume-from.
if container.isDestinationMounted(destination) {
if container.IsDestinationMounted(destination) {
continue
}
path, err := container.GetResourcePath(destination)
@ -61,12 +62,12 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Contain
// never attempt to copy existing content in a container FS to a shared volume
if v.DriverName() == volume.DefaultDriverName {
if err := container.copyImagePathContent(v, destination); err != nil {
if err := container.CopyImagePathContent(v, destination); err != nil {
return err
}
}
container.addMountPointWithVolume(destination, v, true)
container.AddMountPointWithVolume(destination, v, true)
}
return nil
}

View File

@ -3,6 +3,7 @@ package daemon
import (
"fmt"
"github.com/docker/docker/container"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/runconfig"
@ -10,7 +11,7 @@ import (
)
// createContainerPlatformSpecificSettings performs platform specific container create functionality
func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
func (daemon *Daemon) createContainerPlatformSpecificSettings(container *container.Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
for spec := range config.Volumes {
mp, err := volume.ParseMountSpec(spec, hostConfig.VolumeDriver)
@ -25,7 +26,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Contain
// Skip volumes for which we already have something mounted on that
// destination because of a --volume-from.
if container.isDestinationMounted(mp.Destination) {
if container.IsDestinationMounted(mp.Destination) {
continue
}
@ -71,13 +72,13 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *Contain
//
// // never attempt to copy existing content in a container FS to a shared volume
// if v.DriverName() == volume.DefaultDriverName {
// if err := container.copyImagePathContent(v, mp.Destination); err != nil {
// if err := container.CopyImagePathContent(v, mp.Destination); err != nil {
// return err
// }
// }
// Add it to container.MountPoints
container.addMountPointWithVolume(mp.Destination, v, mp.RW)
container.AddMountPointWithVolume(mp.Destination, v, mp.RW)
}
return nil
}

View File

@ -23,6 +23,7 @@ import (
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cliconfig"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/events"
"github.com/docker/docker/daemon/exec"
"github.com/docker/docker/daemon/execdriver"
@ -84,17 +85,17 @@ func (e ErrImageDoesNotExist) Error() string {
}
type contStore struct {
s map[string]*Container
s map[string]*container.Container
sync.Mutex
}
func (c *contStore) Add(id string, cont *Container) {
func (c *contStore) Add(id string, cont *container.Container) {
c.Lock()
c.s[id] = cont
c.Unlock()
}
func (c *contStore) Get(id string) *Container {
func (c *contStore) Get(id string) *container.Container {
c.Lock()
res := c.s[id]
c.Unlock()
@ -107,7 +108,7 @@ func (c *contStore) Delete(id string) {
c.Unlock()
}
func (c *contStore) List() []*Container {
func (c *contStore) List() []*container.Container {
containers := new(History)
c.Lock()
for _, cont := range c.s {
@ -155,7 +156,7 @@ type Daemon struct {
// - A partial container ID prefix (e.g. short ID) of any length that is
// unique enough to only return a single container object
// If none of these searches succeed, an error is returned
func (daemon *Daemon) Get(prefixOrName string) (*Container, error) {
func (daemon *Daemon) Get(prefixOrName string) (*container.Container, error) {
if containerByID := daemon.containers.Get(prefixOrName); containerByID != nil {
// prefix is an exact match to a full container ID
return containerByID, nil
@ -188,7 +189,7 @@ func (daemon *Daemon) Exists(id string) bool {
// IsPaused returns a bool indicating if the specified container is paused.
func (daemon *Daemon) IsPaused(id string) bool {
c, _ := daemon.Get(id)
return c.State.isPaused()
return c.State.IsPaused()
}
func (daemon *Daemon) containerRoot(id string) string {
@ -197,10 +198,10 @@ func (daemon *Daemon) containerRoot(id string) string {
// Load reads the contents of a container from disk
// This is typically done at startup.
func (daemon *Daemon) load(id string) (*Container, error) {
func (daemon *Daemon) load(id string) (*container.Container, error) {
container := daemon.newBaseContainer(id)
if err := container.fromDisk(); err != nil {
if err := container.FromDisk(); err != nil {
return nil, err
}
@ -212,7 +213,7 @@ func (daemon *Daemon) load(id string) (*Container, error) {
}
// Register makes a container object usable by the daemon as <container.ID>
func (daemon *Daemon) Register(container *Container) error {
func (daemon *Daemon) Register(container *container.Container) error {
if daemon.Exists(container.ID) {
return fmt.Errorf("Container is already loaded")
}
@ -238,7 +239,7 @@ func (daemon *Daemon) Register(container *Container) error {
if container.IsRunning() {
logrus.Debugf("killing old running container %s", container.ID)
// Set exit code to 128 + SIGKILL (9) to properly represent unsuccessful exit
container.setStoppedLocking(&execdriver.ExitStatus{ExitCode: 137})
container.SetStoppedLocking(&execdriver.ExitStatus{ExitCode: 137})
// use the current driver and ensure that the container is dead x.x
cmd := &execdriver.Command{
CommonCommand: execdriver.CommonCommand{
@ -247,10 +248,10 @@ func (daemon *Daemon) Register(container *Container) error {
}
daemon.execDriver.Terminate(cmd)
container.unmountIpcMounts(mount.Unmount)
container.UnmountIpcMounts(mount.Unmount)
daemon.Unmount(container)
if err := container.toDiskLocking(); err != nil {
if err := container.ToDiskLocking(); err != nil {
logrus.Errorf("Error saving stopped state to disk: %v", err)
}
}
@ -262,7 +263,7 @@ func (daemon *Daemon) Register(container *Container) error {
return nil
}
func (daemon *Daemon) ensureName(container *Container) error {
func (daemon *Daemon) ensureName(container *container.Container) error {
if container.Name == "" {
name, err := daemon.generateNewName(container.ID)
if err != nil {
@ -270,7 +271,7 @@ func (daemon *Daemon) ensureName(container *Container) error {
}
container.Name = name
if err := container.toDiskLocking(); err != nil {
if err := container.ToDiskLocking(); err != nil {
logrus.Errorf("Error saving container name to disk: %v", err)
}
}
@ -279,7 +280,7 @@ func (daemon *Daemon) ensureName(container *Container) error {
func (daemon *Daemon) restore() error {
type cr struct {
container *Container
container *container.Container
registered bool
}
@ -336,7 +337,7 @@ func (daemon *Daemon) restore() error {
for _, c := range containers {
group.Add(1)
go func(container *Container, registered bool) {
go func(container *container.Container, registered bool) {
defer group.Done()
if !registered {
@ -355,7 +356,7 @@ func (daemon *Daemon) restore() error {
// check the restart policy on the containers and restart any container with
// the restart policy of "always"
if daemon.configStore.AutoRestart && container.shouldRestart() {
if daemon.configStore.AutoRestart && container.ShouldRestart() {
logrus.Debugf("Starting container %s", container.ID)
if err := daemon.containerStart(container); err != nil {
@ -474,7 +475,7 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *stringutils.StrSlic
return cmdSlice[0], cmdSlice[1:]
}
func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID image.ID) (*Container, error) {
func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID image.ID) (*container.Container, error) {
var (
id string
err error
@ -493,7 +494,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID
base.Path = entrypoint
base.Args = args //FIXME: de-duplicate from config
base.Config = config
base.hostConfig = &runconfig.HostConfig{}
base.HostConfig = &runconfig.HostConfig{}
base.ImageID = imgID
base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName}
base.Name = name
@ -516,7 +517,7 @@ func GetFullContainerName(name string) (string, error) {
}
// GetByName returns a container given a name.
func (daemon *Daemon) GetByName(name string) (*Container, error) {
func (daemon *Daemon) GetByName(name string) (*container.Container, error) {
fullName, err := GetFullContainerName(name)
if err != nil {
return nil, err
@ -576,12 +577,12 @@ func (daemon *Daemon) GetLabels(id string) map[string]string {
// children returns all child containers of the container with the
// given name. The containers are returned as a map from the container
// name to a pointer to Container.
func (daemon *Daemon) children(name string) (map[string]*Container, error) {
func (daemon *Daemon) children(name string) (map[string]*container.Container, error) {
name, err := GetFullContainerName(name)
if err != nil {
return nil, err
}
children := make(map[string]*Container)
children := make(map[string]*container.Container)
err = daemon.containerGraphDB.Walk(name, func(p string, e *graphdb.Entity) error {
c, err := daemon.Get(e.ID())
@ -609,7 +610,7 @@ func (daemon *Daemon) parents(name string) ([]string, error) {
return daemon.containerGraphDB.Parents(name)
}
func (daemon *Daemon) registerLink(parent, child *Container, alias string) error {
func (daemon *Daemon) registerLink(parent, child *container.Container, alias string) error {
fullName := filepath.Join(parent.Name, alias)
if !daemon.containerGraphDB.Exists(fullName) {
_, err := daemon.containerGraphDB.Set(fullName, child.ID)
@ -830,7 +831,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
d.ID = trustKey.PublicKey().KeyID()
d.repository = daemonRepo
d.containers = &contStore{s: make(map[string]*Container)}
d.containers = &contStore{s: make(map[string]*container.Container)}
d.execCommands = exec.NewStore()
d.tagStore = tagStore
d.distributionPool = distributionPool
@ -861,9 +862,9 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
return d, nil
}
func (daemon *Daemon) shutdownContainer(c *Container) error {
func (daemon *Daemon) shutdownContainer(c *container.Container) error {
// TODO(windows): Handle docker restart with paused containers
if c.isPaused() {
if c.IsPaused() {
// To terminate a process in freezer cgroup, we should send
// SIGTERM to this process then unfreeze it, and the process will
// force to terminate immediately.
@ -906,20 +907,20 @@ func (daemon *Daemon) Shutdown() error {
if daemon.containers != nil {
group := sync.WaitGroup{}
logrus.Debug("starting clean shutdown of all containers...")
for _, container := range daemon.List() {
if !container.IsRunning() {
for _, cont := range daemon.List() {
if !cont.IsRunning() {
continue
}
logrus.Debugf("stopping %s", container.ID)
logrus.Debugf("stopping %s", cont.ID)
group.Add(1)
go func(c *Container) {
go func(c *container.Container) {
defer group.Done()
if err := daemon.shutdownContainer(c); err != nil {
logrus.Errorf("Stop container error: %v", err)
return
}
logrus.Debugf("container stopped %s", c.ID)
}(container)
}(cont)
}
group.Wait()
}
@ -948,9 +949,9 @@ func (daemon *Daemon) Shutdown() error {
return nil
}
// Mount sets container.basefs
// Mount sets container.BaseFS
// (is it not set coming in? why is it unset?)
func (daemon *Daemon) Mount(container *Container) error {
func (daemon *Daemon) Mount(container *container.Container) error {
var layerID layer.ChainID
if container.ImageID != "" {
img, err := daemon.imageStore.Get(container.ImageID)
@ -959,7 +960,7 @@ func (daemon *Daemon) Mount(container *Container) error {
}
layerID = img.RootFS.ChainID()
}
rwlayer, err := daemon.layerStore.Mount(container.ID, layerID, container.getMountLabel(), daemon.setupInitLayer)
rwlayer, err := daemon.layerStore.Mount(container.ID, layerID, container.GetMountLabel(), daemon.setupInitLayer)
if err != nil {
return err
}
@ -969,56 +970,56 @@ func (daemon *Daemon) Mount(container *Container) error {
}
logrus.Debugf("container mounted via layerStore: %v", dir)
if container.basefs != dir {
if container.BaseFS != dir {
// The mount path reported by the graph driver should always be trusted on Windows, since the
// volume path for a given mounted layer may change over time. This should only be an error
// on non-Windows operating systems.
if container.basefs != "" && runtime.GOOS != "windows" {
if container.BaseFS != "" && runtime.GOOS != "windows" {
daemon.Unmount(container)
return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
daemon.driver, container.ID, container.basefs, dir)
daemon.driver, container.ID, container.BaseFS, dir)
}
}
container.basefs = dir // TODO: combine these fields
container.rwlayer = rwlayer
container.BaseFS = dir // TODO: combine these fields
container.RWLayer = rwlayer
return nil
}
// Unmount unsets the container base filesystem
func (daemon *Daemon) Unmount(container *Container) {
func (daemon *Daemon) Unmount(container *container.Container) {
if err := daemon.layerStore.Unmount(container.ID); err != nil {
logrus.Errorf("Error unmounting container %s: %s", container.ID, err)
}
}
// Run uses the execution driver to run a given container
func (daemon *Daemon) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (execdriver.ExitStatus, error) {
func (daemon *Daemon) Run(c *container.Container, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (execdriver.ExitStatus, error) {
hooks := execdriver.Hooks{
Start: startCallback,
}
hooks.PreStart = append(hooks.PreStart, func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error {
return daemon.setNetworkNamespaceKey(c.ID, pid)
})
return daemon.execDriver.Run(c.command, pipes, hooks)
return daemon.execDriver.Run(c.Command, pipes, hooks)
}
func (daemon *Daemon) kill(c *Container, sig int) error {
return daemon.execDriver.Kill(c.command, sig)
func (daemon *Daemon) kill(c *container.Container, sig int) error {
return daemon.execDriver.Kill(c.Command, sig)
}
func (daemon *Daemon) stats(c *Container) (*execdriver.ResourceStats, error) {
func (daemon *Daemon) stats(c *container.Container) (*execdriver.ResourceStats, error) {
return daemon.execDriver.Stats(c.ID)
}
func (daemon *Daemon) subscribeToContainerStats(c *Container) chan interface{} {
func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} {
return daemon.statsCollector.collect(c)
}
func (daemon *Daemon) unsubscribeToContainerStats(c *Container, ch chan interface{}) {
func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) {
daemon.statsCollector.unsubscribe(c, ch)
}
func (daemon *Daemon) changes(container *Container) ([]archive.Change, error) {
func (daemon *Daemon) changes(container *container.Container) ([]archive.Change, error) {
return daemon.layerStore.Changes(container.ID)
}
@ -1347,7 +1348,7 @@ func tempDir(rootDir string, rootUID, rootGID int) (string, error) {
return tmpDir, idtools.MkdirAllAs(tmpDir, 0700, rootUID, rootGID)
}
func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.HostConfig) error {
func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *runconfig.HostConfig) error {
container.Lock()
if err := parseSecurityOpt(container, hostConfig); err != nil {
container.Unlock()
@ -1369,8 +1370,8 @@ func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.
return err
}
container.hostConfig = hostConfig
container.toDisk()
container.HostConfig = hostConfig
container.ToDisk()
return nil
}
@ -1466,7 +1467,7 @@ func (daemon *Daemon) IsShuttingDown() bool {
}
// GetContainerStats collects all the stats published by a container
func (daemon *Daemon) GetContainerStats(container *Container) (*execdriver.ResourceStats, error) {
func (daemon *Daemon) GetContainerStats(container *container.Container) (*execdriver.ResourceStats, error) {
stats, err := daemon.stats(container)
if err != nil {
return nil, err
@ -1482,7 +1483,7 @@ func (daemon *Daemon) GetContainerStats(container *Container) (*execdriver.Resou
return stats, nil
}
func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) {
func (daemon *Daemon) getNetworkStats(c *container.Container) ([]*libcontainer.NetworkInterface, error) {
var list []*libcontainer.NetworkInterface
sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
@ -1505,8 +1506,8 @@ func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInte
// newBaseContainer creates a new container with its initial
// configuration based on the root storage from the daemon.
func (daemon *Daemon) newBaseContainer(id string) *Container {
return newBaseContainer(id, daemon.containerRoot(id))
func (daemon *Daemon) newBaseContainer(id string) *container.Container {
return container.NewBaseContainer(id, daemon.containerRoot(id))
}
func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface {
@ -1521,3 +1522,10 @@ func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *lib
n.TxDropped = stats.TxDropped
return n
}
func validateID(id string) error {
if id == "" {
return derr.ErrorCodeEmptyID
}
return nil
}

View File

@ -1,13 +1,17 @@
package daemon
import (
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"github.com/docker/docker/container"
"github.com/docker/docker/pkg/graphdb"
"github.com/docker/docker/pkg/truncindex"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/volume"
volumedrivers "github.com/docker/docker/volume/drivers"
"github.com/docker/docker/volume/local"
"github.com/docker/docker/volume/store"
@ -18,43 +22,43 @@ import (
//
func TestGet(t *testing.T) {
c1 := &Container{
CommonContainer: CommonContainer{
c1 := &container.Container{
CommonContainer: container.CommonContainer{
ID: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
Name: "tender_bardeen",
},
}
c2 := &Container{
CommonContainer: CommonContainer{
c2 := &container.Container{
CommonContainer: container.CommonContainer{
ID: "3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de",
Name: "drunk_hawking",
},
}
c3 := &Container{
CommonContainer: CommonContainer{
c3 := &container.Container{
CommonContainer: container.CommonContainer{
ID: "3cdbd1aa394fd68559fd1441d6eff2abfafdcba06e72d2febdba229008b0bf57",
Name: "3cdbd1aa",
},
}
c4 := &Container{
CommonContainer: CommonContainer{
c4 := &container.Container{
CommonContainer: container.CommonContainer{
ID: "75fb0b800922abdbef2d27e60abcdfaf7fb0698b2a96d22d3354da361a6ff4a5",
Name: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
},
}
c5 := &Container{
CommonContainer: CommonContainer{
c5 := &container.Container{
CommonContainer: container.CommonContainer{
ID: "d22d69a2b8960bf7fafdcba06e72d2febdba960bf7fafdcba06e72d2f9008b060b",
Name: "d22d69a2b896",
},
}
store := &contStore{
s: map[string]*Container{
s: map[string]*container.Container{
c1.ID: c1,
c2.ID: c2,
c3.ID: c3,
@ -136,7 +140,7 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
}
func TestParseSecurityOpt(t *testing.T) {
container := &Container{}
container := &container.Container{}
config := &runconfig.HostConfig{}
// test apparmor
@ -190,3 +194,109 @@ func TestNetworkOptions(t *testing.T) {
t.Fatalf("Expected networkOptions error, got nil")
}
}
func TestGetFullName(t *testing.T) {
name, err := GetFullContainerName("testing")
if err != nil {
t.Fatal(err)
}
if name != "/testing" {
t.Fatalf("Expected /testing got %s", name)
}
if _, err := GetFullContainerName(""); err == nil {
t.Fatal("Error should not be nil")
}
}
func TestValidContainerNames(t *testing.T) {
invalidNames := []string{"-rm", "&sdfsfd", "safd%sd"}
validNames := []string{"word-word", "word_word", "1weoid"}
for _, name := range invalidNames {
if validContainerNamePattern.MatchString(name) {
t.Fatalf("%q is not a valid container name and was returned as valid.", name)
}
}
for _, name := range validNames {
if !validContainerNamePattern.MatchString(name) {
t.Fatalf("%q is a valid container name and was returned as invalid.", name)
}
}
}
func TestContainerInitDNS(t *testing.T) {
tmp, err := ioutil.TempDir("", "docker-container-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
containerID := "d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e"
containerPath := filepath.Join(tmp, containerID)
if err := os.MkdirAll(containerPath, 0755); err != nil {
t.Fatal(err)
}
config := `{"State":{"Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":2464,"ExitCode":0,
"Error":"","StartedAt":"2015-05-26T16:48:53.869308965Z","FinishedAt":"0001-01-01T00:00:00Z"},
"ID":"d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e","Created":"2015-05-26T16:48:53.7987917Z","Path":"top",
"Args":[],"Config":{"Hostname":"d59df5276e7b","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"Cpuset":"",
"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":true,"OpenStdin":true,
"StdinOnce":false,"Env":null,"Cmd":["top"],"Image":"ubuntu:latest","Volumes":null,"WorkingDir":"","Entrypoint":null,
"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":{}},"Image":"07f8e8c5e66084bef8f848877857537ffe1c47edd01a93af27e7161672ad0e95",
"NetworkSettings":{"IPAddress":"172.17.0.1","IPPrefixLen":16,"MacAddress":"02:42:ac:11:00:01","LinkLocalIPv6Address":"fe80::42:acff:fe11:1",
"LinkLocalIPv6PrefixLen":64,"GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"Gateway":"172.17.42.1","IPv6Gateway":"","Bridge":"docker0","Ports":{}},
"ResolvConfPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/resolv.conf",
"HostnamePath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hostname",
"HostsPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hosts",
"LogPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e-json.log",
"Name":"/ubuntu","Driver":"aufs","MountLabel":"","ProcessLabel":"","AppArmorProfile":"","RestartCount":0,
"UpdateDns":false,"Volumes":{},"VolumesRW":{},"AppliedVolumesFrom":null}`
// Container struct only used to retrieve path to config file
container := &container.Container{CommonContainer: container.CommonContainer{Root: containerPath}}
configPath, err := container.ConfigPath()
if err != nil {
t.Fatal(err)
}
if err = ioutil.WriteFile(configPath, []byte(config), 0644); err != nil {
t.Fatal(err)
}
hostConfig := `{"Binds":[],"ContainerIDFile":"","Memory":0,"MemorySwap":0,"CpuShares":0,"CpusetCpus":"",
"Privileged":false,"PortBindings":{},"Links":null,"PublishAllPorts":false,"Dns":null,"DnsOptions":null,"DnsSearch":null,"ExtraHosts":null,"VolumesFrom":null,
"Devices":[],"NetworkMode":"bridge","IpcMode":"","PidMode":"","CapAdd":null,"CapDrop":null,"RestartPolicy":{"Name":"no","MaximumRetryCount":0},
"SecurityOpt":null,"ReadonlyRootfs":false,"Ulimits":null,"LogConfig":{"Type":"","Config":null},"CgroupParent":""}`
hostConfigPath, err := container.HostConfigPath()
if err != nil {
t.Fatal(err)
}
if err = ioutil.WriteFile(hostConfigPath, []byte(hostConfig), 0644); err != nil {
t.Fatal(err)
}
daemon, err := initDaemonWithVolumeStore(tmp)
if err != nil {
t.Fatal(err)
}
defer volumedrivers.Unregister(volume.DefaultDriverName)
c, err := daemon.load(containerID)
if err != nil {
t.Fatal(err)
}
if c.HostConfig.DNS == nil {
t.Fatal("Expected container DNS to not be nil")
}
if c.HostConfig.DNSSearch == nil {
t.Fatal("Expected container DNSSearch to not be nil")
}
if c.HostConfig.DNSOptions == nil {
t.Fatal("Expected container DNSOptions to not be nil")
}
}

View File

@ -12,6 +12,7 @@ import (
"syscall"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/graphdriver"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/image"
@ -57,7 +58,7 @@ func getBlkioWeightDevices(config *runconfig.HostConfig) ([]*blkiodev.WeightDevi
return BlkioWeightDevices, nil
}
func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error {
func parseSecurityOpt(container *container.Container, config *runconfig.HostConfig) error {
var (
labelOpts []string
err error
@ -128,7 +129,7 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *runconfig.HostConfig, a
hostConfig.MemorySwap = hostConfig.Memory * 2
}
if hostConfig.ShmSize == nil {
shmSize := DefaultSHMSize
shmSize := container.DefaultSHMSize
hostConfig.ShmSize = &shmSize
}
var err error
@ -575,7 +576,7 @@ func setupInitLayer(initLayer string, rootUID, rootGID int) error {
}
// registerLinks writes the links to a file.
func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig.HostConfig) error {
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *runconfig.HostConfig) error {
if hostConfig == nil || hostConfig.Links == nil {
return nil
}
@ -590,14 +591,14 @@ func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig.
//An error from daemon.Get() means this name could not be found
return fmt.Errorf("Could not get container for %s", name)
}
for child.hostConfig.NetworkMode.IsContainer() {
parts := strings.SplitN(string(child.hostConfig.NetworkMode), ":", 2)
for child.HostConfig.NetworkMode.IsContainer() {
parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2)
child, err = daemon.Get(parts[1])
if err != nil {
return fmt.Errorf("Could not get container for %s", parts[1])
}
}
if child.hostConfig.NetworkMode.IsHost() {
if child.HostConfig.NetworkMode.IsHost() {
return runconfig.ErrConflictHostNetworkAndLinks
}
if err := daemon.registerLink(container, child, alias); err != nil {
@ -608,7 +609,7 @@ func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig.
// After we load all the links into the daemon
// set them to nil on the hostconfig
hostConfig.Links = nil
if err := container.writeHostConfig(); err != nil {
if err := container.WriteHostConfig(); err != nil {
return err
}
@ -617,13 +618,13 @@ func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig.
// conditionalMountOnStart is a platform specific helper function during the
// container start to call mount.
func (daemon *Daemon) conditionalMountOnStart(container *Container) error {
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
return daemon.Mount(container)
}
// conditionalUnmountOnCleanup is a platform specific helper function called
// during the cleanup of a container to unmount.
func (daemon *Daemon) conditionalUnmountOnCleanup(container *Container) {
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) {
daemon.Unmount(container)
}

View File

@ -10,6 +10,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/reference"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/image"
@ -34,7 +35,7 @@ func getBlkioWeightDevices(config *runconfig.HostConfig) ([]*blkiodev.WeightDevi
return nil, nil
}
func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error {
func parseSecurityOpt(container *container.Container, config *runconfig.HostConfig) error {
return nil
}
@ -115,7 +116,7 @@ func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkC
// registerLinks sets up links between containers and writes the
// configuration out for persistence. As of Windows TP4, links are not supported.
func (daemon *Daemon) registerLinks(container *Container, hostConfig *runconfig.HostConfig) error {
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *runconfig.HostConfig) error {
return nil
}
@ -125,9 +126,9 @@ func (daemon *Daemon) cleanupMounts() error {
// conditionalMountOnStart is a platform specific helper function during the
// container start to call mount.
func (daemon *Daemon) conditionalMountOnStart(container *Container) error {
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
// We do not mount if a Hyper-V container
if !container.hostConfig.Isolation.IsHyperV() {
if !container.HostConfig.Isolation.IsHyperV() {
if err := daemon.Mount(container); err != nil {
return err
}
@ -137,9 +138,9 @@ func (daemon *Daemon) conditionalMountOnStart(container *Container) error {
// conditionalUnmountOnCleanup is a platform specific helper function called
// during the cleanup of a container to unmount.
func (daemon *Daemon) conditionalUnmountOnCleanup(container *Container) {
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) {
// We do not unmount if a Hyper-V container
if !container.hostConfig.Isolation.IsHyperV() {
if !container.HostConfig.Isolation.IsHyperV() {
daemon.Unmount(container)
}
}

View File

@ -13,6 +13,7 @@ import (
"github.com/docker/docker/api"
"github.com/docker/docker/builder"
"github.com/docker/docker/cliconfig"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/archive"
@ -80,12 +81,12 @@ func (d Docker) Pull(name string) (*image.Image, error) {
}
// Container looks up a Docker container referenced by `id`.
func (d Docker) Container(id string) (*daemon.Container, error) {
func (d Docker) Container(id string) (*container.Container, error) {
return d.Daemon.Get(id)
}
// Create creates a new Docker container and returns potential warnings
func (d Docker) Create(cfg *runconfig.Config, hostCfg *runconfig.HostConfig) (*daemon.Container, []string, error) {
func (d Docker) Create(cfg *runconfig.Config, hostCfg *runconfig.HostConfig) (*container.Container, []string, error) {
ccr, err := d.Daemon.ContainerCreate(&daemon.ContainerCreateConfig{
Name: "",
Config: cfg,
@ -129,7 +130,7 @@ func (d Docker) Release(sessionID string, activeImages []string) {
// specified by a container object.
// TODO: make sure callers don't unnecessarily convert destPath with filepath.FromSlash (Copy does it already).
// Copy should take in abstract paths (with slashes) and the implementation should convert it to OS-specific paths.
func (d Docker) Copy(c *daemon.Container, destPath string, src builder.FileInfo, decompress bool) error {
func (d Docker) Copy(c *container.Container, destPath string, src builder.FileInfo, decompress bool) error {
srcPath := src.Path()
destExists := true
rootUID, rootGID := d.Daemon.GetRemappedUIDGID()
@ -212,23 +213,23 @@ func (d Docker) GetCachedImage(imgID string, cfg *runconfig.Config) (string, err
}
// Kill stops the container execution abruptly.
func (d Docker) Kill(container *daemon.Container) error {
func (d Docker) Kill(container *container.Container) error {
return d.Daemon.Kill(container)
}
// Mount mounts the root filesystem for the container.
func (d Docker) Mount(c *daemon.Container) error {
func (d Docker) Mount(c *container.Container) error {
return d.Daemon.Mount(c)
}
// Unmount unmounts the root filesystem for the container.
func (d Docker) Unmount(c *daemon.Container) error {
func (d Docker) Unmount(c *container.Container) error {
d.Daemon.Unmount(c)
return nil
}
// Start starts a container
func (d Docker) Start(c *daemon.Container) error {
func (d Docker) Start(c *container.Container) error {
return d.Daemon.Start(c)
}

View File

@ -5,6 +5,7 @@ import (
"path"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/layer"
volumestore "github.com/docker/docker/volume/store"
@ -26,14 +27,14 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error
}
// Container state RemovalInProgress should be used to avoid races.
if err = container.setRemovalInProgress(); err != nil {
if err = container.SetRemovalInProgress(); err != nil {
if err == derr.ErrorCodeAlreadyRemoving {
// do not fail when the removal is in progress started by other request.
return nil
}
return derr.ErrorCodeRmState.WithArgs(err)
}
defer container.resetRemovalInProgress()
defer container.ResetRemovalInProgress()
// check if container wasn't deregistered by previous rm since Get
if c := daemon.containers.Get(container.ID); c == nil {
@ -87,7 +88,7 @@ func (daemon *Daemon) rmLink(name string) error {
// cleanupContainer unregisters a container from the daemon, stops stats
// collection and cleanly removes contents and metadata from the filesystem.
func (daemon *Daemon) cleanupContainer(container *Container, forceRemove bool) (err error) {
func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemove bool) (err error) {
if container.IsRunning() {
if !forceRemove {
return derr.ErrorCodeRmRunning
@ -106,12 +107,12 @@ func (daemon *Daemon) cleanupContainer(container *Container, forceRemove bool) (
}
// Mark container dead. We don't want anybody to be restarting it.
container.setDead()
container.SetDead()
// Save container state to disk. So that if error happens before
// container meta file got removed from disk, then a restart of
// docker should not make a dead container alive.
if err := container.toDiskLocking(); err != nil {
if err := container.ToDiskLocking(); err != nil {
logrus.Errorf("Error saving dying container to disk: %v", err)
}
@ -129,7 +130,7 @@ func (daemon *Daemon) cleanupContainer(container *Container, forceRemove bool) (
}
}()
if err = os.RemoveAll(container.root); err != nil {
if err = os.RemoveAll(container.Root); err != nil {
return derr.ErrorCodeRmFS.WithArgs(container.ID, err)
}

View File

@ -5,6 +5,7 @@ import (
"os"
"testing"
"github.com/docker/docker/container"
"github.com/docker/docker/runconfig"
)
@ -18,19 +19,19 @@ func TestContainerDoubleDelete(t *testing.T) {
repository: tmp,
root: tmp,
}
daemon.containers = &contStore{s: make(map[string]*Container)}
daemon.containers = &contStore{s: make(map[string]*container.Container)}
container := &Container{
CommonContainer: CommonContainer{
container := &container.Container{
CommonContainer: container.CommonContainer{
ID: "test",
State: NewState(),
State: container.NewState(),
Config: &runconfig.Config{},
},
}
daemon.containers.Add(container.ID, container)
// Mark the container as having a delete in progress
if err := container.setRemovalInProgress(); err != nil {
if err := container.SetRemovalInProgress(); err != nil {
t.Fatal(err)
}

View File

@ -1,7 +1,11 @@
package daemon
import (
"github.com/docker/docker/container"
)
// LogContainerEvent generates an event related to a container.
func (daemon *Daemon) LogContainerEvent(container *Container, action string) {
func (daemon *Daemon) LogContainerEvent(container *container.Container, action string) {
daemon.EventsService.Log(
action,
container.ID,

View File

@ -6,6 +6,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/exec"
"github.com/docker/docker/daemon/execdriver"
derr "github.com/docker/docker/errors"
@ -15,15 +16,15 @@ import (
"github.com/docker/docker/runconfig"
)
func (d *Daemon) registerExecCommand(container *Container, config *exec.Config) {
func (d *Daemon) registerExecCommand(container *container.Container, config *exec.Config) {
// Storing execs in container in order to kill them gracefully whenever the container is stopped or removed.
container.execCommands.Add(config.ID, config)
container.ExecCommands.Add(config.ID, config)
// Storing execs in daemon for easy access via remote API.
d.execCommands.Add(config.ID, config)
}
// ExecExists looks up the exec instance and returns a bool if it exists or not.
// It will also return the error produced by `getExecConfig`
// It will also return the error produced by `getConfig`
func (d *Daemon) ExecExists(name string) (bool, error) {
if _, err := d.getExecConfig(name); err != nil {
return false, err
@ -47,7 +48,7 @@ func (d *Daemon) getExecConfig(name string) (*exec.Config, error) {
if !container.IsRunning() {
return nil, derr.ErrorCodeContainerNotRunning.WithArgs(container.ID, container.State.String())
}
if container.isPaused() {
if container.IsPaused() {
return nil, derr.ErrorCodeExecPaused.WithArgs(container.ID)
}
return ec, nil
@ -57,12 +58,12 @@ func (d *Daemon) getExecConfig(name string) (*exec.Config, error) {
return nil, derr.ErrorCodeNoExecID.WithArgs(name)
}
func (d *Daemon) unregisterExecCommand(container *Container, execConfig *exec.Config) {
container.execCommands.Delete(execConfig.ID)
func (d *Daemon) unregisterExecCommand(container *container.Container, execConfig *exec.Config) {
container.ExecCommands.Delete(execConfig.ID)
d.execCommands.Delete(execConfig.ID)
}
func (d *Daemon) getActiveContainer(name string) (*Container, error) {
func (d *Daemon) getActiveContainer(name string) (*container.Container, error) {
container, err := d.Get(name)
if err != nil {
return nil, err
@ -71,7 +72,7 @@ func (d *Daemon) getActiveContainer(name string) (*Container, error) {
if !container.IsRunning() {
return nil, derr.ErrorCodeNotRunning.WithArgs(name)
}
if container.isPaused() {
if container.IsPaused() {
return nil, derr.ErrorCodeExecPaused.WithArgs(name)
}
return container, nil
@ -131,9 +132,9 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
ec.Running = true
ec.Unlock()
container := d.containers.Get(ec.ContainerID)
logrus.Debugf("starting exec command %s in container %s", ec.ID, container.ID)
d.LogContainerEvent(container, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " "))
c := d.containers.Get(ec.ContainerID)
logrus.Debugf("starting exec command %s in container %s", ec.ID, c.ID)
d.LogContainerEvent(c, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " "))
if ec.OpenStdin {
r, w := io.Pipe()
@ -157,8 +158,7 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
ec.NewNopInputPipe()
}
attachErr := attach(ec.StreamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr)
attachErr := container.AttachStreams(ec.StreamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr)
execErr := make(chan error)
// Note, the ExecConfig data will be removed when the container
@ -166,7 +166,7 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
// the exitStatus) even after the cmd is done running.
go func() {
execErr <- d.containerExec(container, ec)
execErr <- d.containerExec(c, ec)
}()
select {
@ -184,19 +184,19 @@ func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.
}
// Maybe the container stopped while we were trying to exec
if !container.IsRunning() {
if !c.IsRunning() {
return derr.ErrorCodeExecContainerStopped
}
return derr.ErrorCodeExecCantRun.WithArgs(ec.ID, container.ID, err)
return derr.ErrorCodeExecCantRun.WithArgs(ec.ID, c.ID, err)
}
}
// Exec calls the underlying exec driver to run
func (d *Daemon) Exec(c *Container, execConfig *exec.Config, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) {
func (d *Daemon) Exec(c *container.Container, execConfig *exec.Config, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) {
hooks := execdriver.Hooks{
Start: startCallback,
}
exitStatus, err := d.execDriver.Exec(c.command, execConfig.ProcessConfig, pipes, hooks)
exitStatus, err := d.execDriver.Exec(c.Command, execConfig.ProcessConfig, pipes, hooks)
// On err, make sure we don't leave ExitCode at zero
if err != nil && exitStatus == 0 {
@ -238,14 +238,14 @@ func (d *Daemon) execCommandGC() {
func (d *Daemon) containerExecIds() map[string]struct{} {
ids := map[string]struct{}{}
for _, c := range d.containers.List() {
for _, id := range c.execCommands.List() {
for _, id := range c.ExecCommands.List() {
ids[id] = struct{}{}
}
}
return ids
}
func (d *Daemon) containerExec(container *Container, ec *exec.Config) error {
func (d *Daemon) containerExec(container *container.Container, ec *exec.Config) error {
container.Lock()
defer container.Unlock()
@ -268,7 +268,7 @@ func (d *Daemon) containerExec(container *Container, ec *exec.Config) error {
return ec.Wait(cErr)
}
func (d *Daemon) monitorExec(container *Container, execConfig *exec.Config, callback execdriver.DriverCallback) error {
func (d *Daemon) monitorExec(container *container.Container, execConfig *exec.Config, callback execdriver.DriverCallback) error {
pipes := execdriver.NewPipes(execConfig.Stdin(), execConfig.Stdout(), execConfig.Stderr(), execConfig.OpenStdin)
exitCode, err := d.Exec(container, execConfig, pipes, callback)
if err != nil {
@ -287,6 +287,6 @@ func (d *Daemon) monitorExec(container *Container, execConfig *exec.Config, call
}
// remove the exec command from the container's store only and not the
// daemon's store so that the exec command can be inspected.
container.execCommands.Delete(execConfig.ID)
container.ExecCommands.Delete(execConfig.ID)
return err
}

View File

@ -3,13 +3,14 @@
package daemon
import (
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/runconfig"
)
// setPlatformSpecificExecProcessConfig sets platform-specific fields in the
// ProcessConfig structure.
func setPlatformSpecificExecProcessConfig(config *runconfig.ExecConfig, container *Container, pc *execdriver.ProcessConfig) {
func setPlatformSpecificExecProcessConfig(config *runconfig.ExecConfig, container *container.Container, pc *execdriver.ProcessConfig) {
user := config.User
if len(user) == 0 {
user = container.Config.User

View File

@ -1,11 +1,12 @@
package daemon
import (
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/runconfig"
)
// setPlatformSpecificExecProcessConfig sets platform-specific fields in the
// ProcessConfig structure. This is a no-op on Windows
func setPlatformSpecificExecProcessConfig(config *runconfig.ExecConfig, container *Container, pc *execdriver.ProcessConfig) {
func setPlatformSpecificExecProcessConfig(config *runconfig.ExecConfig, container *container.Container, pc *execdriver.ProcessConfig) {
}

View File

@ -3,6 +3,7 @@ package daemon
import (
"io"
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/ioutils"
@ -29,13 +30,13 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error {
return nil
}
func (daemon *Daemon) containerExport(container *Container) (archive.Archive, error) {
func (daemon *Daemon) containerExport(container *container.Container) (archive.Archive, error) {
if err := daemon.Mount(container); err != nil {
return nil, err
}
uidMaps, gidMaps := daemon.GetUIDGIDMaps()
archive, err := archive.TarWithOptions(container.basefs, &archive.TarOptions{
archive, err := archive.TarWithOptions(container.BaseFS, &archive.TarOptions{
Compression: archive.Uncompressed,
UIDMaps: uidMaps,
GIDMaps: gidMaps,

View File

@ -2,11 +2,13 @@ package daemon
import (
"sort"
"github.com/docker/docker/container"
)
// History is a convenience type for storing a list of containers,
// ordered by creation date.
type History []*Container
type History []*container.Container
func (history *History) Len() int {
return len(*history)
@ -23,7 +25,7 @@ func (history *History) Swap(i, j int) {
}
// Add the given container to history.
func (history *History) Add(container *Container) {
func (history *History) Add(container *container.Container) {
*history = append(*history, container)
}

View File

@ -6,6 +6,7 @@ import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/stringid"
@ -133,7 +134,7 @@ func isImageIDPrefix(imageID, possiblePrefix string) bool {
// getContainerUsingImage returns a container that was created using the given
// imageID. Returns nil if there is no such container.
func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *Container {
func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Container {
for _, container := range daemon.List() {
if container.ImageID == imageID {
return container

View File

@ -6,6 +6,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions/v1p20"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/exec"
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/layer"
@ -85,7 +86,7 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er
MacAddress: container.Config.MacAddress,
NetworkDisabled: container.Config.NetworkDisabled,
ExposedPorts: container.Config.ExposedPorts,
VolumeDriver: container.hostConfig.VolumeDriver,
VolumeDriver: container.HostConfig.VolumeDriver,
}
networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
@ -97,9 +98,9 @@ func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, er
}, nil
}
func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.ContainerJSONBase, error) {
func (daemon *Daemon) getInspectData(container *container.Container, size bool) (*types.ContainerJSONBase, error) {
// make a copy to play with
hostConfig := *container.hostConfig
hostConfig := *container.HostConfig
if children, err := daemon.children(container.Name); err == nil {
for linkAlias, child := range children {
@ -143,7 +144,7 @@ func (daemon *Daemon) getInspectData(container *Container, size bool) (*types.Co
Driver: container.Driver,
MountLabel: container.MountLabel,
ProcessLabel: container.ProcessLabel,
ExecIDs: container.getExecIDs(),
ExecIDs: container.GetExecIDs(),
HostConfig: &hostConfig,
}

View File

@ -5,10 +5,11 @@ package daemon
import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions/v1p19"
"github.com/docker/docker/container"
)
// This sets platform-specific fields
func setPlatformSpecificContainerFields(container *Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
func setPlatformSpecificContainerFields(container *container.Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
contJSONBase.AppArmorProfile = container.AppArmorProfile
contJSONBase.ResolvConfPath = container.ResolvConfPath
contJSONBase.HostnamePath = container.HostnamePath
@ -44,11 +45,11 @@ func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON,
MacAddress: container.Config.MacAddress,
NetworkDisabled: container.Config.NetworkDisabled,
ExposedPorts: container.Config.ExposedPorts,
VolumeDriver: container.hostConfig.VolumeDriver,
Memory: container.hostConfig.Memory,
MemorySwap: container.hostConfig.MemorySwap,
CPUShares: container.hostConfig.CPUShares,
CPUSet: container.hostConfig.CpusetCpus,
VolumeDriver: container.HostConfig.VolumeDriver,
Memory: container.HostConfig.Memory,
MemorySwap: container.HostConfig.MemorySwap,
CPUShares: container.HostConfig.CPUShares,
CPUSet: container.HostConfig.CpusetCpus,
}
networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
@ -61,7 +62,7 @@ func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON,
}, nil
}
func addMountPoints(container *Container) []types.MountPoint {
func addMountPoints(container *container.Container) []types.MountPoint {
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
for _, m := range container.MountPoints {
mountPoints = append(mountPoints, types.MountPoint{

View File

@ -1,13 +1,16 @@
package daemon
import "github.com/docker/docker/api/types"
import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/container"
)
// This sets platform-specific fields
func setPlatformSpecificContainerFields(container *Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
func setPlatformSpecificContainerFields(container *container.Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
return contJSONBase
}
func addMountPoints(container *Container) []types.MountPoint {
func addMountPoints(container *container.Container) []types.MountPoint {
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
for _, m := range container.MountPoints {
mountPoints = append(mountPoints, types.MountPoint{

View File

@ -7,6 +7,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/signal"
)
@ -37,7 +38,7 @@ func (daemon *Daemon) ContainerKill(name string, sig uint64) error {
// to send the signal. An error is returned if the container is paused
// or not running, or if there is a problem returned from the
// underlying kill command.
func (daemon *Daemon) killWithSignal(container *Container, sig int) error {
func (daemon *Daemon) killWithSignal(container *container.Container, sig int) error {
logrus.Debugf("Sending %d to %s", sig, container.ID)
container.Lock()
defer container.Unlock()
@ -69,7 +70,7 @@ func (daemon *Daemon) killWithSignal(container *Container, sig int) error {
}
// Kill forcefully terminates a container.
func (daemon *Daemon) Kill(container *Container) error {
func (daemon *Daemon) Kill(container *container.Container) error {
if !container.IsRunning() {
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
}
@ -105,7 +106,7 @@ func (daemon *Daemon) Kill(container *Container) error {
}
// killPossibleDeadProcess is a wrapper aroung killSig() suppressing "no such process" error.
func (daemon *Daemon) killPossiblyDeadProcess(container *Container, sig int) error {
func (daemon *Daemon) killPossiblyDeadProcess(container *container.Container, sig int) error {
err := daemon.killWithSignal(container, sig)
if err == syscall.ESRCH {
logrus.Debugf("Cannot kill process (pid=%d) with signal %d: no such process.", container.GetPID(), sig)

View File

@ -8,6 +8,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types"
"github.com/docker/docker/container"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/graphdb"
"github.com/docker/docker/pkg/nat"
@ -19,7 +20,7 @@ type iterationAction int
// containerReducer represents a reducer for a container.
// Returns the object to serialize by the api.
type containerReducer func(*Container, *listContext) (*types.Container, error)
type containerReducer func(*container.Container, *listContext) (*types.Container, error)
const (
// includeContainer is the action to include a container in the reducer.
@ -34,7 +35,7 @@ const (
var errStopIteration = errors.New("container list iteration stopped")
// List returns an array of all containers registered in the daemon.
func (daemon *Daemon) List() []*Container {
func (daemon *Daemon) List() []*container.Container {
return daemon.containers.List()
}
@ -71,10 +72,10 @@ type listContext struct {
exitAllowed []int
// beforeFilter is a filter to ignore containers that appear before the one given
// this is used for --filter=before= and --before=, the latter is deprecated.
beforeFilter *Container
beforeFilter *container.Container
// sinceFilter is a filter to stop the filtering when the iterator arrive to the given container
// this is used for --filter=since= and --since=, the latter is deprecated.
sinceFilter *Container
sinceFilter *container.Container
// ContainersConfig is the filters set by the user
*ContainersConfig
}
@ -110,7 +111,7 @@ func (daemon *Daemon) reduceContainers(config *ContainersConfig, reducer contain
}
// reducePsContainer is the basic representation for a container as expected by the ps command.
func (daemon *Daemon) reducePsContainer(container *Container, ctx *listContext, reducer containerReducer) (*types.Container, error) {
func (daemon *Daemon) reducePsContainer(container *container.Container, ctx *listContext, reducer containerReducer) (*types.Container, error) {
container.Lock()
defer container.Unlock()
@ -148,7 +149,7 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error)
}
err = psFilters.WalkValues("status", func(value string) error {
if !isValidStateString(value) {
if !container.IsValidStateString(value) {
return fmt.Errorf("Unrecognised filter value for status: %s", value)
}
@ -159,7 +160,7 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error)
return nil, err
}
var beforeContFilter, sinceContFilter *Container
var beforeContFilter, sinceContFilter *container.Container
err = psFilters.WalkValues("before", func(value string) error {
beforeContFilter, err = daemon.Get(value)
return err
@ -230,7 +231,7 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error)
// includeContainerInList decides whether a containers should be include in the output or not based in the filter.
// It also decides if the iteration should be stopped or not.
func includeContainerInList(container *Container, ctx *listContext) iterationAction {
func includeContainerInList(container *container.Container, ctx *listContext) iterationAction {
// Do not include container if it's stopped and we're not filters
if !container.Running && !ctx.All && ctx.Limit <= 0 && ctx.beforeFilter == nil && ctx.sinceFilter == nil {
return excludeContainer
@ -309,7 +310,7 @@ func includeContainerInList(container *Container, ctx *listContext) iterationAct
}
// transformContainer generates the container type expected by the docker ps command.
func (daemon *Daemon) transformContainer(container *Container, ctx *listContext) (*types.Container, error) {
func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) {
newC := &types.Container{
ID: container.ID,
Names: ctx.names[container.ID],
@ -349,7 +350,7 @@ func (daemon *Daemon) transformContainer(container *Container, ctx *listContext)
}
newC.Created = container.Created.Unix()
newC.Status = container.State.String()
newC.HostConfig.NetworkMode = string(container.hostConfig.NetworkMode)
newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
newC.Ports = []types.Port{}
for port, bindings := range container.NetworkSettings.Ports {

View File

@ -2,8 +2,10 @@
package daemon
import "github.com/docker/docker/container"
// excludeByIsolation is a platform specific helper function to support PS
// filtering by Isolation. This is a Windows-only concept, so is a no-op on Unix.
func excludeByIsolation(container *Container, ctx *listContext) iterationAction {
func excludeByIsolation(container *container.Container, ctx *listContext) iterationAction {
return includeContainer
}

View File

@ -1,11 +1,15 @@
package daemon
import "strings"
import (
"strings"
"github.com/docker/docker/container"
)
// excludeByIsolation is a platform specific helper function to support PS
// filtering by Isolation. This is a Windows-only concept, so is a no-op on Unix.
func excludeByIsolation(container *Container, ctx *listContext) iterationAction {
i := strings.ToLower(string(container.hostConfig.Isolation))
func excludeByIsolation(container *container.Container, ctx *listContext) iterationAction {
i := strings.ToLower(string(container.HostConfig.Isolation))
if i == "" {
i = "default"
}

View File

@ -6,6 +6,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/logger"
"github.com/docker/docker/daemon/logger/jsonfilelog"
derr "github.com/docker/docker/errors"
@ -99,11 +100,11 @@ func (daemon *Daemon) ContainerLogs(containerName string, config *ContainerLogsC
}
}
func (daemon *Daemon) getLogger(container *Container) (logger.Logger, error) {
if container.logDriver != nil && container.IsRunning() {
return container.logDriver, nil
func (daemon *Daemon) getLogger(container *container.Container) (logger.Logger, error) {
if container.LogDriver != nil && container.IsRunning() {
return container.LogDriver, nil
}
cfg := container.getLogConfig(daemon.defaultLogConfig)
cfg := container.GetLogConfig(daemon.defaultLogConfig)
if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil {
return nil, err
}
@ -111,8 +112,8 @@ func (daemon *Daemon) getLogger(container *Container) (logger.Logger, error) {
}
// StartLogging initializes and starts the container logging stream.
func (daemon *Daemon) StartLogging(container *Container) error {
cfg := container.getLogConfig(daemon.defaultLogConfig)
func (daemon *Daemon) StartLogging(container *container.Container) error {
cfg := container.GetLogConfig(daemon.defaultLogConfig)
if cfg.Type == "none" {
return nil // do not start logging routines
}
@ -126,9 +127,9 @@ func (daemon *Daemon) StartLogging(container *Container) error {
}
copier := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
container.logCopier = copier
container.LogCopier = copier
copier.Run()
container.logDriver = l
container.LogDriver = l
// set LogPath field only for json-file logdriver
if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok {

View File

@ -3,11 +3,12 @@ package daemon
import (
"strings"
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
volumestore "github.com/docker/docker/volume/store"
)
func (daemon *Daemon) prepareMountPoints(container *Container) error {
func (daemon *Daemon) prepareMountPoints(container *container.Container) error {
for _, config := range container.MountPoints {
if len(config.Driver) > 0 {
v, err := daemon.createVolume(config.Name, config.Driver, nil)
@ -20,7 +21,7 @@ func (daemon *Daemon) prepareMountPoints(container *Container) error {
return nil
}
func (daemon *Daemon) removeMountPoints(container *Container, rm bool) error {
func (daemon *Daemon) removeMountPoints(container *container.Container, rm bool) error {
var rmErrors []string
for _, m := range container.MountPoints {
if m.Volume == nil {

View File

@ -1,6 +1,7 @@
package daemon
import (
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
)
@ -20,7 +21,7 @@ func (daemon *Daemon) ContainerPause(name string) error {
// containerPause pauses the container execution without stopping the process.
// The execution can be resumed by calling containerUnpause.
func (daemon *Daemon) containerPause(container *Container) error {
func (daemon *Daemon) containerPause(container *container.Container) error {
container.Lock()
defer container.Unlock()
@ -34,7 +35,7 @@ func (daemon *Daemon) containerPause(container *Container) error {
return derr.ErrorCodeAlreadyPaused.WithArgs(container.ID)
}
if err := daemon.execDriver.Pause(container.command); err != nil {
if err := daemon.execDriver.Pause(container.Command); err != nil {
return err
}
container.Paused = true

View File

@ -13,17 +13,15 @@ import (
// reserved.
func (daemon *Daemon) ContainerRename(oldName, newName string) error {
var (
err error
sid string
sb libnetwork.Sandbox
container *Container
sid string
sb libnetwork.Sandbox
)
if oldName == "" || newName == "" {
return derr.ErrorCodeEmptyRename
}
container, err = daemon.Get(oldName)
container, err := daemon.Get(oldName)
if err != nil {
return err
}
@ -50,7 +48,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
return derr.ErrorCodeRenameDelete.WithArgs(oldName, err)
}
if err = container.toDisk(); err != nil {
if err = container.ToDisk(); err != nil {
return err
}
@ -62,7 +60,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
defer func() {
if err != nil {
container.Name = oldName
if e := container.toDisk(); e != nil {
if e := container.ToDisk(); e != nil {
logrus.Errorf("%s: Failed in writing to Disk on rename failure: %v", container.ID, e)
}
}

View File

@ -1,6 +1,7 @@
package daemon
import (
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
)
@ -25,7 +26,7 @@ func (daemon *Daemon) ContainerRestart(name string, seconds int) error {
// container. When stopping, wait for the given duration in seconds to
// gracefully stop, before forcefully terminating the container. If
// given a negative duration, wait forever for a graceful stop.
func (daemon *Daemon) containerRestart(container *Container, seconds int) error {
func (daemon *Daemon) containerRestart(container *container.Container, seconds int) error {
// Avoid unnecessarily unmounting and then directly mounting
// the container when the container stops and then starts
// again

View File

@ -4,8 +4,8 @@ import (
"runtime"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/promise"
"github.com/docker/docker/runconfig"
)
@ -16,7 +16,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf
return err
}
if container.isPaused() {
if container.IsPaused() {
return derr.ErrorCodeStartPaused
}
@ -42,7 +42,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf
if err := daemon.setHostConfig(container, hostConfig); err != nil {
return err
}
initDNSHostConfig(container)
container.InitDNSHostConfig()
}
} else {
if hostConfig != nil {
@ -52,7 +52,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf
// check if hostConfig is in line with the current system settings.
// It may happen cgroups are umounted or the like.
if _, err = daemon.verifyContainerSettings(container.hostConfig, nil); err != nil {
if _, err = daemon.verifyContainerSettings(container.HostConfig, nil); err != nil {
return err
}
@ -64,7 +64,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *runconfig.HostConf
}
// Start starts a container
func (daemon *Daemon) Start(container *Container) error {
func (daemon *Daemon) Start(container *container.Container) error {
return daemon.containerStart(container)
}
@ -72,7 +72,7 @@ func (daemon *Daemon) Start(container *Container) error {
// container needs, such as storage and networking, as well as links
// between containers. The container is left waiting for a signal to
// begin running.
func (daemon *Daemon) containerStart(container *Container) (err error) {
func (daemon *Daemon) containerStart(container *container.Container) (err error) {
container.Lock()
defer container.Unlock()
@ -80,7 +80,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
return nil
}
if container.removalInProgress || container.Dead {
if container.RemovalInProgress || container.Dead {
return derr.ErrorCodeContainerBeingRemoved
}
@ -88,12 +88,12 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
// setup has been cleaned up properly
defer func() {
if err != nil {
container.setError(err)
container.SetError(err)
// if no one else has set it, make sure we don't leave it at zero
if container.ExitCode == 0 {
container.ExitCode = 128
}
container.toDisk()
container.ToDisk()
daemon.Cleanup(container)
daemon.LogContainerEvent(container, "die")
}
@ -105,7 +105,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
// Make sure NetworkMode has an acceptable value. We do this to ensure
// backwards API compatibility.
container.hostConfig = runconfig.SetDefaultNetModeIfBlank(container.hostConfig)
container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig)
if err := daemon.initializeNetworking(container); err != nil {
return err
@ -114,15 +114,15 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
if err != nil {
return err
}
if err := container.setupWorkingDirectory(); err != nil {
if err := container.SetupWorkingDirectory(); err != nil {
return err
}
env := container.createDaemonEnvironment(linkedEnv)
env := container.CreateDaemonEnvironment(linkedEnv)
if err := daemon.populateCommand(container, env); err != nil {
return err
}
if !container.hostConfig.IpcMode.IsContainer() && !container.hostConfig.IpcMode.IsHost() {
if !container.HostConfig.IpcMode.IsContainer() && !container.HostConfig.IpcMode.IsHost() {
if err := daemon.setupIpcDirs(container); err != nil {
return err
}
@ -132,10 +132,10 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
if err != nil {
return err
}
mounts = append(mounts, container.ipcMounts()...)
mounts = append(mounts, container.tmpfsMounts()...)
mounts = append(mounts, container.IpcMounts()...)
mounts = append(mounts, container.TmpfsMounts()...)
container.command.Mounts = mounts
container.Command.Mounts = mounts
if err := daemon.waitForStart(container); err != nil {
return err
}
@ -143,34 +143,24 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
return nil
}
func (daemon *Daemon) waitForStart(container *Container) error {
container.monitor = daemon.newContainerMonitor(container, container.hostConfig.RestartPolicy)
// block until we either receive an error from the initial start of the container's
// process or until the process is running in the container
select {
case <-container.monitor.startSignal:
case err := <-promise.Go(container.monitor.Start):
return err
}
return nil
func (daemon *Daemon) waitForStart(container *container.Container) error {
return container.StartMonitor(daemon, container.HostConfig.RestartPolicy)
}
// Cleanup releases any network resources allocated to the container along with any rules
// around how containers are linked together. It also unmounts the container's root filesystem.
func (daemon *Daemon) Cleanup(container *Container) {
func (daemon *Daemon) Cleanup(container *container.Container) {
daemon.releaseNetwork(container)
container.unmountIpcMounts(detachMounted)
container.UnmountIpcMounts(detachMounted)
daemon.conditionalUnmountOnCleanup(container)
for _, eConfig := range container.execCommands.Commands() {
for _, eConfig := range container.ExecCommands.Commands() {
daemon.unregisterExecCommand(container, eConfig)
}
if err := container.unmountVolumes(false); err != nil {
if err := container.UnmountVolumes(false); err != nil {
logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
}
}

View File

@ -11,6 +11,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/execdriver"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/pubsub"
@ -19,7 +20,7 @@ import (
type statsSupervisor interface {
// GetContainerStats collects all the stats related to a container
GetContainerStats(container *Container) (*execdriver.ResourceStats, error)
GetContainerStats(container *container.Container) (*execdriver.ResourceStats, error)
}
// newStatsCollector returns a new statsCollector that collections
@ -30,7 +31,7 @@ func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector
s := &statsCollector{
interval: interval,
supervisor: daemon,
publishers: make(map[*Container]*pubsub.Publisher),
publishers: make(map[*container.Container]*pubsub.Publisher),
clockTicksPerSecond: uint64(system.GetClockTicks()),
bufReader: bufio.NewReaderSize(nil, 128),
}
@ -44,14 +45,14 @@ type statsCollector struct {
supervisor statsSupervisor
interval time.Duration
clockTicksPerSecond uint64
publishers map[*Container]*pubsub.Publisher
publishers map[*container.Container]*pubsub.Publisher
bufReader *bufio.Reader
}
// collect registers the container with the collector and adds it to
// the event loop for collection on the specified interval returning
// a channel for the subscriber to receive on.
func (s *statsCollector) collect(c *Container) chan interface{} {
func (s *statsCollector) collect(c *container.Container) chan interface{} {
s.m.Lock()
defer s.m.Unlock()
publisher, exists := s.publishers[c]
@ -64,7 +65,7 @@ func (s *statsCollector) collect(c *Container) chan interface{} {
// stopCollection closes the channels for all subscribers and removes
// the container from metrics collection.
func (s *statsCollector) stopCollection(c *Container) {
func (s *statsCollector) stopCollection(c *container.Container) {
s.m.Lock()
if publisher, exists := s.publishers[c]; exists {
publisher.Close()
@ -74,7 +75,7 @@ func (s *statsCollector) stopCollection(c *Container) {
}
// unsubscribe removes a specific subscriber from receiving updates for a container's stats.
func (s *statsCollector) unsubscribe(c *Container, ch chan interface{}) {
func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) {
s.m.Lock()
publisher := s.publishers[c]
if publisher != nil {
@ -88,7 +89,7 @@ func (s *statsCollector) unsubscribe(c *Container, ch chan interface{}) {
func (s *statsCollector) run() {
type publishersPair struct {
container *Container
container *container.Container
publisher *pubsub.Publisher
}
// we cannot determine the capacity here.

View File

@ -1,6 +1,10 @@
package daemon
import "time"
import (
"time"
"github.com/docker/docker/container"
)
// newStatsCollector returns a new statsCollector for collection stats
// for a registered container at the specified interval. The collector allows
@ -17,15 +21,15 @@ type statsCollector struct {
// collect registers the container with the collector and adds it to
// the event loop for collection on the specified interval returning
// a channel for the subscriber to receive on.
func (s *statsCollector) collect(c *Container) chan interface{} {
func (s *statsCollector) collect(c *container.Container) chan interface{} {
return nil
}
// stopCollection closes the channels for all subscribers and removes
// the container from metrics collection.
func (s *statsCollector) stopCollection(c *Container) {
func (s *statsCollector) stopCollection(c *container.Container) {
}
// unsubscribe removes a specific subscriber from receiving updates for a container's stats.
func (s *statsCollector) unsubscribe(c *Container, ch chan interface{}) {
func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) {
}

View File

@ -4,6 +4,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
)
@ -32,13 +33,13 @@ func (daemon *Daemon) ContainerStop(name string, seconds int) error {
// process to exit. If a negative duration is given, Stop will wait
// for the initial signal forever. If the container is not running Stop returns
// immediately.
func (daemon *Daemon) containerStop(container *Container, seconds int) error {
func (daemon *Daemon) containerStop(container *container.Container, seconds int) error {
if !container.IsRunning() {
return nil
}
// 1. Send a SIGTERM
if err := daemon.killPossiblyDeadProcess(container, container.stopSignal()); err != nil {
if err := daemon.killPossiblyDeadProcess(container, container.StopSignal()); err != nil {
logrus.Infof("Failed to send SIGTERM to the process, force killing")
if err := daemon.killPossiblyDeadProcess(container, 9); err != nil {
return err

View File

@ -1,6 +1,7 @@
package daemon
import (
"github.com/docker/docker/container"
derr "github.com/docker/docker/errors"
)
@ -19,7 +20,7 @@ func (daemon *Daemon) ContainerUnpause(name string) error {
}
// containerUnpause resumes the container execution after the container is paused.
func (daemon *Daemon) containerUnpause(container *Container) error {
func (daemon *Daemon) containerUnpause(container *container.Container) error {
container.Lock()
defer container.Unlock()
@ -33,7 +34,7 @@ func (daemon *Daemon) containerUnpause(container *Container) error {
return derr.ErrorCodeNotPaused.WithArgs(container.ID)
}
if err := daemon.execDriver.Unpause(container.command); err != nil {
if err := daemon.execDriver.Unpause(container.Command); err != nil {
return err
}

View File

@ -7,6 +7,7 @@ import (
"strings"
"github.com/docker/docker/api/types"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/execdriver"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/runconfig"
@ -70,7 +71,7 @@ func (m mounts) parts(i int) int {
// 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
// 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
// 4. Cleanup old volumes that are about to be reasigned.
func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runconfig.HostConfig) error {
func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *runconfig.HostConfig) error {
binds := map[string]bool{}
mountPoints := map[string]*volume.MountPoint{}

View File

@ -3,66 +3,27 @@
package daemon
import (
"io/ioutil"
"os"
"sort"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/volume"
volumedrivers "github.com/docker/docker/volume/drivers"
"github.com/docker/docker/volume/local"
)
// copyExistingContents copies from the source to the destination and
// ensures the ownership is appropriately set.
func copyExistingContents(source, destination string) error {
volList, err := ioutil.ReadDir(source)
if err != nil {
return err
}
if len(volList) > 0 {
srcList, err := ioutil.ReadDir(destination)
if err != nil {
return err
}
if len(srcList) == 0 {
// If the source volume is empty copy files from the root into the volume
if err := chrootarchive.CopyWithTar(source, destination); err != nil {
return err
}
}
}
return copyOwnership(source, destination)
}
// copyOwnership copies the permissions and uid:gid of the source file
// to the destination file
func copyOwnership(source, destination string) error {
stat, err := system.Stat(source)
if err != nil {
return err
}
if err := os.Chown(destination, int(stat.UID()), int(stat.GID())); err != nil {
return err
}
return os.Chmod(destination, os.FileMode(stat.Mode()))
}
// setupMounts iterates through each of the mount points for a container and
// calls Setup() on each. It also looks to see if is a network mount such as
// /etc/resolv.conf, and if it is not, appends it to the array of mounts.
func (daemon *Daemon) setupMounts(container *Container) ([]execdriver.Mount, error) {
func (daemon *Daemon) setupMounts(container *container.Container) ([]execdriver.Mount, error) {
var mounts []execdriver.Mount
for _, m := range container.MountPoints {
path, err := m.Setup()
if err != nil {
return nil, err
}
if !container.trySetNetworkMount(m.Destination, path) {
if !container.TrySetNetworkMount(m.Destination, path) {
mounts = append(mounts, execdriver.Mount{
Source: path,
Destination: m.Destination,
@ -72,7 +33,7 @@ func (daemon *Daemon) setupMounts(container *Container) ([]execdriver.Mount, err
}
mounts = sortMounts(mounts)
netMounts := container.networkMounts()
netMounts := container.NetworkMounts()
// if we are going to mount any of the network files from container
// metadata, the ownership must be set properly for potential container
// remapped root (user namespaces)

View File

@ -5,6 +5,7 @@ package daemon
import (
"sort"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/execdriver"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/volume"
@ -14,7 +15,7 @@ import (
// of the configured mounts on the container to the execdriver mount structure
// which will ultimately be passed into the exec driver during container creation.
// It also ensures each of the mounts are lexographically sorted.
func (daemon *Daemon) setupMounts(container *Container) ([]execdriver.Mount, error) {
func (daemon *Daemon) setupMounts(container *container.Container) ([]execdriver.Mount, error) {
var mnts []execdriver.Mount
for _, mount := range container.MountPoints { // type is volume.MountPoint
// If there is no source, take it from the volume path

View File

@ -22,7 +22,7 @@ package opts
// time="2015-11-06T13:38:37.259627400-08:00" level=debug msg="After createRootfs"
// time="2015-11-06T13:38:37.263626300-08:00" level=debug msg="After setHostConfig"
// time="2015-11-06T13:38:37.267631200-08:00" level=debug msg="before createContainerPl...."
// time="2015-11-06T13:38:37.271629500-08:00" level=debug msg=toDiskLocking....
// time="2015-11-06T13:38:37.271629500-08:00" level=debug msg=ToDiskLocking....
// time="2015-11-06T13:38:37.275643200-08:00" level=debug msg="loggin event...."
// time="2015-11-06T13:38:37.277627600-08:00" level=debug msg="logged event...."
// time="2015-11-06T13:38:37.279631800-08:00" level=debug msg="In defer func"