Docker integration with libnetwork
- Updated Dockerfile to satisfy libnetwork GOPATH requirements. - Reworked daemon to allocate network resources using libnetwork. - Reworked remove link code to also update network resources in libnetwork. - Adjusted the exec driver command population to reflect libnetwork design. - Adjusted the exec driver create command steps. - Updated a few test cases to reflect the change in design. - Removed the dns setup code from docker as resolv.conf is entirely managed in libnetwork. - Integrated with lxc exec driver. Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
This commit is contained in:
parent
272f8cd4bc
commit
d18919e304
20 changed files with 746 additions and 622 deletions
1
Makefile
1
Makefile
|
@ -9,6 +9,7 @@ DOCKER_ENVS := \
|
|||
-e DOCKER_EXECDRIVER \
|
||||
-e DOCKER_GRAPHDRIVER \
|
||||
-e DOCKER_STORAGE_OPTS \
|
||||
-e DOCKER_USERLANDPROXY \
|
||||
-e TESTDIRS \
|
||||
-e TESTFLAGS \
|
||||
-e TIMEOUT
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/docker/docker/pkg/resolvconf/dns"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libnetwork/resolvconf/dns"
|
||||
)
|
||||
|
||||
func (cid *cidFile) Close() error {
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/daemon/networkdriver/bridge"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
|
@ -36,6 +35,7 @@ import (
|
|||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/libnetwork/portallocator"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
|
@ -1548,8 +1548,9 @@ func allocateDaemonPort(addr string) error {
|
|||
return fmt.Errorf("failed to lookup %s address in host specification", host)
|
||||
}
|
||||
|
||||
pa := portallocator.Get()
|
||||
for _, hostIP := range hostIPs {
|
||||
if _, err := bridge.RequestPort(hostIP, "tcp", intPort); err != nil {
|
||||
if _, err := pa.RequestPort(hostIP, "tcp", intPort); err != nil {
|
||||
return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/daemon/networkdriver"
|
||||
"github.com/docker/docker/daemon/networkdriver/bridge"
|
||||
"net"
|
||||
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/runconfig"
|
||||
|
@ -16,8 +16,9 @@ const (
|
|||
// CommonConfig defines the configuration of a docker daemon which are
|
||||
// common across platforms.
|
||||
type CommonConfig struct {
|
||||
AutoRestart bool
|
||||
Bridge bridge.Config
|
||||
AutoRestart bool
|
||||
// Bridge holds bridge network specific configuration.
|
||||
Bridge bridgeConfig
|
||||
Context map[string][]string
|
||||
CorsHeaders string
|
||||
DisableNetwork bool
|
||||
|
@ -35,6 +36,24 @@ type CommonConfig struct {
|
|||
TrustKeyPath string
|
||||
}
|
||||
|
||||
// bridgeConfig stores all the bridge driver specific
|
||||
// configuration.
|
||||
type bridgeConfig struct {
|
||||
EnableIPv6 bool
|
||||
EnableIPTables bool
|
||||
EnableIPForward bool
|
||||
EnableIPMasq bool
|
||||
EnableUserlandProxy bool
|
||||
DefaultIP net.IP
|
||||
Iface string
|
||||
IP string
|
||||
FixedCIDR string
|
||||
FixedCIDRv6 string
|
||||
DefaultGatewayIPv4 string
|
||||
DefaultGatewayIPv6 string
|
||||
InterContainerCommunication bool
|
||||
}
|
||||
|
||||
// InstallCommonFlags adds command-line options to the top-level flag parser for
|
||||
// the current process.
|
||||
// Subsequent calls to `flag.Parse` will populate config with values parsed
|
||||
|
@ -45,9 +64,9 @@ func (config *Config) InstallCommonFlags() {
|
|||
flag.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, "Root of the Docker runtime")
|
||||
flag.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", "Root of the Docker execdriver")
|
||||
flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run")
|
||||
flag.BoolVar(&config.Bridge.EnableIptables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules")
|
||||
flag.BoolVar(&config.Bridge.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
|
||||
flag.BoolVar(&config.Bridge.EnableIpMasq, []string{"-ip-masq"}, true, "Enable IP masquerading")
|
||||
flag.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules")
|
||||
flag.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
|
||||
flag.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, "Enable IP masquerading")
|
||||
flag.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, "Enable IPv6 networking")
|
||||
flag.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", "Specify network bridge IP")
|
||||
flag.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", "Attach containers to a network bridge")
|
||||
|
@ -61,7 +80,7 @@ func (config *Config) InstallCommonFlags() {
|
|||
flag.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, "Set the containers network MTU")
|
||||
flag.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, "Enable CORS headers in the remote API, this is deprecated by --api-cors-header")
|
||||
flag.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", "Set CORS headers in the remote API")
|
||||
opts.IPVar(&config.Bridge.DefaultIp, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports")
|
||||
opts.IPVar(&config.Bridge.DefaultIP, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports")
|
||||
// FIXME: why the inconsistency between "hosts" and "sockets"?
|
||||
opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use")
|
||||
opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use")
|
||||
|
@ -71,10 +90,3 @@ func (config *Config) InstallCommonFlags() {
|
|||
flag.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, "Use userland proxy for loopback traffic")
|
||||
|
||||
}
|
||||
|
||||
func getDefaultNetworkMtu() int {
|
||||
if iface, err := networkdriver.GetDefaultRouteIface(); err == nil {
|
||||
return iface.MTU
|
||||
}
|
||||
return defaultNetworkMtu
|
||||
}
|
||||
|
|
|
@ -252,18 +252,12 @@ func (container *Container) Start() (err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
if err := container.setupContainerDns(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Mount(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.initializeNetworking(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.updateParentsHosts(); err != nil {
|
||||
return err
|
||||
}
|
||||
container.verifyDaemonSettings()
|
||||
if err := container.prepareVolumes(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -15,20 +17,21 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/daemon/networkdriver/bridge"
|
||||
"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/etchosts"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/resolvconf"
|
||||
"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/netutils"
|
||||
"github.com/docker/libnetwork/options"
|
||||
)
|
||||
|
||||
const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
@ -65,179 +68,6 @@ func killProcessDirectly(container *Container) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) setupContainerDns() error {
|
||||
if container.ResolvConfPath != "" {
|
||||
// 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
|
||||
logrus.Debugf("Check container (%s) for update to resolv.conf - UpdateDns flag was set", container.ID)
|
||||
latestResolvConf, latestHash := resolvconf.GetLastModified()
|
||||
|
||||
// clean container resolv.conf re: localhost nameservers and IPv6 NS (if IPv6 disabled)
|
||||
updatedResolvConf, modified := resolvconf.FilterResolvDns(latestResolvConf, container.daemon.config.Bridge.EnableIPv6)
|
||||
if modified {
|
||||
// changes have occurred during resolv.conf localhost cleanup: generate an updated hash
|
||||
newHash, err := ioutils.HashData(bytes.NewReader(updatedResolvConf))
|
||||
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
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
config = container.hostConfig
|
||||
daemon = container.daemon
|
||||
)
|
||||
|
||||
resolvConf, err := resolvconf.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.NetworkMode.IsBridge() || config.NetworkMode.IsNone() {
|
||||
// check configurations for any container/daemon dns settings
|
||||
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)
|
||||
}
|
||||
|
||||
// replace any localhost/127.*, and remove IPv6 nameservers if IPv6 disabled in daemon
|
||||
resolvConf, _ = resolvconf.FilterResolvDns(resolvConf, daemon.config.Bridge.EnableIPv6)
|
||||
}
|
||||
//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)
|
||||
resolvHash, err := ioutils.HashData(bytes.NewReader(resolvConf))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resolvHashFile := container.ResolvConfPath + ".hash"
|
||||
if err = ioutil.WriteFile(resolvHashFile, []byte(resolvHash), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
curHash, err := ioutils.HashData(bytes.NewReader(resolvBytes))
|
||||
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 {
|
||||
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("")
|
||||
}
|
||||
|
||||
//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 {
|
||||
logrus.Debugf("replacing %q with updated host resolv.conf", container.ResolvConfPath)
|
||||
|
||||
// for atomic updates to these files, use temporary files with os.Rename:
|
||||
dir := filepath.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
|
||||
}
|
||||
|
||||
func (container *Container) updateParentsHosts() error {
|
||||
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)
|
||||
if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, ref.Name); err != nil {
|
||||
logrus.Errorf("Failed to update /etc/hosts in parent container %s for alias %s: %v", c.ID, ref.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) setupLinkedContainers() ([]string, error) {
|
||||
var (
|
||||
env []string
|
||||
|
@ -360,39 +190,16 @@ func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs.
|
|||
|
||||
func populateCommand(c *Container, env []string) error {
|
||||
en := &execdriver.Network{
|
||||
Mtu: c.daemon.config.Mtu,
|
||||
Interface: nil,
|
||||
NamespacePath: c.NetworkSettings.SandboxKey,
|
||||
}
|
||||
|
||||
parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2)
|
||||
switch parts[0] {
|
||||
case "none":
|
||||
case "host":
|
||||
en.HostNetworking = true
|
||||
case "bridge", "": // empty string to support existing containers
|
||||
if !c.Config.NetworkDisabled {
|
||||
network := c.NetworkSettings
|
||||
en.Interface = &execdriver.NetworkInterface{
|
||||
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,
|
||||
HairpinMode: network.HairpinMode,
|
||||
}
|
||||
}
|
||||
case "container":
|
||||
if parts[0] == "container" {
|
||||
nc, err := c.getNetworkedContainer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
en.ContainerID = nc.ID
|
||||
default:
|
||||
return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode)
|
||||
}
|
||||
|
||||
ipc := &execdriver.Ipc{}
|
||||
|
@ -537,40 +344,318 @@ func (container *Container) GetSize() (int64, int64) {
|
|||
return sizeRw, sizeRootfs
|
||||
}
|
||||
|
||||
func (container *Container) AllocateNetwork() error {
|
||||
mode := container.hostConfig.NetworkMode
|
||||
if container.Config.NetworkDisabled || !mode.IsPrivate() {
|
||||
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.([]netutils.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
|
||||
}
|
||||
|
||||
var err error
|
||||
container.NetworkSettings.Gateway = epInfo.Gateway().String()
|
||||
if epInfo.GatewayIPv6().To16() != nil {
|
||||
container.NetworkSettings.IPv6Gateway = epInfo.GatewayIPv6().String()
|
||||
}
|
||||
|
||||
networkSettings, err := bridge.Allocate(container.ID, container.Config.MacAddress, "", "")
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
if container.Config.PortSpecs != nil {
|
||||
if err = migratePortMappings(container.Config, container.hostConfig); err != nil {
|
||||
bridge.Release(container.ID)
|
||||
return err
|
||||
}
|
||||
container.Config.PortSpecs = nil
|
||||
if err = container.WriteHostConfig(); err != nil {
|
||||
bridge.Release(container.ID)
|
||||
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)
|
||||
portSpecs = make(nat.PortSet)
|
||||
bindings = make(nat.PortMap)
|
||||
pbList []netutils.PortBinding
|
||||
exposeList []netutils.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
|
||||
}
|
||||
|
@ -597,52 +682,99 @@ func (container *Container) AllocateNetwork() error {
|
|||
}
|
||||
nat.SortPortMap(ports, bindings)
|
||||
for _, port := range ports {
|
||||
if err = container.allocatePort(port, bindings); err != nil {
|
||||
bridge.Release(container.ID)
|
||||
return err
|
||||
expose := netutils.TransportPort{}
|
||||
expose.Proto = netutils.ParseProtocol(port.Proto())
|
||||
expose.Port = uint16(port.Int())
|
||||
exposeList = append(exposeList, expose)
|
||||
|
||||
pb := netutils.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)
|
||||
}
|
||||
}
|
||||
container.WriteHostConfig()
|
||||
|
||||
networkSettings.Ports = bindings
|
||||
container.NetworkSettings = networkSettings
|
||||
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)
|
||||
}
|
||||
|
||||
container.WriteHostConfig()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) initializeNetworking() error {
|
||||
var err error
|
||||
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]
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile("/etc/hosts")
|
||||
if os.IsNotExist(err) {
|
||||
return container.buildHostnameAndHostsFiles("")
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := container.buildHostnameFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostsPath, err := container.GetRootResourcePath("hosts")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container.HostsPath = hostsPath
|
||||
|
||||
return ioutil.WriteFile(container.HostsPath, content, 0644)
|
||||
// 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()
|
||||
|
@ -656,14 +788,30 @@ func (container *Container) initializeNetworking() error {
|
|||
container.Config.Domainname = nc.Config.Domainname
|
||||
return nil
|
||||
}
|
||||
|
||||
if container.daemon.config.DisableNetwork {
|
||||
container.Config.NetworkDisabled = true
|
||||
return container.buildHostnameAndHostsFiles("127.0.1.1")
|
||||
}
|
||||
|
||||
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.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress)
|
||||
|
||||
return container.buildHostnameFile()
|
||||
}
|
||||
|
||||
// Make sure the config is compatible with the current kernel
|
||||
|
@ -701,62 +849,6 @@ func (container *Container) ExportRw() (archive.Archive, error) {
|
|||
nil
|
||||
}
|
||||
|
||||
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) buildHostsFiles(IP string) error {
|
||||
|
||||
hostsPath, err := container.GetRootResourcePath("hosts")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container.HostsPath = hostsPath
|
||||
|
||||
var extraContent []etchosts.Record
|
||||
|
||||
children, err := container.daemon.Children(container.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for linkAlias, child := range children {
|
||||
_, alias := filepath.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:]
|
||||
}
|
||||
extraContent = append(extraContent, etchosts.Record{Hosts: aliasList, IP: child.NetworkSettings.IPAddress})
|
||||
}
|
||||
|
||||
for _, extraHost := range container.hostConfig.ExtraHosts {
|
||||
// allow IPv6 addresses in extra hosts; only split on first ":"
|
||||
parts := strings.SplitN(extraHost, ":", 2)
|
||||
extraContent = append(extraContent, etchosts.Record{Hosts: parts[0], IP: parts[1]})
|
||||
}
|
||||
|
||||
return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, extraContent)
|
||||
}
|
||||
|
||||
func (container *Container) buildHostnameAndHostsFiles(IP string) error {
|
||||
if err := container.buildHostnameFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return container.buildHostsFiles(IP)
|
||||
}
|
||||
|
||||
func (container *Container) getIpcContainer() (*Container, error) {
|
||||
containerID := container.hostConfig.IpcMode.Container()
|
||||
c, err := container.daemon.Get(containerID)
|
||||
|
@ -795,23 +887,6 @@ func (container *Container) setupWorkingDirectory() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) allocatePort(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++ {
|
||||
b, err := bridge.AllocatePort(container.ID, port, binding[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binding[i] = b
|
||||
}
|
||||
bindings[port] = binding
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) getNetworkedContainer() (*Container, error) {
|
||||
parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2)
|
||||
switch parts[0] {
|
||||
|
@ -836,36 +911,33 @@ func (container *Container) getNetworkedContainer() (*Container, error) {
|
|||
}
|
||||
|
||||
func (container *Container) ReleaseNetwork() {
|
||||
if container.Config.NetworkDisabled || !container.hostConfig.NetworkMode.IsPrivate() {
|
||||
if container.hostConfig.NetworkMode.IsContainer() {
|
||||
return
|
||||
}
|
||||
bridge.Release(container.ID)
|
||||
|
||||
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 (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
|
||||
}
|
||||
|
||||
// Re-allocate the interface with the same IP and MAC address.
|
||||
if _, err := bridge.Allocate(container.ID, container.NetworkSettings.MacAddress, container.NetworkSettings.IPAddress, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Re-allocate any previously allocated ports.
|
||||
for port := range container.NetworkSettings.Ports {
|
||||
if err := container.allocatePort(port, container.NetworkSettings.Ports); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func disableAllActiveLinks(container *Container) {
|
||||
if container.activeLinks != nil {
|
||||
for _, link := range container.activeLinks {
|
||||
|
@ -878,6 +950,10 @@ 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)
|
||||
}
|
||||
|
|
172
daemon/daemon.go
172
daemon/daemon.go
|
@ -1,10 +1,10 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
@ -15,6 +15,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/libcontainer/label"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api"
|
||||
|
@ -26,7 +29,6 @@ import (
|
|||
_ "github.com/docker/docker/daemon/graphdriver/vfs"
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/daemon/networkdriver/bridge"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
|
@ -37,7 +39,6 @@ import (
|
|||
"github.com/docker/docker/pkg/namesgenerator"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/docker/docker/pkg/resolvconf"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/sysinfo"
|
||||
"github.com/docker/docker/pkg/truncindex"
|
||||
|
@ -46,8 +47,6 @@ import (
|
|||
"github.com/docker/docker/trust"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/docker/volumes"
|
||||
|
||||
"github.com/go-fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -109,6 +108,7 @@ type Daemon struct {
|
|||
defaultLogConfig runconfig.LogConfig
|
||||
RegistryService *registry.Service
|
||||
EventsService *events.Events
|
||||
netController libnetwork.NetworkController
|
||||
}
|
||||
|
||||
// Get looks for a container using the provided information, which could be
|
||||
|
@ -349,61 +349,6 @@ func (daemon *Daemon) restore() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// set up the watch on the host's /etc/resolv.conf so that we can update container's
|
||||
// live resolv.conf when the network changes on the host
|
||||
func (daemon *Daemon) setupResolvconfWatcher() error {
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//this goroutine listens for the events on the watch we add
|
||||
//on the resolv.conf file on the host
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-watcher.Events:
|
||||
if event.Name == "/etc/resolv.conf" &&
|
||||
(event.Op&(fsnotify.Write|fsnotify.Create) != 0) {
|
||||
// verify a real change happened before we go further--a file write may have happened
|
||||
// without an actual change to the file
|
||||
updatedResolvConf, newResolvConfHash, err := resolvconf.GetIfChanged()
|
||||
if err != nil {
|
||||
logrus.Debugf("Error retrieving updated host resolv.conf: %v", err)
|
||||
} else if updatedResolvConf != nil {
|
||||
// because the new host resolv.conf might have localhost nameservers..
|
||||
updatedResolvConf, modified := resolvconf.FilterResolvDns(updatedResolvConf, daemon.config.Bridge.EnableIPv6)
|
||||
if modified {
|
||||
// changes have occurred during localhost cleanup: generate an updated hash
|
||||
newHash, err := ioutils.HashData(bytes.NewReader(updatedResolvConf))
|
||||
if err != nil {
|
||||
logrus.Debugf("Error generating hash of new resolv.conf: %v", err)
|
||||
} else {
|
||||
newResolvConfHash = newHash
|
||||
}
|
||||
}
|
||||
logrus.Debug("host network resolv.conf changed--walking container list for updates")
|
||||
contList := daemon.containers.List()
|
||||
for _, container := range contList {
|
||||
if err := container.updateResolvConf(updatedResolvConf, newResolvConfHash); err != nil {
|
||||
logrus.Debugf("Error on resolv.conf update check for container ID: %s: %v", container.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
logrus.Debugf("host resolv.conf notify error: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := watcher.Add("/etc"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) checkDeprecatedExpose(config *runconfig.Config) bool {
|
||||
if config != nil {
|
||||
if config.PortSpecs != nil {
|
||||
|
@ -727,18 +672,15 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.
|
|||
}
|
||||
|
||||
func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemon, err error) {
|
||||
if config.Mtu == 0 {
|
||||
config.Mtu = getDefaultNetworkMtu()
|
||||
}
|
||||
// Check for mutually incompatible config options
|
||||
if config.Bridge.Iface != "" && config.Bridge.IP != "" {
|
||||
return nil, fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.")
|
||||
}
|
||||
if !config.Bridge.EnableIptables && !config.Bridge.InterContainerCommunication {
|
||||
if !config.Bridge.EnableIPTables && !config.Bridge.InterContainerCommunication {
|
||||
return nil, fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.")
|
||||
}
|
||||
if !config.Bridge.EnableIptables && config.Bridge.EnableIpMasq {
|
||||
config.Bridge.EnableIpMasq = false
|
||||
if !config.Bridge.EnableIPTables && config.Bridge.EnableIPMasq {
|
||||
config.Bridge.EnableIPMasq = false
|
||||
}
|
||||
config.DisableNetwork = config.Bridge.Iface == disableNetworkBridge
|
||||
|
||||
|
@ -882,8 +824,9 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
|||
}
|
||||
|
||||
if !config.DisableNetwork {
|
||||
if err := bridge.InitDriver(&config.Bridge); err != nil {
|
||||
return nil, fmt.Errorf("Error initializing Bridge: %v", err)
|
||||
d.netController, err = initNetworkController(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error initializing network controller: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -942,12 +885,97 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// set up filesystem watch on resolv.conf for network changes
|
||||
if err := d.setupResolvconfWatcher(); err != nil {
|
||||
return nil, err
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func initNetworkController(config *Config) (libnetwork.NetworkController, error) {
|
||||
controller, err := libnetwork.New()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error obtaining controller instance: %v", err)
|
||||
}
|
||||
|
||||
return d, nil
|
||||
// Initialize default driver "null"
|
||||
|
||||
if err := controller.ConfigureNetworkDriver("null", options.Generic{}); err != nil {
|
||||
return nil, fmt.Errorf("Error initializing null driver: %v", err)
|
||||
}
|
||||
|
||||
// Initialize default network on "null"
|
||||
if _, err := controller.NewNetwork("null", "none"); err != nil {
|
||||
return nil, fmt.Errorf("Error creating default \"null\" network: %v", err)
|
||||
}
|
||||
|
||||
// Initialize default driver "host"
|
||||
if err := controller.ConfigureNetworkDriver("host", options.Generic{}); err != nil {
|
||||
return nil, fmt.Errorf("Error initializing host driver: %v", err)
|
||||
}
|
||||
|
||||
// Initialize default network on "host"
|
||||
if _, err := controller.NewNetwork("host", "host"); err != nil {
|
||||
return nil, fmt.Errorf("Error creating default \"host\" network: %v", err)
|
||||
}
|
||||
|
||||
// Initialize default driver "bridge"
|
||||
option := options.Generic{
|
||||
"EnableIPForwarding": config.Bridge.EnableIPForward}
|
||||
|
||||
if err := controller.ConfigureNetworkDriver("bridge", options.Generic{netlabel.GenericData: option}); err != nil {
|
||||
return nil, fmt.Errorf("Error initializing bridge driver: %v", err)
|
||||
}
|
||||
|
||||
netOption := options.Generic{
|
||||
"BridgeName": config.Bridge.Iface,
|
||||
"Mtu": config.Mtu,
|
||||
"EnableIPTables": config.Bridge.EnableIPTables,
|
||||
"EnableIPMasquerade": config.Bridge.EnableIPMasq,
|
||||
"EnableICC": config.Bridge.InterContainerCommunication,
|
||||
"EnableUserlandProxy": config.Bridge.EnableUserlandProxy,
|
||||
}
|
||||
|
||||
if config.Bridge.IP != "" {
|
||||
ip, bipNet, err := net.ParseCIDR(config.Bridge.IP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bipNet.IP = ip
|
||||
netOption["AddressIPv4"] = bipNet
|
||||
}
|
||||
|
||||
if config.Bridge.FixedCIDR != "" {
|
||||
_, fCIDR, err := net.ParseCIDR(config.Bridge.FixedCIDR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
netOption["FixedCIDR"] = fCIDR
|
||||
}
|
||||
|
||||
if config.Bridge.FixedCIDRv6 != "" {
|
||||
_, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
netOption["FixedCIDRv6"] = fCIDRv6
|
||||
}
|
||||
|
||||
// --ip processing
|
||||
if config.Bridge.DefaultIP != nil {
|
||||
netOption["DefaultBindingIP"] = config.Bridge.DefaultIP
|
||||
}
|
||||
|
||||
// Initialize default network on "bridge" with the same name
|
||||
_, err = controller.NewNetwork("bridge", "bridge",
|
||||
libnetwork.NetworkOptionGeneric(options.Generic{
|
||||
netlabel.GenericData: netOption,
|
||||
netlabel.EnableIPv6: config.Bridge.EnableIPv6,
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating default \"bridge\" network: %v", err)
|
||||
}
|
||||
|
||||
return controller, nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) Shutdown() error {
|
||||
|
|
|
@ -35,13 +35,14 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error
|
|||
}
|
||||
parentContainer, _ := daemon.Get(pe.ID())
|
||||
|
||||
if err := daemon.ContainerGraph().Delete(name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parentContainer != nil {
|
||||
parentContainer.DisableLink(n)
|
||||
}
|
||||
|
||||
if err := daemon.ContainerGraph().Delete(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ type Network struct {
|
|||
Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
|
||||
Mtu int `json:"mtu"`
|
||||
ContainerID string `json:"container_id"` // id of the container to join network.
|
||||
NamespacePath string `json:"namespace_path"`
|
||||
HostNetworking bool `json:"host_networking"`
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -30,6 +31,7 @@ import (
|
|||
"github.com/docker/libcontainer/system"
|
||||
"github.com/docker/libcontainer/user"
|
||||
"github.com/kr/pty"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
const DriverName = "lxc"
|
||||
|
@ -80,6 +82,41 @@ func (d *driver) Name() string {
|
|||
return fmt.Sprintf("%s-%s", DriverName, version)
|
||||
}
|
||||
|
||||
func setupNetNs(nsPath string) (*os.Process, error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
origns, err := netns.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer origns.Close()
|
||||
|
||||
f, err := os.OpenFile(nsPath, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get network namespace %q: %v", nsPath, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
nsFD := f.Fd()
|
||||
if err := netns.Set(netns.NsHandle(nsFD)); err != nil {
|
||||
return nil, fmt.Errorf("failed to set network namespace %q: %v", nsPath, err)
|
||||
}
|
||||
defer netns.Set(origns)
|
||||
|
||||
cmd := exec.Command("/bin/sh", "-c", "while true; do sleep 1; done")
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("failed to start netns process: %v", err)
|
||||
}
|
||||
|
||||
return cmd.Process, nil
|
||||
}
|
||||
|
||||
func killNetNsProc(proc *os.Process) {
|
||||
proc.Kill()
|
||||
proc.Wait()
|
||||
}
|
||||
|
||||
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
|
||||
var (
|
||||
term execdriver.Terminal
|
||||
|
@ -87,6 +124,10 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
dataPath = d.containerDir(c.ID)
|
||||
)
|
||||
|
||||
if c.Network.NamespacePath == "" && c.Network.ContainerID == "" {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("empty namespace path for non-container network")
|
||||
}
|
||||
|
||||
container, err := d.createContainer(c)
|
||||
if err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
|
@ -136,10 +177,20 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
params = append(params, "-F")
|
||||
}
|
||||
|
||||
proc := &os.Process{}
|
||||
if c.Network.ContainerID != "" {
|
||||
params = append(params,
|
||||
"--share-net", c.Network.ContainerID,
|
||||
)
|
||||
} else {
|
||||
proc, err = setupNetNs(c.Network.NamespacePath)
|
||||
if err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
pidStr := fmt.Sprintf("%d", proc.Pid)
|
||||
params = append(params,
|
||||
"--share-net", pidStr)
|
||||
}
|
||||
if c.Ipc != nil {
|
||||
if c.Ipc.ContainerID != "" {
|
||||
|
@ -157,15 +208,6 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
"--",
|
||||
c.InitPath,
|
||||
)
|
||||
if c.Network.Interface != nil {
|
||||
params = append(params,
|
||||
"-g", c.Network.Interface.Gateway,
|
||||
"-i", fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
|
||||
)
|
||||
}
|
||||
params = append(params,
|
||||
"-mtu", strconv.Itoa(c.Network.Mtu),
|
||||
)
|
||||
|
||||
if c.ProcessConfig.User != "" {
|
||||
params = append(params, "-u", c.ProcessConfig.User)
|
||||
|
@ -214,10 +256,12 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
c.ProcessConfig.Args = append([]string{name}, arg...)
|
||||
|
||||
if err := createDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil {
|
||||
killNetNsProc(proc)
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
if err := c.ProcessConfig.Start(); err != nil {
|
||||
killNetNsProc(proc)
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
|
@ -245,8 +289,10 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
// Poll lxc for RUNNING status
|
||||
pid, err := d.waitForStart(c, waitLock)
|
||||
if err != nil {
|
||||
killNetNsProc(proc)
|
||||
return terminate(err)
|
||||
}
|
||||
killNetNsProc(proc)
|
||||
|
||||
cgroupPaths, err := cgroupPaths(c.ID)
|
||||
if err != nil {
|
||||
|
|
|
@ -15,8 +15,7 @@ import (
|
|||
"github.com/docker/libcontainer/label"
|
||||
)
|
||||
|
||||
const LxcTemplate = `
|
||||
{{if .Network.Interface}}
|
||||
/* {{if .Network.Interface}}
|
||||
# network configuration
|
||||
lxc.network.type = veth
|
||||
lxc.network.link = {{.Network.Interface.Bridge}}
|
||||
|
@ -30,8 +29,10 @@ lxc.network.type = none
|
|||
lxc.network.type = empty
|
||||
lxc.network.flags = up
|
||||
lxc.network.mtu = {{.Network.Mtu}}
|
||||
{{end}}
|
||||
{{end}} */
|
||||
|
||||
const LxcTemplate = `
|
||||
lxc.network.type = none
|
||||
# root filesystem
|
||||
{{$ROOTFS := .Rootfs}}
|
||||
lxc.rootfs = {{$ROOTFS}}
|
||||
|
@ -145,6 +146,7 @@ lxc.network.ipv4.gateway = {{.Network.Interface.Gateway}}
|
|||
{{if .Network.Interface.MacAddress}}
|
||||
lxc.network.hwaddr = {{.Network.Interface.MacAddress}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .ProcessConfig.Env}}
|
||||
lxc.utsname = {{getHostname .ProcessConfig.Env}}
|
||||
{{end}}
|
||||
|
@ -164,7 +166,6 @@ lxc.cap.drop = {{.}}
|
|||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
var LxcTemplateCompiled *template.Template
|
||||
|
|
|
@ -264,13 +264,8 @@ func TestCustomLxcConfigMisc(t *testing.T) {
|
|||
"lxc.cgroup.cpuset.cpus = 0,1",
|
||||
},
|
||||
Network: &execdriver.Network{
|
||||
Mtu: 1500,
|
||||
Interface: &execdriver.NetworkInterface{
|
||||
Gateway: "10.10.10.1",
|
||||
IPAddress: "10.10.10.10",
|
||||
IPPrefixLen: 24,
|
||||
Bridge: "docker0",
|
||||
},
|
||||
Mtu: 1500,
|
||||
Interface: nil,
|
||||
},
|
||||
ProcessConfig: processConfig,
|
||||
CapAdd: []string{"net_admin", "syslog"},
|
||||
|
@ -282,13 +277,6 @@ func TestCustomLxcConfigMisc(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// network
|
||||
grepFile(t, p, "lxc.network.type = veth")
|
||||
grepFile(t, p, "lxc.network.link = docker0")
|
||||
grepFile(t, p, "lxc.network.name = eth0")
|
||||
grepFile(t, p, "lxc.network.ipv4 = 10.10.10.10/24")
|
||||
grepFile(t, p, "lxc.network.ipv4.gateway = 10.10.10.1")
|
||||
grepFile(t, p, "lxc.network.flags = up")
|
||||
grepFile(t, p, "lxc.aa_profile = lxc-container-default-with-nesting")
|
||||
// hostname
|
||||
grepFile(t, p, "lxc.utsname = testhost")
|
||||
|
@ -329,13 +317,8 @@ func TestCustomLxcConfigMiscOverride(t *testing.T) {
|
|||
"lxc.network.ipv4 = 172.0.0.1",
|
||||
},
|
||||
Network: &execdriver.Network{
|
||||
Mtu: 1500,
|
||||
Interface: &execdriver.NetworkInterface{
|
||||
Gateway: "10.10.10.1",
|
||||
IPAddress: "10.10.10.10",
|
||||
IPPrefixLen: 24,
|
||||
Bridge: "docker0",
|
||||
},
|
||||
Mtu: 1500,
|
||||
Interface: nil,
|
||||
},
|
||||
ProcessConfig: processConfig,
|
||||
CapAdd: []string{"NET_ADMIN", "SYSLOG"},
|
||||
|
@ -346,13 +329,6 @@ func TestCustomLxcConfigMiscOverride(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// network
|
||||
grepFile(t, p, "lxc.network.type = veth")
|
||||
grepFile(t, p, "lxc.network.link = docker0")
|
||||
grepFile(t, p, "lxc.network.name = eth0")
|
||||
grepFile(t, p, "lxc.network.ipv4 = 172.0.0.1")
|
||||
grepFile(t, p, "lxc.network.ipv4.gateway = 10.10.10.1")
|
||||
grepFile(t, p, "lxc.network.flags = up")
|
||||
|
||||
// hostname
|
||||
grepFile(t, p, "lxc.utsname = testhost")
|
||||
|
|
|
@ -89,40 +89,6 @@ func generateIfaceName() (string, error) {
|
|||
}
|
||||
|
||||
func (d *driver) createNetwork(container *configs.Config, c *execdriver.Command) error {
|
||||
if c.Network.HostNetworking {
|
||||
container.Namespaces.Remove(configs.NEWNET)
|
||||
return nil
|
||||
}
|
||||
|
||||
container.Networks = []*configs.Network{
|
||||
{
|
||||
Type: "loopback",
|
||||
},
|
||||
}
|
||||
|
||||
iName, err := generateIfaceName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Network.Interface != nil {
|
||||
vethNetwork := configs.Network{
|
||||
Name: "eth0",
|
||||
HostInterfaceName: iName,
|
||||
Mtu: c.Network.Mtu,
|
||||
Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
|
||||
MacAddress: c.Network.Interface.MacAddress,
|
||||
Gateway: c.Network.Interface.Gateway,
|
||||
Type: "veth",
|
||||
Bridge: c.Network.Interface.Bridge,
|
||||
HairpinMode: c.Network.Interface.HairpinMode,
|
||||
}
|
||||
if c.Network.Interface.GlobalIPv6Address != "" {
|
||||
vethNetwork.IPv6Address = fmt.Sprintf("%s/%d", c.Network.Interface.GlobalIPv6Address, c.Network.Interface.GlobalIPv6PrefixLen)
|
||||
vethNetwork.IPv6Gateway = c.Network.Interface.IPv6Gateway
|
||||
}
|
||||
container.Networks = append(container.Networks, &vethNetwork)
|
||||
}
|
||||
|
||||
if c.Network.ContainerID != "" {
|
||||
d.Lock()
|
||||
active := d.activeContainers[c.Network.ContainerID]
|
||||
|
@ -138,8 +104,14 @@ func (d *driver) createNetwork(container *configs.Config, c *execdriver.Command)
|
|||
}
|
||||
|
||||
container.Namespaces.Add(configs.NEWNET, state.NamespacePaths[configs.NEWNET])
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.Network.NamespacePath == "" {
|
||||
return fmt.Errorf("network namespace path is empty")
|
||||
}
|
||||
|
||||
container.Namespaces.Add(configs.NEWNET, c.Network.NamespacePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -2,18 +2,28 @@ package network
|
|||
|
||||
import "github.com/docker/docker/nat"
|
||||
|
||||
type Address struct {
|
||||
Addr string
|
||||
PrefixLen int
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
IPAddress string
|
||||
IPPrefixLen int
|
||||
MacAddress string
|
||||
LinkLocalIPv6Address string
|
||||
LinkLocalIPv6PrefixLen int
|
||||
Bridge string
|
||||
EndpointID string
|
||||
Gateway string
|
||||
GlobalIPv6Address string
|
||||
GlobalIPv6PrefixLen int
|
||||
Gateway string
|
||||
HairpinMode bool
|
||||
IPAddress string
|
||||
IPPrefixLen int
|
||||
IPv6Gateway string
|
||||
Bridge string
|
||||
LinkLocalIPv6Address string
|
||||
LinkLocalIPv6PrefixLen int
|
||||
MacAddress string
|
||||
NetworkID string
|
||||
PortMapping map[string]map[string]string // Deprecated
|
||||
Ports nat.PortMap
|
||||
HairpinMode bool
|
||||
SandboxKey string
|
||||
SecondaryIPAddresses []Address
|
||||
SecondaryIPv6Addresses []Address
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
"github.com/docker/docker/pkg/reexec"
|
||||
)
|
||||
|
||||
const userlandProxyCommandName = "docker-proxy"
|
||||
const userlandProxyCommandName = "docker-proxy-deprecated"
|
||||
|
||||
func init() {
|
||||
reexec.Register(userlandProxyCommandName, execProxy)
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/libnetwork/iptables"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
@ -721,13 +722,49 @@ func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) {
|
|||
c.Assert(matched, check.Equals, true,
|
||||
check.Commentf("iptables output should have contained %q, but was %q", regex, out))
|
||||
|
||||
_, err = d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567")
|
||||
c.Assert(err, check.IsNil)
|
||||
out, err = d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
|
||||
out, err = d.Cmd("run", "--link", "icc1:icc1", "busybox", "nc", "icc1", "4567")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *check.C) {
|
||||
bridgeName := "external-bridge"
|
||||
bridgeIp := "192.169.1.1/24"
|
||||
|
||||
out, err := createInterface(c, "bridge", bridgeName, bridgeIp)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
defer deleteInterface(c, bridgeName)
|
||||
|
||||
args := []string{"--bridge", bridgeName, "--icc=false"}
|
||||
err = s.d.StartWithBusybox(args...)
|
||||
c.Assert(err, check.IsNil)
|
||||
defer s.d.Restart()
|
||||
|
||||
_, err = s.d.Cmd("run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top")
|
||||
c.Assert(err, check.IsNil)
|
||||
_, err = s.d.Cmd("run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top")
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
childIP := s.d.findContainerIP("child")
|
||||
parentIP := s.d.findContainerIP("parent")
|
||||
|
||||
sourceRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
|
||||
destinationRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
|
||||
if !iptables.Exists("filter", "DOCKER", sourceRule...) || !iptables.Exists("filter", "DOCKER", destinationRule...) {
|
||||
c.Fatal("Iptables rules not found")
|
||||
}
|
||||
|
||||
s.d.Cmd("rm", "--link", "parent/http")
|
||||
if iptables.Exists("filter", "DOCKER", sourceRule...) || iptables.Exists("filter", "DOCKER", destinationRule...) {
|
||||
c.Fatal("Iptables rules should be removed when unlink")
|
||||
}
|
||||
|
||||
s.d.Cmd("kill", "child")
|
||||
s.d.Cmd("kill", "parent")
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) {
|
||||
testRequires(c, NativeExecDriver)
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/iptables"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
|
@ -110,31 +109,6 @@ func (s *DockerSuite) TestLinksPingLinkedContainersAfterRename(c *check.C) {
|
|||
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestLinksIpTablesRulesWhenLinkAndUnlink(c *check.C) {
|
||||
testRequires(c, SameHostDaemon)
|
||||
|
||||
dockerCmd(c, "run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top")
|
||||
dockerCmd(c, "run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top")
|
||||
|
||||
childIP := findContainerIP(c, "child")
|
||||
parentIP := findContainerIP(c, "parent")
|
||||
|
||||
sourceRule := []string{"-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
|
||||
destinationRule := []string{"-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
|
||||
if !iptables.Exists("filter", "DOCKER", sourceRule...) || !iptables.Exists("filter", "DOCKER", destinationRule...) {
|
||||
c.Fatal("Iptables rules not found")
|
||||
}
|
||||
|
||||
dockerCmd(c, "rm", "--link", "parent/http")
|
||||
if iptables.Exists("filter", "DOCKER", sourceRule...) || iptables.Exists("filter", "DOCKER", destinationRule...) {
|
||||
c.Fatal("Iptables rules should be removed when unlink")
|
||||
}
|
||||
|
||||
dockerCmd(c, "kill", "child")
|
||||
dockerCmd(c, "kill", "parent")
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestLinksInspectLinksStarted(c *check.C) {
|
||||
var (
|
||||
expected = map[string]struct{}{"/container1:/testinspectlink/alias1": {}, "/container2:/testinspectlink/alias2": {}}
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/docker/nat"
|
||||
"github.com/docker/docker/pkg/resolvconf"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
|
@ -1459,14 +1459,11 @@ func (s *DockerSuite) TestRunDnsOptionsBasedOnHostResolvConf(c *check.C) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test the file watch notifier on docker host's /etc/resolv.conf
|
||||
// A go-routine is responsible for auto-updating containers which are
|
||||
// stopped and have an unmodified copy of resolv.conf, as well as
|
||||
// marking running containers as requiring an update on next restart
|
||||
func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
|
||||
// Because overlay doesn't support inotify properly, we need to skip
|
||||
// this test if the docker daemon has Storage Driver == overlay
|
||||
testRequires(c, SameHostDaemon, NotOverlay)
|
||||
// Test if container resolv.conf gets updated the next time it restarts
|
||||
// if host /etc/resolv.conf has changed. This only applies if the container
|
||||
// uses the host's /etc/resolv.conf and does not have any dns options provided.
|
||||
func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
|
||||
testRequires(c, SameHostDaemon)
|
||||
|
||||
tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78")
|
||||
tmpLocalhostResolvConf := []byte("nameserver 127.0.0.1")
|
||||
|
@ -1492,7 +1489,7 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
|
|||
}
|
||||
}()
|
||||
|
||||
//1. test that a non-running container gets an updated resolv.conf
|
||||
//1. test that a restarting container gets an updated resolv.conf
|
||||
cmd = exec.Command(dockerBinary, "run", "--name='first'", "busybox", "true")
|
||||
if _, err := runCommand(cmd); err != nil {
|
||||
c.Fatal(err)
|
||||
|
@ -1508,17 +1505,26 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
|
|||
c.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
// start the container again to pickup changes
|
||||
cmd = exec.Command(dockerBinary, "start", "first")
|
||||
if out, err := runCommand(cmd); err != nil {
|
||||
c.Fatalf("Errored out %s, \nerror: %v", string(out), err)
|
||||
}
|
||||
|
||||
// check for update in container
|
||||
containerResolv, err := readContainerFile(containerID1, "resolv.conf")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(containerResolv, bytesResolvConf) {
|
||||
c.Fatalf("Stopped container does not have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv))
|
||||
c.Fatalf("Restarted container does not have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv))
|
||||
}
|
||||
|
||||
//2. test that a non-running container does not receive resolv.conf updates
|
||||
/* //make a change to resolv.conf (in this case replacing our tmp copy with orig copy)
|
||||
if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
|
||||
c.Fatal(err)
|
||||
} */
|
||||
//2. test that a restarting container does not receive resolv.conf updates
|
||||
// if it modified the container copy of the starting point resolv.conf
|
||||
cmd = exec.Command(dockerBinary, "run", "--name='second'", "busybox", "sh", "-c", "echo 'search mylittlepony.com' >>/etc/resolv.conf")
|
||||
if _, err = runCommand(cmd); err != nil {
|
||||
|
@ -1528,24 +1534,26 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
|
|||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
containerResolvHashBefore, err := readContainerFile(containerID2, "resolv.conf.hash")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
//make a change to resolv.conf (in this case replacing our tmp copy with orig copy)
|
||||
if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
containerResolvHashAfter, err := readContainerFile(containerID2, "resolv.conf.hash")
|
||||
// start the container again
|
||||
cmd = exec.Command(dockerBinary, "start", "second")
|
||||
if out, err := runCommand(cmd); err != nil {
|
||||
c.Fatalf("Errored out %s, \nerror: %v", string(out), err)
|
||||
}
|
||||
|
||||
// check for update in container
|
||||
containerResolv, err = readContainerFile(containerID2, "resolv.conf")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(containerResolvHashBefore, containerResolvHashAfter) {
|
||||
c.Fatalf("Stopped container with modified resolv.conf should not have been updated; expected hash: %v, new hash: %v", containerResolvHashBefore, containerResolvHashAfter)
|
||||
if bytes.Equal(containerResolv, resolvConfSystem) {
|
||||
c.Fatalf("Restarting a container after container updated resolv.conf should not pick up host changes; expected %q, got %q", string(containerResolv), string(resolvConfSystem))
|
||||
}
|
||||
|
||||
//3. test that a running container's resolv.conf is not modified while running
|
||||
|
@ -1556,26 +1564,19 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
|
|||
}
|
||||
runningContainerID := strings.TrimSpace(out)
|
||||
|
||||
containerResolvHashBefore, err = readContainerFile(runningContainerID, "resolv.conf.hash")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// replace resolv.conf
|
||||
if err := ioutil.WriteFile("/etc/resolv.conf", bytesResolvConf, 0644); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// make sure the updater has time to run to validate we really aren't
|
||||
// getting updated
|
||||
time.Sleep(time.Second / 2)
|
||||
containerResolvHashAfter, err = readContainerFile(runningContainerID, "resolv.conf.hash")
|
||||
// check for update in container
|
||||
containerResolv, err = readContainerFile(runningContainerID, "resolv.conf")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(containerResolvHashBefore, containerResolvHashAfter) {
|
||||
c.Fatalf("Running container's resolv.conf should not be updated; expected hash: %v, new hash: %v", containerResolvHashBefore, containerResolvHashAfter)
|
||||
if bytes.Equal(containerResolv, bytesResolvConf) {
|
||||
c.Fatalf("Running container should not have updated resolv.conf; expected %q, got %q", string(resolvConfSystem), string(containerResolv))
|
||||
}
|
||||
|
||||
//4. test that a running container's resolv.conf is updated upon restart
|
||||
|
@ -1591,7 +1592,7 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
|
|||
c.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(containerResolv, bytesResolvConf) {
|
||||
c.Fatalf("Restarted container should have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv))
|
||||
c.Fatalf("Restarted container should have updated resolv.conf; expected %q, got %q", string(bytesResolvConf), string(containerResolv))
|
||||
}
|
||||
|
||||
//5. test that additions of a localhost resolver are cleaned from
|
||||
|
@ -1603,7 +1604,12 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
|
|||
c.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
// start the container again to pickup changes
|
||||
cmd = exec.Command(dockerBinary, "start", "first")
|
||||
if out, err := runCommand(cmd); err != nil {
|
||||
c.Fatalf("Errored out %s, \nerror: %v", string(out), err)
|
||||
}
|
||||
|
||||
// our first exited container ID should have been updated, but with default DNS
|
||||
// after the cleanup of resolv.conf found only a localhost nameserver:
|
||||
containerResolv, err = readContainerFile(containerID1, "resolv.conf")
|
||||
|
@ -1645,7 +1651,12 @@ func (s *DockerSuite) TestRunResolvconfUpdater(c *check.C) {
|
|||
c.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
// start the container again to pickup changes
|
||||
cmd = exec.Command(dockerBinary, "start", "third")
|
||||
if out, err := runCommand(cmd); err != nil {
|
||||
c.Fatalf("Errored out %s, \nerror: %v", string(out), err)
|
||||
}
|
||||
|
||||
// check for update in container
|
||||
containerResolv, err = readContainerFile(containerID3, "resolv.conf")
|
||||
if err != nil {
|
||||
|
|
|
@ -5,9 +5,7 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/daemon/networkdriver/bridge"
|
||||
"github.com/docker/docker/nat"
|
||||
"github.com/docker/docker/pkg/iptables"
|
||||
)
|
||||
|
||||
type Link struct {
|
||||
|
@ -140,26 +138,10 @@ func (l *Link) getDefaultPort() *nat.Port {
|
|||
}
|
||||
|
||||
func (l *Link) Enable() error {
|
||||
// -A == iptables append flag
|
||||
if err := l.toggle("-A", false); err != nil {
|
||||
return err
|
||||
}
|
||||
// call this on Firewalld reload
|
||||
iptables.OnReloaded(func() { l.toggle("-A", false) })
|
||||
l.IsEnabled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Link) Disable() {
|
||||
// We do not care about errors here because the link may not
|
||||
// exist in iptables
|
||||
// -D == iptables delete flag
|
||||
l.toggle("-D", true)
|
||||
// call this on Firewalld reload
|
||||
iptables.OnReloaded(func() { l.toggle("-D", true) })
|
||||
l.IsEnabled = false
|
||||
}
|
||||
|
||||
func (l *Link) toggle(action string, ignoreErrors bool) error {
|
||||
return bridge.LinkContainers(action, l.ParentIP, l.ChildIP, l.Ports, ignoreErrors)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ type NetworkMode string
|
|||
|
||||
// IsPrivate indicates whether container use it's private network stack
|
||||
func (n NetworkMode) IsPrivate() bool {
|
||||
return !(n.IsHost() || n.IsContainer() || n.IsNone())
|
||||
return !(n.IsHost() || n.IsContainer())
|
||||
}
|
||||
|
||||
func (n NetworkMode) IsBridge() bool {
|
||||
|
|
Loading…
Add table
Reference in a new issue