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:
Jana Radhakrishnan 2015-05-06 22:39:29 +00:00
parent 272f8cd4bc
commit d18919e304
20 changed files with 746 additions and 622 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"`
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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": {}}

View File

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

View File

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

View File

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