1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Bridge to handle port mapping

- libnetwork cares for list of exposed ports, driver cares
  for list of port bindings. At endpoint creation:
  - list of exposed ports will be passed as libnetwork otion
  - list of port mapping will be passed as driver option

Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
Alessandro Boch 2015-05-01 17:01:21 -07:00
parent 781bcc94a7
commit da1cddc320
18 changed files with 451 additions and 275 deletions

View file

@ -1,5 +1,5 @@
{
"ImportPath": "github.com/docker/docker/vendor/src/github.com/docker/libnetwork",
"ImportPath": "github.com/docker/libnetwork",
"GoVersion": "go1.4.2",
"Packages": [
"./..."
@ -52,8 +52,8 @@
},
{
"ImportPath": "github.com/docker/libcontainer/user",
"Comment": "v1.4.0",
"Rev": "53eca435e63db58b06cf796d3a9326db5fd42253"
"Comment": "v1.4.0-495-g3e66118",
"Rev": "3e661186ba24f259d3860f067df052c7f6904bee"
},
{
"ImportPath": "github.com/vishvananda/netlink",

View file

@ -1,93 +0,0 @@
package kernel
import (
"bytes"
"errors"
"fmt"
)
type KernelVersionInfo struct {
Kernel int
Major int
Minor int
Flavor string
}
func (k *KernelVersionInfo) String() string {
return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor)
}
// Compare two KernelVersionInfo struct.
// Returns -1 if a < b, 0 if a == b, 1 it a > b
func CompareKernelVersion(a, b *KernelVersionInfo) int {
if a.Kernel < b.Kernel {
return -1
} else if a.Kernel > b.Kernel {
return 1
}
if a.Major < b.Major {
return -1
} else if a.Major > b.Major {
return 1
}
if a.Minor < b.Minor {
return -1
} else if a.Minor > b.Minor {
return 1
}
return 0
}
func GetKernelVersion() (*KernelVersionInfo, error) {
var (
err error
)
uts, err := uname()
if err != nil {
return nil, err
}
release := make([]byte, len(uts.Release))
i := 0
for _, c := range uts.Release {
release[i] = byte(c)
i++
}
// Remove the \x00 from the release for Atoi to parse correctly
release = release[:bytes.IndexByte(release, 0)]
return ParseRelease(string(release))
}
func ParseRelease(release string) (*KernelVersionInfo, error) {
var (
kernel, major, minor, parsed int
flavor, partial string
)
// Ignore error from Sscanf to allow an empty flavor. Instead, just
// make sure we got all the version numbers.
parsed, _ = fmt.Sscanf(release, "%d.%d%s", &kernel, &major, &partial)
if parsed < 2 {
return nil, errors.New("Can't parse kernel version " + release)
}
// sometimes we have 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64
parsed, _ = fmt.Sscanf(partial, ".%d%s", &minor, &flavor)
if parsed < 1 {
flavor = partial
}
return &KernelVersionInfo{
Kernel: kernel,
Major: major,
Minor: minor,
Flavor: flavor,
}, nil
}

View file

@ -1,61 +0,0 @@
package kernel
import (
"testing"
)
func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) {
var (
a *KernelVersionInfo
)
a, _ = ParseRelease(release)
if r := CompareKernelVersion(a, b); r != result {
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
}
if a.Flavor != b.Flavor {
t.Fatalf("Unexpected parsed kernel flavor. Found %s, expected %s", a.Flavor, b.Flavor)
}
}
func TestParseRelease(t *testing.T) {
assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0)
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0)
assertParseRelease(t, "3.12.8tag", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0)
assertParseRelease(t, "3.12-1-amd64", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0)
}
func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
if r := CompareKernelVersion(a, b); r != result {
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
}
}
func TestCompareKernelVersion(t *testing.T) {
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
0)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
-1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
0)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
-1)
}

View file

@ -1,16 +0,0 @@
package kernel
import (
"syscall"
)
type Utsname syscall.Utsname
func uname() (*syscall.Utsname, error) {
uts := &syscall.Utsname{}
if err := syscall.Uname(uts); err != nil {
return nil, err
}
return uts, nil
}

View file

