Explorar o código

Merge pull request #15399 from Microsoft/10662-portmapping

Windows: [TP3] Enable NAT port mapping
Arnaud Porterie %!s(int64=10) %!d(string=hai) anos
pai
achega
72e55cb0ec

+ 8 - 4
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:

+ 0 - 23
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

+ 10 - 0
daemon/execdriver/driver_linux.go → 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.

+ 20 - 0
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"`
+}

+ 0 - 11
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}}

+ 5 - 10
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"},

+ 82 - 5
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")

+ 1 - 0
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