| // +build linux |
| |
| package daemon |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "net" |
| "os" |
| "path" |
| "path/filepath" |
| "strconv" |
| "strings" |
| "syscall" |
| "time" |
| |
| "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/daemon/execdriver" |
| "github.com/docker/docker/daemon/network" |
| "github.com/docker/docker/links" |
| "github.com/docker/docker/nat" |
| "github.com/docker/docker/pkg/archive" |
| "github.com/docker/docker/pkg/directory" |
| "github.com/docker/docker/pkg/ioutils" |
| "github.com/docker/docker/pkg/stringid" |
| "github.com/docker/docker/pkg/ulimit" |
| "github.com/docker/docker/runconfig" |
| "github.com/docker/docker/utils" |
| "github.com/docker/libcontainer/configs" |
| "github.com/docker/libcontainer/devices" |
| "github.com/docker/libnetwork" |
| "github.com/docker/libnetwork/netlabel" |
| "github.com/docker/libnetwork/options" |
| "github.com/docker/libnetwork/types" |
| ) |
| |
| const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" |
| |
| type Container struct { |
| CommonContainer |
| |
| // Fields below here are platform specific. |
| |
| AppArmorProfile string |
| activeLinks map[string]*links.Link |
| } |
| |
| func killProcessDirectly(container *Container) error { |
| if _, err := container.WaitStop(10 * time.Second); err != nil { |
| // Ensure that we don't kill ourselves |
| if pid := container.GetPid(); pid != 0 { |
| logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(container.ID)) |
| if err := syscall.Kill(pid, 9); err != nil { |
| if err != syscall.ESRCH { |
| return err |
| } |
| logrus.Debugf("Cannot kill process (pid=%d) with signal 9: no such process.", pid) |
| } |
| } |
| } |
| return nil |
| } |
| |
| func (container *Container) setupLinkedContainers() ([]string, error) { |
| var ( |
| env []string |
| daemon = container.daemon |
| ) |
| children, err := daemon.Children(container.Name) |
| 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 |
| // config and iptables changes |
| rollback := func() { |
| for _, link := range container.activeLinks { |
| link.Disable() |
| } |
| container.activeLinks = nil |
| } |
| |
| for linkAlias, child := range children { |
| if !child.IsRunning() { |
| 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, |
| ) |
| |
| 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 |
| } |
| |
| func (container *Container) createDaemonEnvironment(linkedEnv []string) []string { |
| // if a domain name was specified, append it to the hostname (see #7851) |
| fullHostname := container.Config.Hostname |
| if container.Config.Domainname != "" { |
| fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) |
| } |
| // Setup environment |
| env := []string{ |
| "PATH=" + DefaultPathEnv, |
| "HOSTNAME=" + fullHostname, |
| // Note: we don't set HOME here because it'll get autoset intelligently |
| // based on the value of USER inside dockerinit, but only if it isn't |
| // set already (ie, that can be overridden by setting HOME via -e or ENV |
| // in a Dockerfile). |
| } |
| if container.Config.Tty { |
| env = append(env, "TERM=xterm") |
| } |
| env = append(env, linkedEnv...) |
| // because the env on the container can override certain default values |
| // we need to replace the 'env' keys where they match and append anything |
| // else. |
| env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env) |
| |
| return env |
| } |
| |
| func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs.Device, err error) { |
| device, err := devices.DeviceFromPath(deviceMapping.PathOnHost, deviceMapping.CgroupPermissions) |
| // if there was no error, return the device |
| if err == nil { |
| device.Path = deviceMapping.PathInContainer |
| return append(devs, device), nil |
| } |
| |
| // if the device is not a device node |
| // try to see if it's a directory holding many devices |
| if err == devices.ErrNotADevice { |
| |
| // check if it is a directory |
| if src, e := os.Stat(deviceMapping.PathOnHost); e == nil && src.IsDir() { |
| |
| // mount the internal devices recursively |
| filepath.Walk(deviceMapping.PathOnHost, func(dpath string, f os.FileInfo, e error) error { |
| childDevice, e := devices.DeviceFromPath(dpath, deviceMapping.CgroupPermissions) |
| if e != nil { |
| // ignore the device |
| return nil |
| } |
| |
| // add the device to userSpecified devices |
| childDevice.Path = strings.Replace(dpath, deviceMapping.PathOnHost, deviceMapping.PathInContainer, 1) |
| devs = append(devs, childDevice) |
| |
| return nil |
| }) |
| } |
| } |
| |
| if len(devs) > 0 { |
| return devs, nil |
| } |
| |
| return devs, fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err) |
| } |
| |
| func populateCommand(c *Container, env []string) error { |
| var en *execdriver.Network |
| if !c.daemon.config.DisableNetwork { |
| en = &execdriver.Network{ |
| NamespacePath: c.NetworkSettings.SandboxKey, |
| } |
| |
| parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2) |
| if parts[0] == "container" { |
| nc, err := c.getNetworkedContainer() |
| if err != nil { |
| return err |
| } |
| en.ContainerID = nc.ID |
| } |
| } |
| |
| 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() |
| } |
| |
| 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 := append(configs.DefaultAllowedDevices, userSpecifiedDevices...) |
| |
| autoCreatedDevices := append(configs.DefaultAutoCreatedDevices, userSpecifiedDevices...) |
| |
| // TODO: this can be removed after lxc-conf is fully deprecated |
| lxcConfig, err := mergeLxcConfIntoOptions(c.hostConfig) |
| if err != nil { |
| return err |
| } |
| |
| 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) |
| } |
| |
| resources := &execdriver.Resources{ |
| Memory: c.hostConfig.Memory, |
| MemorySwap: c.hostConfig.MemorySwap, |
| CpuShares: c.hostConfig.CpuShares, |
| CpusetCpus: c.hostConfig.CpusetCpus, |
| CpusetMems: c.hostConfig.CpusetMems, |
| CpuPeriod: c.hostConfig.CpuPeriod, |
| CpuQuota: c.hostConfig.CpuQuota, |
| BlkioWeight: c.hostConfig.BlkioWeight, |
| Rlimits: rlimits, |
| OomKillDisable: c.hostConfig.OomKillDisable, |
| } |
| |
| processConfig := execdriver.ProcessConfig{ |
| Privileged: c.hostConfig.Privileged, |
| Entrypoint: c.Path, |
| Arguments: c.Args, |
| Tty: c.Config.Tty, |
| User: c.Config.User, |
| } |
| |
| processConfig.SysProcAttr = &syscall.SysProcAttr{Setsid: true} |
| processConfig.Env = env |
| |
| c.command = &execdriver.Command{ |
| ID: c.ID, |
| Rootfs: c.RootfsPath(), |
| ReadonlyRootfs: c.hostConfig.ReadonlyRootfs, |
| InitPath: "/.dockerinit", |
| WorkingDir: c.Config.WorkingDir, |
| Network: en, |
| Ipc: ipc, |
| Pid: pid, |
| UTS: uts, |
| Resources: resources, |
| AllowedDevices: allowedDevices, |
| AutoCreatedDevices: autoCreatedDevices, |
| CapAdd: c.hostConfig.CapAdd, |
| CapDrop: c.hostConfig.CapDrop, |
| ProcessConfig: processConfig, |
| ProcessLabel: c.GetProcessLabel(), |
| MountLabel: c.GetMountLabel(), |
| LxcConfig: lxcConfig, |
| AppArmorProfile: c.AppArmorProfile, |
| CgroupParent: c.hostConfig.CgroupParent, |
| } |
| |
| return nil |
| } |
| |
| // GetSize, return real size, virtual size |
| func (container *Container) GetSize() (int64, int64) { |
| var ( |
| sizeRw, sizeRootfs int64 |
| err error |
| driver = container.daemon.driver |
| ) |
| |
| if err := container.Mount(); err != nil { |
| logrus.Errorf("Failed to compute size of container rootfs %s: %s", container.ID, err) |
| return sizeRw, sizeRootfs |
| } |
| defer container.Unmount() |
| |
| initID := fmt.Sprintf("%s-init", container.ID) |
| sizeRw, err = driver.DiffSize(container.ID, initID) |
| if err != nil { |
| logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", driver, container.ID, err) |
| // FIXME: GetSize should return an error. Not changing it now in case |
| // there is a side-effect. |
| sizeRw = -1 |
| } |
| |
| if _, err = os.Stat(container.basefs); err == nil { |
| if sizeRootfs, err = directory.Size(container.basefs); err != nil { |
| sizeRootfs = -1 |
| } |
| } |
| return sizeRw, sizeRootfs |
| } |
| |
| func (container *Container) buildHostnameFile() error { |
| hostnamePath, err := container.GetRootResourcePath("hostname") |
| if err != nil { |
| return err |
| } |
| container.HostnamePath = hostnamePath |
| |
| if container.Config.Domainname != "" { |
| return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644) |
| } |
| return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) |
| } |
| |
| func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, error) { |
| var ( |
| joinOptions []libnetwork.EndpointOption |
| err error |
| dns []string |
| dnsSearch []string |
| ) |
| |
| joinOptions = append(joinOptions, libnetwork.JoinOptionHostname(container.Config.Hostname), |
| libnetwork.JoinOptionDomainname(container.Config.Domainname)) |
| |
| if container.hostConfig.NetworkMode.IsHost() { |
| joinOptions = append(joinOptions, libnetwork.JoinOptionUseDefaultSandbox()) |
| } |
| |
| container.HostsPath, err = container.GetRootResourcePath("hosts") |
| if err != nil { |
| return nil, err |
| } |
| joinOptions = append(joinOptions, libnetwork.JoinOptionHostsPath(container.HostsPath)) |
| |
| container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf") |
| if err != nil { |
| return nil, err |
| } |
| joinOptions = append(joinOptions, libnetwork.JoinOptionResolvConfPath(container.ResolvConfPath)) |
| |
| if len(container.hostConfig.Dns) > 0 { |
| dns = container.hostConfig.Dns |
| } else if len(container.daemon.config.Dns) > 0 { |
| dns = container.daemon.config.Dns |
| } |
| |
| for _, d := range dns { |
| joinOptions = append(joinOptions, libnetwork.JoinOptionDNS(d)) |
| } |
| |
| if len(container.hostConfig.DnsSearch) > 0 { |
| dnsSearch = container.hostConfig.DnsSearch |
| } else if len(container.daemon.config.DnsSearch) > 0 { |
| dnsSearch = container.daemon.config.DnsSearch |
| } |
| |
| for _, ds := range dnsSearch { |
| joinOptions = append(joinOptions, libnetwork.JoinOptionDNSSearch(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 { |
| joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(name, a.Addr)) |
| } |
| } |
| |
| var childEndpoints, parentEndpoints []string |
| |
| children, err := container.daemon.Children(container.Name) |
| if err != nil { |
| return nil, err |
| } |
| |
| for linkAlias, child := range children { |
| _, 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:] |
| } |
| joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(aliasList, child.NetworkSettings.IPAddress)) |
| if child.NetworkSettings.EndpointID != "" { |
| childEndpoints = append(childEndpoints, child.NetworkSettings.EndpointID) |
| } |
| } |
| |
| for _, extraHost := range container.hostConfig.ExtraHosts { |
| // allow IPv6 addresses in extra hosts; only split on first ":" |
| parts := strings.SplitN(extraHost, ":", 2) |
| joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(parts[0], parts[1])) |
| } |
| |
| refs := container.daemon.ContainerGraph().RefPaths(container.ID) |
| for _, ref := range refs { |
| if ref.ParentID == "0" { |
| continue |
| } |
| |
| c, err := container.daemon.Get(ref.ParentID) |
| if err != nil { |
| logrus.Error(err) |
| } |
| |
| if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() { |
| logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress) |
| joinOptions = append(joinOptions, libnetwork.JoinOptionParentUpdate(c.NetworkSettings.EndpointID, ref.Name, container.NetworkSettings.IPAddress)) |
| if c.NetworkSettings.EndpointID != "" { |
| parentEndpoints = append(parentEndpoints, c.NetworkSettings.EndpointID) |
| } |
| } |
| } |
| |
| linkOptions := options.Generic{ |
| netlabel.GenericData: options.Generic{ |
| "ParentEndpoints": parentEndpoints, |
| "ChildEndpoints": childEndpoints, |
| }, |
| } |
| |
| joinOptions = append(joinOptions, libnetwork.JoinOptionGeneric(linkOptions)) |
| |
| return joinOptions, nil |
| } |
| |
| func (container *Container) buildPortMapInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { |
| if ep == nil { |
| return nil, fmt.Errorf("invalid endpoint while building port map info") |
| } |
| |
| if networkSettings == nil { |
| return nil, fmt.Errorf("invalid networksettings while building port map info") |
| } |
| |
| driverInfo, err := ep.DriverInfo() |
| if err != nil { |
| return nil, err |
| } |
| |
| if driverInfo == nil { |
| // It is not an error for epInfo to be nil |
| return networkSettings, nil |
| } |
| |
| if mac, ok := driverInfo[netlabel.MacAddress]; ok { |
| networkSettings.MacAddress = mac.(net.HardwareAddr).String() |
| } |
| |
| mapData, ok := driverInfo[netlabel.PortMap] |
| if !ok { |
| return networkSettings, nil |
| } |
| |
| if portMapping, ok := mapData.([]types.PortBinding); ok { |
| networkSettings.Ports = nat.PortMap{} |
| for _, pp := range portMapping { |
| natPort := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) |
| natBndg := nat.PortBinding{HostIp: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))} |
| networkSettings.Ports[natPort] = append(networkSettings.Ports[natPort], natBndg) |
| } |
| } |
| |
| return networkSettings, nil |
| } |
| |
| func (container *Container) buildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { |
| if ep == nil { |
| return nil, fmt.Errorf("invalid endpoint while building port map info") |
| } |
| |
| if networkSettings == nil { |
| return nil, fmt.Errorf("invalid networksettings while building port map info") |
| } |
| |
| epInfo := ep.Info() |
| if epInfo == nil { |
| // It is not an error to get an empty endpoint info |
| return networkSettings, nil |
| } |
| |
| ifaceList := epInfo.InterfaceList() |
| if len(ifaceList) == 0 { |
| return networkSettings, nil |
| } |
| |
| iface := ifaceList[0] |
| |
| ones, _ := iface.Address().Mask.Size() |
| networkSettings.IPAddress = iface.Address().IP.String() |
| networkSettings.IPPrefixLen = ones |
| |
| if iface.AddressIPv6().IP.To16() != nil { |
| onesv6, _ := iface.AddressIPv6().Mask.Size() |
| networkSettings.GlobalIPv6Address = iface.AddressIPv6().IP.String() |
| networkSettings.GlobalIPv6PrefixLen = onesv6 |
| } |
| |
| if len(ifaceList) == 1 { |
| return networkSettings, nil |
| } |
| |
| networkSettings.SecondaryIPAddresses = make([]network.Address, 0, len(ifaceList)-1) |
| networkSettings.SecondaryIPv6Addresses = make([]network.Address, 0, len(ifaceList)-1) |
| for _, iface := range ifaceList[1:] { |
| ones, _ := iface.Address().Mask.Size() |
| addr := network.Address{Addr: iface.Address().IP.String(), PrefixLen: ones} |
| networkSettings.SecondaryIPAddresses = append(networkSettings.SecondaryIPAddresses, addr) |
| |
| if iface.AddressIPv6().IP.To16() != nil { |
| onesv6, _ := iface.AddressIPv6().Mask.Size() |
| addrv6 := network.Address{Addr: iface.AddressIPv6().IP.String(), PrefixLen: onesv6} |
| networkSettings.SecondaryIPv6Addresses = append(networkSettings.SecondaryIPv6Addresses, addrv6) |
| } |
| } |
| |
| return networkSettings, nil |
| } |
| |
| func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error { |
| epInfo := ep.Info() |
| if epInfo == nil { |
| // It is not an error to get an empty endpoint info |
| return nil |
| } |
| |
| container.NetworkSettings.Gateway = epInfo.Gateway().String() |
| if epInfo.GatewayIPv6().To16() != nil { |
| container.NetworkSettings.IPv6Gateway = epInfo.GatewayIPv6().String() |
| } |
| |
| container.NetworkSettings.SandboxKey = epInfo.SandboxKey() |
| |
| return nil |
| } |
| |
| func (container *Container) updateNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error { |
| networkSettings := &network.Settings{NetworkID: n.ID(), EndpointID: ep.ID()} |
| |
| networkSettings, err := container.buildPortMapInfo(n, ep, networkSettings) |
| if err != nil { |
| return err |
| } |
| |
| networkSettings, err = container.buildEndpointInfo(n, ep, networkSettings) |
| if err != nil { |
| return err |
| } |
| |
| if container.hostConfig.NetworkMode == runconfig.NetworkMode("bridge") { |
| networkSettings.Bridge = container.daemon.config.Bridge.Iface |
| } |
| |
| container.NetworkSettings = networkSettings |
| return nil |
| } |
| |
| func (container *Container) UpdateNetwork() error { |
| n, err := container.daemon.netController.NetworkByID(container.NetworkSettings.NetworkID) |
| if err != nil { |
| return fmt.Errorf("error locating network id %s: %v", container.NetworkSettings.NetworkID, err) |
| } |
| |
| ep, err := n.EndpointByID(container.NetworkSettings.EndpointID) |
| if err != nil { |
| return fmt.Errorf("error locating endpoint id %s: %v", container.NetworkSettings.EndpointID, err) |
| } |
| |
| if err := ep.Leave(container.ID); err != nil { |
| return fmt.Errorf("endpoint leave failed: %v", err) |
| |
| } |
| |
| joinOptions, err := container.buildJoinOptions() |
| if err != nil { |
| return fmt.Errorf("Update network failed: %v", err) |
| } |
| |
| if _, err := ep.Join(container.ID, joinOptions...); err != nil { |
| return fmt.Errorf("endpoint join failed: %v", err) |
| } |
| |
| if err := container.updateJoinInfo(ep); err != nil { |
| return fmt.Errorf("Updating join info failed: %v", err) |
| } |
| |
| return nil |
| } |
| |
| func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointOption, error) { |
| var ( |
| portSpecs = make(nat.PortSet) |
| bindings = make(nat.PortMap) |
| pbList []types.PortBinding |
| exposeList []types.TransportPort |
| createOptions []libnetwork.EndpointOption |
| ) |
| |
| if container.Config.PortSpecs != nil { |
| if err := migratePortMappings(container.Config, container.hostConfig); err != nil { |
| return nil, err |
| } |
| container.Config.PortSpecs = nil |
| if err := container.WriteHostConfig(); err != nil { |
| return nil, err |
| } |
| } |
| |
| 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, |
| }) |
| } |
| } |
| } |
| |
| container.NetworkSettings.PortMapping = nil |
| |
| 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() |
| pbCopy.HostPort = uint16(nat.Port(binding[i].HostPort).Int()) |
| pbCopy.HostIP = net.ParseIP(binding[i].HostIp) |
| pbList = append(pbList, pbCopy) |
| } |
| |
| if container.hostConfig.PublishAllPorts && len(binding) == 0 { |
| pbList = append(pbList, pb) |
| } |
| } |
| |
| createOptions = append(createOptions, |
| libnetwork.CreateOptionPortMapping(pbList), |
| libnetwork.CreateOptionExposedPorts(exposeList)) |
| |
| if container.Config.MacAddress != "" { |
| mac, err := net.ParseMAC(container.Config.MacAddress) |
| if err != nil { |
| return nil, err |
| } |
| |
| genericOption := options.Generic{ |
| netlabel.MacAddress: mac, |
| } |
| |
| createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) |
| } |
| |
| return createOptions, nil |
| } |
| |
| func (container *Container) AllocateNetwork() error { |
| mode := container.hostConfig.NetworkMode |
| if container.Config.NetworkDisabled || mode.IsContainer() { |
| return nil |
| } |
| |
| var err error |
| |
| n, err := container.daemon.netController.NetworkByName(string(mode)) |
| if err != nil { |
| return fmt.Errorf("error locating network with name %s: %v", string(mode), err) |
| } |
| |
| createOptions, err := container.buildCreateEndpointOptions() |
| if err != nil { |
| return err |
| } |
| |
| ep, err := n.CreateEndpoint(container.Name, createOptions...) |
| if err != nil { |
| return err |
| } |
| |
| if err := container.updateNetworkSettings(n, ep); err != nil { |
| return err |
| } |
| |
| joinOptions, err := container.buildJoinOptions() |
| if err != nil { |
| return err |
| } |
| |
| if _, err := ep.Join(container.ID, joinOptions...); err != nil { |
| return err |
| } |
| |
| if err := container.updateJoinInfo(ep); err != nil { |
| return fmt.Errorf("Updating join info failed: %v", err) |
| } |
| |
| if err := container.WriteHostConfig(); err != nil { |
| return err |
| } |
| |
| return nil |
| } |
| |
| func (container *Container) initializeNetworking() error { |
| var err error |
| |
| // Make sure NetworkMode has an acceptable value before |
| // initializing networking. |
| if container.hostConfig.NetworkMode == runconfig.NetworkMode("") { |
| container.hostConfig.NetworkMode = runconfig.NetworkMode("bridge") |
| } |
| |
| if container.hostConfig.NetworkMode.IsContainer() { |
| // we need to get the hosts files from the container to join |
| nc, err := container.getNetworkedContainer() |
| 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.daemon.config.DisableNetwork { |
| container.Config.NetworkDisabled = true |
| } |
| |
| 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 := container.AllocateNetwork(); err != nil { |
| return err |
| } |
| |
| return container.buildHostnameFile() |
| } |
| |
| // Make sure the config is compatible with the current kernel |
| func (container *Container) verifyDaemonSettings() { |
| if container.hostConfig.Memory > 0 && !container.daemon.sysInfo.MemoryLimit { |
| logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.") |
| container.hostConfig.Memory = 0 |
| } |
| if container.hostConfig.Memory > 0 && container.hostConfig.MemorySwap != -1 && !container.daemon.sysInfo.SwapLimit { |
| logrus.Warnf("Your kernel does not support swap limit capabilities. Limitation discarded.") |
| container.hostConfig.MemorySwap = -1 |
| } |
| if container.daemon.sysInfo.IPv4ForwardingDisabled { |
| logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") |
| } |
| } |
| |
| func (container *Container) ExportRw() (archive.Archive, error) { |
| if err := container.Mount(); err != nil { |
| return nil, err |
| } |
| if container.daemon == nil { |
| return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID) |
| } |
| archive, err := container.daemon.Diff(container) |
| if err != nil { |
| container.Unmount() |
| return nil, err |
| } |
| return ioutils.NewReadCloserWrapper(archive, func() error { |
| err := archive.Close() |
| container.Unmount() |
| return err |
| }), |
| nil |
| } |
| |
| func (container *Container) getIpcContainer() (*Container, error) { |
| containerID := container.hostConfig.IpcMode.Container() |
| c, err := container.daemon.Get(containerID) |
| if err != nil { |
| return nil, err |
| } |
| if !c.IsRunning() { |
| return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID) |
| } |
| return c, nil |
| } |
| |
| func (container *Container) setupWorkingDirectory() error { |
| if container.Config.WorkingDir != "" { |
| 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 := os.MkdirAll(pth, 0755); err != nil { |
| return err |
| } |
| } |
| if pthInfo != nil && !pthInfo.IsDir() { |
| return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir) |
| } |
| } |
| return nil |
| } |
| |
| func (container *Container) getNetworkedContainer() (*Container, error) { |
| parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2) |
| switch parts[0] { |
| case "container": |
| if len(parts) != 2 { |
| return nil, fmt.Errorf("no container specified to join network") |
| } |
| nc, err := container.daemon.Get(parts[1]) |
| if err != nil { |
| return nil, err |
| } |
| if container == nc { |
| return nil, fmt.Errorf("cannot join own network") |
| } |
| if !nc.IsRunning() { |
| 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") |
| } |
| } |
| |
| func (container *Container) ReleaseNetwork() { |
| if container.hostConfig.NetworkMode.IsContainer() || container.daemon.config.DisableNetwork { |
| return |
| } |
| |
| n, err := container.daemon.netController.NetworkByID(container.NetworkSettings.NetworkID) |
| if err != nil { |
| logrus.Errorf("error locating network id %s: %v", container.NetworkSettings.NetworkID, err) |
| return |
| } |
| |
| ep, err := n.EndpointByID(container.NetworkSettings.EndpointID) |
| if err != nil { |
| logrus.Errorf("error locating endpoint id %s: %v", container.NetworkSettings.EndpointID, err) |
| return |
| } |
| |
| if err := ep.Leave(container.ID); err != nil { |
| logrus.Errorf("leaving endpoint failed: %v", err) |
| } |
| |
| if err := ep.Delete(); err != nil { |
| logrus.Errorf("deleting endpoint failed: %v", err) |
| } |
| |
| container.NetworkSettings = &network.Settings{} |
| } |
| |
| func disableAllActiveLinks(container *Container) { |
| if container.activeLinks != nil { |
| for _, link := range container.activeLinks { |
| link.Disable() |
| } |
| } |
| } |
| |
| func (container *Container) DisableLink(name string) { |
| if container.activeLinks != nil { |
| if link, exists := container.activeLinks[name]; exists { |
| link.Disable() |
| delete(container.activeLinks, name) |
| if err := container.UpdateNetwork(); err != nil { |
| logrus.Debugf("Could not update network to remove link: %v", err) |
| } |
| } else { |
| logrus.Debugf("Could not find active link for %s", name) |
| } |
| } |
| } |
| |
| func (container *Container) UnmountVolumes(forceSyscall bool) error { |
| var volumeMounts []mountPoint |
| |
| for _, mntPoint := range container.MountPoints { |
| dest, err := container.GetResourcePath(mntPoint.Destination) |
| if err != nil { |
| return err |
| } |
| |
| volumeMounts = append(volumeMounts, mountPoint{Destination: dest, Volume: mntPoint.Volume}) |
| } |
| |
| for _, mnt := range container.networkMounts() { |
| dest, err := container.GetResourcePath(mnt.Destination) |
| if err != nil { |
| return err |
| } |
| |
| volumeMounts = append(volumeMounts, mountPoint{Destination: dest}) |
| } |
| |
| for _, volumeMount := range volumeMounts { |
| if forceSyscall { |
| syscall.Unmount(volumeMount.Destination, 0) |
| } |
| |
| if volumeMount.Volume != nil { |
| if err := volumeMount.Volume.Unmount(); err != nil { |
| return err |
| } |
| } |
| } |
| |
| return nil |
| } |