From 68ae284db587035e877a1043bef52c1e0bc2ff28 Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Mon, 13 Apr 2015 18:40:42 +0000 Subject: [PATCH] Libnetwork refactor for container network model - Added controller, network, endpoint and sandbox interfaces - Created netutils package for miscallaneous network utilities - Created driverapi package to break cyclic dependency b/w driver and libnetwork - Made libnetwork multithread safe - Made bridge driver multithread safe - Fixed README.md Signed-off-by: Jana Radhakrishnan --- libnetwork/README.md | 65 ++-- libnetwork/cmd/test/main.go | 7 +- libnetwork/configure.go | 81 ----- libnetwork/driverapi/driverapi.go | 72 +++++ libnetwork/drivers.go | 65 +--- libnetwork/drivers/bridge/bridge.go | 296 +++++++++++++++++- libnetwork/drivers/bridge/bridge_test.go | 28 +- libnetwork/drivers/bridge/interface_test.go | 6 +- libnetwork/drivers/bridge/network.go | 128 -------- libnetwork/drivers/bridge/network_test.go | 58 ++-- libnetwork/drivers/bridge/setup_device.go | 4 +- .../drivers/bridge/setup_device_test.go | 14 +- .../drivers/bridge/setup_fixedcidrv4_test.go | 6 +- .../drivers/bridge/setup_fixedcidrv6_test.go | 4 +- libnetwork/drivers/bridge/setup_ip_tables.go | 4 +- .../drivers/bridge/setup_ip_tables_test.go | 6 +- libnetwork/drivers/bridge/setup_ipv4.go | 6 +- libnetwork/drivers/bridge/setup_ipv4_test.go | 6 +- libnetwork/drivers/bridge/setup_ipv6_test.go | 4 +- .../drivers/bridge/setup_verify_test.go | 12 +- libnetwork/ipallocator/allocator.go | 6 +- libnetwork/libnetwork_test.go | 7 +- .../{testing.go => netutils/test_utils.go} | 2 +- libnetwork/{ => netutils}/utils.go | 2 +- libnetwork/{ => netutils}/utils_test.go | 2 +- libnetwork/network.go | 287 ++++++++++++----- libnetwork/sandbox/configure_linux.go | 66 ++++ .../namespace_linux.go} | 49 ++- libnetwork/sandbox/sandbox.go | 25 ++ libnetwork/sandbox/sandbox_linux_test.go | 54 ++++ libnetwork/sandbox/sandbox_test.go | 21 ++ 31 files changed, 910 insertions(+), 483 deletions(-) delete mode 100644 libnetwork/configure.go create mode 100644 libnetwork/driverapi/driverapi.go delete mode 100644 libnetwork/drivers/bridge/network.go rename libnetwork/{testing.go => netutils/test_utils.go} (97%) rename libnetwork/{ => netutils}/utils.go (99%) rename libnetwork/{ => netutils}/utils_test.go (99%) create mode 100644 libnetwork/sandbox/configure_linux.go rename libnetwork/{namespace.go => sandbox/namespace_linux.go} (65%) create mode 100644 libnetwork/sandbox/sandbox.go create mode 100644 libnetwork/sandbox/sandbox_linux_test.go create mode 100644 libnetwork/sandbox/sandbox_test.go diff --git a/libnetwork/README.md b/libnetwork/README.md index 037c3163ee..5fee2a195e 100644 --- a/libnetwork/README.md +++ b/libnetwork/README.md @@ -17,34 +17,43 @@ Please refer to the [roadmap](ROADMAP.md) for more information. There are many networking solutions available to suit a broad range of use-cases. libnetwork uses a driver / plugin model to support all of these solutions while abstracting the complexity of the driver implementations by exposing a simple and consistent Network Model to users. ```go -//Create a network for containers to join. -network, err := libnetwork.NewNetwork("simplebridge", &Options{}) -if err != nil { - return err -} -// -// For a new container: create network namespace (providing the path). -networkPath := "/var/lib/docker/.../4d23e" -networkNamespace, err := libnetwork.NewNetworkNamespace(networkPath) -if err != nil { - return err -} -// -// For each new container: allocate IP and interfaces. The returned network -// settings will be used for container infos (inspect and such), as well as -// iptables rules for port publishing. -interfaces, err := network.Link(containerID) -if err != nil { - return err -} -// -// Add interfaces to the namespace. -for _, interface := range interfaces { - if err := networkNamespace.AddInterface(interface); err != nil { - return err - } -} -// + // Create a new controller instance + controller := libnetwork.New() + + options := options.Generic{} + // Create a network for containers to join. + network, err := controller.NewNetwork("simplebridge", "network1", options) + if err != nil { + return + } + + // For a new container: create a sandbox instance (providing a unique key). + // For linux it is a filesystem path + networkPath := "/var/lib/docker/.../4d23e" + networkNamespace, err := sandbox.NewSandbox(networkPath) + if err != nil { + return + } + + // For each new container: allocate IP and interfaces. The returned network + // settings will be used for container infos (inspect and such), as well as + // iptables rules for port publishing. + _, sinfo, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), "") + if err != nil { + return + } + + // Add interfaces to the namespace. + for _, iface := range sinfo.Interfaces { + if err := networkNamespace.AddInterface(iface); err != nil { + return + } + } + + // Set the gateway IP + if err := networkNamespace.SetGateway(sinfo.Gateway); err != nil { + return + } ``` ## Future diff --git a/libnetwork/cmd/test/main.go b/libnetwork/cmd/test/main.go index f7234607c7..afac59db0b 100644 --- a/libnetwork/cmd/test/main.go +++ b/libnetwork/cmd/test/main.go @@ -6,15 +6,16 @@ import ( "net" "github.com/docker/libnetwork" - _ "github.com/docker/libnetwork/drivers/bridge" + "github.com/docker/libnetwork/pkg/options" ) func main() { ip, net, _ := net.ParseCIDR("192.168.100.1/24") net.IP = ip - options := libnetwork.DriverParams{"AddressIPv4": net} - netw, err := libnetwork.NewNetwork("simplebridge", "dummy", options) + options := options.Generic{"AddressIPv4": net} + controller := libnetwork.New() + netw, err := controller.NewNetwork("simplebridge", "dummy", options) if err != nil { log.Fatal(err) } diff --git a/libnetwork/configure.go b/libnetwork/configure.go deleted file mode 100644 index 35cfab743d..0000000000 --- a/libnetwork/configure.go +++ /dev/null @@ -1,81 +0,0 @@ -package libnetwork - -import ( - "fmt" - "net" - - "github.com/vishvananda/netlink" -) - -func configureInterface(iface netlink.Link, settings *Interface) error { - ifaceName := iface.Attrs().Name - ifaceConfigurators := []struct { - Fn func(netlink.Link, *Interface) error - ErrMessage string - }{ - {setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, settings.DstName)}, - {setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %q", ifaceName, settings.Address)}, - {setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %q", ifaceName, settings.AddressIPv6)}, - {setInterfaceGateway, fmt.Sprintf("error setting interface %q gateway to %q", ifaceName, settings.Gateway)}, - {setInterfaceGatewayIPv6, fmt.Sprintf("error setting interface %q IPv6 gateway to %q", ifaceName, settings.GatewayIPv6)}, - } - - for _, config := range ifaceConfigurators { - if err := config.Fn(iface, settings); err != nil { - return fmt.Errorf("%s: %v", config.ErrMessage, err) - } - } - return nil -} - -func setGatewayIP(iface netlink.Link, ip net.IP) error { - return netlink.RouteAdd(&netlink.Route{ - LinkIndex: iface.Attrs().Index, - Scope: netlink.SCOPE_UNIVERSE, - Gw: ip, - }) -} - -func setInterfaceGateway(iface netlink.Link, settings *Interface) error { - ip := net.ParseIP(settings.Gateway) - if ip == nil { - return fmt.Errorf("bad address format %q", settings.Gateway) - } - return setGatewayIP(iface, ip) -} - -func setInterfaceGatewayIPv6(iface netlink.Link, settings *Interface) error { - if settings.GatewayIPv6 != "" { - return nil - } - - ip := net.ParseIP(settings.GatewayIPv6) - if ip == nil { - return fmt.Errorf("bad address format %q", settings.GatewayIPv6) - } - return setGatewayIP(iface, ip) -} - -func setInterfaceIP(iface netlink.Link, settings *Interface) (err error) { - var ipAddr *netlink.Addr - if ipAddr, err = netlink.ParseAddr(settings.Address); err == nil { - err = netlink.AddrAdd(iface, ipAddr) - } - return err -} - -func setInterfaceIPv6(iface netlink.Link, settings *Interface) (err error) { - if settings.AddressIPv6 != "" { - return nil - } - - var ipAddr *netlink.Addr - if ipAddr, err = netlink.ParseAddr(settings.AddressIPv6); err == nil { - err = netlink.AddrAdd(iface, ipAddr) - } - return err -} - -func setInterfaceName(iface netlink.Link, settings *Interface) error { - return netlink.LinkSetName(iface, settings.DstName) -} diff --git a/libnetwork/driverapi/driverapi.go b/libnetwork/driverapi/driverapi.go new file mode 100644 index 0000000000..dd13a37b1d --- /dev/null +++ b/libnetwork/driverapi/driverapi.go @@ -0,0 +1,72 @@ +package driverapi + +import "errors" + +var ( + // ErrEndpointExists is returned if more than one endpoint is added to the network + ErrEndpointExists = errors.New("Endpoint already exists (Only one endpoint allowed)") + // ErrNoNetwork is returned if no network with the specified id exists + ErrNoNetwork = errors.New("No network exists") + // ErrNoEndpoint is returned if no endpoint with the specified id exists + ErrNoEndpoint = errors.New("No endpoint exists") +) + +// UUID represents a globally unique ID of various resources like network and endpoint +type UUID string + +// Driver is an interface that every plugin driver needs to implement. +type Driver interface { + // CreateNetwork invokes the driver method to create a network passing + // the network id and driver specific config. The config mechanism will + // eventually be replaced with labels which are yet to be introduced. + CreateNetwork(nid UUID, config interface{}) error + + // DeleteNetwork invokes the driver method to delete network passing + // the network id. + DeleteNetwork(nid UUID) error + + // CreateEndpoint invokes the driver method to create an endpoint + // passing the network id, endpoint id, sandbox key and driver + // specific config. The config mechanism will eventually be replaced + // with labels which are yet to be introduced. + CreateEndpoint(nid, eid UUID, key string, config interface{}) (*SandboxInfo, error) + + // DeleteEndpoint invokes the driver method to delete an endpoint + // passing the network id and endpoint id. + DeleteEndpoint(nid, eid UUID) error +} + +// Interface represents the settings and identity of a network device. It is +// used as a return type for Network.Link, and it is common practice for the +// caller to use this information when moving interface SrcName from host +// namespace to DstName in a different net namespace with the appropriate +// network settings. +type Interface struct { + // The name of the interface in the origin network namespace. + SrcName string + + // The name that will be assigned to the interface once moves inside a + // network namespace. + DstName string + + // IPv4 address for the interface. + Address string + + // IPv6 address for the interface. + AddressIPv6 string +} + +// SandboxInfo represents all possible information that +// the driver wants to place in the sandbox which includes +// interfaces, routes and gateway +type SandboxInfo struct { + Interfaces []*Interface + + // IPv4 gateway for the sandbox. + Gateway string + + // IPv6 gateway for the sandbox. + GatewayIPv6 string + + // TODO: Add routes and ip tables etc. +} diff --git a/libnetwork/drivers.go b/libnetwork/drivers.go index 16dd185b3d..1980f93f24 100644 --- a/libnetwork/drivers.go +++ b/libnetwork/drivers.go @@ -1,65 +1,18 @@ package libnetwork import ( - "fmt" - - "github.com/docker/libnetwork/pkg/options" + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/drivers/bridge" ) -// DriverParams are a generic structure to hold driver specific settings. -type DriverParams options.Generic +type driverTable map[string]driverapi.Driver -// DriverInterface is an interface that every plugin driver needs to implement. -type DriverInterface interface { - CreateNetwork(string, interface{}) (Network, error) -} - -var drivers = map[string]struct { - creatorFn DriverInterface - creatorArg interface{} -}{} - -// RegisterNetworkType associates a textual identifier with a way to create a -// new network. It is called by the various network implementations, and used -// upon invokation of the libnetwork.NetNetwork function. -// -// creatorFn must implement DriverInterface -// -// For example: -// -// type driver struct{} -// -// func (d *driver) CreateNetwork(name string, config *TestNetworkConfig) (Network, error) { -// } -// -// func init() { -// RegisterNetworkType("test", &driver{}, &TestNetworkConfig{}) -// } -// -func RegisterNetworkType(name string, creatorFn DriverInterface, creatorArg interface{}) error { - // Store the new driver information to invoke at creation time. - if _, ok := drivers[name]; ok { - return fmt.Errorf("a driver for network type %q is already registed", name) +func enumerateDrivers() driverTable { + var drivers driverTable + for _, fn := range [](func() (string, driverapi.Driver)){bridge.New} { + name, driver := fn() + drivers[name] = driver } - drivers[name] = struct { - creatorFn DriverInterface - creatorArg interface{} - }{creatorFn, creatorArg} - - return nil -} - -func createNetwork(networkType, name string, generic DriverParams) (Network, error) { - d, ok := drivers[networkType] - if !ok { - return nil, fmt.Errorf("unknown driver %q", networkType) - } - - config, err := options.GenerateFromModel(options.Generic(generic), d.creatorArg) - if err != nil { - return nil, fmt.Errorf("failed to generate driver config: %v", err) - } - - return d.creatorFn.CreateNetwork(name, config) + return drivers } diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index 39deb5c1db..d1b64c0357 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -1,12 +1,18 @@ package bridge import ( + "errors" + "fmt" "net" + "strings" "sync" - "github.com/docker/libnetwork" + "github.com/docker/libcontainer/utils" + "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/ipallocator" + "github.com/docker/libnetwork/pkg/options" "github.com/docker/libnetwork/portmapper" + "github.com/vishvananda/netlink" ) const ( @@ -39,23 +45,76 @@ type Configuration struct { EnableIPForwarding bool } -type driver struct{} +type bridgeEndpoint struct { + id driverapi.UUID + addressIPv4 net.IP + addressIPv6 net.IP +} + +type bridgeNetwork struct { + id driverapi.UUID + // bridge interface points to the linux bridge and it's configuration + bridge *bridgeInterface + endpoint *bridgeEndpoint + sync.Mutex +} + +type driver struct { + network *bridgeNetwork + sync.Mutex +} func init() { ipAllocator = ipallocator.New() initPortMapper() - libnetwork.RegisterNetworkType(networkType, &driver{}, &Configuration{}) +} + +// New provides a new instance of bridge driver instance +func New() (string, driverapi.Driver) { + return networkType, &driver{} } // Create a new network using simplebridge plugin -func (d *driver) CreateNetwork(name string, opaqueConfig interface{}) (libnetwork.Network, error) { - config := opaqueConfig.(*Configuration) - bridgeIntfc := newInterface(config) - bridgeSetup := newBridgeSetup(bridgeIntfc) +func (d *driver) CreateNetwork(id driverapi.UUID, option interface{}) error { + + var ( + config *Configuration + err error + ) + + d.Lock() + if d.network != nil { + d.Unlock() + return fmt.Errorf("network already exists, simplebridge can only have one network") + } + d.network = &bridgeNetwork{id: id} + d.Unlock() + defer func() { + // On failure make sure to reset d.network to nil + if err != nil { + d.Lock() + d.network = nil + d.Unlock() + } + }() + + switch opt := option.(type) { + case options.Generic: + opaqueConfig, err := options.GenerateFromModel(opt, &Configuration{}) + if err != nil { + return fmt.Errorf("failed to generate driver config: %v", err) + } + config = opaqueConfig.(*Configuration) + case *Configuration: + config = opt + } + + bridgeIface := newInterface(config) + bridgeSetup := newBridgeSetup(bridgeIface) // If the bridge interface doesn't exist, we need to start the setup steps // by creating a new device and assigning it an IPv4 address. - bridgeAlreadyExists := bridgeIntfc.exists() + bridgeAlreadyExists := bridgeIface.exists() if !bridgeAlreadyExists { bridgeSetup.queueStep(setupDevice) bridgeSetup.queueStep(setupBridgeIPv4) @@ -97,9 +156,226 @@ func (d *driver) CreateNetwork(name string, opaqueConfig interface{}) (libnetwor // Apply the prepared list of steps, and abort at the first error. bridgeSetup.queueStep(setupDeviceUp) - if err := bridgeSetup.apply(); err != nil { + if err = bridgeSetup.apply(); err != nil { + return err + } + + d.network.bridge = bridgeIface + return nil +} + +func (d *driver) DeleteNetwork(nid driverapi.UUID) error { + var err error + d.Lock() + n := d.network + d.network = nil + d.Unlock() + defer func() { + if err != nil { + // On failure set d.network back to n + // but only if is not already take over + // by some other thread + d.Lock() + if d.network == nil { + d.network = n + } + d.Unlock() + } + }() + + if n == nil { + err = driverapi.ErrNoNetwork + return err + } + + if n.endpoint != nil { + err = fmt.Errorf("Network %s has active endpoint %s", n.id, n.endpoint.id) + return err + } + + err = netlink.LinkDel(n.bridge.Link) + return err +} + +func (d *driver) CreateEndpoint(nid, eid driverapi.UUID, sboxKey string, config interface{}) (*driverapi.SandboxInfo, error) { + var ( + ipv6Addr net.IPNet + err error + ) + + d.Lock() + n := d.network + d.Unlock() + if n == nil { + return nil, driverapi.ErrNoNetwork + } + + n.Lock() + if n.id != nid { + n.Unlock() + return nil, fmt.Errorf("invalid network id %s", nid) + } + + if n.endpoint != nil { + n.Unlock() + return nil, driverapi.ErrEndpointExists + } + n.endpoint = &bridgeEndpoint{id: eid} + n.Unlock() + defer func() { + // On failye make sure to reset n.endpoint to nil + if err != nil { + n.Lock() + n.endpoint = nil + n.Unlock() + } + }() + + name1, err := generateIfaceName() + if err != nil { return nil, err } - return &bridgeNetwork{NetworkName: name, bridge: bridgeIntfc}, nil + name2, err := generateIfaceName() + if err != nil { + return nil, err + } + + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0}, + PeerName: name2} + if err = netlink.LinkAdd(veth); err != nil { + return nil, err + } + + host, err := netlink.LinkByName(name1) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + netlink.LinkDel(host) + } + }() + + container, err := netlink.LinkByName(name2) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + netlink.LinkDel(container) + } + }() + + if err = netlink.LinkSetMaster(host, + &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: n.bridge.Config.BridgeName}}); err != nil { + return nil, err + } + + ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil) + if err != nil { + return nil, err + } + ipv4Addr := net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask} + + if n.bridge.Config.EnableIPv6 { + ip6, err := ipAllocator.RequestIP(n.bridge.bridgeIPv6, nil) + if err != nil { + return nil, err + } + ipv6Addr = net.IPNet{IP: ip6, Mask: n.bridge.bridgeIPv6.Mask} + } + + var interfaces []*driverapi.Interface + sinfo := &driverapi.SandboxInfo{} + + intf := &driverapi.Interface{} + intf.SrcName = name2 + intf.DstName = "eth0" + intf.Address = ipv4Addr.String() + sinfo.Gateway = n.bridge.bridgeIPv4.IP.String() + if n.bridge.Config.EnableIPv6 { + intf.AddressIPv6 = ipv6Addr.String() + sinfo.GatewayIPv6 = n.bridge.bridgeIPv6.IP.String() + } + + n.endpoint.addressIPv4 = ip4 + n.endpoint.addressIPv6 = ipv6Addr.IP + interfaces = append(interfaces, intf) + sinfo.Interfaces = interfaces + return sinfo, nil +} + +func (d *driver) DeleteEndpoint(nid, eid driverapi.UUID) error { + var err error + + d.Lock() + n := d.network + d.Unlock() + if n == nil { + return driverapi.ErrNoNetwork + } + + n.Lock() + if n.id != nid { + n.Unlock() + return fmt.Errorf("invalid network id %s", nid) + } + + if n.endpoint == nil { + n.Unlock() + return driverapi.ErrNoEndpoint + } + + ep := n.endpoint + if ep.id != eid { + n.Unlock() + return fmt.Errorf("invalid endpoint id %s", eid) + } + + n.endpoint = nil + n.Unlock() + defer func() { + if err != nil { + // On failure make to set back n.endpoint with ep + // but only if it hasn't been taken over + // already by some other thread. + n.Lock() + if n.endpoint == nil { + n.endpoint = ep + } + n.Unlock() + } + }() + + err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.addressIPv4) + if err != nil { + return err + } + + if n.bridge.Config.EnableIPv6 { + err := ipAllocator.ReleaseIP(n.bridge.bridgeIPv6, n.endpoint.addressIPv6) + if err != nil { + return err + } + } + + return nil +} + +func generateIfaceName() (string, error) { + for i := 0; i < 10; i++ { + name, err := utils.GenerateRandomName("veth", 7) + if err != nil { + continue + } + if _, err := net.InterfaceByName(name); err != nil { + if strings.Contains(err.Error(), "no such") { + return name, nil + } + return "", err + } + } + return "", errors.New("Failed to find name for new interface") } diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index 50529f5392..eb1cfa2c41 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -4,37 +4,33 @@ import ( "net" "testing" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" ) func TestCreate(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() - d := &driver{} + defer netutils.SetupTestNetNS(t)() + _, d := New() config := &Configuration{BridgeName: DefaultBridgeName} - netw, err := d.CreateNetwork("dummy", config) + err := d.CreateNetwork("dummy", config) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - - if expected := networkType; netw.Type() != expected { - t.Fatalf("Expected networkType %q, got %q", expected, netw.Type()) - } } func TestCreateFail(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() - d := &driver{} + defer netutils.SetupTestNetNS(t)() + _, d := New() config := &Configuration{BridgeName: "dummy0"} - if _, err := d.CreateNetwork("dummy", config); err == nil { + if err := d.CreateNetwork("dummy", config); err == nil { t.Fatal("Bridge creation was expected to fail") } } func TestCreateFullOptions(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() - d := &driver{} + defer netutils.SetupTestNetNS(t)() + _, d := New() config := &Configuration{ BridgeName: DefaultBridgeName, @@ -45,12 +41,8 @@ func TestCreateFullOptions(t *testing.T) { } _, config.FixedCIDRv6, _ = net.ParseCIDR("2001:db8::/48") - netw, err := d.CreateNetwork("dummy", config) + err := d.CreateNetwork("dummy", config) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - - if expected := networkType; netw.Type() != expected { - t.Fatalf("Expected networkType %q, got %q", expected, netw.Type()) - } } diff --git a/libnetwork/drivers/bridge/interface_test.go b/libnetwork/drivers/bridge/interface_test.go index fe2472458e..c67cb1a169 100644 --- a/libnetwork/drivers/bridge/interface_test.go +++ b/libnetwork/drivers/bridge/interface_test.go @@ -3,12 +3,12 @@ package bridge import ( "testing" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" "github.com/vishvananda/netlink" ) func TestInterfaceDefaultName(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() if inf := newInterface(&Configuration{}); inf.Config.BridgeName != DefaultBridgeName { t.Fatalf("Expected default interface name %q, got %q", DefaultBridgeName, inf.Config.BridgeName) @@ -16,7 +16,7 @@ func TestInterfaceDefaultName(t *testing.T) { } func TestAddressesEmptyInterface(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() inf := newInterface(&Configuration{}) addrv4, addrsv6, err := inf.addresses() diff --git a/libnetwork/drivers/bridge/network.go b/libnetwork/drivers/bridge/network.go deleted file mode 100644 index 75fad892df..0000000000 --- a/libnetwork/drivers/bridge/network.go +++ /dev/null @@ -1,128 +0,0 @@ -package bridge - -import ( - "errors" - "net" - "strings" - - "github.com/docker/libcontainer/utils" - "github.com/docker/libnetwork" - "github.com/vishvananda/netlink" -) - -// ErrEndpointExists is returned if more than one endpoint is added to the network -var ErrEndpointExists = errors.New("Endpoint already exists (Only one endpoint allowed)") - -type bridgeNetwork struct { - NetworkName string - bridge *bridgeInterface - EndPoint *libnetwork.Interface -} - -func (b *bridgeNetwork) Name() string { - return b.NetworkName -} - -func (b *bridgeNetwork) Type() string { - return networkType -} - -func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) { - - var ipv6Addr net.IPNet - - if b.EndPoint != nil { - return nil, ErrEndpointExists - } - - name1, err := generateIfaceName() - if err != nil { - return nil, err - } - - name2, err := generateIfaceName() - if err != nil { - return nil, err - } - - veth := &netlink.Veth{ - LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0}, - PeerName: name2} - if err := netlink.LinkAdd(veth); err != nil { - return nil, err - } - - host, err := netlink.LinkByName(name1) - if err != nil { - return nil, err - } - defer func() { - if err != nil { - netlink.LinkDel(host) - } - }() - - container, err := netlink.LinkByName(name2) - if err != nil { - return nil, err - } - defer func() { - if err != nil { - netlink.LinkDel(container) - } - }() - - if err = netlink.LinkSetMaster(host, - &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: b.bridge.Config.BridgeName}}); err != nil { - return nil, err - } - - ip4, err := ipAllocator.RequestIP(b.bridge.bridgeIPv4, nil) - if err != nil { - return nil, err - } - ipv4Addr := net.IPNet{IP: ip4, Mask: b.bridge.bridgeIPv4.Mask} - - if b.bridge.Config.EnableIPv6 { - ip6, err := ipAllocator.RequestIP(b.bridge.bridgeIPv6, nil) - if err != nil { - return nil, err - } - ipv6Addr = net.IPNet{IP: ip6, Mask: b.bridge.bridgeIPv6.Mask} - } - - var interfaces []*libnetwork.Interface - intf := &libnetwork.Interface{} - intf.SrcName = name2 - intf.DstName = "eth0" - intf.Address = ipv4Addr.String() - intf.Gateway = b.bridge.bridgeIPv4.IP.String() - if b.bridge.Config.EnableIPv6 { - intf.AddressIPv6 = ipv6Addr.String() - intf.GatewayIPv6 = b.bridge.bridgeIPv6.IP.String() - } - - b.EndPoint = intf - interfaces = append(interfaces, intf) - return interfaces, nil -} - -func generateIfaceName() (string, error) { - for i := 0; i < 10; i++ { - name, err := utils.GenerateRandomName("veth", 7) - if err != nil { - continue - } - if _, err := net.InterfaceByName(name); err != nil { - if strings.Contains(err.Error(), "no such") { - return name, nil - } - return "", err - } - } - return "", errors.New("Failed to find name for new interface") -} - -func (b *bridgeNetwork) Delete() error { - return netlink.LinkDel(b.bridge.Link) -} diff --git a/libnetwork/drivers/bridge/network_test.go b/libnetwork/drivers/bridge/network_test.go index 2815ca1cfa..ef41bfff07 100644 --- a/libnetwork/drivers/bridge/network_test.go +++ b/libnetwork/drivers/bridge/network_test.go @@ -4,27 +4,30 @@ import ( "net" "testing" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/netutils" "github.com/vishvananda/netlink" ) func TestLinkCreate(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() - d := &driver{} + defer netutils.SetupTestNetNS(t)() + _, d := New() + dr := d.(*driver) config := &Configuration{ BridgeName: DefaultBridgeName, EnableIPv6: true} - netw, err := d.CreateNetwork("dummy", config) + err := d.CreateNetwork("dummy", config) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - interfaces, err := netw.Link("ep") + sinfo, err := d.CreateEndpoint("dummy", "ep", "", "") if err != nil { t.Fatalf("Failed to create a link: %v", err) } + interfaces := sinfo.Interfaces if len(interfaces) != 1 { t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(interfaces)) } @@ -43,9 +46,9 @@ func TestLinkCreate(t *testing.T) { t.Fatalf("Invalid IPv4 address returned, ip = %s: %v", interfaces[0].Address, err) } - b := netw.(*bridgeNetwork) - if !b.bridge.bridgeIPv4.Contains(ip) { - t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), b.bridge.bridgeIPv4.String()) + n := dr.network + if !n.bridge.bridgeIPv4.Contains(ip) { + t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), n.bridge.bridgeIPv4.String()) } ip6, _, err := net.ParseCIDR(interfaces[0].AddressIPv6) @@ -53,41 +56,41 @@ func TestLinkCreate(t *testing.T) { t.Fatalf("Invalid IPv6 address returned, ip = %s: %v", interfaces[0].AddressIPv6, err) } - if !b.bridge.bridgeIPv6.Contains(ip6) { + if !n.bridge.bridgeIPv6.Contains(ip6) { t.Fatalf("IP %s is not a valid ip in the subnet %s", ip6.String(), bridgeIPv6.String()) } - if interfaces[0].Gateway != b.bridge.bridgeIPv4.IP.String() { - t.Fatalf("Invalid default gateway. Expected %s. Got %s", b.bridge.bridgeIPv4.IP.String(), - interfaces[0].Gateway) + if sinfo.Gateway != n.bridge.bridgeIPv4.IP.String() { + t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(), + sinfo.Gateway) } - if interfaces[0].GatewayIPv6 != b.bridge.bridgeIPv6.IP.String() { - t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", b.bridge.bridgeIPv6.IP.String(), - interfaces[0].GatewayIPv6) + if sinfo.GatewayIPv6 != n.bridge.bridgeIPv6.IP.String() { + t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(), + sinfo.GatewayIPv6) } } func TestLinkCreateTwo(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() - d := &driver{} + defer netutils.SetupTestNetNS(t)() + _, d := New() config := &Configuration{ BridgeName: DefaultBridgeName, EnableIPv6: true} - netw, err := d.CreateNetwork("dummy", config) + err := d.CreateNetwork("dummy", config) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - _, err = netw.Link("ep") + _, err = d.CreateEndpoint("dummy", "ep", "", "") if err != nil { t.Fatalf("Failed to create a link: %v", err) } - _, err = netw.Link("ep1") + _, err = d.CreateEndpoint("dummy", "ep1", "", "") if err != nil { - if err != ErrEndpointExists { + if err != driverapi.ErrEndpointExists { t.Fatalf("Failed with a wrong error :%v", err) } } else { @@ -96,24 +99,25 @@ func TestLinkCreateTwo(t *testing.T) { } func TestLinkCreateNoEnableIPv6(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() - d := &driver{} + defer netutils.SetupTestNetNS(t)() + _, d := New() config := &Configuration{ BridgeName: DefaultBridgeName} - netw, err := d.CreateNetwork("dummy", config) + err := d.CreateNetwork("dummy", config) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - interfaces, err := netw.Link("ep") + sinfo, err := d.CreateEndpoint("dummy", "ep", "", "") if err != nil { t.Fatalf("Failed to create a link: %v", err) } + interfaces := sinfo.Interfaces if interfaces[0].AddressIPv6 != "" || - interfaces[0].GatewayIPv6 != "" { + sinfo.GatewayIPv6 != "" { t.Fatalf("Expected IPv6 address and GatewayIPv6 to be empty when IPv6 enabled. Instead got IPv6 = %s and GatewayIPv6 = %s", - interfaces[0].AddressIPv6, interfaces[0].GatewayIPv6) + interfaces[0].AddressIPv6, sinfo.GatewayIPv6) } } diff --git a/libnetwork/drivers/bridge/setup_device.go b/libnetwork/drivers/bridge/setup_device.go index bb947f660f..6717d948f5 100644 --- a/libnetwork/drivers/bridge/setup_device.go +++ b/libnetwork/drivers/bridge/setup_device.go @@ -5,7 +5,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" "github.com/vishvananda/netlink" ) @@ -28,7 +28,7 @@ func setupDevice(i *bridgeInterface) error { // was not supported before that. kv, err := kernel.GetKernelVersion() if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) { - i.Link.Attrs().HardwareAddr = libnetwork.GenerateRandomMAC() + i.Link.Attrs().HardwareAddr = netutils.GenerateRandomMAC() log.Debugf("Setting bridge mac address to %s", i.Link.Attrs().HardwareAddr) } diff --git a/libnetwork/drivers/bridge/setup_device_test.go b/libnetwork/drivers/bridge/setup_device_test.go index 04fd27cb30..220297373e 100644 --- a/libnetwork/drivers/bridge/setup_device_test.go +++ b/libnetwork/drivers/bridge/setup_device_test.go @@ -6,12 +6,12 @@ import ( "strings" "testing" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" "github.com/vishvananda/netlink" ) func TestSetupNewBridge(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() br := &bridgeInterface{ Config: &Configuration{ @@ -33,7 +33,7 @@ func TestSetupNewBridge(t *testing.T) { } func TestSetupNewNonDefaultBridge(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() br := &bridgeInterface{ Config: &Configuration{ @@ -46,7 +46,7 @@ func TestSetupNewNonDefaultBridge(t *testing.T) { } func TestSetupDeviceUp(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() br := &bridgeInterface{ Config: &Configuration{ @@ -67,10 +67,10 @@ func TestSetupDeviceUp(t *testing.T) { } func TestGenerateRandomMAC(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() - mac1 := libnetwork.GenerateRandomMAC() - mac2 := libnetwork.GenerateRandomMAC() + mac1 := netutils.GenerateRandomMAC() + mac2 := netutils.GenerateRandomMAC() if bytes.Compare(mac1, mac2) == 0 { t.Fatalf("Generated twice the same MAC address %v", mac1) } diff --git a/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go b/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go index 8913c7e71b..4f878955fe 100644 --- a/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go +++ b/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go @@ -4,11 +4,11 @@ import ( "net" "testing" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" ) func TestSetupFixedCIDRv4(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() br := &bridgeInterface{ Config: &Configuration{ @@ -36,7 +36,7 @@ func TestSetupFixedCIDRv4(t *testing.T) { } func TestSetupBadFixedCIDRv4(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() br := &bridgeInterface{ Config: &Configuration{ diff --git a/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go b/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go index 2d185f3617..f106919a78 100644 --- a/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go +++ b/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go @@ -4,11 +4,11 @@ import ( "net" "testing" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" ) func TestSetupFixedCIDRv6(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() br := newInterface(&Configuration{}) diff --git a/libnetwork/drivers/bridge/setup_ip_tables.go b/libnetwork/drivers/bridge/setup_ip_tables.go index f74c5a704e..16ad7ab9f6 100644 --- a/libnetwork/drivers/bridge/setup_ip_tables.go +++ b/libnetwork/drivers/bridge/setup_ip_tables.go @@ -5,7 +5,7 @@ import ( "net" "github.com/docker/docker/pkg/iptables" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" ) // DockerChain: DOCKER iptable chain name @@ -19,7 +19,7 @@ func setupIPTables(i *bridgeInterface) error { return fmt.Errorf("Unexpected request to set IP tables for interface: %s", i.Config.BridgeName) } - addrv4, _, err := libnetwork.GetIfaceAddr(i.Config.BridgeName) + addrv4, _, err := netutils.GetIfaceAddr(i.Config.BridgeName) if err != nil { return fmt.Errorf("Failed to setup IP tables, cannot acquire Interface address: %s", err.Error()) } diff --git a/libnetwork/drivers/bridge/setup_ip_tables_test.go b/libnetwork/drivers/bridge/setup_ip_tables_test.go index ca2913f058..733de95b80 100644 --- a/libnetwork/drivers/bridge/setup_ip_tables_test.go +++ b/libnetwork/drivers/bridge/setup_ip_tables_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/docker/docker/pkg/iptables" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" ) const ( @@ -14,7 +14,7 @@ const ( func TestProgramIPTable(t *testing.T) { // Create a test bridge with a basic bridge configuration (name + IPv4). - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() createTestBridge(getBasicTestConfig(), t) // Store various iptables chain rules we care for. @@ -38,7 +38,7 @@ func TestProgramIPTable(t *testing.T) { func TestSetupIPTables(t *testing.T) { // Create a test bridge with a basic bridge configuration (name + IPv4). - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() br := getBasicTestConfig() createTestBridge(br, t) diff --git a/libnetwork/drivers/bridge/setup_ipv4.go b/libnetwork/drivers/bridge/setup_ipv4.go index 5f942c4175..9ecf4cb946 100644 --- a/libnetwork/drivers/bridge/setup_ipv4.go +++ b/libnetwork/drivers/bridge/setup_ipv4.go @@ -5,7 +5,7 @@ import ( "net" log "github.com/Sirupsen/logrus" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" "github.com/vishvananda/netlink" ) @@ -73,8 +73,8 @@ func electBridgeIPv4(config *Configuration) (*net.IPNet, error) { // Try to automatically elect appropriate brige IPv4 settings. for _, n := range bridgeNetworks { - if err := libnetwork.CheckNameserverOverlaps(nameservers, n); err == nil { - if err := libnetwork.CheckRouteOverlaps(n); err == nil { + if err := netutils.CheckNameserverOverlaps(nameservers, n); err == nil { + if err := netutils.CheckRouteOverlaps(n); err == nil { return n, nil } } diff --git a/libnetwork/drivers/bridge/setup_ipv4_test.go b/libnetwork/drivers/bridge/setup_ipv4_test.go index 6639f3b9fc..2e148f5239 100644 --- a/libnetwork/drivers/bridge/setup_ipv4_test.go +++ b/libnetwork/drivers/bridge/setup_ipv4_test.go @@ -4,7 +4,7 @@ import ( "net" "testing" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" "github.com/vishvananda/netlink" ) @@ -21,7 +21,7 @@ func setupTestInterface(t *testing.T) *bridgeInterface { } func TestSetupBridgeIPv4Fixed(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() ip, netw, err := net.ParseCIDR("192.168.1.1/24") if err != nil { @@ -53,7 +53,7 @@ func TestSetupBridgeIPv4Fixed(t *testing.T) { } func TestSetupBridgeIPv4Auto(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() br := setupTestInterface(t) if err := setupBridgeIPv4(br); err != nil { diff --git a/libnetwork/drivers/bridge/setup_ipv6_test.go b/libnetwork/drivers/bridge/setup_ipv6_test.go index c85340cd5c..24edcd3025 100644 --- a/libnetwork/drivers/bridge/setup_ipv6_test.go +++ b/libnetwork/drivers/bridge/setup_ipv6_test.go @@ -6,12 +6,12 @@ import ( "io/ioutil" "testing" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" "github.com/vishvananda/netlink" ) func TestSetupIPv6(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() br := setupTestInterface(t) if err := setupBridgeIPv6(br); err != nil { diff --git a/libnetwork/drivers/bridge/setup_verify_test.go b/libnetwork/drivers/bridge/setup_verify_test.go index 3201df635a..3555c31348 100644 --- a/libnetwork/drivers/bridge/setup_verify_test.go +++ b/libnetwork/drivers/bridge/setup_verify_test.go @@ -4,7 +4,7 @@ import ( "net" "testing" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" "github.com/vishvananda/netlink" ) @@ -23,7 +23,7 @@ func setupVerifyTest(t *testing.T) *bridgeInterface { } func TestSetupVerify(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() addrv4 := net.IPv4(192, 168, 1, 1) inf := setupVerifyTest(t) @@ -39,7 +39,7 @@ func TestSetupVerify(t *testing.T) { } func TestSetupVerifyBad(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() addrv4 := net.IPv4(192, 168, 1, 1) inf := setupVerifyTest(t) @@ -56,7 +56,7 @@ func TestSetupVerifyBad(t *testing.T) { } func TestSetupVerifyMissing(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() addrv4 := net.IPv4(192, 168, 1, 1) inf := setupVerifyTest(t) @@ -68,7 +68,7 @@ func TestSetupVerifyMissing(t *testing.T) { } func TestSetupVerifyIPv6(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() addrv4 := net.IPv4(192, 168, 1, 1) inf := setupVerifyTest(t) @@ -88,7 +88,7 @@ func TestSetupVerifyIPv6(t *testing.T) { } func TestSetupVerifyIPv6Missing(t *testing.T) { - defer libnetwork.SetupTestNetNS(t)() + defer netutils.SetupTestNetNS(t)() addrv4 := net.IPv4(192, 168, 1, 1) inf := setupVerifyTest(t) diff --git a/libnetwork/ipallocator/allocator.go b/libnetwork/ipallocator/allocator.go index 9783456a7e..36a5871310 100644 --- a/libnetwork/ipallocator/allocator.go +++ b/libnetwork/ipallocator/allocator.go @@ -9,7 +9,7 @@ import ( "sync" "github.com/Sirupsen/logrus" - "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netutils" ) // allocatedMap is thread-unsafe set of allocated IP @@ -21,7 +21,7 @@ type allocatedMap struct { } func newAllocatedMap(network *net.IPNet) *allocatedMap { - firstIP, lastIP := libnetwork.NetworkRange(network) + firstIP, lastIP := netutils.NetworkRange(network) begin := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1)) end := big.NewInt(0).Sub(ipToBigInt(lastIP), big.NewInt(1)) @@ -71,7 +71,7 @@ func (a *IPAllocator) RegisterSubnet(network *net.IPNet, subnet *net.IPNet) erro return ErrNetworkAlreadyRegistered } n := newAllocatedMap(network) - beginIP, endIP := libnetwork.NetworkRange(subnet) + beginIP, endIP := netutils.NetworkRange(subnet) begin := big.NewInt(0).Add(ipToBigInt(beginIP), big.NewInt(1)) end := big.NewInt(0).Sub(ipToBigInt(endIP), big.NewInt(1)) diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index ebc59f831f..ffa20fb8da 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -8,6 +8,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork" _ "github.com/docker/libnetwork/drivers/bridge" + "github.com/docker/libnetwork/pkg/options" "github.com/vishvananda/netlink" ) @@ -41,7 +42,7 @@ func TestSimplebridge(t *testing.T) { cidrv6.IP = ip log.Debug("Adding a simple bridge") - options := libnetwork.DriverParams{ + options := options.Generic{ "BridgeName": bridgeName, "AddressIPv4": subnet, "FixedCIDR": cidr, @@ -52,7 +53,9 @@ func TestSimplebridge(t *testing.T) { "EnableICC": true, "EnableIPForwarding": true} - network, err := libnetwork.NewNetwork("simplebridge", "dummy", options) + controller := libnetwork.New() + + network, err := controller.NewNetwork("simplebridge", "dummy", options) if err != nil { t.Fatal(err) } diff --git a/libnetwork/testing.go b/libnetwork/netutils/test_utils.go similarity index 97% rename from libnetwork/testing.go rename to libnetwork/netutils/test_utils.go index 3c8960ab34..841b49ec71 100644 --- a/libnetwork/testing.go +++ b/libnetwork/netutils/test_utils.go @@ -1,4 +1,4 @@ -package libnetwork +package netutils import ( "runtime" diff --git a/libnetwork/utils.go b/libnetwork/netutils/utils.go similarity index 99% rename from libnetwork/utils.go rename to libnetwork/netutils/utils.go index 64e6d7e852..ce1c6193f3 100644 --- a/libnetwork/utils.go +++ b/libnetwork/netutils/utils.go @@ -1,7 +1,7 @@ // Network utility functions. // Imported unchanged from Docker -package libnetwork +package netutils import ( "errors" diff --git a/libnetwork/utils_test.go b/libnetwork/netutils/utils_test.go similarity index 99% rename from libnetwork/utils_test.go rename to libnetwork/netutils/utils_test.go index f6f0eb29c6..96faa0897b 100644 --- a/libnetwork/utils_test.go +++ b/libnetwork/netutils/utils_test.go @@ -1,4 +1,4 @@ -package libnetwork +package netutils import ( "net" diff --git a/libnetwork/network.go b/libnetwork/network.go index 9d81745f27..8b7361c558 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -1,60 +1,61 @@ -// Package libnetwork provides basic fonctionalities and extension points to -// create network namespaces and allocate interfaces for containers to use. -// -// // Create a network for containers to join. -// network, err := libnetwork.NewNetwork("simplebridge", &Options{}) -// if err != nil { -// return err -// } -// -// // For a new container: create network namespace (providing the path). -// networkPath := "/var/lib/docker/.../4d23e" -// networkNamespace, err := libnetwork.NewNetworkNamespace(networkPath) -// if err != nil { -// return err -// } -// -// // For each new container: allocate IP and interfaces. The returned network -// // settings will be used for container infos (inspect and such), as well as -// // iptables rules for port publishing. -// interfaces, err := network.Link(containerID) -// if err != nil { -// return err -// } -// -// // Add interfaces to the namespace. -// for _, interface := range interfaces { -// if err := networkNamespace.AddInterface(interface); err != nil { -// return err -// } -// } -// +/* +Package libnetwork provides basic fonctionalities and extension points to +create network namespaces and allocate interfaces for containers to use. + +// Create a new controller instance +controller := libnetwork.New() + +options := options.Generic{} +// Create a network for containers to join. +network, err := controller.NewNetwork("simplebridge", "network1", options) +if err != nil { + return +} + +// For a new container: create a sandbox instance (providing a unique key). +// For linux it is a filesystem path +networkPath := "/var/lib/docker/.../4d23e" +networkNamespace, err := sandbox.NewSandbox(networkPath) +if err != nil { + return +} + +// For each new container: allocate IP and interfaces. The returned network +// settings will be used for container infos (inspect and such), as well as +// iptables rules for port publishing. +_, sinfo, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), "") +if err != nil { + return +} + +// Add interfaces to the namespace. +for _, iface := range sinfo.Interfaces { + if err := networkNamespace.AddInterface(iface); err != nil { + return + } +} + +// Set the gateway IP +if err := networkNamespace.SetGateway(sinfo.Gateway); err != nil { + return +} +*/ package libnetwork -// Interface represents the settings and identity of a network device. It is -// used as a return type for Network.Link, and it is common practice for the -// caller to use this information when moving interface SrcName from host -// namespace to DstName in a different net namespace with the appropriate -// network settings. -type Interface struct { - // The name of the interface in the origin network namespace. - SrcName string +import ( + "fmt" + "sync" - // The name that will be assigned to the interface once moves inside a - // network namespace. - DstName string + "github.com/docker/docker/pkg/common" + "github.com/docker/libnetwork/driverapi" +) - // IPv4 address for the interface. - Address string - - // IPv6 address for the interface. - AddressIPv6 string - - // IPv4 gateway for the interface. - Gateway string - - // IPv6 gateway for the interface. - GatewayIPv6 string +// NetworkController provides the interface for controller instance which manages +// networks. +type NetworkController interface { + // Create a new network. The options parameter carry driver specific options. + // Labels support will be added in the near future. + NewNetwork(networkType, name string, options interface{}) (Network, error) } // A Network represents a logical connectivity zone that containers may @@ -64,42 +65,176 @@ type Network interface { // A user chosen name for this network. Name() string + // A system generated id for this network. + ID() string + // The type of network, which corresponds to its managing driver. Type() string - // Create a new link to this network symbolically identified by the - // specified unique name. - Link(name string) ([]*Interface, error) + // Create a new endpoint to this network symbolically identified by the + // specified unique name. The options parameter carry driver specific options. + // Labels support will be added in the near future. + CreateEndpoint(name string, sboxKey string, options interface{}) (Endpoint, *driverapi.SandboxInfo, error) + // Delete the network. Delete() error } -// Namespace represents a network namespace, mounted on a specific Path. It -// holds a list of Interface, and more can be added dynamically. -type Namespace interface { - // The path where the network namespace is mounted. - Path() string +// Endpoint represents a logical connection between a network and a sandbox. +type Endpoint interface { + // Delete endpoint. + Delete() error +} - // The collection of Interface previously added with the AddInterface - // method. Note that this doesn't incude network interfaces added in any - // other way (such as the default loopback interface existing in any newly - // created network namespace). - Interfaces() []*Interface +type endpoint struct { + name string + id driverapi.UUID + network *network + sandboxInfo *driverapi.SandboxInfo +} - // Add an existing Interface to this namespace. The operation will rename - // from the Interface SrcName to DstName as it moves, and reconfigure the - // interface according to the specified settings. - AddInterface(*Interface) error +type network struct { + ctrlr *controller + name string + networkType string + id driverapi.UUID + endpoints map[driverapi.UUID]*endpoint + sync.Mutex +} + +type networkTable map[driverapi.UUID]*network + +type controller struct { + networks networkTable + drivers driverTable + sync.Mutex +} + +// New creates a new instance of network controller. +func New() NetworkController { + return &controller{networkTable{}, enumerateDrivers(), sync.Mutex{}} } // NewNetwork creates a new network of the specified networkType. The options // are driver specific and modeled in a generic way. -func NewNetwork(networkType, name string, options DriverParams) (Network, error) { - return createNetwork(networkType, name, options) +func (c *controller) NewNetwork(networkType, name string, options interface{}) (Network, error) { + network := &network{name: name, networkType: networkType} + network.id = driverapi.UUID(common.GenerateRandomID()) + network.ctrlr = c + + d, ok := c.drivers[networkType] + if !ok { + return nil, fmt.Errorf("unknown driver %q", networkType) + } + + if err := d.CreateNetwork(network.id, options); err != nil { + return nil, err + } + + c.Lock() + c.networks[network.id] = network + c.Unlock() + + return network, nil } -// NewNetworkNamespace creates a new network namespace mounted on the specified -// path. -func NewNetworkNamespace(path string) (Namespace, error) { - return createNetworkNamespace(path) +func (n *network) Name() string { + return n.name +} + +func (n *network) ID() string { + return string(n.id) +} + +func (n *network) Type() string { + return n.networkType +} + +func (n *network) Delete() error { + var err error + + d, ok := n.ctrlr.drivers[n.networkType] + if !ok { + return fmt.Errorf("unknown driver %q", n.networkType) + } + + n.ctrlr.Lock() + _, ok = n.ctrlr.networks[n.id] + if !ok { + n.ctrlr.Unlock() + return fmt.Errorf("unknown network %s id %s", n.name, n.id) + } + + n.Lock() + numEps := len(n.endpoints) + n.Unlock() + if numEps != 0 { + n.ctrlr.Unlock() + return fmt.Errorf("network %s has active endpoints", n.id) + } + + delete(n.ctrlr.networks, n.id) + n.ctrlr.Unlock() + defer func() { + if err != nil { + n.ctrlr.Lock() + n.ctrlr.networks[n.id] = n + n.ctrlr.Unlock() + } + }() + + err = d.DeleteNetwork(n.id) + return err +} + +func (n *network) CreateEndpoint(name string, sboxKey string, options interface{}) (Endpoint, *driverapi.SandboxInfo, error) { + ep := &endpoint{name: name} + ep.id = driverapi.UUID(common.GenerateRandomID()) + ep.network = n + + d, ok := n.ctrlr.drivers[n.networkType] + if !ok { + return nil, nil, fmt.Errorf("unknown driver %q", n.networkType) + } + + sinfo, err := d.CreateEndpoint(n.id, ep.id, sboxKey, options) + if err != nil { + return nil, nil, err + } + + ep.sandboxInfo = sinfo + n.Lock() + n.endpoints[ep.id] = ep + n.Unlock() + return ep, sinfo, nil +} + +func (ep *endpoint) Delete() error { + var err error + + d, ok := ep.network.ctrlr.drivers[ep.network.networkType] + if !ok { + return fmt.Errorf("unknown driver %q", ep.network.networkType) + } + + n := ep.network + n.Lock() + _, ok = n.endpoints[ep.id] + if !ok { + n.Unlock() + return fmt.Errorf("unknown endpoint %s id %s", ep.name, ep.id) + } + + delete(n.endpoints, ep.id) + n.Unlock() + defer func() { + if err != nil { + n.Lock() + n.endpoints[ep.id] = ep + n.Unlock() + } + }() + + err = d.DeleteEndpoint(n.id, ep.id) + return err } diff --git a/libnetwork/sandbox/configure_linux.go b/libnetwork/sandbox/configure_linux.go new file mode 100644 index 0000000000..44f642b8a3 --- /dev/null +++ b/libnetwork/sandbox/configure_linux.go @@ -0,0 +1,66 @@ +package sandbox + +import ( + "fmt" + "net" + + "github.com/docker/libnetwork/driverapi" + "github.com/vishvananda/netlink" +) + +func configureInterface(iface netlink.Link, settings *driverapi.Interface) error { + ifaceName := iface.Attrs().Name + ifaceConfigurators := []struct { + Fn func(netlink.Link, *driverapi.Interface) error + ErrMessage string + }{ + {setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, settings.DstName)}, + {setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %q", ifaceName, settings.Address)}, + {setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %q", ifaceName, settings.AddressIPv6)}, + /* {setInterfaceGateway, fmt.Sprintf("error setting interface %q gateway to %q", ifaceName, settings.Gateway)}, + {setInterfaceGatewayIPv6, fmt.Sprintf("error setting interface %q IPv6 gateway to %q", ifaceName, settings.GatewayIPv6)}, */ + } + + for _, config := range ifaceConfigurators { + if err := config.Fn(iface, settings); err != nil { + return fmt.Errorf("%s: %v", config.ErrMessage, err) + } + } + return nil +} + +func setGatewayIP(gw string) error { + ip := net.ParseIP(gw) + if ip == nil { + return fmt.Errorf("bad address format %q", gw) + } + + return netlink.RouteAdd(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + Gw: ip, + }) +} + +func setInterfaceIP(iface netlink.Link, settings *driverapi.Interface) error { + ipAddr, err := netlink.ParseAddr(settings.Address) + if err == nil { + err = netlink.AddrAdd(iface, ipAddr) + } + return err +} + +func setInterfaceIPv6(iface netlink.Link, settings *driverapi.Interface) error { + if settings.AddressIPv6 == "" { + return nil + } + + ipAddr, err := netlink.ParseAddr(settings.AddressIPv6) + if err == nil { + err = netlink.AddrAdd(iface, ipAddr) + } + return err +} + +func setInterfaceName(iface netlink.Link, settings *driverapi.Interface) error { + return netlink.LinkSetName(iface, settings.DstName) +} diff --git a/libnetwork/namespace.go b/libnetwork/sandbox/namespace_linux.go similarity index 65% rename from libnetwork/namespace.go rename to libnetwork/sandbox/namespace_linux.go index 4c4c9ac975..f39d50be45 100644 --- a/libnetwork/namespace.go +++ b/libnetwork/sandbox/namespace_linux.go @@ -1,4 +1,4 @@ -package libnetwork +package sandbox import ( "fmt" @@ -6,19 +6,26 @@ import ( "runtime" "syscall" + "github.com/docker/libnetwork/driverapi" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" ) -// The networkNamespace type is the default implementation of the Namespace -// interface. It simply creates a new network namespace, and moves an interface -// into it when called on method AddInterface. +// The networkNamespace type is the linux implementation of the Sandbox +// interface. It represents a linux network namespace, and moves an interface +// into it when called on method AddInterface or sets the gateway etc. type networkNamespace struct { - path string - interfaces []*Interface + path string + sinfo *driverapi.SandboxInfo } -func createNetworkNamespace(path string) (Namespace, error) { +// NewSandbox provides a new sandbox instance created in an os specific way +// provided a key which uniquely identifies the sandbox +func NewSandbox(key string) (Sandbox, error) { + return createNetworkNamespace(key) +} + +func createNetworkNamespace(path string) (Sandbox, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -66,7 +73,7 @@ func loopbackUp() error { return netlink.LinkSetUp(iface) } -func (n *networkNamespace) AddInterface(i *Interface) error { +func (n *networkNamespace) AddInterface(i *driverapi.Interface) error { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -109,15 +116,33 @@ func (n *networkNamespace) AddInterface(i *Interface) error { return err } - n.interfaces = append(n.interfaces, i) + n.sinfo.Interfaces = append(n.sinfo.Interfaces, i) return nil } -func (n *networkNamespace) Interfaces() []*Interface { - return n.interfaces +func (n *networkNamespace) SetGateway(gw string) error { + err := setGatewayIP(gw) + if err == nil { + n.sinfo.Gateway = gw + } + + return err } -func (n *networkNamespace) Path() string { +func (n *networkNamespace) SetGatewayIPv6(gw string) error { + err := setGatewayIP(gw) + if err == nil { + n.sinfo.GatewayIPv6 = gw + } + + return err +} + +func (n *networkNamespace) Interfaces() []*driverapi.Interface { + return n.sinfo.Interfaces +} + +func (n *networkNamespace) Key() string { return n.path } diff --git a/libnetwork/sandbox/sandbox.go b/libnetwork/sandbox/sandbox.go new file mode 100644 index 0000000000..554f91ad2a --- /dev/null +++ b/libnetwork/sandbox/sandbox.go @@ -0,0 +1,25 @@ +package sandbox + +import "github.com/docker/libnetwork/driverapi" + +// Sandbox represents a network sandbox, identified by a specific key. It +// holds a list of Interfaces, routes etc, and more can be added dynamically. +type Sandbox interface { + // The path where the network namespace is mounted. + Key() string + + // The collection of Interface previously added with the AddInterface + // method. Note that this doesn't incude network interfaces added in any + // other way (such as the default loopback interface which are automatically + // created on creation of a sandbox). + Interfaces() []*driverapi.Interface + + // Add an existing Interface to this sandbox. The operation will rename + // from the Interface SrcName to DstName as it moves, and reconfigure the + // interface according to the specified settings. + AddInterface(*driverapi.Interface) error + + SetGateway(gw string) error + + SetGatewayIPv6(gw string) error +} diff --git a/libnetwork/sandbox/sandbox_linux_test.go b/libnetwork/sandbox/sandbox_linux_test.go new file mode 100644 index 0000000000..abbebd7ca4 --- /dev/null +++ b/libnetwork/sandbox/sandbox_linux_test.go @@ -0,0 +1,54 @@ +package sandbox + +import ( + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/docker/libcontainer/utils" + "github.com/vishvananda/netns" +) + +func newKey(t *testing.T) (string, error) { + name, err := utils.GenerateRandomName("netns", 12) + if err != nil { + return "", err + } + + name = filepath.Join("/tmp", name) + if _, err := os.Create(name); err != nil { + return "", err + } + + return name, nil +} + +func verifySandbox(t *testing.T, s Sandbox) { + _, ok := s.(*networkNamespace) + if !ok { + t.Fatalf("The sandox interface returned is not of type networkNamespace") + } + + origns, err := netns.Get() + if err != nil { + t.Fatalf("Could not get the current netns: %v", err) + } + defer origns.Close() + + f, err := os.OpenFile(s.Key(), os.O_RDONLY, 0) + if err != nil { + t.Fatalf("Failed top open network namespace path %q: %v", s.Key(), err) + } + defer f.Close() + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + nsFD := f.Fd() + if err = netns.Set(netns.NsHandle(nsFD)); err != nil { + t.Fatalf("Setting to the namespace pointed to by the sandbox %s failed: %v", s.Key(), err) + } + + netns.Set(origns) +} diff --git a/libnetwork/sandbox/sandbox_test.go b/libnetwork/sandbox/sandbox_test.go new file mode 100644 index 0000000000..07ea68dc1b --- /dev/null +++ b/libnetwork/sandbox/sandbox_test.go @@ -0,0 +1,21 @@ +package sandbox + +import "testing" + +func TestSandboxCreate(t *testing.T) { + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + s, err := NewSandbox(key) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + + if s.Key() != key { + t.Fatalf("s.Key() returned %s. Expected %s", s.Key(), key) + } + + verifySandbox(t, s) +}