2014-04-17 17:43:01 -04:00
|
|
|
package daemon
|
2013-01-18 19:13:39 -05:00
|
|
|
|
|
|
|
import (
|
2014-10-07 16:04:06 -04:00
|
|
|
"bytes"
|
2013-01-18 19:13:39 -05:00
|
|
|
"encoding/json"
|
2013-08-15 14:38:17 -04:00
|
|
|
"errors"
|
2013-03-21 03:25:00 -04:00
|
|
|
"fmt"
|
2014-04-28 17:36:04 -04:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path"
|
2014-05-11 16:49:46 -04:00
|
|
|
"path/filepath"
|
2014-04-28 17:36:04 -04:00
|
|
|
"strings"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
|
2015-03-05 12:55:14 -05:00
|
|
|
"github.com/docker/libcontainer/configs"
|
2014-07-24 16:37:44 -04:00
|
|
|
"github.com/docker/libcontainer/devices"
|
|
|
|
"github.com/docker/libcontainer/label"
|
|
|
|
|
2015-03-26 18:22:04 -04:00
|
|
|
"github.com/Sirupsen/logrus"
|
2014-07-24 18:19:50 -04:00
|
|
|
"github.com/docker/docker/daemon/execdriver"
|
2015-02-04 14:04:58 -05:00
|
|
|
"github.com/docker/docker/daemon/logger"
|
|
|
|
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
2015-03-09 14:40:57 -04:00
|
|
|
"github.com/docker/docker/daemon/logger/syslog"
|
2015-04-04 00:06:48 -04:00
|
|
|
"github.com/docker/docker/daemon/network"
|
|
|
|
"github.com/docker/docker/daemon/networkdriver/bridge"
|
2014-07-24 18:19:50 -04:00
|
|
|
"github.com/docker/docker/engine"
|
|
|
|
"github.com/docker/docker/image"
|
|
|
|
"github.com/docker/docker/links"
|
|
|
|
"github.com/docker/docker/nat"
|
2014-09-30 02:23:36 -04:00
|
|
|
"github.com/docker/docker/pkg/archive"
|
2014-07-30 11:16:10 -04:00
|
|
|
"github.com/docker/docker/pkg/broadcastwriter"
|
2015-02-27 13:50:55 -05:00
|
|
|
"github.com/docker/docker/pkg/directory"
|
2015-03-23 02:27:04 -04:00
|
|
|
"github.com/docker/docker/pkg/etchosts"
|
2014-08-12 12:10:43 -04:00
|
|
|
"github.com/docker/docker/pkg/ioutils"
|
2014-09-30 02:16:27 -04:00
|
|
|
"github.com/docker/docker/pkg/promise"
|
2015-03-23 02:27:04 -04:00
|
|
|
"github.com/docker/docker/pkg/resolvconf"
|
2015-03-24 07:25:26 -04:00
|
|
|
"github.com/docker/docker/pkg/stringid"
|
2014-07-24 18:19:50 -04:00
|
|
|
"github.com/docker/docker/pkg/symlink"
|
2015-02-11 14:21:38 -05:00
|
|
|
"github.com/docker/docker/pkg/ulimit"
|
2014-07-24 18:19:50 -04:00
|
|
|
"github.com/docker/docker/runconfig"
|
|
|
|
"github.com/docker/docker/utils"
|
2013-01-18 19:13:39 -05:00
|
|
|
)
|
|
|
|
|
2014-03-07 21:42:29 -05:00
|
|
|
const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
2014-02-22 22:01:45 -05:00
|
|
|
|
2013-11-29 10:40:44 -05:00
|
|
|
var (
|
2014-02-14 20:15:40 -05:00
|
|
|
ErrNotATTY = errors.New("The PTY is not a file")
|
|
|
|
ErrNoTTY = errors.New("No PTY found")
|
|
|
|
ErrContainerStart = errors.New("The container failed to start. Unknown error")
|
|
|
|
ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
|
2013-11-29 10:40:44 -05:00
|
|
|
)
|
|
|
|
|
2014-08-26 18:44:00 -04:00
|
|
|
type StreamConfig struct {
|
2014-08-26 18:05:37 -04:00
|
|
|
stdout *broadcastwriter.BroadcastWriter
|
|
|
|
stderr *broadcastwriter.BroadcastWriter
|
|
|
|
stdin io.ReadCloser
|
|
|
|
stdinPipe io.WriteCloser
|
|
|
|
}
|
|
|
|
|
2013-01-18 19:13:39 -05:00
|
|
|
type Container struct {
|
2014-09-05 17:35:55 -04:00
|
|
|
*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
|
2013-03-21 03:25:00 -04:00
|
|
|
|
2013-06-04 14:00:22 -04:00
|
|
|
ID string
|
2013-01-22 14:13:22 -05:00
|
|
|
|
|
|
|
Created time.Time
|
|
|
|
|
2013-01-18 19:13:39 -05:00
|
|
|
Path string
|
|
|
|
Args []string
|
|
|
|
|
2014-10-28 17:06:23 -04:00
|
|
|
Config *runconfig.Config
|
|
|
|
ImageID string `json:"Image"`
|
2013-01-18 19:13:39 -05:00
|
|
|
|
2015-04-04 00:06:48 -04:00
|
|
|
NetworkSettings *network.Settings
|
2013-01-18 19:13:39 -05:00
|
|
|
|
2013-04-10 21:23:34 -04:00
|
|
|
ResolvConfPath string
|
2013-09-09 14:57:25 -04:00
|
|
|
HostnamePath string
|
|
|
|
HostsPath string
|
2015-02-06 12:25:42 -05:00
|
|
|
LogPath string
|
2013-10-28 19:58:59 -04:00
|
|
|
Name string
|
2013-11-15 01:52:08 -05:00
|
|
|
Driver string
|
2014-03-06 17:14:25 -05:00
|
|
|
ExecDriver string
|
2013-04-10 21:23:34 -04:00
|
|
|
|
2014-08-26 18:44:00 -04:00
|
|
|
command *execdriver.Command
|
|
|
|
StreamConfig
|
2013-03-30 12:05:53 -04:00
|
|
|
|
2014-04-17 19:47:27 -04:00
|
|
|
daemon *Daemon
|
2014-04-21 17:09:26 -04:00
|
|
|
MountLabel, ProcessLabel string
|
2014-09-29 19:34:45 -04:00
|
|
|
AppArmorProfile string
|
2014-08-04 18:53:10 -04:00
|
|
|
RestartCount int
|
2014-12-10 00:55:09 -05:00
|
|
|
UpdateDns bool
|
2013-04-09 10:57:59 -04:00
|
|
|
|
2014-08-28 10:18:08 -04:00
|
|
|
// Maps container paths to volume paths. The key in this is the path to which
|
|
|
|
// the volume is being mounted inside the container. Value is the path of the
|
|
|
|
// volume on disk
|
2014-06-06 07:30:04 -04:00
|
|
|
Volumes map[string]string
|
2013-07-26 04:10:42 -04:00
|
|
|
// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
|
2013-06-25 02:30:48 -04:00
|
|
|
// Easier than migrating older container configs :)
|
2013-10-31 17:58:43 -04:00
|
|
|
VolumesRW map[string]bool
|
2014-02-11 23:04:39 -05:00
|
|
|
hostConfig *runconfig.HostConfig
|
2013-10-04 22:25:15 -04:00
|
|
|
|
2015-02-04 14:04:58 -05:00
|
|
|
activeLinks map[string]*links.Link
|
|
|
|
monitor *containerMonitor
|
|
|
|
execCommands *execStore
|
|
|
|
// logDriver for closing
|
|
|
|
logDriver logger.Logger
|
2015-03-18 14:44:14 -04:00
|
|
|
logCopier *logger.Copier
|
2014-12-18 09:57:36 -05:00
|
|
|
AppliedVolumesFrom map[string]struct{}
|
2013-01-18 19:13:39 -05:00
|
|
|
}
|
|
|
|
|
2013-03-21 03:25:00 -04:00
|
|
|
func (container *Container) FromDisk() error {
|
2014-05-27 22:15:42 -04:00
|
|
|
pth, err := container.jsonPath()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-11-04 08:43:58 -05:00
|
|
|
jsonSource, err := os.Open(pth)
|
2013-01-25 17:39:02 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-11-04 08:43:58 -05:00
|
|
|
defer jsonSource.Close()
|
|
|
|
|
|
|
|
dec := json.NewDecoder(jsonSource)
|
|
|
|
|
2013-03-21 03:25:00 -04:00
|
|
|
// Load container settings
|
2013-07-29 12:40:35 -04:00
|
|
|
// udp broke compat of docker.PortMapping, but it's not used when loading a container, we can skip it
|
2014-11-04 08:43:58 -05:00
|
|
|
if err := dec.Decode(container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") {
|
2013-01-25 17:39:02 -05:00
|
|
|
return err
|
|
|
|
}
|
2014-04-28 17:36:04 -04:00
|
|
|
|
|
|
|
if err := label.ReserveLabel(container.ProcessLabel); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-10-31 17:58:43 -04:00
|
|
|
return container.readHostConfig()
|
2013-01-25 17:39:02 -05:00
|
|
|
}
|
|
|
|
|
2014-07-11 13:49:24 -04:00
|
|
|
func (container *Container) toDisk() error {
|
2013-01-22 14:13:22 -05:00
|
|
|
data, err := json.Marshal(container)
|
2013-01-18 19:13:39 -05:00
|
|
|
if err != nil {
|
2014-05-27 22:15:42 -04:00
|
|
|
return err
|
2013-01-18 19:13:39 -05:00
|
|
|
}
|
2014-05-27 22:15:42 -04:00
|
|
|
|
|
|
|
pth, err := container.jsonPath()
|
2013-10-31 17:58:43 -04:00
|
|
|
if err != nil {
|
2014-05-27 22:15:42 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ioutil.WriteFile(pth, data, 0666)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2013-10-31 17:58:43 -04:00
|
|
|
}
|
2014-05-27 22:15:42 -04:00
|
|
|
|
2014-03-07 21:42:29 -05:00
|
|
|
return container.WriteHostConfig()
|
2013-01-18 19:13:39 -05:00
|
|
|
}
|
|
|
|
|
2014-07-11 13:49:24 -04:00
|
|
|
func (container *Container) ToDisk() error {
|
|
|
|
container.Lock()
|
|
|
|
err := container.toDisk()
|
|
|
|
container.Unlock()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-10-31 17:58:43 -04:00
|
|
|
func (container *Container) readHostConfig() error {
|
2014-02-11 23:04:39 -05:00
|
|
|
container.hostConfig = &runconfig.HostConfig{}
|
2013-10-31 17:58:43 -04:00
|
|
|
// If the hostconfig file does not exist, do not read it.
|
|
|
|
// (We still have to initialize container.hostConfig,
|
|
|
|
// but that's OK, since we just did that above.)
|
2014-05-27 22:15:42 -04:00
|
|
|
pth, err := container.hostConfigPath()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = os.Stat(pth)
|
2013-10-31 17:58:43 -04:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil
|
|
|
|
}
|
2014-05-27 22:15:42 -04:00
|
|
|
|
|
|
|
data, err := ioutil.ReadFile(pth)
|
2013-07-02 08:19:25 -04:00
|
|
|
if err != nil {
|
2013-10-31 17:58:43 -04:00
|
|
|
return err
|
2013-07-02 08:19:25 -04:00
|
|
|
}
|
2013-10-31 17:58:43 -04:00
|
|
|
return json.Unmarshal(data, container.hostConfig)
|
2013-07-02 08:19:25 -04:00
|
|
|
}
|
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
func (container *Container) WriteHostConfig() error {
|
2013-10-31 17:58:43 -04:00
|
|
|
data, err := json.Marshal(container.hostConfig)
|
2013-07-02 08:19:25 -04:00
|
|
|
if err != nil {
|
2014-05-27 22:15:42 -04:00
|
|
|
return err
|
2013-07-02 08:19:25 -04:00
|
|
|
}
|
2014-05-27 22:15:42 -04:00
|
|
|
|
|
|
|
pth, err := container.hostConfigPath()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ioutil.WriteFile(pth, data, 0666)
|
2013-07-02 08:19:25 -04:00
|
|
|
}
|
|
|
|
|
2014-07-31 07:50:59 -04:00
|
|
|
func (container *Container) LogEvent(action string) {
|
|
|
|
d := container.daemon
|
2015-04-03 18:17:49 -04:00
|
|
|
d.EventsService.Log(
|
|
|
|
action,
|
|
|
|
container.ID,
|
2015-03-28 22:17:17 -04:00
|
|
|
container.Config.Image,
|
2015-04-03 18:17:49 -04:00
|
|
|
)
|
2014-07-31 07:50:59 -04:00
|
|
|
}
|
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
func (container *Container) getResourcePath(path string) (string, error) {
|
2014-05-11 16:49:46 -04:00
|
|
|
cleanPath := filepath.Join("/", path)
|
2014-05-28 19:12:29 -04:00
|
|
|
return symlink.FollowSymlinkInScope(filepath.Join(container.basefs, cleanPath), container.basefs)
|
2014-05-11 16:49:46 -04:00
|
|
|
}
|
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
func (container *Container) getRootResourcePath(path string) (string, error) {
|
2014-05-11 16:49:46 -04:00
|
|
|
cleanPath := filepath.Join("/", path)
|
2014-05-28 19:12:29 -04:00
|
|
|
return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root)
|
2014-05-11 16:49:46 -04:00
|
|
|
}
|
|
|
|
|
2015-02-12 13:51:51 -05:00
|
|
|
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, fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err)
|
|
|
|
}
|
|
|
|
|
2014-04-30 18:46:56 -04:00
|
|
|
func populateCommand(c *Container, env []string) error {
|
2014-09-29 18:40:26 -04:00
|
|
|
en := &execdriver.Network{
|
2014-04-17 17:43:01 -04:00
|
|
|
Mtu: c.daemon.config.Mtu,
|
2014-03-16 15:52:27 -04:00
|
|
|
Interface: nil,
|
|
|
|
}
|
|
|
|
|
2014-05-02 19:59:28 -04:00
|
|
|
parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2)
|
2014-05-02 17:06:05 -04:00
|
|
|
switch parts[0] {
|
|
|
|
case "none":
|
2014-05-02 17:17:31 -04:00
|
|
|
case "host":
|
|
|
|
en.HostNetworking = true
|
2014-05-02 17:45:39 -04:00
|
|
|
case "bridge", "": // empty string to support existing containers
|
2014-05-02 17:06:05 -04:00
|
|
|
if !c.Config.NetworkDisabled {
|
|
|
|
network := c.NetworkSettings
|
|
|
|
en.Interface = &execdriver.NetworkInterface{
|
2015-01-08 18:03:19 -05:00
|
|
|
Gateway: network.Gateway,
|
|
|
|
Bridge: network.Bridge,
|
|
|
|
IPAddress: network.IPAddress,
|
|
|
|
IPPrefixLen: network.IPPrefixLen,
|
|
|
|
MacAddress: network.MacAddress,
|
|
|
|
LinkLocalIPv6Address: network.LinkLocalIPv6Address,
|
|
|
|
GlobalIPv6Address: network.GlobalIPv6Address,
|
|
|
|
GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen,
|
|
|
|
IPv6Gateway: network.IPv6Gateway,
|
2014-05-02 17:06:05 -04:00
|
|
|
}
|
2014-01-20 21:07:18 -05:00
|
|
|
}
|
2014-05-02 17:06:05 -04:00
|
|
|
case "container":
|
2014-05-02 19:59:28 -04:00
|
|
|
nc, err := c.getNetworkedContainer()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2014-04-30 18:46:56 -04:00
|
|
|
}
|
|
|
|
en.ContainerID = nc.ID
|
2014-05-02 17:06:05 -04:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode)
|
2014-04-30 18:46:56 -04:00
|
|
|
}
|
|
|
|
|
2014-11-10 16:14:17 -05:00
|
|
|
ipc := &execdriver.Ipc{}
|
|
|
|
|
|
|
|
if c.hostConfig.IpcMode.IsContainer() {
|
|
|
|
ic, err := c.getIpcContainer()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ipc.ContainerID = ic.ID
|
|
|
|
} else {
|
|
|
|
ipc.HostIpc = c.hostConfig.IpcMode.IsHost()
|
|
|
|
}
|
|
|
|
|
2014-11-25 15:10:53 -05:00
|
|
|
pid := &execdriver.Pid{}
|
|
|
|
pid.HostPid = c.hostConfig.PidMode.IsHost()
|
|
|
|
|
2014-05-31 00:00:47 -04:00
|
|
|
// Build lists of devices allowed and created within the container.
|
2015-02-12 13:51:51 -05:00
|
|
|
var userSpecifiedDevices []*configs.Device
|
|
|
|
for _, deviceMapping := range c.hostConfig.Devices {
|
|
|
|
devs, err := getDevicesFromPath(deviceMapping)
|
2014-05-31 00:00:47 -04:00
|
|
|
if err != nil {
|
2015-02-12 13:51:51 -05:00
|
|
|
return err
|
2014-05-31 00:00:47 -04:00
|
|
|
}
|
2015-02-12 13:51:51 -05:00
|
|
|
|
|
|
|
userSpecifiedDevices = append(userSpecifiedDevices, devs...)
|
2014-05-31 00:00:47 -04:00
|
|
|
}
|
2015-03-05 12:55:14 -05:00
|
|
|
allowedDevices := append(configs.DefaultAllowedDevices, userSpecifiedDevices...)
|
2014-05-31 00:00:47 -04:00
|
|
|
|
2015-03-05 12:55:14 -05:00
|
|
|
autoCreatedDevices := append(configs.DefaultAutoCreatedDevices, userSpecifiedDevices...)
|
2014-05-31 00:00:47 -04:00
|
|
|
|
2014-05-02 17:06:05 -04:00
|
|
|
// TODO: this can be removed after lxc-conf is fully deprecated
|
2014-12-08 19:45:42 -05:00
|
|
|
lxcConfig, err := mergeLxcConfIntoOptions(c.hostConfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-05-02 17:06:05 -04:00
|
|
|
|
2015-02-11 14:21:38 -05:00
|
|
|
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 c.daemon.config.Ulimits {
|
|
|
|
if _, exists := ulIdx[name]; !exists {
|
|
|
|
ulimits = append(ulimits, ul)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, limit := range ulimits {
|
|
|
|
rl, err := limit.GetRlimit()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
rlimits = append(rlimits, rl)
|
|
|
|
}
|
|
|
|
|
2014-01-20 21:07:18 -05:00
|
|
|
resources := &execdriver.Resources{
|
move resources from Config to HostConfig
Cgroup resources are host dependent, they should be in hostConfig.
For backward compatibility, we just copy it to hostConfig, and leave it in
Config for now, so there is no regressions, but the right way to use this
throught json is to put it in HostConfig, like:
{
"Hostname": "",
...
"HostConfig": {
"CpuShares": 512,
"Memory": 314572800,
...
}
}
As we will add CpusetMems, CpusetCpus is definitely a better name, but some
users are already using Cpuset in their http APIs, we also make it compatible.
The main idea is keep using Cpuset in Config Struct, and make it has the same
value as CpusetCpus, but not always, some scenarios:
- Users use --cpuset in docker command, it can setup cpuset.cpus and can
get Cpuset field from docker inspect or other http API which will get
config info.
- Users use --cpuset-cpus in docker command, ditto.
- Users use Cpuset field in their http APIs, ditto.
- Users use CpusetCpus field in their http APIs, they won't get Cpuset field
in Config info, because by then, they should already know what happens
to Cpuset.
Signed-off-by: Qiang Huang <h.huangqiang@huawei.com>
2015-03-10 21:31:18 -04:00
|
|
|
Memory: c.hostConfig.Memory,
|
|
|
|
MemorySwap: c.hostConfig.MemorySwap,
|
|
|
|
CpuShares: c.hostConfig.CpuShares,
|
|
|
|
CpusetCpus: c.hostConfig.CpusetCpus,
|
2015-02-11 14:21:38 -05:00
|
|
|
Rlimits: rlimits,
|
2014-01-20 21:07:18 -05:00
|
|
|
}
|
2014-08-26 18:05:37 -04:00
|
|
|
|
|
|
|
processConfig := execdriver.ProcessConfig{
|
|
|
|
Privileged: c.hostConfig.Privileged,
|
|
|
|
Entrypoint: c.Path,
|
|
|
|
Arguments: c.Args,
|
|
|
|
Tty: c.Config.Tty,
|
|
|
|
User: c.Config.User,
|
|
|
|
}
|
2014-09-29 18:40:26 -04:00
|
|
|
|
2014-08-26 18:05:37 -04:00
|
|
|
processConfig.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
|
|
|
processConfig.Env = env
|
2014-09-29 18:40:26 -04:00
|
|
|
|
2014-01-20 21:07:18 -05:00
|
|
|
c.command = &execdriver.Command{
|
2014-02-17 18:14:30 -05:00
|
|
|
ID: c.ID,
|
|
|
|
Rootfs: c.RootfsPath(),
|
2015-01-13 16:52:51 -05:00
|
|
|
ReadonlyRootfs: c.hostConfig.ReadonlyRootfs,
|
2014-02-17 18:14:30 -05:00
|
|
|
InitPath: "/.dockerinit",
|
|
|
|
WorkingDir: c.Config.WorkingDir,
|
|
|
|
Network: en,
|
2014-11-10 16:14:17 -05:00
|
|
|
Ipc: ipc,
|
2014-11-25 15:10:53 -05:00
|
|
|
Pid: pid,
|
2014-02-17 18:14:30 -05:00
|
|
|
Resources: resources,
|
2014-05-31 00:00:47 -04:00
|
|
|
AllowedDevices: allowedDevices,
|
|
|
|
AutoCreatedDevices: autoCreatedDevices,
|
2014-07-10 14:41:11 -04:00
|
|
|
CapAdd: c.hostConfig.CapAdd,
|
|
|
|
CapDrop: c.hostConfig.CapDrop,
|
2014-08-26 18:05:37 -04:00
|
|
|
ProcessConfig: processConfig,
|
2014-09-29 18:40:26 -04:00
|
|
|
ProcessLabel: c.GetProcessLabel(),
|
|
|
|
MountLabel: c.GetMountLabel(),
|
|
|
|
LxcConfig: lxcConfig,
|
2014-09-29 19:34:45 -04:00
|
|
|
AppArmorProfile: c.AppArmorProfile,
|
2015-03-16 18:42:15 -04:00
|
|
|
CgroupParent: c.hostConfig.CgroupParent,
|
2014-01-20 21:07:18 -05:00
|
|
|
}
|
2014-08-26 18:05:37 -04:00
|
|
|
|
2014-04-30 18:46:56 -04:00
|
|
|
return nil
|
2014-01-20 21:07:18 -05:00
|
|
|
}
|
|
|
|
|
2013-10-31 17:58:43 -04:00
|
|
|
func (container *Container) Start() (err error) {
|
2013-11-21 15:21:03 -05:00
|
|
|
container.Lock()
|
|
|
|
defer container.Unlock()
|
|
|
|
|
2014-08-31 11:20:35 -04:00
|
|
|
if container.Running {
|
2014-03-30 01:12:34 -04:00
|
|
|
return nil
|
2013-11-11 21:15:38 -05:00
|
|
|
}
|
2014-08-04 18:53:10 -04:00
|
|
|
|
2015-03-12 15:26:17 -04:00
|
|
|
if container.removalInProgress || container.Dead {
|
|
|
|
return fmt.Errorf("Container is marked for removal and cannot be started.")
|
|
|
|
}
|
|
|
|
|
2015-01-27 20:23:57 -05:00
|
|
|
// if we encounter an error during start we need to ensure that any other
|
2014-04-14 02:40:49 -04:00
|
|
|
// setup has been cleaned up properly
|
2013-10-16 16:12:56 -04:00
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
2014-09-30 04:30:58 -04:00
|
|
|
container.setError(err)
|
2014-11-05 21:23:42 -05:00
|
|
|
// if no one else has set it, make sure we don't leave it at zero
|
|
|
|
if container.ExitCode == 0 {
|
|
|
|
container.ExitCode = 128
|
|
|
|
}
|
2014-09-30 04:30:58 -04:00
|
|
|
container.toDisk()
|
2013-10-16 16:12:56 -04:00
|
|
|
container.cleanup()
|
2013-10-16 15:01:55 -04:00
|
|
|
}
|
2013-10-16 16:12:56 -04:00
|
|
|
}()
|
2014-01-29 21:34:43 -05:00
|
|
|
|
2014-04-14 02:40:49 -04:00
|
|
|
if err := container.setupContainerDns(); err != nil {
|
|
|
|
return err
|
2014-04-07 22:12:22 -04:00
|
|
|
}
|
2013-12-06 06:15:14 -05:00
|
|
|
if err := container.Mount(); err != nil {
|
2013-10-16 16:12:56 -04:00
|
|
|
return err
|
|
|
|
}
|
2014-04-14 02:40:49 -04:00
|
|
|
if err := container.initializeNetworking(); err != nil {
|
|
|
|
return err
|
2013-10-16 16:12:56 -04:00
|
|
|
}
|
2014-07-14 19:19:37 -04:00
|
|
|
if err := container.updateParentsHosts(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-04-17 17:43:01 -04:00
|
|
|
container.verifyDaemonSettings()
|
2014-08-28 10:18:08 -04:00
|
|
|
if err := container.prepareVolumes(); err != nil {
|
2013-12-03 17:47:49 -05:00
|
|
|
return err
|
2013-10-16 16:12:56 -04:00
|
|
|
}
|
2014-04-14 02:40:49 -04:00
|
|
|
linkedEnv, err := container.setupLinkedContainers()
|
2013-10-04 22:25:15 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-04-14 02:40:49 -04:00
|
|
|
if err := container.setupWorkingDirectory(); err != nil {
|
2014-01-30 10:43:53 -05:00
|
|
|
return err
|
|
|
|
}
|
2014-05-05 17:45:14 -04:00
|
|
|
env := container.createDaemonEnvironment(linkedEnv)
|
2014-04-30 18:46:56 -04:00
|
|
|
if err := populateCommand(container, env); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-08-28 10:18:08 -04:00
|
|
|
if err := container.setupMounts(); err != nil {
|
2013-10-16 16:12:56 -04:00
|
|
|
return err
|
|
|
|
}
|
2014-01-13 18:37:17 -05:00
|
|
|
|
2014-04-14 02:40:49 -04:00
|
|
|
return container.waitForStart()
|
2013-01-18 19:13:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) Run() error {
|
2013-10-31 17:58:43 -04:00
|
|
|
if err := container.Start(); err != nil {
|
2013-01-18 19:13:39 -05:00
|
|
|
return err
|
|
|
|
}
|
2014-08-31 11:20:35 -04:00
|
|
|
container.WaitStop(-1 * time.Second)
|
2013-01-18 19:13:39 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) Output() (output []byte, err error) {
|
2014-12-04 16:12:29 -05:00
|
|
|
pipe := container.StdoutPipe()
|
2013-01-18 19:13:39 -05:00
|
|
|
defer pipe.Close()
|
2013-10-31 17:58:43 -04:00
|
|
|
if err := container.Start(); err != nil {
|
2013-01-18 19:13:39 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
output, err = ioutil.ReadAll(pipe)
|
2014-08-31 11:20:35 -04:00
|
|
|
container.WaitStop(-1 * time.Second)
|
2013-01-18 19:13:39 -05:00
|
|
|
return output, err
|
|
|
|
}
|
|
|
|
|
2014-08-26 18:44:00 -04:00
|
|
|
// StreamConfig.StdinPipe returns a WriteCloser which can be used to feed data
|
2013-10-16 13:41:36 -04:00
|
|
|
// to the standard input of the container's active process.
|
|
|
|
// Container.StdoutPipe and Container.StderrPipe each return a ReadCloser
|
|
|
|
// which can be used to retrieve the standard output (and error) generated
|
|
|
|
// by the container's active process. The output (and error) are actually
|
|
|
|
// copied and delivered to all StdoutPipe and StderrPipe consumers, using
|
|
|
|
// a kind of "broadcaster".
|
|
|
|
|
2014-12-04 16:12:29 -05:00
|
|
|
func (streamConfig *StreamConfig) StdinPipe() io.WriteCloser {
|
|
|
|
return streamConfig.stdinPipe
|
2013-01-28 20:50:12 -05:00
|
|
|
}
|
|
|
|
|
2014-12-04 16:12:29 -05:00
|
|
|
func (streamConfig *StreamConfig) StdoutPipe() io.ReadCloser {
|
2013-01-18 19:13:39 -05:00
|
|
|
reader, writer := io.Pipe()
|
2014-08-26 18:44:00 -04:00
|
|
|
streamConfig.stdout.AddWriter(writer, "")
|
2014-12-04 16:12:29 -05:00
|
|
|
return ioutils.NewBufReader(reader)
|
2013-01-18 19:13:39 -05:00
|
|
|
}
|
|
|
|
|
2014-12-04 16:12:29 -05:00
|
|
|
func (streamConfig *StreamConfig) StderrPipe() io.ReadCloser {
|
2013-01-18 19:13:39 -05:00
|
|
|
reader, writer := io.Pipe()
|
2014-08-26 18:44:00 -04:00
|
|
|
streamConfig.stderr.AddWriter(writer, "")
|
2014-12-04 16:12:29 -05:00
|
|
|
return ioutils.NewBufReader(reader)
|
2013-01-18 19:13:39 -05:00
|
|
|
}
|
|
|
|
|
2014-08-26 18:44:00 -04:00
|
|
|
func (streamConfig *StreamConfig) StdoutLogPipe() io.ReadCloser {
|
2014-04-02 15:26:06 -04:00
|
|
|
reader, writer := io.Pipe()
|
2014-08-26 18:44:00 -04:00
|
|
|
streamConfig.stdout.AddWriter(writer, "stdout")
|
2014-08-12 12:10:43 -04:00
|
|
|
return ioutils.NewBufReader(reader)
|
2014-04-02 15:26:06 -04:00
|
|
|
}
|
|
|
|
|
2014-08-26 18:44:00 -04:00
|
|
|
func (streamConfig *StreamConfig) StderrLogPipe() io.ReadCloser {
|
2014-04-02 15:26:06 -04:00
|
|
|
reader, writer := io.Pipe()
|
2014-08-26 18:44:00 -04:00
|
|
|
streamConfig.stderr.AddWriter(writer, "stderr")
|
2014-08-12 12:10:43 -04:00
|
|
|
return ioutils.NewBufReader(reader)
|
2014-04-02 15:26:06 -04:00
|
|
|
}
|
|
|
|
|
2014-05-05 19:23:14 -04:00
|
|
|
func (container *Container) buildHostnameFile() error {
|
2014-05-27 22:15:42 -04:00
|
|
|
hostnamePath, err := container.getRootResourcePath("hostname")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
container.HostnamePath = hostnamePath
|
|
|
|
|
2014-05-02 17:45:39 -04:00
|
|
|
if container.Config.Domainname != "" {
|
2014-05-05 19:23:14 -04:00
|
|
|
return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644)
|
2014-05-02 17:45:39 -04:00
|
|
|
}
|
2014-05-05 19:23:14 -04:00
|
|
|
return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
|
2014-05-02 17:45:39 -04:00
|
|
|
}
|
|
|
|
|
2014-07-14 19:19:37 -04:00
|
|
|
func (container *Container) buildHostsFiles(IP string) error {
|
2013-10-08 17:29:22 -04:00
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
hostsPath, err := container.getRootResourcePath("hosts")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
container.HostsPath = hostsPath
|
2014-04-07 14:34:07 -04:00
|
|
|
|
2014-11-05 15:24:15 -05:00
|
|
|
var extraContent []etchosts.Record
|
2014-04-07 14:34:07 -04:00
|
|
|
|
|
|
|
children, err := container.daemon.Children(container.Name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for linkAlias, child := range children {
|
|
|
|
_, alias := path.Split(linkAlias)
|
2015-02-16 00:00:51 -05:00
|
|
|
// 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:]
|
|
|
|
}
|
|
|
|
extraContent = append(extraContent, etchosts.Record{Hosts: aliasList, IP: child.NetworkSettings.IPAddress})
|
2014-04-07 14:34:07 -04:00
|
|
|
}
|
|
|
|
|
2014-09-13 00:35:59 -04:00
|
|
|
for _, extraHost := range container.hostConfig.ExtraHosts {
|
2015-02-04 10:20:28 -05:00
|
|
|
// allow IPv6 addresses in extra hosts; only split on first ":"
|
|
|
|
parts := strings.SplitN(extraHost, ":", 2)
|
2014-11-05 15:24:15 -05:00
|
|
|
extraContent = append(extraContent, etchosts.Record{Hosts: parts[0], IP: parts[1]})
|
2014-09-13 00:35:59 -04:00
|
|
|
}
|
|
|
|
|
2014-11-05 15:24:15 -05:00
|
|
|
return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, extraContent)
|
2013-10-08 17:29:22 -04:00
|
|
|
}
|
|
|
|
|
2014-07-14 19:19:37 -04:00
|
|
|
func (container *Container) buildHostnameAndHostsFiles(IP string) error {
|
|
|
|
if err := container.buildHostnameFile(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return container.buildHostsFiles(IP)
|
|
|
|
}
|
|
|
|
|
2014-10-06 20:59:12 -04:00
|
|
|
func (container *Container) AllocateNetwork() error {
|
2014-05-02 19:59:28 -04:00
|
|
|
mode := container.hostConfig.NetworkMode
|
2014-09-05 02:43:44 -04:00
|
|
|
if container.Config.NetworkDisabled || !mode.IsPrivate() {
|
2013-07-21 20:11:47 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2013-11-21 15:21:03 -05:00
|
|
|
var (
|
2014-10-06 20:59:12 -04:00
|
|
|
err error
|
2014-04-17 17:43:01 -04:00
|
|
|
eng = container.daemon.eng
|
2013-11-21 15:21:03 -05:00
|
|
|
)
|
2014-01-30 15:02:56 -05:00
|
|
|
|
2015-04-04 00:06:48 -04:00
|
|
|
networkSettings, err := bridge.Allocate(container.ID, container.Config.MacAddress, "", "")
|
|
|
|
if err != nil {
|
2014-04-14 07:23:43 -04:00
|
|
|
return err
|
2013-02-25 17:06:22 -05:00
|
|
|
}
|
2013-08-21 10:37:58 -04:00
|
|
|
|
2014-09-29 19:56:10 -04:00
|
|
|
// Error handling: At this point, the interface is allocated so we have to
|
|
|
|
// make sure that it is always released in case of error, otherwise we
|
|
|
|
// might leak resources.
|
|
|
|
|
2013-10-04 22:25:15 -04:00
|
|
|
if container.Config.PortSpecs != nil {
|
2014-10-06 20:59:12 -04:00
|
|
|
if err = migratePortMappings(container.Config, container.hostConfig); err != nil {
|
2015-04-04 00:06:48 -04:00
|
|
|
bridge.Release(container.ID)
|
2013-10-04 22:25:15 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
container.Config.PortSpecs = nil
|
2014-10-06 20:59:12 -04:00
|
|
|
if err = container.WriteHostConfig(); err != nil {
|
2015-04-04 00:06:48 -04:00
|
|
|
bridge.Release(container.ID)
|
2013-10-30 11:15:12 -04:00
|
|
|
return err
|
|
|
|
}
|
2013-10-04 22:25:15 -04:00
|
|
|
}
|
|
|
|
|
2013-11-21 15:21:03 -05:00
|
|
|
var (
|
2014-02-11 19:48:44 -05:00
|
|
|
portSpecs = make(nat.PortSet)
|
|
|
|
bindings = make(nat.PortMap)
|
2013-11-21 15:21:03 -05:00
|
|
|
)
|
2013-10-04 22:25:15 -04:00
|
|
|
|
2014-04-14 07:23:43 -04:00
|
|
|
if container.Config.ExposedPorts != nil {
|
|
|
|
portSpecs = container.Config.ExposedPorts
|
|
|
|
}
|
2014-07-22 03:16:26 -04:00
|
|
|
|
2014-04-14 07:23:43 -04:00
|
|
|
if container.hostConfig.PortBindings != nil {
|
2014-07-22 03:16:26 -04:00
|
|
|
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,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2013-02-25 17:06:22 -05:00
|
|
|
}
|
2013-08-27 15:14:21 -04:00
|
|
|
|
2013-10-04 22:25:15 -04:00
|
|
|
container.NetworkSettings.PortMapping = nil
|
|
|
|
|
|
|
|
for port := range portSpecs {
|
2014-10-06 20:59:12 -04:00
|
|
|
if err = container.allocatePort(eng, port, bindings); err != nil {
|
2015-04-04 00:06:48 -04:00
|
|
|
bridge.Release(container.ID)
|
2014-04-14 07:23:43 -04:00
|
|
|
return err
|
2013-02-28 14:51:14 -05:00
|
|
|
}
|
|
|
|
}
|
2014-03-07 21:42:29 -05:00
|
|
|
container.WriteHostConfig()
|
2013-10-04 22:25:15 -04:00
|
|
|
|
2015-04-04 00:06:48 -04:00
|
|
|
networkSettings.Ports = bindings
|
|
|
|
container.NetworkSettings = networkSettings
|
2013-10-04 22:25:15 -04:00
|
|
|
|
2013-02-25 17:06:22 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-09-29 19:52:27 -04:00
|
|
|
func (container *Container) ReleaseNetwork() {
|
2014-12-10 14:59:18 -05:00
|
|
|
if container.Config.NetworkDisabled || !container.hostConfig.NetworkMode.IsPrivate() {
|
2013-07-21 20:11:47 -04:00
|
|
|
return
|
|
|
|
}
|
2014-01-29 21:34:43 -05:00
|
|
|
|
2015-04-04 00:06:48 -04:00
|
|
|
bridge.Release(container.ID)
|
|
|
|
|
|
|
|
container.NetworkSettings = &network.Settings{}
|
2013-01-26 18:56:42 -05:00
|
|
|
}
|
|
|
|
|
2014-09-29 19:56:10 -04:00
|
|
|
func (container *Container) isNetworkAllocated() bool {
|
|
|
|
return container.NetworkSettings.IPAddress != ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) RestoreNetwork() error {
|
|
|
|
mode := container.hostConfig.NetworkMode
|
|
|
|
// Don't attempt a restore if we previously didn't allocate networking.
|
|
|
|
// This might be a legacy container with no network allocated, in which case the
|
|
|
|
// allocation will happen once and for all at start.
|
|
|
|
if !container.isNetworkAllocated() || container.Config.NetworkDisabled || !mode.IsPrivate() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
eng := container.daemon.eng
|
|
|
|
|
2014-10-03 16:29:09 -04:00
|
|
|
// Re-allocate the interface with the same IP and MAC address.
|
2015-04-04 00:06:48 -04:00
|
|
|
if _, err := bridge.Allocate(container.ID, container.NetworkSettings.MacAddress, container.NetworkSettings.IPAddress, ""); err != nil {
|
2014-09-29 19:56:10 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Re-allocate any previously allocated ports.
|
2014-09-29 20:06:26 -04:00
|
|
|
for port := range container.NetworkSettings.Ports {
|
2014-09-29 19:56:10 -04:00
|
|
|
if err := container.allocatePort(eng, port, container.NetworkSettings.Ports); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-08-04 18:53:10 -04:00
|
|
|
// 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.
|
2013-10-16 15:01:55 -04:00
|
|
|
func (container *Container) cleanup() {
|
2014-10-09 18:15:17 -04:00
|
|
|
container.ReleaseNetwork()
|
|
|
|
|
2013-10-04 22:25:15 -04:00
|
|
|
// Disable all active links
|
|
|
|
if container.activeLinks != nil {
|
|
|
|
for _, link := range container.activeLinks {
|
|
|
|
link.Disable()
|
|
|
|
}
|
|
|
|
}
|
2013-03-30 12:05:53 -04:00
|
|
|
|
2013-03-21 03:25:00 -04:00
|
|
|
if err := container.Unmount(); err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
|
2013-01-21 21:03:23 -05:00
|
|
|
}
|
2014-11-17 18:50:09 -05:00
|
|
|
|
|
|
|
for _, eConfig := range container.execCommands.s {
|
|
|
|
container.daemon.unregisterExecCommand(eConfig)
|
|
|
|
}
|
2013-01-18 19:13:39 -05:00
|
|
|
}
|
|
|
|
|
2014-03-07 21:42:29 -05:00
|
|
|
func (container *Container) KillSig(sig int) error {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Sending %d to %s", sig, container.ID)
|
2013-11-21 15:21:03 -05:00
|
|
|
container.Lock()
|
|
|
|
defer container.Unlock()
|
2013-10-08 15:15:29 -04:00
|
|
|
|
2014-06-03 11:44:21 -04:00
|
|
|
// We could unpause the container for them rather than returning this error
|
2014-08-31 11:20:35 -04:00
|
|
|
if container.Paused {
|
2014-06-03 11:44:21 -04:00
|
|
|
return fmt.Errorf("Container %s is paused. Unpause the container before stopping", container.ID)
|
|
|
|
}
|
|
|
|
|
2014-08-31 11:20:35 -04:00
|
|
|
if !container.Running {
|
2013-03-23 22:51:35 -04:00
|
|
|
return nil
|
|
|
|
}
|
2014-08-04 20:05:56 -04:00
|
|
|
|
|
|
|
// signal to the monitor that it should not restart the container
|
|
|
|
// after we send the kill signal
|
|
|
|
container.monitor.ExitOnNext()
|
2014-08-04 18:53:10 -04:00
|
|
|
|
2014-08-11 14:35:18 -04:00
|
|
|
// if the container is currently restarting we do not need to send the signal
|
|
|
|
// to the process. Telling the monitor that it should exit on it's next event
|
|
|
|
// loop is enough
|
2014-08-31 11:20:35 -04:00
|
|
|
if container.Restarting {
|
2014-08-11 14:35:18 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-04-17 17:43:01 -04:00
|
|
|
return container.daemon.Kill(container, sig)
|
2013-10-08 15:15:29 -04:00
|
|
|
}
|
|
|
|
|
2015-01-19 07:44:04 -05:00
|
|
|
// Wrapper aroung KillSig() suppressing "no such process" error.
|
|
|
|
func (container *Container) killPossiblyDeadProcess(sig int) error {
|
|
|
|
err := container.KillSig(sig)
|
|
|
|
if err == syscall.ESRCH {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Cannot kill process (pid=%d) with signal %d: no such process.", container.GetPid(), sig)
|
2015-01-19 07:44:04 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-05-21 17:06:18 -04:00
|
|
|
func (container *Container) Pause() error {
|
2014-08-31 11:20:35 -04:00
|
|
|
if container.IsPaused() {
|
2014-05-21 17:06:18 -04:00
|
|
|
return fmt.Errorf("Container %s is already paused", container.ID)
|
|
|
|
}
|
2014-08-31 11:20:35 -04:00
|
|
|
if !container.IsRunning() {
|
2014-05-21 17:06:18 -04:00
|
|
|
return fmt.Errorf("Container %s is not running", container.ID)
|
|
|
|
}
|
|
|
|
return container.daemon.Pause(container)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) Unpause() error {
|
2014-08-31 11:20:35 -04:00
|
|
|
if !container.IsPaused() {
|
2014-05-21 17:06:18 -04:00
|
|
|
return fmt.Errorf("Container %s is not paused", container.ID)
|
|
|
|
}
|
2014-08-31 11:20:35 -04:00
|
|
|
if !container.IsRunning() {
|
2014-05-21 17:06:18 -04:00
|
|
|
return fmt.Errorf("Container %s is not running", container.ID)
|
|
|
|
}
|
|
|
|
return container.daemon.Unpause(container)
|
|
|
|
}
|
|
|
|
|
2013-10-08 15:15:29 -04:00
|
|
|
func (container *Container) Kill() error {
|
2014-08-31 11:20:35 -04:00
|
|
|
if !container.IsRunning() {
|
2013-10-08 15:15:29 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1. Send SIGKILL
|
2015-01-19 07:44:04 -05:00
|
|
|
if err := container.killPossiblyDeadProcess(9); err != nil {
|
2013-10-08 15:15:29 -04:00
|
|
|
return err
|
2013-01-18 19:13:39 -05:00
|
|
|
}
|
2013-04-11 12:02:34 -04:00
|
|
|
|
|
|
|
// 2. Wait for the process to die, in last resort, try to kill the process directly
|
2014-08-31 11:20:35 -04:00
|
|
|
if _, err := container.WaitStop(10 * time.Second); err != nil {
|
2014-04-22 16:56:18 -04:00
|
|
|
// Ensure that we don't kill ourselves
|
2014-08-31 11:20:35 -04:00
|
|
|
if pid := container.GetPid(); pid != 0 {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(container.ID))
|
2014-04-22 16:56:18 -04:00
|
|
|
if err := syscall.Kill(pid, 9); err != nil {
|
2015-01-19 07:44:04 -05:00
|
|
|
if err != syscall.ESRCH {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Cannot kill process (pid=%d) with signal 9: no such process.", pid)
|
2014-04-22 16:56:18 -04:00
|
|
|
}
|
2013-04-11 12:02:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-31 11:20:35 -04:00
|
|
|
container.WaitStop(-1 * time.Second)
|
2013-01-21 21:03:23 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2013-04-16 12:43:44 -04:00
|
|
|
func (container *Container) Stop(seconds int) error {
|
2014-08-31 11:20:35 -04:00
|
|
|
if !container.IsRunning() {
|
2013-01-18 19:13:39 -05:00
|
|
|
return nil
|
|
|
|
}
|
2013-01-21 21:03:23 -05:00
|
|
|
|
|
|
|
// 1. Send a SIGTERM
|
2015-01-19 07:44:04 -05:00
|
|
|
if err := container.killPossiblyDeadProcess(15); err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Infof("Failed to send SIGTERM to the process, force killing")
|
2015-01-19 07:44:04 -05:00
|
|
|
if err := container.killPossiblyDeadProcess(9); err != nil {
|
2014-03-24 08:03:56 -04:00
|
|
|
return err
|
|
|
|
}
|
2013-01-21 21:03:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 2. Wait for the process to exit on its own
|
2014-08-31 11:20:35 -04:00
|
|
|
if _, err := container.WaitStop(time.Duration(seconds) * time.Second); err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
|
2014-03-24 08:03:56 -04:00
|
|
|
// 3. If it doesn't, then send SIGKILL
|
|
|
|
if err := container.Kill(); err != nil {
|
2014-08-31 11:20:35 -04:00
|
|
|
container.WaitStop(-1 * time.Second)
|
2014-03-24 08:03:56 -04:00
|
|
|
return err
|
|
|
|
}
|
2013-01-21 21:03:23 -05:00
|
|
|
}
|
2013-01-18 19:13:39 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2013-04-16 12:43:44 -04:00
|
|
|
func (container *Container) Restart(seconds int) error {
|
2014-02-17 10:10:54 -05:00
|
|
|
// Avoid unnecessarily unmounting and then directly mounting
|
|
|
|
// the container when the container stops and then starts
|
|
|
|
// again
|
|
|
|
if err := container.Mount(); err == nil {
|
|
|
|
defer container.Unmount()
|
|
|
|
}
|
|
|
|
|
2013-04-16 12:43:44 -04:00
|
|
|
if err := container.Stop(seconds); err != nil {
|
2013-01-22 18:03:40 -05:00
|
|
|
return err
|
|
|
|
}
|
2013-10-31 17:58:43 -04:00
|
|
|
return container.Start()
|
2013-01-22 18:03:40 -05:00
|
|
|
}
|
|
|
|
|
2013-05-23 22:33:28 -04:00
|
|
|
func (container *Container) Resize(h, w int) error {
|
2014-10-23 16:30:18 -04:00
|
|
|
if !container.IsRunning() {
|
|
|
|
return fmt.Errorf("Cannot resize container %s, container is not running", container.ID)
|
|
|
|
}
|
2014-08-26 18:05:37 -04:00
|
|
|
return container.command.ProcessConfig.Terminal.Resize(h, w)
|
2013-05-23 22:33:28 -04:00
|
|
|
}
|
|
|
|
|
2013-10-31 19:57:45 -04:00
|
|
|
func (container *Container) ExportRw() (archive.Archive, error) {
|
2013-12-06 06:15:14 -05:00
|
|
|
if err := container.Mount(); err != nil {
|
2013-11-07 21:48:52 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-04-17 17:43:01 -04:00
|
|
|
if container.daemon == nil {
|
2013-11-07 15:34:01 -05:00
|
|
|
return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
|
2013-04-19 11:25:55 -04:00
|
|
|
}
|
2014-04-17 17:43:01 -04:00
|
|
|
archive, err := container.daemon.Diff(container)
|
2014-02-03 18:56:58 -05:00
|
|
|
if err != nil {
|
|
|
|
container.Unmount()
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-08-12 12:10:43 -04:00
|
|
|
return ioutils.NewReadCloserWrapper(archive, func() error {
|
2014-03-23 11:55:20 -04:00
|
|
|
err := archive.Close()
|
|
|
|
container.Unmount()
|
|
|
|
return err
|
|
|
|
}),
|
|
|
|
nil
|
2013-04-19 11:25:55 -04:00
|
|
|
}
|
|
|
|
|
2013-10-31 19:57:45 -04:00
|
|
|
func (container *Container) Export() (archive.Archive, error) {
|
2013-12-06 06:15:14 -05:00
|
|
|
if err := container.Mount(); err != nil {
|
2013-03-21 03:25:00 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
2013-12-05 16:18:02 -05:00
|
|
|
|
2014-01-30 10:43:53 -05:00
|
|
|
archive, err := archive.Tar(container.basefs, archive.Uncompressed)
|
2013-12-05 16:18:02 -05:00
|
|
|
if err != nil {
|
2014-02-03 18:56:58 -05:00
|
|
|
container.Unmount()
|
2013-12-05 16:18:02 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-08-12 12:10:43 -04:00
|
|
|
return ioutils.NewReadCloserWrapper(archive, func() error {
|
2014-03-23 11:55:20 -04:00
|
|
|
err := archive.Close()
|
|
|
|
container.Unmount()
|
|
|
|
return err
|
|
|
|
}),
|
|
|
|
nil
|
2013-03-21 03:25:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) Mount() error {
|
2014-04-17 17:43:01 -04:00
|
|
|
return container.daemon.Mount(container)
|
2013-03-21 03:25:00 -04:00
|
|
|
}
|
|
|
|
|
2014-09-12 03:44:25 -04:00
|
|
|
func (container *Container) changes() ([]archive.Change, error) {
|
|
|
|
return container.daemon.Changes(container)
|
|
|
|
}
|
|
|
|
|
2013-11-07 19:35:26 -05:00
|
|
|
func (container *Container) Changes() ([]archive.Change, error) {
|
2014-05-27 15:04:20 -04:00
|
|
|
container.Lock()
|
|
|
|
defer container.Unlock()
|
2014-09-12 03:44:25 -04:00
|
|
|
return container.changes()
|
2013-03-21 03:25:00 -04:00
|
|
|
}
|
|
|
|
|
2014-03-07 20:36:47 -05:00
|
|
|
func (container *Container) GetImage() (*image.Image, error) {
|
2014-04-17 17:43:01 -04:00
|
|
|
if container.daemon == nil {
|
2013-03-21 03:25:00 -04:00
|
|
|
return nil, fmt.Errorf("Can't get image of unregistered container")
|
|
|
|
}
|
2014-10-28 17:06:23 -04:00
|
|
|
return container.daemon.graph.Get(container.ImageID)
|
2013-03-21 03:25:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) Unmount() error {
|
2014-04-17 17:43:01 -04:00
|
|
|
return container.daemon.Unmount(container)
|
2013-03-21 03:25:00 -04:00
|
|
|
}
|
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
func (container *Container) logPath(name string) (string, error) {
|
2014-05-11 16:49:46 -04:00
|
|
|
return container.getRootResourcePath(fmt.Sprintf("%s-%s.log", container.ID, name))
|
2013-03-21 03:25:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) ReadLog(name string) (io.Reader, error) {
|
2014-05-27 22:15:42 -04:00
|
|
|
pth, err := container.logPath(name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return os.Open(pth)
|
2013-03-21 03:25:00 -04:00
|
|
|
}
|
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
func (container *Container) hostConfigPath() (string, error) {
|
2014-05-11 16:49:46 -04:00
|
|
|
return container.getRootResourcePath("hostconfig.json")
|
2013-07-02 08:19:25 -04:00
|
|
|
}
|
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
func (container *Container) jsonPath() (string, error) {
|
2014-05-11 16:49:46 -04:00
|
|
|
return container.getRootResourcePath("config.json")
|
2013-03-21 03:25:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// This method must be exported to be used from the lxc template
|
2014-01-30 10:43:53 -05:00
|
|
|
// This directory is only usable when the container is running
|
2013-03-21 03:25:00 -04:00
|
|
|
func (container *Container) RootfsPath() string {
|
2014-01-30 10:43:53 -05:00
|
|
|
return container.basefs
|
2013-03-21 03:25:00 -04:00
|
|
|
}
|
|
|
|
|
2013-06-04 14:00:22 -04:00
|
|
|
func validateID(id string) error {
|
2013-03-21 03:25:00 -04:00
|
|
|
if id == "" {
|
|
|
|
return fmt.Errorf("Invalid empty id")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2013-05-13 09:10:26 -04:00
|
|
|
|
|
|
|
// GetSize, return real size, virtual size
|
|
|
|
func (container *Container) GetSize() (int64, int64) {
|
2013-11-11 15:09:26 -05:00
|
|
|
var (
|
|
|
|
sizeRw, sizeRootfs int64
|
|
|
|
err error
|
2014-04-17 17:43:01 -04:00
|
|
|
driver = container.daemon.driver
|
2013-11-11 15:09:26 -05:00
|
|
|
)
|
2013-05-13 09:10:26 -04:00
|
|
|
|
2013-12-06 06:15:14 -05:00
|
|
|
if err := container.Mount(); err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err)
|
2013-11-07 15:30:56 -05:00
|
|
|
return sizeRw, sizeRootfs
|
|
|
|
}
|
2013-12-05 16:18:02 -05:00
|
|
|
defer container.Unmount()
|
2013-11-19 04:36:54 -05:00
|
|
|
|
2014-09-10 23:30:52 -04:00
|
|
|
initID := fmt.Sprintf("%s-init", container.ID)
|
|
|
|
sizeRw, err = driver.DiffSize(container.ID, initID)
|
|
|
|
if err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
|
2014-09-10 23:30:52 -04:00
|
|
|
// FIXME: GetSize should return an error. Not changing it now in case
|
|
|
|
// there is a side-effect.
|
|
|
|
sizeRw = -1
|
2013-11-19 04:36:54 -05:00
|
|
|
}
|
|
|
|
|
2014-01-30 10:43:53 -05:00
|
|
|
if _, err = os.Stat(container.basefs); err != nil {
|
2015-02-27 13:50:55 -05:00
|
|
|
if sizeRootfs, err = directory.Size(container.basefs); err != nil {
|
2013-11-21 20:18:41 -05:00
|
|
|
sizeRootfs = -1
|
2013-05-13 09:10:26 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return sizeRw, sizeRootfs
|
|
|
|
}
|
2013-07-17 00:07:41 -04:00
|
|
|
|
2014-02-11 09:21:33 -05:00
|
|
|
func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
2013-12-06 06:15:14 -05:00
|
|
|
if err := container.Mount(); err != nil {
|
2013-07-17 00:07:41 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-05-10 02:38:47 -04:00
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
basePath, err := container.getResourcePath(resource)
|
2014-05-23 03:48:01 -04:00
|
|
|
if err != nil {
|
|
|
|
container.Unmount()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2014-09-24 09:07:11 -04:00
|
|
|
// Check if this is actually in a volume
|
|
|
|
for _, mnt := range container.VolumeMounts() {
|
|
|
|
if len(mnt.MountToPath) > 0 && strings.HasPrefix(resource, mnt.MountToPath[1:]) {
|
|
|
|
return mnt.Export(resource)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-29 06:58:57 -04:00
|
|
|
// Check if this is a special one (resolv.conf, hostname, ..)
|
|
|
|
if resource == "etc/resolv.conf" {
|
|
|
|
basePath = container.ResolvConfPath
|
|
|
|
}
|
|
|
|
if resource == "etc/hostname" {
|
|
|
|
basePath = container.HostnamePath
|
|
|
|
}
|
|
|
|
if resource == "etc/hosts" {
|
|
|
|
basePath = container.HostsPath
|
|
|
|
}
|
|
|
|
|
2013-07-17 10:25:49 -04:00
|
|
|
stat, err := os.Stat(basePath)
|
|
|
|
if err != nil {
|
2013-12-05 16:18:02 -05:00
|
|
|
container.Unmount()
|
2013-07-17 10:25:49 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-09-24 09:07:11 -04:00
|
|
|
var filter []string
|
2013-07-17 10:25:49 -04:00
|
|
|
if !stat.IsDir() {
|
|
|
|
d, f := path.Split(basePath)
|
|
|
|
basePath = d
|
|
|
|
filter = []string{f}
|
|
|
|
} else {
|
|
|
|
filter = []string{path.Base(basePath)}
|
|
|
|
basePath = path.Dir(basePath)
|
|
|
|
}
|
2013-12-05 16:18:02 -05:00
|
|
|
|
2014-02-13 19:05:36 -05:00
|
|
|
archive, err := archive.TarWithOptions(basePath, &archive.TarOptions{
|
2014-10-23 17:30:11 -04:00
|
|
|
Compression: archive.Uncompressed,
|
|
|
|
IncludeFiles: filter,
|
2013-11-11 17:30:38 -05:00
|
|
|
})
|
2013-12-05 16:18:02 -05:00
|
|
|
if err != nil {
|
2014-05-23 03:48:01 -04:00
|
|
|
container.Unmount()
|
2013-12-05 16:18:02 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-08-12 12:10:43 -04:00
|
|
|
return ioutils.NewReadCloserWrapper(archive, func() error {
|
2014-03-23 11:55:20 -04:00
|
|
|
err := archive.Close()
|
|
|
|
container.Unmount()
|
|
|
|
return err
|
|
|
|
}),
|
|
|
|
nil
|
2013-07-17 00:07:41 -04:00
|
|
|
}
|
2013-10-04 22:25:15 -04:00
|
|
|
|
|
|
|
// Returns true if the container exposes a certain port
|
2014-02-11 19:48:44 -05:00
|
|
|
func (container *Container) Exposes(p nat.Port) bool {
|
2013-10-04 22:25:15 -04:00
|
|
|
_, exists := container.Config.ExposedPorts[p]
|
|
|
|
return exists
|
|
|
|
}
|
2013-11-29 10:40:44 -05:00
|
|
|
|
2014-03-07 21:42:29 -05:00
|
|
|
func (container *Container) HostConfig() *runconfig.HostConfig {
|
2014-06-05 03:17:09 -04:00
|
|
|
container.Lock()
|
|
|
|
res := container.hostConfig
|
|
|
|
container.Unlock()
|
|
|
|
return res
|
2014-03-07 21:42:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) SetHostConfig(hostConfig *runconfig.HostConfig) {
|
2014-06-05 03:17:09 -04:00
|
|
|
container.Lock()
|
2014-03-07 21:42:29 -05:00
|
|
|
container.hostConfig = hostConfig
|
2014-06-05 03:17:09 -04:00
|
|
|
container.Unlock()
|
2014-03-07 21:42:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) DisableLink(name string) {
|
|
|
|
if container.activeLinks != nil {
|
|
|
|
if link, exists := container.activeLinks[name]; exists {
|
|
|
|
link.Disable()
|
|
|
|
} else {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Could not find active link for %s", name)
|
2014-03-07 21:42:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-04-07 22:12:22 -04:00
|
|
|
|
|
|
|
func (container *Container) setupContainerDns() error {
|
2014-04-14 02:58:04 -04:00
|
|
|
if container.ResolvConfPath != "" {
|
2014-12-10 00:55:09 -05:00
|
|
|
// check if this is an existing container that needs DNS update:
|
|
|
|
if container.UpdateDns {
|
|
|
|
// read the host's resolv.conf, get the hash and call updateResolvConf
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Check container (%s) for update to resolv.conf - UpdateDns flag was set", container.ID)
|
2014-12-10 00:55:09 -05:00
|
|
|
latestResolvConf, latestHash := resolvconf.GetLastModified()
|
|
|
|
|
2015-01-09 12:06:48 -05:00
|
|
|
// clean container resolv.conf re: localhost nameservers and IPv6 NS (if IPv6 disabled)
|
2015-04-04 00:06:48 -04:00
|
|
|
updatedResolvConf, modified := resolvconf.FilterResolvDns(latestResolvConf, container.daemon.config.Bridge.EnableIPv6)
|
2014-12-10 00:55:09 -05:00
|
|
|
if modified {
|
|
|
|
// changes have occurred during resolv.conf localhost cleanup: generate an updated hash
|
2015-03-29 17:17:23 -04:00
|
|
|
newHash, err := ioutils.HashData(bytes.NewReader(updatedResolvConf))
|
2014-12-10 00:55:09 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
latestHash = newHash
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := container.updateResolvConf(updatedResolvConf, latestHash); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// successful update of the restarting container; set the flag off
|
|
|
|
container.UpdateDns = false
|
|
|
|
}
|
2014-04-14 02:40:49 -04:00
|
|
|
return nil
|
|
|
|
}
|
2014-05-02 17:45:39 -04:00
|
|
|
|
2014-04-07 22:12:22 -04:00
|
|
|
var (
|
2014-04-17 17:43:01 -04:00
|
|
|
config = container.hostConfig
|
|
|
|
daemon = container.daemon
|
2014-04-07 22:12:22 -04:00
|
|
|
)
|
2014-05-02 17:45:39 -04:00
|
|
|
|
2014-05-05 18:51:32 -04:00
|
|
|
resolvConf, err := resolvconf.Get()
|
2014-04-07 22:12:22 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-04-09 19:17:39 -04:00
|
|
|
container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-05-02 17:45:39 -04:00
|
|
|
|
2014-10-07 16:04:06 -04:00
|
|
|
if config.NetworkMode != "host" {
|
2014-10-07 20:58:39 -04:00
|
|
|
// check configurations for any container/daemon dns settings
|
2014-10-07 16:04:06 -04:00
|
|
|
if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 {
|
|
|
|
var (
|
|
|
|
dns = resolvconf.GetNameservers(resolvConf)
|
|
|
|
dnsSearch = resolvconf.GetSearchDomains(resolvConf)
|
|
|
|
)
|
|
|
|
if len(config.Dns) > 0 {
|
|
|
|
dns = config.Dns
|
|
|
|
} else if len(daemon.config.Dns) > 0 {
|
|
|
|
dns = daemon.config.Dns
|
|
|
|
}
|
|
|
|
if len(config.DnsSearch) > 0 {
|
|
|
|
dnsSearch = config.DnsSearch
|
|
|
|
} else if len(daemon.config.DnsSearch) > 0 {
|
|
|
|
dnsSearch = daemon.config.DnsSearch
|
|
|
|
}
|
|
|
|
return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch)
|
2014-04-07 22:12:22 -04:00
|
|
|
}
|
2014-10-07 20:58:39 -04:00
|
|
|
|
2015-01-09 12:06:48 -05:00
|
|
|
// replace any localhost/127.*, and remove IPv6 nameservers if IPv6 disabled in daemon
|
2015-04-04 00:06:48 -04:00
|
|
|
resolvConf, _ = resolvconf.FilterResolvDns(resolvConf, daemon.config.Bridge.EnableIPv6)
|
2014-12-10 00:55:09 -05:00
|
|
|
}
|
|
|
|
//get a sha256 hash of the resolv conf at this point so we can check
|
|
|
|
//for changes when the host resolv.conf changes (e.g. network update)
|
2015-03-29 17:17:23 -04:00
|
|
|
resolvHash, err := ioutils.HashData(bytes.NewReader(resolvConf))
|
2014-12-10 00:55:09 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resolvHashFile := container.ResolvConfPath + ".hash"
|
|
|
|
if err = ioutil.WriteFile(resolvHashFile, []byte(resolvHash), 0644); err != nil {
|
|
|
|
return err
|
2014-04-07 22:12:22 -04:00
|
|
|
}
|
2014-04-09 19:17:39 -04:00
|
|
|
return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644)
|
2014-04-07 22:12:22 -04:00
|
|
|
}
|
2014-04-14 02:40:49 -04:00
|
|
|
|
2014-12-10 00:55:09 -05:00
|
|
|
// called when the host's resolv.conf changes to check whether container's resolv.conf
|
|
|
|
// is unchanged by the container "user" since container start: if unchanged, the
|
|
|
|
// container's resolv.conf will be updated to match the host's new resolv.conf
|
|
|
|
func (container *Container) updateResolvConf(updatedResolvConf []byte, newResolvHash string) error {
|
|
|
|
|
|
|
|
if container.ResolvConfPath == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if container.Running {
|
|
|
|
//set a marker in the hostConfig to update on next start/restart
|
|
|
|
container.UpdateDns = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
resolvHashFile := container.ResolvConfPath + ".hash"
|
|
|
|
|
|
|
|
//read the container's current resolv.conf and compute the hash
|
|
|
|
resolvBytes, err := ioutil.ReadFile(container.ResolvConfPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-29 17:17:23 -04:00
|
|
|
curHash, err := ioutils.HashData(bytes.NewReader(resolvBytes))
|
2014-12-10 00:55:09 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
//read the hash from the last time we wrote resolv.conf in the container
|
|
|
|
hashBytes, err := ioutil.ReadFile(resolvHashFile)
|
|
|
|
if err != nil {
|
2015-01-09 21:18:57 -05:00
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// backwards compat: if no hash file exists, this container pre-existed from
|
|
|
|
// a Docker daemon that didn't contain this update feature. Given we can't know
|
|
|
|
// if the user has modified the resolv.conf since container start time, safer
|
|
|
|
// to just never update the container's resolv.conf during it's lifetime which
|
|
|
|
// we can control by setting hashBytes to an empty string
|
|
|
|
hashBytes = []byte("")
|
2014-12-10 00:55:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
//if the user has not modified the resolv.conf of the container since we wrote it last
|
|
|
|
//we will replace it with the updated resolv.conf from the host
|
|
|
|
if string(hashBytes) == curHash {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("replacing %q with updated host resolv.conf", container.ResolvConfPath)
|
2014-12-10 00:55:09 -05:00
|
|
|
|
|
|
|
// for atomic updates to these files, use temporary files with os.Rename:
|
|
|
|
dir := path.Dir(container.ResolvConfPath)
|
|
|
|
tmpHashFile, err := ioutil.TempFile(dir, "hash")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
tmpResolvFile, err := ioutil.TempFile(dir, "resolv")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// write the updates to the temp files
|
|
|
|
if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newResolvHash), 0644); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = ioutil.WriteFile(tmpResolvFile.Name(), updatedResolvConf, 0644); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// rename the temp files for atomic replace
|
|
|
|
if err = os.Rename(tmpHashFile.Name(), resolvHashFile); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return os.Rename(tmpResolvFile.Name(), container.ResolvConfPath)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-14 19:19:37 -04:00
|
|
|
func (container *Container) updateParentsHosts() error {
|
2015-01-21 17:32:36 -05:00
|
|
|
refs := container.daemon.ContainerGraph().RefPaths(container.ID)
|
|
|
|
for _, ref := range refs {
|
|
|
|
if ref.ParentID == "0" {
|
2014-07-14 19:19:37 -04:00
|
|
|
continue
|
|
|
|
}
|
2014-12-16 18:06:35 -05:00
|
|
|
|
|
|
|
c, err := container.daemon.Get(ref.ParentID)
|
|
|
|
if err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Error(err)
|
2014-12-16 18:06:35 -05:00
|
|
|
}
|
|
|
|
|
2014-09-05 02:43:44 -04:00
|
|
|
if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress)
|
2015-01-21 17:32:36 -05:00
|
|
|
if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, ref.Name); err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Errorf("Failed to update /etc/hosts in parent container %s for alias %s: %v", c.ID, ref.Name, err)
|
2014-07-14 19:19:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-04-14 02:40:49 -04:00
|
|
|
func (container *Container) initializeNetworking() error {
|
2014-05-02 17:45:39 -04:00
|
|
|
var err error
|
2014-05-02 19:59:28 -04:00
|
|
|
if container.hostConfig.NetworkMode.IsHost() {
|
2014-05-02 17:45:39 -04:00
|
|
|
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]
|
|
|
|
}
|
|
|
|
|
2014-05-16 18:01:25 -04:00
|
|
|
content, err := ioutil.ReadFile("/etc/hosts")
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return container.buildHostnameAndHostsFiles("")
|
2014-06-12 15:20:57 -04:00
|
|
|
} else if err != nil {
|
|
|
|
return err
|
2014-05-16 18:01:25 -04:00
|
|
|
}
|
2014-06-12 15:20:57 -04:00
|
|
|
|
|
|
|
if err := container.buildHostnameFile(); err != nil {
|
2014-05-16 18:01:25 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
hostsPath, err := container.getRootResourcePath("hosts")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
container.HostsPath = hostsPath
|
|
|
|
|
2014-05-16 18:01:25 -04:00
|
|
|
return ioutil.WriteFile(container.HostsPath, content, 0644)
|
2014-08-20 09:03:05 -04:00
|
|
|
}
|
|
|
|
if container.hostConfig.NetworkMode.IsContainer() {
|
2014-05-02 19:59:28 -04:00
|
|
|
// we need to get the hosts files from the container to join
|
|
|
|
nc, err := container.getNetworkedContainer()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-19 05:03:40 -04:00
|
|
|
container.HostnamePath = nc.HostnamePath
|
2014-05-02 19:59:28 -04:00
|
|
|
container.HostsPath = nc.HostsPath
|
|
|
|
container.ResolvConfPath = nc.ResolvConfPath
|
|
|
|
container.Config.Hostname = nc.Config.Hostname
|
|
|
|
container.Config.Domainname = nc.Config.Domainname
|
2014-08-20 09:03:05 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if container.daemon.config.DisableNetwork {
|
2014-04-14 02:40:49 -04:00
|
|
|
container.Config.NetworkDisabled = true
|
2014-05-05 18:05:14 -04:00
|
|
|
return container.buildHostnameAndHostsFiles("127.0.1.1")
|
2014-04-14 02:40:49 -04:00
|
|
|
}
|
2014-10-09 18:15:17 -04:00
|
|
|
if err := container.AllocateNetwork(); err != nil {
|
|
|
|
return err
|
2014-08-20 09:03:05 -04:00
|
|
|
}
|
|
|
|
return container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress)
|
2014-04-14 02:40:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the config is compatible with the current kernel
|
2014-04-17 17:43:01 -04:00
|
|
|
func (container *Container) verifyDaemonSettings() {
|
2015-04-11 04:37:28 -04:00
|
|
|
if container.hostConfig.Memory > 0 && !container.daemon.sysInfo.MemoryLimit {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.")
|
2015-04-11 04:37:28 -04:00
|
|
|
container.hostConfig.Memory = 0
|
2014-04-14 02:40:49 -04:00
|
|
|
}
|
2015-04-10 20:07:05 -04:00
|
|
|
if container.hostConfig.Memory > 0 && container.hostConfig.MemorySwap != -1 && !container.daemon.sysInfo.SwapLimit {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Warnf("Your kernel does not support swap limit capabilities. Limitation discarded.")
|
2015-04-11 04:37:28 -04:00
|
|
|
container.hostConfig.MemorySwap = -1
|
2014-04-14 02:40:49 -04:00
|
|
|
}
|
2014-04-17 17:43:01 -04:00
|
|
|
if container.daemon.sysInfo.IPv4ForwardingDisabled {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Warnf("IPv4 forwarding is disabled. Networking will not work")
|
2014-04-14 02:40:49 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) setupLinkedContainers() ([]string, error) {
|
|
|
|
var (
|
2014-04-17 17:43:01 -04:00
|
|
|
env []string
|
|
|
|
daemon = container.daemon
|
2014-04-14 02:40:49 -04:00
|
|
|
)
|
2014-04-17 17:43:01 -04:00
|
|
|
children, err := daemon.Children(container.Name)
|
2014-04-14 02:40:49 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(children) > 0 {
|
|
|
|
container.activeLinks = make(map[string]*links.Link, len(children))
|
|
|
|
|
|
|
|
// If we encounter an error make sure that we rollback any network
|
2015-02-28 05:43:28 -05:00
|
|
|
// config and iptables changes
|
2014-04-14 02:40:49 -04:00
|
|
|
rollback := func() {
|
|
|
|
for _, link := range container.activeLinks {
|
|
|
|
link.Disable()
|
|
|
|
}
|
|
|
|
container.activeLinks = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for linkAlias, child := range children {
|
2014-08-31 11:20:35 -04:00
|
|
|
if !child.IsRunning() {
|
2014-04-14 02:40:49 -04:00
|
|
|
return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
|
|
|
|
}
|
|
|
|
|
|
|
|
link, err := links.NewLink(
|
|
|
|
container.NetworkSettings.IPAddress,
|
|
|
|
child.NetworkSettings.IPAddress,
|
|
|
|
linkAlias,
|
|
|
|
child.Config.Env,
|
|
|
|
child.Config.ExposedPorts,
|
2015-04-12 10:25:10 -04:00
|
|
|
)
|
2014-04-14 02:40:49 -04:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
rollback()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
container.activeLinks[link.Alias()] = link
|
|
|
|
if err := link.Enable(); err != nil {
|
|
|
|
rollback()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, envVar := range link.ToEnv() {
|
|
|
|
env = append(env, envVar)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return env, nil
|
|
|
|
}
|
|
|
|
|
2014-04-17 17:43:01 -04:00
|
|
|
func (container *Container) createDaemonEnvironment(linkedEnv []string) []string {
|
2014-09-24 11:00:01 -04:00
|
|
|
// 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)
|
|
|
|
}
|
2014-04-14 02:40:49 -04:00
|
|
|
// Setup environment
|
|
|
|
env := []string{
|
|
|
|
"PATH=" + DefaultPathEnv,
|
2014-09-24 11:00:01 -04:00
|
|
|
"HOSTNAME=" + fullHostname,
|
2014-02-22 22:06:37 -05:00
|
|
|
// 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).
|
2014-04-14 02:40:49 -04:00
|
|
|
}
|
|
|
|
if container.Config.Tty {
|
|
|
|
env = append(env, "TERM=xterm")
|
|
|
|
}
|
|
|
|
env = append(env, linkedEnv...)
|
|
|
|
// because the env on the container can override certain default values
|
|
|
|
// we need to replace the 'env' keys where they match and append anything
|
|
|
|
// else.
|
|
|
|
env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env)
|
|
|
|
|
|
|
|
return env
|
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) setupWorkingDirectory() error {
|
|
|
|
if container.Config.WorkingDir != "" {
|
|
|
|
container.Config.WorkingDir = path.Clean(container.Config.WorkingDir)
|
|
|
|
|
2014-05-27 22:15:42 -04:00
|
|
|
pth, err := container.getResourcePath(container.Config.WorkingDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pthInfo, err := os.Stat(pth)
|
2014-04-14 02:40:49 -04:00
|
|
|
if err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
2014-05-27 22:15:42 -04:00
|
|
|
|
|
|
|
if err := os.MkdirAll(pth, 0755); err != nil {
|
2014-04-14 02:40:49 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if pthInfo != nil && !pthInfo.IsDir() {
|
|
|
|
return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-04 14:04:58 -05:00
|
|
|
func (container *Container) startLogging() error {
|
|
|
|
cfg := container.hostConfig.LogConfig
|
|
|
|
if cfg.Type == "" {
|
|
|
|
cfg = container.daemon.defaultLogConfig
|
2014-04-14 02:40:49 -04:00
|
|
|
}
|
2015-02-04 14:04:58 -05:00
|
|
|
var l logger.Logger
|
|
|
|
switch cfg.Type {
|
|
|
|
case "json-file":
|
|
|
|
pth, err := container.logPath("json")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-05-27 22:15:42 -04:00
|
|
|
|
2015-02-04 14:04:58 -05:00
|
|
|
dl, err := jsonfilelog.New(pth)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
l = dl
|
2015-03-09 14:40:57 -04:00
|
|
|
case "syslog":
|
|
|
|
dl, err := syslog.New(container.ID[:12])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
l = dl
|
2015-02-04 14:04:58 -05:00
|
|
|
case "none":
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Unknown logging driver: %s", cfg.Type)
|
2014-04-14 02:40:49 -04:00
|
|
|
}
|
2014-05-27 22:15:42 -04:00
|
|
|
|
2015-03-18 14:44:14 -04:00
|
|
|
copier, err := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
|
|
|
|
if err != nil {
|
2014-05-27 22:15:42 -04:00
|
|
|
return err
|
|
|
|
}
|
2015-03-18 14:44:14 -04:00
|
|
|
container.logCopier = copier
|
|
|
|
copier.Run()
|
2015-02-04 14:04:58 -05:00
|
|
|
container.logDriver = l
|
2014-05-27 22:15:42 -04:00
|
|
|
|
2014-04-14 02:40:49 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) waitForStart() error {
|
2014-08-04 20:05:56 -04:00
|
|
|
container.monitor = newContainerMonitor(container, container.hostConfig.RestartPolicy)
|
2014-08-04 18:53:10 -04:00
|
|
|
|
2014-08-12 21:03:11 -04:00
|
|
|
// 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
|
2014-04-14 02:40:49 -04:00
|
|
|
select {
|
2014-08-12 21:03:11 -04:00
|
|
|
case <-container.monitor.startSignal:
|
2014-09-30 02:16:27 -04:00
|
|
|
case err := <-promise.Go(container.monitor.Start):
|
2014-04-14 02:40:49 -04:00
|
|
|
return err
|
|
|
|
}
|
2014-08-12 21:03:11 -04:00
|
|
|
|
2014-04-14 02:40:49 -04:00
|
|
|
return nil
|
|
|
|
}
|
2014-04-14 07:23:43 -04:00
|
|
|
|
|
|
|
func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bindings nat.PortMap) error {
|
|
|
|
binding := bindings[port]
|
|
|
|
if container.hostConfig.PublishAllPorts && len(binding) == 0 {
|
|
|
|
binding = append(binding, nat.PortBinding{})
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < len(binding); i++ {
|
2015-04-04 00:06:48 -04:00
|
|
|
b, err := bridge.AllocatePort(container.ID, port, binding[i])
|
2014-04-14 07:23:43 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
binding[i] = b
|
|
|
|
}
|
|
|
|
bindings[port] = binding
|
|
|
|
return nil
|
|
|
|
}
|
2014-04-29 04:08:19 -04:00
|
|
|
|
|
|
|
func (container *Container) GetProcessLabel() string {
|
|
|
|
// even if we have a process label return "" if we are running
|
|
|
|
// in privileged mode
|
|
|
|
if container.hostConfig.Privileged {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return container.ProcessLabel
|
|
|
|
}
|
|
|
|
|
|
|
|
func (container *Container) GetMountLabel() string {
|
|
|
|
if container.hostConfig.Privileged {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return container.MountLabel
|
|
|
|
}
|
2014-05-02 19:59:28 -04:00
|
|
|
|
2014-11-10 16:14:17 -05:00
|
|
|
func (container *Container) getIpcContainer() (*Container, error) {
|
|
|
|
containerID := container.hostConfig.IpcMode.Container()
|
2014-12-16 18:06:35 -05:00
|
|
|
c, err := container.daemon.Get(containerID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2014-11-10 16:14:17 -05:00
|
|
|
}
|
|
|
|
if !c.IsRunning() {
|
|
|
|
return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID)
|
|
|
|
}
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
2014-05-02 19:59:28 -04:00
|
|
|
func (container *Container) getNetworkedContainer() (*Container, error) {
|
|
|
|
parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2)
|
|
|
|
switch parts[0] {
|
|
|
|
case "container":
|
2014-11-10 16:14:17 -05:00
|
|
|
if len(parts) != 2 {
|
|
|
|
return nil, fmt.Errorf("no container specified to join network")
|
|
|
|
}
|
2014-12-16 18:06:35 -05:00
|
|
|
nc, err := container.daemon.Get(parts[1])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2014-05-02 19:59:28 -04:00
|
|
|
}
|
2014-08-31 11:20:35 -04:00
|
|
|
if !nc.IsRunning() {
|
2014-05-02 19:59:28 -04:00
|
|
|
return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1])
|
|
|
|
}
|
|
|
|
return nc, nil
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("network mode not set to container")
|
|
|
|
}
|
|
|
|
}
|
2015-01-07 17:43:04 -05:00
|
|
|
|
|
|
|
func (container *Container) Stats() (*execdriver.ResourceStats, error) {
|
|
|
|
return container.daemon.Stats(container)
|
|
|
|
}
|
2015-02-05 19:24:47 -05:00
|
|
|
|
|
|
|
func (c *Container) LogDriverType() string {
|
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
|
|
|
if c.hostConfig.LogConfig.Type == "" {
|
|
|
|
return c.daemon.defaultLogConfig.Type
|
|
|
|
}
|
|
|
|
return c.hostConfig.LogConfig.Type
|
|
|
|
}
|