@ -1,15 +0,0 @@
// +build !linux
package kernel
import (
"errors"
)
type Utsname struct {
Release [65]byte
}
func uname() (*Utsname, error) {
return nil, errors.New("Kernel version detection is available only on linux")
}

View file

@ -1 +1,2 @@
Tianon Gravi <admwiggin@gmail.com> (@tianon)
Aleksa Sarai <cyphar@cyphar.com> (@cyphar)

View file

@ -9,22 +9,22 @@ import (
// Unix-specific path to the passwd and group formatted files.
const (
unixPasswdFile = "/etc/passwd"
unixGroupFile = "/etc/group"
unixPasswdPath = "/etc/passwd"
unixGroupPath = "/etc/group"
)
func GetPasswdFile() (string, error) {
return unixPasswdFile, nil
func GetPasswdPath() (string, error) {
return unixPasswdPath, nil
}
func GetPasswd() (io.ReadCloser, error) {
return os.Open(unixPasswdFile)
return os.Open(unixPasswdPath)
}
func GetGroupFile() (string, error) {
return unixGroupFile, nil
func GetGroupPath() (string, error) {
return unixGroupPath, nil
}
func GetGroup() (io.ReadCloser, error) {
return os.Open(unixGroupFile)
return os.Open(unixGroupPath)
}

View file

@ -4,7 +4,7 @@ package user
import "io"
func GetPasswdFile() (string, error) {
func GetPasswdPath() (string, error) {
return "", ErrUnsupported
}
@ -12,7 +12,7 @@ func GetPasswd() (io.ReadCloser, error) {
return nil, ErrUnsupported
}
func GetGroupFile() (string, error) {
func GetGroupPath() (string, error) {
return "", ErrUnsupported
}

View file

@ -197,11 +197,11 @@ type ExecUser struct {
Home string
}
// GetExecUserFile is a wrapper for GetExecUser. It reads data from each of the
// GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the
// given file paths and uses that data as the arguments to GetExecUser. If the
// files cannot be opened for any reason, the error is ignored and a nil
// io.Reader is passed instead.
func GetExecUserFile(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
passwd, err := os.Open(passwdPath)
if err != nil {
passwd = nil

View file

@ -16,10 +16,11 @@ import (
)
const (
networkType = "bridge"
vethPrefix = "veth"
vethLen = 7
containerVeth = "eth0"
networkType = "bridge"
vethPrefix = "veth"
vethLen = 7
containerVeth = "eth0"
maxAllocatePortAttempts = 10
)
var (
@ -42,11 +43,13 @@ type Configuration struct {
Mtu int
DefaultGatewayIPv4 net.IP
DefaultGatewayIPv6 net.IP
DefaultBindingIP net.IP
}
// EndpointConfiguration represents the user specified configuration for the sandbox endpoint
type EndpointConfiguration struct {
MacAddress net.HardwareAddr
MacAddress net.HardwareAddr
PortBindings []netutils.PortBinding
}
// ContainerConfiguration represents the user specified configuration for a container
@ -55,9 +58,10 @@ type ContainerConfiguration struct {
}
type bridgeEndpoint struct {
id types.UUID
port *sandbox.Interface
config *EndpointConfiguration // User specified parameters
id types.UUID
port *sandbox.Interface
config *EndpointConfiguration // User specified parameters
portMapping []netutils.PortBinding // Operation port bindings
}
type bridgeNetwork struct {
@ -343,7 +347,6 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
// Try to convert the options to endpoint configuration
epConfig, err := parseEndpointOptions(epOptions)
if err != nil {
n.Unlock()
return nil, err
}
@ -462,14 +465,13 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask}
}
// Store the sandbox side pipe interface
// This is needed for cleanup on DeleteEndpoint()
// Create the sandbox side pipe interface
intf := &sandbox.Interface{}
intf.SrcName = name2
intf.DstName = containerVeth
intf.Address = ipv4Addr
// Update endpoint with the sandbox interface info
// Store the interface in endpoint, this is needed for cleanup on DeleteEndpoint()
endpoint.port = intf
// Generate the sandbox info to return
@ -482,6 +484,12 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
sinfo.GatewayIPv6 = n.bridge.gatewayIPv6
}
// Program any required port mapping and store them in the endpoint
endpoint.portMapping, err = allocatePorts(epConfig, sinfo, config.DefaultBindingIP)
if err != nil {
return nil, err
}
return sinfo, nil
}
@ -531,6 +539,9 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
}
}()
// Remove port mappings. Do not stop endpoint delete on unmap failure
releasePorts(ep)
// Release the v4 address allocated to this endpoint's sandbox interface
err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.port.Address.IP)
if err != nil {
@ -573,22 +584,26 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*EndpointConfigurat
if epOptions == nil {
return nil, nil
}
genericData := epOptions[options.GenericData]
if genericData == nil {
return nil, nil
}
switch opt := genericData.(type) {
case options.Generic:
opaqueConfig, err := options.GenerateFromModel(opt, &EndpointConfiguration{})
if err != nil {
return nil, err
ec := &EndpointConfiguration{}
if opt, ok := epOptions[options.MacAddress]; ok {
if mac, ok := opt.(net.HardwareAddr); ok {
ec.MacAddress = mac
} else {
return nil, ErrInvalidEndpointConfig
}
return opaqueConfig.(*EndpointConfiguration), nil
case *EndpointConfiguration:
return opt, nil
default:
return nil, ErrInvalidEndpointConfig
}
if opt, ok := epOptions[options.PortMap]; ok {
if bs, ok := opt.([]netutils.PortBinding); ok {
ec.PortBindings = bs
} else {
return nil, ErrInvalidEndpointConfig
}
}
return ec, nil
}
func parseContainerOptions(cOptions interface{}) (*ContainerConfiguration, error) {

View file

@ -74,10 +74,10 @@ func TestCreateLinkWithOptions(t *testing.T) {
_, d := New()
config := &Configuration{BridgeName: DefaultBridgeName}
genericOption := make(map[string]interface{})
genericOption[options.GenericData] = config
driverOptions := make(map[string]interface{})
driverOptions[options.GenericData] = config
if err := d.Config(genericOption); err != nil {
if err := d.Config(driverOptions); err != nil {
t.Fatalf("Failed to setup driver config: %v", err)
}
@ -87,10 +87,10 @@ func TestCreateLinkWithOptions(t *testing.T) {
}
mac := net.HardwareAddr([]byte{0x1e, 0x67, 0x66, 0x44, 0x55, 0x66})
epConf := &EndpointConfiguration{MacAddress: mac}
genericOption[options.GenericData] = epConf
epOptions := make(map[string]interface{})
epOptions[options.MacAddress] = mac
sinfo, err := d.CreateEndpoint("net1", "ep", genericOption)
sinfo, err := d.CreateEndpoint("net1", "ep", epOptions)
if err != nil {
t.Fatalf("Failed to create a link: %s", err.Error())
}

View file

@ -37,10 +37,31 @@ var (
// ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid.
ErrInvalidContainerSubnet = errors.New("container subnet must be a subset of bridge network")
// ErrInvalidMtu is returned when the user provided MTU is not valid
// ErrInvalidMtu is returned when the user provided MTU is not valid.
ErrInvalidMtu = errors.New("invalid MTU number")
)
// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid.
type ErrInvalidPort string
func (ip ErrInvalidPort) Error() string {
return fmt.Sprintf("invalid transport port: %s", string(ip))
}
// ErrUnsupportedAddressType is returned when the specified address type is not supported.
type ErrUnsupportedAddressType string
func (uat ErrUnsupportedAddressType) Error() string {
return fmt.Sprintf("unsupported address type: %s", string(uat))
}
// ErrInvalidAddressBinding is returned when the host address specfied in the port binding is not valid.
type ErrInvalidAddressBinding string
func (iab ErrInvalidAddressBinding) Error() string {
return fmt.Sprintf("invalid host address in port binding: %s", string(iab))
}
// ActiveEndpointsError is returned when there are
// still active endpoints in the network being deleted.
type ActiveEndpointsError string

View file

@ -0,0 +1,124 @@
package bridge
import (
"bytes"
"errors"
"fmt"
"net"
"github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/sandbox"
)
var (
defaultBindingIP = net.IPv4(0, 0, 0, 0)
)
func allocatePorts(epConfig *EndpointConfiguration, sinfo *sandbox.Info, reqDefBindIP net.IP) ([]netutils.PortBinding, error) {
if epConfig == nil || epConfig.PortBindings == nil {
return nil, nil
}
defHostIP := defaultBindingIP
if reqDefBindIP != nil {
defHostIP = reqDefBindIP
}
return allocatePortsInternal(epConfig.PortBindings, sinfo.Interfaces[0].Address.IP, defHostIP)
}
func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHostIP net.IP) ([]netutils.PortBinding, error) {
bs := make([]netutils.PortBinding, 0, len(bindings))
for _, c := range bindings {
b := c.GetCopy()
if err := allocatePort(&b, containerIP, defHostIP); err != nil {
// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
if cuErr := releasePortsInternal(bs); cuErr != nil {
logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr)
}
return nil, err
}
bs = append(bs, b)
}
return bs, nil
}
func allocatePort(bnd *netutils.PortBinding, containerIP, defHostIP net.IP) error {
var (
host net.Addr
err error
)
// Store the container interface address in the operational binding
bnd.IP = containerIP
// Adjust the host address in the operational binding
if len(bnd.HostIP) == 0 {
bnd.HostIP = defHostIP
}
// Construct the container side transport address
container, err := bnd.ContainerAddr()
if err != nil {
return err
}
// Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
for i := 0; i < maxAllocatePortAttempts; i++ {
if host, err = portMapper.Map(container, bnd.HostIP, int(bnd.HostPort)); err == nil {
break
}
// There is no point in immediately retrying to map an explicitly chosen port.
if bnd.HostPort != 0 {
logrus.Warnf("Failed to allocate and map port %d: %s", bnd.HostPort, err)
break
}
logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
}
if err != nil {
return err
}
// Save the host port (regardless it was or not specified in the binding)
switch netAddr := host.(type) {
case *net.TCPAddr:
bnd.HostPort = uint16(host.(*net.TCPAddr).Port)
return nil
case *net.UDPAddr:
bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
return nil
default:
// For completeness
return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
}
}
func releasePorts(ep *bridgeEndpoint) error {
return releasePortsInternal(ep.portMapping)
}
func releasePortsInternal(bindings []netutils.PortBinding) error {
var errorBuf bytes.Buffer
// Attempt to release all port bindings, do not stop on failure
for _, m := range bindings {
if err := releasePort(m); err != nil {
errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err))
}
}
if errorBuf.Len() != 0 {
return errors.New(errorBuf.String())
}
return nil
}
func releasePort(bnd netutils.PortBinding) error {
// Construct the host side transport address
host, err := bnd.HostAddr()
if err != nil {
return err
}
return portMapper.Unmap(host)
}

View file

@ -0,0 +1,72 @@
package bridge
import (
"fmt"
"os"
"testing"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/pkg/options"
)
func TestMain(m *testing.M) {
if reexec.Init() {
return
}
os.Exit(m.Run())
}
func TestPortMappingConfig(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
_, d := New()
binding1 := netutils.PortBinding{Proto: netutils.UDP, Port: uint16(400), HostPort: uint16(54000)}
binding2 := netutils.PortBinding{Proto: netutils.TCP, Port: uint16(500), HostPort: uint16(65000)}
portBindings := []netutils.PortBinding{binding1, binding2}
epOptions := make(map[string]interface{})
epOptions[options.PortMap] = portBindings
driverConfig := &Configuration{
BridgeName: DefaultBridgeName,
EnableIPTables: true,
}
driverOptions := make(map[string]interface{})
driverOptions[options.GenericData] = driverConfig
if err := d.Config(driverOptions); err != nil {
t.Fatalf("Failed to setup driver config: %v", err)
}
err := d.CreateNetwork("dummy", nil)
if err != nil {
t.Fatalf("Failed to create bridge: %v", err)
}
_, err = d.CreateEndpoint("dummy", "ep1", epOptions)
if err != nil {
t.Fatalf("Failed to create the endpoint: %s", err.Error())
}
dd := d.(*driver)
ep, _ := dd.network.endpoints["ep1"]
if len(ep.portMapping) != 2 {
t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping)
}
if ep.portMapping[0].Proto != binding1.Proto || ep.portMapping[0].Port != binding1.Port ||
ep.portMapping[1].Proto != binding2.Proto || ep.portMapping[1].Port != binding2.Port {
t.Fatalf("bridgeEndpoint has incorrect port mapping values")
}
if ep.portMapping[0].HostIP == nil || ep.portMapping[0].HostPort == 0 ||
ep.portMapping[1].HostIP == nil || ep.portMapping[1].HostPort == 0 {
t.Fatalf("operational port mapping data not found on bridgeEndpoint")
}
fmt.Printf("\nendpoint: %v\n", ep.portMapping)
err = releasePorts(ep)
if err != nil {
t.Fatalf("Failed to release mapped ports: %v", err)
}
}

View file

@ -5,6 +5,8 @@ import (
"path/filepath"
"github.com/docker/docker/pkg/etchosts"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/pkg/options"
"github.com/docker/libnetwork/sandbox"
"github.com/docker/libnetwork/types"
)
@ -65,14 +67,15 @@ type containerInfo struct {
}
type endpoint struct {
name string
id types.UUID
network *network
sandboxInfo *sandbox.Info
sandBox sandbox.Sandbox
container *containerInfo
generic map[string]interface{}
context map[string]interface{}
name string
id types.UUID
network *network
sandboxInfo *sandbox.Info
sandBox sandbox.Sandbox
container *containerInfo
exposedPorts []netutils.TransportPort
generic map[string]interface{}
context map[string]interface{}
}
const prefix = "/var/lib/docker/network/files"
@ -186,7 +189,7 @@ func (ep *endpoint) Join(containerID string, options ...JoinOption) (*ContainerD
}()
n := ep.network
err = n.driver.Join(n.id, ep.id, sboxKey, ep.container.Config.generic)
err = n.driver.Join(n.id, ep.id, sboxKey, ep.container.config.generic)
if err != nil {
return nil, err
}
@ -297,12 +300,26 @@ func JoinOptionDomainname(name string) JoinOption {
}
}
// CreateOptionPortMapping function returns an option setter for the container exposed
// ports option to be passed to endpoint Join method.
func CreateOptionPortMapping(portBindings []netutils.PortBinding) EndpointOption {
return func(ep *endpoint) {
// Store endpoint label
ep.generic[options.PortMap] = portBindings
// Extract exposed ports as this is the only concern of libnetwork endpoint
ep.exposedPorts = make([]netutils.TransportPort, 0, len(portBindings))
for _, b := range portBindings {
ep.exposedPorts = append(ep.exposedPorts, netutils.TransportPort{Proto: b.Proto, Port: b.Port})
}
}
}
// JoinOptionGeneric function returns an option setter for Generic configuration
// that is not managed by libNetwork but can be used by the Drivers during the call to
// endpoint join method. Container Labels are a good example.
func JoinOptionGeneric(generic map[string]interface{}) JoinOption {
return func(ep *endpoint) {
ep.container.Config.generic = generic
ep.container.config.generic = generic
}
}

View file

@ -2,19 +2,28 @@ package libnetwork_test
import (
"net"
"os"
"testing"
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/pkg/options"
)
const (
netType = "bridge"
bridgeName = "dockertest0"
bridgeNetType = "bridge"
bridgeName = "docker0"
)
func TestMain(m *testing.M) {
if reexec.Init() {
return
}
os.Exit(m.Run())
}
func createTestNetwork(networkType, networkName string, option options.Generic) (libnetwork.Network, error) {
controller := libnetwork.New()
genericOption := make(map[string]interface{})
@ -39,6 +48,14 @@ func getEmptyGenericOption() map[string]interface{} {
return genericOption
}
func getPortMapping() []netutils.PortBinding {
return []netutils.PortBinding{
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)},
netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)},
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)},
}
}
func TestNull(t *testing.T) {
network, err := createTestNetwork("null", "testnetwork", options.Generic{})
if err != nil {
@ -104,12 +121,12 @@ func TestBridge(t *testing.T) {
"EnableIPForwarding": true,
"AllowNonDefaultBridge": true}
network, err := createTestNetwork(netType, "testnetwork", option)
network, err := createTestNetwork(bridgeNetType, "testnetwork", option)
if err != nil {
t.Fatal(err)
}
ep, err := network.CreateEndpoint("testep")
ep, err := network.CreateEndpoint("testep", libnetwork.CreateOptionPortMapping(getPortMapping()))
if err != nil {
t.Fatal(err)
}
@ -171,17 +188,17 @@ func TestDuplicateNetwork(t *testing.T) {
genericOption := make(map[string]interface{})
genericOption[options.GenericData] = options.Generic{}
err := controller.ConfigureNetworkDriver(netType, genericOption)
err := controller.ConfigureNetworkDriver(bridgeNetType, genericOption)
if err != nil {
t.Fatal(err)
}
_, err = controller.NewNetwork(netType, "testnetwork", nil)
_, err = controller.NewNetwork(bridgeNetType, "testnetwork", nil)
if err != nil {
t.Fatal(err)
}
_, err = controller.NewNetwork(netType, "testnetwork")
_, err = controller.NewNetwork(bridgeNetType, "testnetwork")
if err == nil {
t.Fatal("Expected to fail. But instead succeeded")
}
@ -192,9 +209,10 @@ func TestDuplicateNetwork(t *testing.T) {
}
func TestNetworkName(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
networkName := "testnetwork"
n, err := createTestNetwork(netType, networkName, options.Generic{})
n, err := createTestNetwork(bridgeNetType, networkName, options.Generic{})
if err != nil {
t.Fatal(err)
}
@ -205,22 +223,21 @@ func TestNetworkName(t *testing.T) {
}
func TestNetworkType(t *testing.T) {
networkType := netType
n, err := createTestNetwork(networkType, "testnetwork", options.Generic{})
defer netutils.SetupTestNetNS(t)()
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
if err != nil {
t.Fatal(err)
}
if n.Type() != networkType {
t.Fatalf("Expected network type %s, got %s", networkType, n.Type())
if n.Type() != bridgeNetType {
t.Fatalf("Expected network type %s, got %s", bridgeNetType, n.Type())
}
}
func TestNetworkID(t *testing.T) {
networkType := netType
defer netutils.SetupTestNetNS(t)()
n, err := createTestNetwork(networkType, "testnetwork", options.Generic{})
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
if err != nil {
t.Fatal(err)
}
@ -236,7 +253,7 @@ func TestDeleteNetworkWithActiveEndpoints(t *testing.T) {
"BridgeName": bridgeName,
"AllowNonDefaultBridge": true}
network, err := createTestNetwork(netType, "testnetwork", option)
network, err := createTestNetwork(bridgeNetType, "testnetwork", option)
if err != nil {
t.Fatal(err)
}
@ -271,7 +288,7 @@ func TestUnknownNetwork(t *testing.T) {
"BridgeName": bridgeName,
"AllowNonDefaultBridge": true}
network, err := createTestNetwork(netType, "testnetwork", option)
network, err := createTestNetwork(bridgeNetType, "testnetwork", option)
if err != nil {
t.Fatal(err)
}
@ -304,7 +321,7 @@ func TestUnknownEndpoint(t *testing.T) {
"AddressIPv4": subnet,
"AllowNonDefaultBridge": true}
network, err := createTestNetwork(netType, "testnetwork", option)
network, err := createTestNetwork(bridgeNetType, "testnetwork", option)
if err != nil {
t.Fatal(err)
}
@ -337,15 +354,14 @@ func TestUnknownEndpoint(t *testing.T) {
func TestNetworkEndpointsWalkers(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
controller := libnetwork.New()
netType := "bridge"
err := controller.ConfigureNetworkDriver(netType, getEmptyGenericOption())
err := controller.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption())
if err != nil {
t.Fatal(err)
}
// Create network 1 and add 2 endpoint: ep11, ep12
net1, err := controller.NewNetwork(netType, "network1")
net1, err := controller.NewNetwork(bridgeNetType, "network1")
if err != nil {
t.Fatal(err)
}
@ -416,15 +432,14 @@ func TestNetworkEndpointsWalkers(t *testing.T) {
func TestControllerQuery(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
controller := libnetwork.New()
netType := "bridge"
err := controller.ConfigureNetworkDriver(netType, getEmptyGenericOption())
err := controller.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption())
if err != nil {
t.Fatal(err)
}
// Create network 1
net1, err := controller.NewNetwork(netType, "network1")
net1, err := controller.NewNetwork(bridgeNetType, "network1")
if err != nil {
t.Fatal(err)
}
@ -461,15 +476,14 @@ func TestControllerQuery(t *testing.T) {
func TestNetworkQuery(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
controller := libnetwork.New()
netType := "bridge"
err := controller.ConfigureNetworkDriver(netType, getEmptyGenericOption())
err := controller.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption())
if err != nil {
t.Fatal(err)
}
// Create network 1 and add 2 endpoint: ep11, ep12
net1, err := controller.NewNetwork(netType, "network1")
net1, err := controller.NewNetwork(bridgeNetType, "network1")
if err != nil {
t.Fatal(err)
}
@ -514,7 +528,7 @@ const containerID = "valid_container"
func TestEndpointJoin(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
if err != nil {
t.Fatal(err)
}
@ -540,7 +554,7 @@ func TestEndpointJoin(t *testing.T) {
func TestEndpointJoinInvalidContainerId(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
if err != nil {
t.Fatal(err)
}
@ -563,7 +577,7 @@ func TestEndpointJoinInvalidContainerId(t *testing.T) {
func TestEndpointMultipleJoins(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
if err != nil {
t.Fatal(err)
}
@ -599,7 +613,7 @@ func TestEndpointMultipleJoins(t *testing.T) {
func TestEndpointInvalidLeave(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
if err != nil {
t.Fatal(err)
}

View file

@ -10,6 +10,7 @@ import (
"fmt"
"io"
"net"
"strings"
"github.com/vishvananda/netlink"
)
@ -25,6 +26,102 @@ var (
networkGetRoutesFct = netlink.RouteList
)
// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid.
type ErrInvalidProtocolBinding string
func (ipb ErrInvalidProtocolBinding) Error() string {
return fmt.Sprintf("invalid transport protocol: %s", string(ipb))
}
// TransportPort represent a local Layer 4 endpoint
type TransportPort struct {
Proto Protocol
Port uint16
}
// PortBinding represent a port binding between the container an the host
type PortBinding struct {
Proto Protocol
IP net.IP
Port uint16
HostIP net.IP
HostPort uint16
}
// HostAddr returns the host side tranport address
func (p PortBinding) HostAddr() (net.Addr, error) {
switch p.Proto {
case UDP:
return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
case TCP:
return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
}
// ContainerAddr returns the container side tranport address
func (p PortBinding) ContainerAddr() (net.Addr, error) {
switch p.Proto {
case UDP:
return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil
case TCP:
return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
}
// GetCopy returns a copy of this PortBinding structure instance
func (p *PortBinding) GetCopy() PortBinding {
return PortBinding{
Proto: p.Proto,
IP: GetIPCopy(p.IP),
Port: p.Port,
HostIP: GetIPCopy(p.HostIP),
HostPort: p.HostPort,
}
}
const (
// ICMP is for the ICMP ip protocol
ICMP = 1
// TCP is for the TCP ip protocol
TCP = 6
// UDP is for the UDP ip protocol
UDP = 17
)
// Protocol represents a IP protocol number
type Protocol uint8
func (p Protocol) String() string {
switch p {
case 1:
return "icmp"
case 6:
return "tcp"
case 17:
return "udp"
default:
return fmt.Sprintf("%d", p)
}
}
// ParseProtocol returns the respective Protocol type for the passed string
func ParseProtocol(s string) Protocol {
switch strings.ToLower(s) {
case "icmp":
return 1
case "udp":
return 6
case "tcp":
return 17
default:
return 0
}
}
// CheckNameserverOverlaps checks whether the passed network overlaps with any of the nameservers
func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
if len(nameservers) > 0 {

View file

@ -127,7 +127,7 @@ func (n *network) Delete() error {
}
func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) {
ep := &endpoint{name: name}
ep := &endpoint{name: name, generic: make(map[string]interface{})}
ep.id = types.UUID(stringid.GenerateRandomID())
ep.network = n
ep.processOptions(options...)