diff --git a/daemon/container_windows.go b/daemon/container_windows.go index cc9d75a88c..425e1abe54 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -55,19 +55,23 @@ func (container *Container) setupWorkingDirectory() error { func populateCommand(c *Container, env []string) error { en := &execdriver.Network{ - Mtu: c.daemon.config.Mtu, Interface: nil, } parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2) switch parts[0] { - case "none": case "default", "": // empty string to support existing containers if !c.Config.NetworkDisabled { en.Interface = &execdriver.NetworkInterface{ - MacAddress: c.Config.MacAddress, - Bridge: c.daemon.config.Bridge.VirtualSwitchName, + MacAddress: c.Config.MacAddress, + Bridge: c.daemon.config.Bridge.VirtualSwitchName, + PortBindings: c.hostConfig.PortBindings, + + // TODO Windows. Include IPAddress. There already is a + // property IPAddress on execDrive.CommonNetworkInterface, + // but there is no CLI option in docker to pass through + // an IPAddress on docker run. } } default: diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 61cf024a57..6a9049ba98 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -91,15 +91,6 @@ type Driver interface { Stats(id string) (*ResourceStats, error) } -// Network settings of the container -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"` -} - // Ipc settings of the container // It is for IPC namespace setting. Usually different containers // have their own IPC namespace, however this specifies to use @@ -130,20 +121,6 @@ type UTS struct { HostUTS bool `json:"host_uts"` } -// NetworkInterface contains all network configs for a driver -type NetworkInterface struct { - Gateway string `json:"gateway"` - IPAddress string `json:"ip"` - IPPrefixLen int `json:"ip_prefix_len"` - MacAddress string `json:"mac"` - Bridge string `json:"bridge"` - GlobalIPv6Address string `json:"global_ipv6"` - LinkLocalIPv6Address string `json:"link_local_ipv6"` - GlobalIPv6PrefixLen int `json:"global_ipv6_prefix_len"` - IPv6Gateway string `json:"ipv6_gateway"` - HairpinMode bool `json:"hairpin_mode"` -} - // Resources contains all resource configs for a driver. // Currently these are all for cgroup configs. // TODO Windows: Factor out ulimit.Rlimit diff --git a/daemon/execdriver/driver_linux.go b/daemon/execdriver/driver_unix.go similarity index 94% rename from daemon/execdriver/driver_linux.go rename to daemon/execdriver/driver_unix.go index 4c4f63156e..8d2f75ac05 100644 --- a/daemon/execdriver/driver_linux.go +++ b/daemon/execdriver/driver_unix.go @@ -1,3 +1,5 @@ +// +build !windows + package execdriver import ( @@ -15,6 +17,14 @@ import ( "github.com/opencontainers/runc/libcontainer/configs" ) +// Network settings of the container +type Network struct { + 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"` +} + // InitContainer is the initialization of a container config. // It returns the initial configs for a container. It's mostly // defined by the default template. diff --git a/daemon/execdriver/driver_windows.go b/daemon/execdriver/driver_windows.go new file mode 100644 index 0000000000..08568bc242 --- /dev/null +++ b/daemon/execdriver/driver_windows.go @@ -0,0 +1,20 @@ +package execdriver + +import "github.com/docker/docker/pkg/nat" + +// Network settings of the container +type Network struct { + Interface *NetworkInterface `json:"interface"` + ContainerID string `json:"container_id"` // id of the container to join network. +} + +// NetworkInterface contains network configs for a driver +type NetworkInterface struct { + MacAddress string `json:"mac"` + Bridge string `json:"bridge"` + IPAddress string `json:"ip"` + + // PortBindings is the port mapping between the exposed port in the + // container and the port on the host. + PortBindings nat.PortMap `json:"port_bindings"` +} diff --git a/daemon/execdriver/lxc/lxc_template.go b/daemon/execdriver/lxc/lxc_template.go index 74845398f1..af69bf0a3b 100644 --- a/daemon/execdriver/lxc/lxc_template.go +++ b/daemon/execdriver/lxc/lxc_template.go @@ -128,17 +128,6 @@ lxc.{{$value}} {{end}} {{end}} -{{if .Network.Interface}} -{{if .Network.Interface.IPAddress}} -lxc.network.ipv4 = {{.Network.Interface.IPAddress}}/{{.Network.Interface.IPPrefixLen}} -{{end}} -{{if .Network.Interface.Gateway}} -lxc.network.ipv4.gateway = {{.Network.Interface.Gateway}} -{{end}} -{{if .Network.Interface.MacAddress}} -lxc.network.hwaddr = {{.Network.Interface.MacAddress}} -{{end}} -{{end}} {{if .ProcessConfig.Env}} lxc.utsname = {{getHostname .ProcessConfig.Env}} {{end}} diff --git a/daemon/execdriver/lxc/lxc_template_unit_test.go b/daemon/execdriver/lxc/lxc_template_unit_test.go index 5ef8f020c7..afb5b1eb6b 100644 --- a/daemon/execdriver/lxc/lxc_template_unit_test.go +++ b/daemon/execdriver/lxc/lxc_template_unit_test.go @@ -50,8 +50,7 @@ func TestLXCConfig(t *testing.T) { CPUShares: int64(cpu), }, Network: &execdriver.Network{ - Mtu: 1500, - Interface: nil, + Mtu: 1500, }, AllowedDevices: make([]*configs.Device, 0), ProcessConfig: execdriver.ProcessConfig{}, @@ -90,8 +89,7 @@ func TestCustomLxcConfig(t *testing.T) { "lxc.cgroup.cpuset.cpus = 0,1", }, Network: &execdriver.Network{ - Mtu: 1500, - Interface: nil, + Mtu: 1500, }, ProcessConfig: processConfig, } @@ -222,8 +220,7 @@ func TestCustomLxcConfigMounts(t *testing.T) { "lxc.cgroup.cpuset.cpus = 0,1", }, Network: &execdriver.Network{ - Mtu: 1500, - Interface: nil, + Mtu: 1500, }, Mounts: mounts, ProcessConfig: processConfig, @@ -264,8 +261,7 @@ func TestCustomLxcConfigMisc(t *testing.T) { "lxc.cgroup.cpuset.cpus = 0,1", }, Network: &execdriver.Network{ - Mtu: 1500, - Interface: nil, + Mtu: 1500, }, ProcessConfig: processConfig, CapAdd: []string{"net_admin", "syslog"}, @@ -317,8 +313,7 @@ func TestCustomLxcConfigMiscOverride(t *testing.T) { "lxc.network.ipv4 = 172.0.0.1", }, Network: &execdriver.Network{ - Mtu: 1500, - Interface: nil, + Mtu: 1500, }, ProcessConfig: processConfig, CapAdd: []string{"NET_ADMIN", "SYSLOG"}, diff --git a/daemon/execdriver/windows/run.go b/daemon/execdriver/windows/run.go index 8e90821ca2..b027f8e8d1 100644 --- a/daemon/execdriver/windows/run.go +++ b/daemon/execdriver/windows/run.go @@ -8,6 +8,8 @@ import ( "encoding/json" "errors" "fmt" + "os" + "strconv" "strings" "github.com/Sirupsen/logrus" @@ -16,6 +18,10 @@ import ( "github.com/natefinch/npipe" ) +// defaultContainerNAT is the default name of the container NAT device that is +// preconfigured on the server. +const defaultContainerNAT = "ContainerNAT" + type layer struct { ID string Path string @@ -25,9 +31,23 @@ type defConfig struct { DefFile string } +type portBinding struct { + Protocol string + InternalPort int + ExternalPort int +} + +type natSettings struct { + Name string + PortBindings []portBinding +} + type networkConnection struct { NetworkName string - EnableNat bool + // TODO Windows: Add Ip4Address string to this structure when hooked up in + // docker CLI. This is present in the HCS JSON handler. + EnableNat bool + Nat natSettings } type networkSettings struct { MacAddress string @@ -81,12 +101,72 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba }) } + // TODO Windows. At some point, when there is CLI on docker run to + // enable the IP Address of the container to be passed into docker run, + // the IP Address needs to be wired through to HCS in the JSON. It + // would be present in c.Network.Interface.IPAddress. See matching + // TODO in daemon\container_windows.go, function populateCommand. + if c.Network.Interface != nil { + + var pbs []portBinding + + // Enumerate through the port bindings specified by the user and convert + // them into the internal structure matching the JSON blob that can be + // understood by the HCS. + for i, v := range c.Network.Interface.PortBindings { + proto := strings.ToUpper(i.Proto()) + if proto != "TCP" && proto != "UDP" { + return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("invalid protocol %s", i.Proto()) + } + + if len(v) > 1 { + return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("Windows does not support more than one host port in NAT settings") + } + + for _, v2 := range v { + var ( + iPort, ePort int + err error + ) + if len(v2.HostIP) != 0 { + return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("Windows does not support host IP addresses in NAT settings") + } + if ePort, err = strconv.Atoi(v2.HostPort); err != nil { + return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("invalid container port %s: %s", v2.HostPort, err) + } + if iPort, err = strconv.Atoi(i.Port()); err != nil { + return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("invalid internal port %s: %s", i.Port(), err) + } + if iPort < 0 || iPort > 65535 || ePort < 0 || ePort > 65535 { + return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("specified NAT port is not in allowed range") + } + pbs = append(pbs, + portBinding{ExternalPort: ePort, + InternalPort: iPort, + Protocol: proto}) + } + } + + // TODO Windows: TP3 workaround. Allow the user to override the name of + // the Container NAT device through an environment variable. This will + // ultimately be a global daemon parameter on Windows, similar to -b + // for the name of the virtual switch (aka bridge). + cn := os.Getenv("DOCKER_CONTAINER_NAT") + if len(cn) == 0 { + cn = defaultContainerNAT + } + dev := device{ DeviceType: "Network", Connection: &networkConnection{ NetworkName: c.Network.Interface.Bridge, - EnableNat: false, + // TODO Windows: Fixme, next line. Needs HCS fix. + EnableNat: false, + Nat: natSettings{ + Name: cn, + PortBindings: pbs, + }, }, } @@ -97,9 +177,6 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba MacAddress: windowsStyleMAC, } } - - logrus.Debugf("Virtual switch '%s', mac='%s'", c.Network.Interface.Bridge, c.Network.Interface.MacAddress) - cu.Devices = append(cu.Devices, dev) } else { logrus.Debugln("No network interface") diff --git a/daemon/network/settings.go b/daemon/network/settings.go index a2e61eb9b1..d00f84a47a 100644 --- a/daemon/network/settings.go +++ b/daemon/network/settings.go @@ -9,6 +9,7 @@ type Address struct { } // Settings stores configuration details about the daemon network config +// TODO Windows. Many of these fields can be factored out., type Settings struct { Bridge string EndpointID string