소스 검색

Merge pull request #152 from mrjana/cnm

Driver api refactor
Madhu Venugopal 10 년 전
부모
커밋
4b385bdbb4

+ 1 - 1
libnetwork/README.md

@@ -59,7 +59,7 @@ There are many networking solutions available to suit a broad range of use-cases
         }
         }
 
 
 		// libentwork client can check the endpoint's operational data via the Info() API
 		// libentwork client can check the endpoint's operational data via the Info() API
-		epInfo, err := ep.Info()
+		epInfo, err := ep.DriverInfo()
 		mapData, ok := epInfo[netlabel.PortMap]
 		mapData, ok := epInfo[netlabel.PortMap]
 		if ok {
 		if ok {
 			portMapping, ok := mapData.([]netutils.PortBinding)
 			portMapping, ok := mapData.([]netutils.PortBinding)

+ 1 - 8
libnetwork/api/api.go

@@ -8,7 +8,6 @@ import (
 
 
 	"github.com/docker/libnetwork"
 	"github.com/docker/libnetwork"
 	"github.com/docker/libnetwork/netutils"
 	"github.com/docker/libnetwork/netutils"
-	"github.com/docker/libnetwork/sandbox"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
 )
 )
 
 
@@ -143,7 +142,6 @@ type endpointResource struct {
 	Name    string
 	Name    string
 	ID      string
 	ID      string
 	Network string
 	Network string
-	Info    sandbox.Info
 }
 }
 
 
 func buildNetworkResource(nw libnetwork.Network) *networkResource {
 func buildNetworkResource(nw libnetwork.Network) *networkResource {
@@ -168,11 +166,6 @@ func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource {
 		r.Name = ep.Name()
 		r.Name = ep.Name()
 		r.ID = ep.ID()
 		r.ID = ep.ID()
 		r.Network = ep.Network()
 		r.Network = ep.Network()
-
-		i := ep.SandboxInfo()
-		if i != nil {
-			r.Info = *i
-		}
 	}
 	}
 	return r
 	return r
 }
 }
@@ -430,7 +423,7 @@ func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, b
 		return nil, errRsp
 		return nil, errRsp
 	}
 	}
 
 
-	err := ep.Leave(vars[urlCnID], nil)
+	err := ep.Leave(vars[urlCnID])
 	if err != nil {
 	if err != nil {
 		return nil, convertNetworkError(err)
 		return nil, convertNetworkError(err)
 	}
 	}

+ 1 - 1
libnetwork/cmd/readme_test/readme.go

@@ -55,7 +55,7 @@ func main() {
 	}
 	}
 
 
 	// libentwork client can check the endpoint's operational data via the Info() API
 	// libentwork client can check the endpoint's operational data via the Info() API
-	epInfo, err := ep.Info()
+	epInfo, err := ep.DriverInfo()
 	mapData, ok := epInfo[netlabel.PortMap]
 	mapData, ok := epInfo[netlabel.PortMap]
 	if ok {
 	if ok {
 		portMapping, ok := mapData.([]netutils.PortBinding)
 		portMapping, ok := mapData.([]netutils.PortBinding)

+ 2 - 2
libnetwork/docs/design.md

@@ -73,14 +73,14 @@ Consumers of the CNM, like Docker for example, interact through the CNM Objects
 
 
 3. `controller.NewNetwork()` API also takes in optional `options` parameter which carries Driver-specific options and `Labels`, which the Drivers can make use for its purpose.
 3. `controller.NewNetwork()` API also takes in optional `options` parameter which carries Driver-specific options and `Labels`, which the Drivers can make use for its purpose.
 
 
-4. `network.CreateEndpoint()` can be called to create a new Endpoint in a given network. This API also accepts optional `options` parameter which drivers can make use of. These 'options' carry both well-known labels and driver-specific labels. Drivers will inturn be called with `driver.CreateEndpoint` and it can choose to reserve any required resources when an `Endpoint` is created in a `Network`. The `Driver` must return the reserved resources via the `sandbox.Info` return object. LibNetwork will make use of the `SandboxInfo` when a Container is attached later. The reason we get the `sandbox.Info` at the time of endpoint creation and not during the `Join()` is that, `Endpoint` represents a Service endpoint and not neccessarily the container that attaches later.
+4. `network.CreateEndpoint()` can be called to create a new Endpoint in a given network. This API also accepts optional `options` parameter which drivers can make use of. These 'options' carry both well-known labels and driver-specific labels. Drivers will in turn be called with `driver.CreateEndpoint` and it can choose to reserve IPv4/IPv6 addresses when an `Endpoint` is created in a `Network`. The `Driver` will assign these addresses using `InterfaceInfo` interface defined in the `driverapi`. The IP/IPv6 are needed to complete the endpoint as service definition along with the ports the endpoint exposes since essentially a service endpoint is nothing but a network address and the port number that the application container is listening on.
 
 
 5. `endpoint.Join()` can be used to attach a container to a `Endpoint`. The Join operation will create a `Sandbox` if it doesnt exist already for that container. The Drivers can make use of the Sandbox Key to identify multiple endpoints attached to a same container. This API also accepts optional `options` parameter which drivers can make use of.
 5. `endpoint.Join()` can be used to attach a container to a `Endpoint`. The Join operation will create a `Sandbox` if it doesnt exist already for that container. The Drivers can make use of the Sandbox Key to identify multiple endpoints attached to a same container. This API also accepts optional `options` parameter which drivers can make use of.
   * Though it is not a direct design issue of LibNetwork, it is highly encouraged to have users like `Docker` to call the endpoint.Join() during Container's `Start()` lifecycle that is invoked *before* the container is made operational. As part of Docker integration, this will be taken care of.
   * Though it is not a direct design issue of LibNetwork, it is highly encouraged to have users like `Docker` to call the endpoint.Join() during Container's `Start()` lifecycle that is invoked *before* the container is made operational. As part of Docker integration, this will be taken care of.
   * one of a FAQ on endpoint join() API is that, why do we need an API to create an Endpoint and another to join the endpoint.
   * one of a FAQ on endpoint join() API is that, why do we need an API to create an Endpoint and another to join the endpoint.
     - The answer is based on the fact that Endpoint represents a Service which may or may not be backed by a Container. When an Endpoint is created, it will have its resources reserved so that any container can get attached to the endpoint later and get a consistent networking behaviour.
     - The answer is based on the fact that Endpoint represents a Service which may or may not be backed by a Container. When an Endpoint is created, it will have its resources reserved so that any container can get attached to the endpoint later and get a consistent networking behaviour.
 
 
-6. `endpoint.Leave()` can be invoked when a container is stopped. The `Driver` can cleanup the states that it allocated during the `Join()` call. LibNetwork will delete the `Sandbox` when the last referencing endpoint leaves the network. But LibNetwork keeps hold of the `sandbox.Info` and will be reused when the container joins again. This ensures that the container's resources are reused when they are Stopped and Started again.
+6. `endpoint.Leave()` can be invoked when a container is stopped. The `Driver` can cleanup the states that it allocated during the `Join()` call. LibNetwork will delete the `Sandbox` when the last referencing endpoint leaves the network. But LibNetwork keeps hold of the IP addresses as long as the endpoint is still present and will be reused when the container(or any container) joins again. This ensures that the container's resources are reused when they are Stopped and Started again.
 
 
 7. `endpoint.Delete()` is used to delete an endpoint from a network. This results in deleting an endpoint and cleaning up the cached `sandbox.Info`.
 7. `endpoint.Delete()` is used to delete an endpoint from a network. This results in deleting an endpoint and cleaning up the cached `sandbox.Info`.
 
 

+ 70 - 11
libnetwork/driverapi/driverapi.go

@@ -3,8 +3,8 @@ package driverapi
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"net"
 
 
-	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
 	"github.com/docker/libnetwork/types"
 )
 )
 
 
@@ -37,32 +37,91 @@ type Driver interface {
 	DeleteNetwork(nid types.UUID) error
 	DeleteNetwork(nid types.UUID) error
 
 
 	// CreateEndpoint invokes the driver method to create an endpoint
 	// CreateEndpoint invokes the driver method to create an endpoint
-	// passing the network id, endpoint id and driver
-	// specific config. The config mechanism will eventually be replaced
-	// with labels which are yet to be introduced.
-	CreateEndpoint(nid, eid types.UUID, options map[string]interface{}) (*sandbox.Info, error)
+	// passing the network id, endpoint id endpoint information and driver
+	// specific config. The endpoint information can be either consumed by
+	// the driver or populated by the driver. The config mechanism will
+	// eventually be replaced with labels which are yet to be introduced.
+	CreateEndpoint(nid, eid types.UUID, epInfo EndpointInfo, options map[string]interface{}) error
 
 
 	// DeleteEndpoint invokes the driver method to delete an endpoint
 	// DeleteEndpoint invokes the driver method to delete an endpoint
 	// passing the network id and endpoint id.
 	// passing the network id and endpoint id.
 	DeleteEndpoint(nid, eid types.UUID) error
 	DeleteEndpoint(nid, eid types.UUID) error
 
 
-	// EndpointInfo retrieves from the driver the operational data related to the specified endpoint
-	EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error)
+	// EndpointOperInfo retrieves from the driver the operational data related to the specified endpoint
+	EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error)
 
 
 	// Join method is invoked when a Sandbox is attached to an endpoint.
 	// Join method is invoked when a Sandbox is attached to an endpoint.
-	Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*JoinInfo, error)
+	Join(nid, eid types.UUID, sboxKey string, jinfo JoinInfo, options map[string]interface{}) error
 
 
 	// Leave method is invoked when a Sandbox detaches from an endpoint.
 	// Leave method is invoked when a Sandbox detaches from an endpoint.
-	Leave(nid, eid types.UUID, options map[string]interface{}) error
+	Leave(nid, eid types.UUID) error
 
 
 	// Type returns the the type of this driver, the network type this driver manages
 	// Type returns the the type of this driver, the network type this driver manages
 	Type() string
 	Type() string
 }
 }
 
 
+// EndpointInfo provides a go interface to fetch or populate endpoint assigned network resources.
+type EndpointInfo interface {
+	// Interfaces returns a list of interfaces bound to the endpoint.
+	// If the list is not empty the driver is only expected to consume the interfaces.
+	// It is an error to try to add interfaces to a non-empty list.
+	// If the list is empty the driver is expected to populate with 0 or more interfaces.
+	Interfaces() []InterfaceInfo
+
+	// AddInterface is used by the driver to add an interface to the interface list.
+	// This method will return an error if the driver attempts to add interfaces
+	// if the Interfaces() method returned a non-empty list.
+	// ID field need only have significance within the endpoint so it can be a simple
+	// monotonically increasing number
+	AddInterface(ID int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error
+}
+
+// InterfaceInfo provides a go interface for drivers to retrive
+// network information to interface resources.
+type InterfaceInfo interface {
+	// MacAddress returns the MAC address.
+	MacAddress() net.HardwareAddr
+
+	// Address returns the IPv4 address.
+	Address() net.IPNet
+
+	// AddressIPv6 returns the IPv6 address.
+	AddressIPv6() net.IPNet
+
+	// ID returns the numerical id of the interface and has significance only within
+	// the endpoint.
+	ID() int
+}
+
+// InterfaceNameInfo provides a go interface for the drivers to assign names
+// to interfaces.
+type InterfaceNameInfo interface {
+	// SetNames method assigns the srcName and dstName for the interface.
+	SetNames(srcName, dstName string) error
+
+	// ID returns the numerical id that was assigned to the interface by the driver
+	// CreateEndpoint.
+	ID() int
+}
+
 // JoinInfo represents a set of resources that the driver has the ability to provide during
 // JoinInfo represents a set of resources that the driver has the ability to provide during
 // join time.
 // join time.
-type JoinInfo struct {
-	HostsPath string
+type JoinInfo interface {
+	// InterfaceNames returns a list of InterfaceNameInfo go interface to facilitate
+	// setting the names for the interfaces.
+	InterfaceNames() []InterfaceNameInfo
+
+	// SetGateway sets the default IPv4 gateway when a container joins the endpoint.
+	SetGateway(net.IP) error
+
+	// SetGatewayIPv6 sets the default IPv6 gateway when a container joins the endpoint.
+	SetGatewayIPv6(net.IP) error
+
+	// SetHostsPath sets the overriding /etc/hosts path to use for the container.
+	SetHostsPath(string) error
+
+	// SetResolvConfPath sets the overriding /etc/resolv.conf path to use for the container.
+	SetResolvConfPath(string) error
 }
 }
 
 
 // ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered
 // ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered

+ 80 - 42
libnetwork/drivers/bridge/bridge.go

@@ -1,6 +1,7 @@
 package bridge
 package bridge
 
 
 import (
 import (
+	"errors"
 	"net"
 	"net"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
@@ -22,6 +23,7 @@ const (
 	vethLen                 = 7
 	vethLen                 = 7
 	containerVeth           = "eth0"
 	containerVeth           = "eth0"
 	maxAllocatePortAttempts = 10
 	maxAllocatePortAttempts = 10
+	ifaceID                 = 1
 )
 )
 
 
 var (
 var (
@@ -371,44 +373,52 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
 	return err
 	return err
 }
 }
 
 
-func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interface{}) (*sandbox.Info, error) {
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
 	var (
 	var (
 		ipv6Addr *net.IPNet
 		ipv6Addr *net.IPNet
 		err      error
 		err      error
 	)
 	)
 
 
+	if epInfo == nil {
+		return errors.New("invalid endpoint info passed")
+	}
+
+	if len(epInfo.Interfaces()) != 0 {
+		return errors.New("non empty interface list passed to bridge(local) driver")
+	}
+
 	// Get the network handler and make sure it exists
 	// Get the network handler and make sure it exists
 	d.Lock()
 	d.Lock()
 	n := d.network
 	n := d.network
 	config := n.config
 	config := n.config
 	d.Unlock()
 	d.Unlock()
 	if n == nil {
 	if n == nil {
-		return nil, driverapi.ErrNoNetwork
+		return driverapi.ErrNoNetwork
 	}
 	}
 
 
 	// Sanity check
 	// Sanity check
 	n.Lock()
 	n.Lock()
 	if n.id != nid {
 	if n.id != nid {
 		n.Unlock()
 		n.Unlock()
-		return nil, InvalidNetworkIDError(nid)
+		return InvalidNetworkIDError(nid)
 	}
 	}
 	n.Unlock()
 	n.Unlock()
 
 
 	// Check if endpoint id is good and retrieve correspondent endpoint
 	// Check if endpoint id is good and retrieve correspondent endpoint
 	ep, err := n.getEndpoint(eid)
 	ep, err := n.getEndpoint(eid)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 
 
 	// Endpoint with that id exists either on desired or other sandbox
 	// Endpoint with that id exists either on desired or other sandbox
 	if ep != nil {
 	if ep != nil {
-		return nil, driverapi.ErrEndpointExists
+		return driverapi.ErrEndpointExists
 	}
 	}
 
 
 	// Try to convert the options to endpoint configuration
 	// Try to convert the options to endpoint configuration
 	epConfig, err := parseEndpointOptions(epOptions)
 	epConfig, err := parseEndpointOptions(epOptions)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 
 
 	// Create and add the endpoint
 	// Create and add the endpoint
@@ -429,13 +439,13 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
 	// Generate a name for what will be the host side pipe interface
 	// Generate a name for what will be the host side pipe interface
 	name1, err := generateIfaceName()
 	name1, err := generateIfaceName()
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 
 
 	// Generate a name for what will be the sandbox side pipe interface
 	// Generate a name for what will be the sandbox side pipe interface
 	name2, err := generateIfaceName()
 	name2, err := generateIfaceName()
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 
 
 	// Generate and add the interface pipe host <-> sandbox
 	// Generate and add the interface pipe host <-> sandbox
@@ -443,13 +453,13 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
 		LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
 		LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
 		PeerName:  name2}
 		PeerName:  name2}
 	if err = netlink.LinkAdd(veth); err != nil {
 	if err = netlink.LinkAdd(veth); err != nil {
-		return nil, err
+		return err
 	}
 	}
 
 
 	// Get the host side pipe interface handler
 	// Get the host side pipe interface handler
 	host, err := netlink.LinkByName(name1)
 	host, err := netlink.LinkByName(name1)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 	defer func() {
 	defer func() {
 		if err != nil {
 		if err != nil {
@@ -460,7 +470,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
 	// Get the sandbox side pipe interface handler
 	// Get the sandbox side pipe interface handler
 	sbox, err := netlink.LinkByName(name2)
 	sbox, err := netlink.LinkByName(name2)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 	defer func() {
 	defer func() {
 		if err != nil {
 		if err != nil {
@@ -472,7 +482,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
 	mac := electMacAddress(epConfig)
 	mac := electMacAddress(epConfig)
 	err = netlink.LinkSetHardwareAddr(sbox, mac)
 	err = netlink.LinkSetHardwareAddr(sbox, mac)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 	endpoint.macAddress = mac
 	endpoint.macAddress = mac
 
 
@@ -480,28 +490,29 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
 	if config.Mtu != 0 {
 	if config.Mtu != 0 {
 		err = netlink.LinkSetMTU(host, config.Mtu)
 		err = netlink.LinkSetMTU(host, config.Mtu)
 		if err != nil {
 		if err != nil {
-			return nil, err
+			return err
 		}
 		}
 		err = netlink.LinkSetMTU(sbox, config.Mtu)
 		err = netlink.LinkSetMTU(sbox, config.Mtu)
 		if err != nil {
 		if err != nil {
-			return nil, err
+			return err
 		}
 		}
 	}
 	}
 
 
 	// Attach host side pipe interface into the bridge
 	// Attach host side pipe interface into the bridge
 	if err = netlink.LinkSetMaster(host,
 	if err = netlink.LinkSetMaster(host,
 		&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil {
 		&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil {
-		return nil, err
+		return err
 	}
 	}
 
 
 	// v4 address for the sandbox side pipe interface
 	// v4 address for the sandbox side pipe interface
 	ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil)
 	ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 	ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
 	ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
 
 
 	// v6 address for the sandbox side pipe interface
 	// v6 address for the sandbox side pipe interface
+	ipv6Addr = &net.IPNet{}
 	if config.EnableIPv6 {
 	if config.EnableIPv6 {
 		var ip6 net.IP
 		var ip6 net.IP
 
 
@@ -521,7 +532,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
 
 
 		ip6, err := ipAllocator.RequestIP(network, ip6)
 		ip6, err := ipAllocator.RequestIP(network, ip6)
 		if err != nil {
 		if err != nil {
-			return nil, err
+			return err
 		}
 		}
 
 
 		ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask}
 		ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask}
@@ -533,26 +544,25 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
 	intf.DstName = containerVeth
 	intf.DstName = containerVeth
 	intf.Address = ipv4Addr
 	intf.Address = ipv4Addr
 
 
+	if config.EnableIPv6 {
+		intf.AddressIPv6 = ipv6Addr
+	}
+
 	// Store the interface in endpoint, this is needed for cleanup on DeleteEndpoint()
 	// Store the interface in endpoint, this is needed for cleanup on DeleteEndpoint()
 	endpoint.intf = intf
 	endpoint.intf = intf
 
 
-	// Generate the sandbox info to return
-	sinfo := &sandbox.Info{Interfaces: []*sandbox.Interface{intf}}
-
-	// Set the default gateway(s) for the sandbox
-	sinfo.Gateway = n.bridge.gatewayIPv4
-	if config.EnableIPv6 {
-		intf.AddressIPv6 = ipv6Addr
-		sinfo.GatewayIPv6 = n.bridge.gatewayIPv6
+	err = epInfo.AddInterface(ifaceID, endpoint.macAddress, *ipv4Addr, *ipv6Addr)
+	if err != nil {
+		return err
 	}
 	}
 
 
 	// Program any required port mapping and store them in the endpoint
 	// Program any required port mapping and store them in the endpoint
-	endpoint.portMapping, err = allocatePorts(epConfig, sinfo, config.DefaultBindingIP)
+	endpoint.portMapping, err = allocatePorts(epConfig, intf, config.DefaultBindingIP)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 
 
-	return sinfo, nil
+	return nil
 }
 }
 
 
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
@@ -628,7 +638,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 	return nil
 	return nil
 }
 }
 
 
-func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
+func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
 	// Get the network handler and make sure it exists
 	// Get the network handler and make sure it exists
 	d.Lock()
 	d.Lock()
 	n := d.network
 	n := d.network
@@ -673,40 +683,55 @@ func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, erro
 }
 }
 
 
 // Join method is invoked when a Sandbox is attached to an endpoint.
 // Join method is invoked when a Sandbox is attached to an endpoint.
-func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*driverapi.JoinInfo, error) {
+func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
 	network, err := d.getNetwork(nid)
 	network, err := d.getNetwork(nid)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return err
 	}
 	}
 
 
-	if !network.config.EnableICC {
-		return nil, d.link(nid, eid, options, true)
+	endpoint, err := network.getEndpoint(eid)
+	if err != nil {
+		return err
 	}
 	}
 
 
-	return nil, nil
-}
+	if endpoint == nil {
+		return EndpointNotFoundError(eid)
+	}
 
 
-// Leave method is invoked when a Sandbox detaches from an endpoint.
-func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) error {
-	network, err := d.getNetwork(nid)
+	for _, iNames := range jinfo.InterfaceNames() {
+		// Make sure to set names on the correct interface ID.
+		if iNames.ID() == ifaceID {
+			err = iNames.SetNames(endpoint.intf.SrcName, endpoint.intf.DstName)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	err = jinfo.SetGateway(network.bridge.gatewayIPv4)
+	if err != nil {
+		return err
+	}
+
+	err = jinfo.SetGatewayIPv6(network.bridge.gatewayIPv6)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	if !network.config.EnableICC {
 	if !network.config.EnableICC {
-		return d.link(nid, eid, options, false)
+		return d.link(network, endpoint, options, true)
 	}
 	}
 
 
 	return nil
 	return nil
 }
 }
 
 
-func (d *driver) link(nid, eid types.UUID, options map[string]interface{}, enable bool) error {
-	var cc *ContainerConfiguration
-
+// Leave method is invoked when a Sandbox detaches from an endpoint.
+func (d *driver) Leave(nid, eid types.UUID) error {
 	network, err := d.getNetwork(nid)
 	network, err := d.getNetwork(nid)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+
 	endpoint, err := network.getEndpoint(eid)
 	endpoint, err := network.getEndpoint(eid)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -716,6 +741,19 @@ func (d *driver) link(nid, eid types.UUID, options map[string]interface{}, enabl
 		return EndpointNotFoundError(eid)
 		return EndpointNotFoundError(eid)
 	}
 	}
 
 
+	if !network.config.EnableICC {
+		return d.link(network, endpoint, nil, false)
+	}
+
+	return nil
+}
+
+func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options map[string]interface{}, enable bool) error {
+	var (
+		cc  *ContainerConfiguration
+		err error
+	)
+
 	if enable {
 	if enable {
 		cc, err = parseContainerOptions(options)
 		cc, err = parseContainerOptions(options)
 		if err != nil {
 		if err != nil {

+ 120 - 19
libnetwork/drivers/bridge/bridge_test.go

@@ -7,6 +7,7 @@ import (
 	"regexp"
 	"regexp"
 	"testing"
 	"testing"
 
 
+	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/iptables"
 	"github.com/docker/libnetwork/iptables"
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/netutils"
 	"github.com/docker/libnetwork/netutils"
@@ -70,6 +71,91 @@ func TestCreateFail(t *testing.T) {
 	}
 	}
 }
 }
 
 
+type testInterface struct {
+	id      int
+	mac     net.HardwareAddr
+	addr    net.IPNet
+	addrv6  net.IPNet
+	srcName string
+	dstName string
+}
+
+type testEndpoint struct {
+	ifaces         []*testInterface
+	gw             net.IP
+	gw6            net.IP
+	hostsPath      string
+	resolvConfPath string
+}
+
+func (te *testEndpoint) Interfaces() []driverapi.InterfaceInfo {
+	iList := make([]driverapi.InterfaceInfo, len(te.ifaces))
+
+	for i, iface := range te.ifaces {
+		iList[i] = iface
+	}
+
+	return iList
+}
+
+func (te *testEndpoint) AddInterface(id int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
+	iface := &testInterface{id: id, addr: ipv4, addrv6: ipv6}
+	te.ifaces = append(te.ifaces, iface)
+	return nil
+}
+
+func (i *testInterface) ID() int {
+	return i.id
+}
+
+func (i *testInterface) MacAddress() net.HardwareAddr {
+	return i.mac
+}
+
+func (i *testInterface) Address() net.IPNet {
+	return i.addr
+}
+
+func (i *testInterface) AddressIPv6() net.IPNet {
+	return i.addrv6
+}
+
+func (i *testInterface) SetNames(srcName string, dstName string) error {
+	i.srcName = srcName
+	i.dstName = dstName
+	return nil
+}
+
+func (te *testEndpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
+	iList := make([]driverapi.InterfaceNameInfo, len(te.ifaces))
+
+	for i, iface := range te.ifaces {
+		iList[i] = iface
+	}
+
+	return iList
+}
+
+func (te *testEndpoint) SetGateway(gw net.IP) error {
+	te.gw = gw
+	return nil
+}
+
+func (te *testEndpoint) SetGatewayIPv6(gw6 net.IP) error {
+	te.gw6 = gw6
+	return nil
+}
+
+func (te *testEndpoint) SetHostsPath(path string) error {
+	te.hostsPath = path
+	return nil
+}
+
+func (te *testEndpoint) SetResolvConfPath(path string) error {
+	te.resolvConfPath = path
+	return nil
+}
+
 func TestQueryEndpointInfo(t *testing.T) {
 func TestQueryEndpointInfo(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
 	d := newDriver()
 	d := newDriver()
@@ -92,13 +178,14 @@ func TestQueryEndpointInfo(t *testing.T) {
 	epOptions := make(map[string]interface{})
 	epOptions := make(map[string]interface{})
 	epOptions[netlabel.PortMap] = portMappings
 	epOptions[netlabel.PortMap] = portMappings
 
 
-	_, err = d.CreateEndpoint("net1", "ep1", epOptions)
+	te := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("net1", "ep1", te, epOptions)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to create an endpoint : %s", err.Error())
 		t.Fatalf("Failed to create an endpoint : %s", err.Error())
 	}
 	}
 
 
 	ep, _ := dd.network.endpoints["ep1"]
 	ep, _ := dd.network.endpoints["ep1"]
-	data, err := d.EndpointInfo(dd.network.id, ep.id)
+	data, err := d.EndpointOperInfo(dd.network.id, ep.id)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to ask for endpoint operational data:  %v", err)
 		t.Fatalf("Failed to ask for endpoint operational data:  %v", err)
 	}
 	}
@@ -143,12 +230,18 @@ func TestCreateLinkWithOptions(t *testing.T) {
 	epOptions := make(map[string]interface{})
 	epOptions := make(map[string]interface{})
 	epOptions[netlabel.MacAddress] = mac
 	epOptions[netlabel.MacAddress] = mac
 
 
-	sinfo, err := d.CreateEndpoint("net1", "ep", epOptions)
+	te := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("net1", "ep", te, epOptions)
+	if err != nil {
+		t.Fatalf("Failed to create an endpoint: %s", err.Error())
+	}
+
+	err = d.Join("net1", "ep", "sbox", te, nil)
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Failed to create a link: %s", err.Error())
+		t.Fatalf("Failed to join the endpoint: %v", err)
 	}
 	}
 
 
-	ifaceName := sinfo.Interfaces[0].SrcName
+	ifaceName := te.ifaces[0].srcName
 	veth, err := netlink.LinkByName(ifaceName)
 	veth, err := netlink.LinkByName(ifaceName)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -197,23 +290,25 @@ func TestLinkContainers(t *testing.T) {
 	epOptions := make(map[string]interface{})
 	epOptions := make(map[string]interface{})
 	epOptions[netlabel.ExposedPorts] = exposedPorts
 	epOptions[netlabel.ExposedPorts] = exposedPorts
 
 
-	sinfo, err := d.CreateEndpoint("net1", "ep1", epOptions)
+	te1 := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("net1", "ep1", te1, epOptions)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to create an endpoint : %s", err.Error())
 		t.Fatalf("Failed to create an endpoint : %s", err.Error())
 	}
 	}
 
 
-	addr1 := sinfo.Interfaces[0].Address
-	if addr1 == nil {
+	addr1 := te1.ifaces[0].addr
+	if addr1.IP.To4() == nil {
 		t.Fatalf("No Ipv4 address assigned to the endpoint:  ep1")
 		t.Fatalf("No Ipv4 address assigned to the endpoint:  ep1")
 	}
 	}
 
 
-	sinfo, err = d.CreateEndpoint("net1", "ep2", nil)
+	te2 := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("net1", "ep2", te2, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to create an endpoint : %s", err.Error())
 		t.Fatalf("Failed to create an endpoint : %s", err.Error())
 	}
 	}
 
 
-	addr2 := sinfo.Interfaces[0].Address
-	if addr2 == nil {
+	addr2 := te2.ifaces[0].addr
+	if addr2.IP.To4() == nil {
 		t.Fatalf("No Ipv4 address assigned to the endpoint:  ep2")
 		t.Fatalf("No Ipv4 address assigned to the endpoint:  ep2")
 	}
 	}
 
 
@@ -222,7 +317,7 @@ func TestLinkContainers(t *testing.T) {
 	genericOption = make(map[string]interface{})
 	genericOption = make(map[string]interface{})
 	genericOption[netlabel.GenericData] = cConfig
 	genericOption[netlabel.GenericData] = cConfig
 
 
-	_, err = d.Join("net1", "ep2", "", genericOption)
+	err = d.Join("net1", "ep2", "", te2, genericOption)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to link ep1 and ep2")
 		t.Fatalf("Failed to link ep1 and ep2")
 	}
 	}
@@ -243,7 +338,7 @@ func TestLinkContainers(t *testing.T) {
 		}
 		}
 	}
 	}
 
 
-	err = d.Leave("net1", "ep2", genericOption)
+	err = d.Leave("net1", "ep2")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to unlink ep1 and ep2")
 		t.Fatalf("Failed to unlink ep1 and ep2")
 	}
 	}
@@ -270,7 +365,7 @@ func TestLinkContainers(t *testing.T) {
 	genericOption = make(map[string]interface{})
 	genericOption = make(map[string]interface{})
 	genericOption[netlabel.GenericData] = cConfig
 	genericOption[netlabel.GenericData] = cConfig
 
 
-	_, err = d.Join("net1", "ep2", "", genericOption)
+	err = d.Join("net1", "ep2", "", te2, genericOption)
 	if err != nil {
 	if err != nil {
 		out, err = iptables.Raw("-L", DockerChain)
 		out, err = iptables.Raw("-L", DockerChain)
 		for _, pm := range exposedPorts {
 		for _, pm := range exposedPorts {
@@ -406,16 +501,22 @@ func TestSetDefaultGw(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	sinfo, err := d.CreateEndpoint("dummy", "ep", nil)
+	te := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("dummy", "ep", te, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to create endpoint: %v", err)
 		t.Fatalf("Failed to create endpoint: %v", err)
 	}
 	}
 
 
-	if !gw4.Equal(sinfo.Gateway) {
-		t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw4, sinfo.Gateway)
+	err = d.Join("dummy", "ep", "sbox", te, nil)
+	if err != nil {
+		t.Fatalf("Failed to join endpoint: %v", err)
+	}
+
+	if !gw4.Equal(te.gw) {
+		t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw4, te.gw)
 	}
 	}
 
 
-	if !gw6.Equal(sinfo.GatewayIPv6) {
-		t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw6, sinfo.GatewayIPv6)
+	if !gw6.Equal(te.gw6) {
+		t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw6, te.gw6)
 	}
 	}
 }
 }

+ 35 - 30
libnetwork/drivers/bridge/network_test.go

@@ -28,7 +28,8 @@ func TestLinkCreate(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	sinfo, err := d.CreateEndpoint("dummy", "", nil)
+	te := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("dummy", "", te, nil)
 	if err != nil {
 	if err != nil {
 		if _, ok := err.(InvalidEndpointIDError); !ok {
 		if _, ok := err.(InvalidEndpointIDError); !ok {
 			t.Fatalf("Failed with a wrong error :%s", err.Error())
 			t.Fatalf("Failed with a wrong error :%s", err.Error())
@@ -38,13 +39,18 @@ func TestLinkCreate(t *testing.T) {
 	}
 	}
 
 
 	// Good endpoint creation
 	// Good endpoint creation
-	sinfo, err = d.CreateEndpoint("dummy", "ep", nil)
+	err = d.CreateEndpoint("dummy", "ep", te, nil)
+	if err != nil {
+		t.Fatalf("Failed to create a link: %s", err.Error())
+	}
+
+	err = d.Join("dummy", "ep", "sbox", te, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to create a link: %s", err.Error())
 		t.Fatalf("Failed to create a link: %s", err.Error())
 	}
 	}
 
 
 	// Verify sbox endoint interface inherited MTU value from bridge config
 	// Verify sbox endoint interface inherited MTU value from bridge config
-	sboxLnk, err := netlink.LinkByName(sinfo.Interfaces[0].SrcName)
+	sboxLnk, err := netlink.LinkByName(te.ifaces[0].srcName)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -54,44 +60,44 @@ func TestLinkCreate(t *testing.T) {
 	// TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName
 	// TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName
 	// then we could check the MTU on hostLnk as well.
 	// then we could check the MTU on hostLnk as well.
 
 
-	_, err = d.CreateEndpoint("dummy", "ep", nil)
+	te1 := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("dummy", "ep", te1, nil)
 	if err == nil {
 	if err == nil {
 		t.Fatalf("Failed to detect duplicate endpoint id on same network")
 		t.Fatalf("Failed to detect duplicate endpoint id on same network")
 	}
 	}
 
 
-	interfaces := sinfo.Interfaces
-	if len(interfaces) != 1 {
-		t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(interfaces))
+	if len(te.ifaces) != 1 {
+		t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(te.ifaces))
 	}
 	}
 
 
-	if interfaces[0].DstName == "" {
+	if te.ifaces[0].dstName == "" {
 		t.Fatal("Invalid Dstname returned")
 		t.Fatal("Invalid Dstname returned")
 	}
 	}
 
 
-	_, err = netlink.LinkByName(interfaces[0].SrcName)
+	_, err = netlink.LinkByName(te.ifaces[0].srcName)
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Could not find source link %s: %v", interfaces[0].SrcName, err)
+		t.Fatalf("Could not find source link %s: %v", te.ifaces[0].srcName, err)
 	}
 	}
 
 
 	n := dr.network
 	n := dr.network
-	ip := interfaces[0].Address.IP
+	ip := te.ifaces[0].addr.IP
 	if !n.bridge.bridgeIPv4.Contains(ip) {
 	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())
 		t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), n.bridge.bridgeIPv4.String())
 	}
 	}
 
 
-	ip6 := interfaces[0].AddressIPv6.IP
+	ip6 := te.ifaces[0].addrv6.IP
 	if !n.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())
 		t.Fatalf("IP %s is not a valid ip in the subnet %s", ip6.String(), bridgeIPv6.String())
 	}
 	}
 
 
-	if !sinfo.Gateway.Equal(n.bridge.bridgeIPv4.IP) {
+	if !te.gw.Equal(n.bridge.bridgeIPv4.IP) {
 		t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(),
 		t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(),
-			sinfo.Gateway.String())
+			te.gw.String())
 	}
 	}
 
 
-	if !sinfo.GatewayIPv6.Equal(n.bridge.bridgeIPv6.IP) {
+	if !te.gw6.Equal(n.bridge.bridgeIPv6.IP) {
 		t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(),
 		t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(),
-			sinfo.GatewayIPv6.String())
+			te.gw6.String())
 	}
 	}
 }
 }
 
 
@@ -110,12 +116,14 @@ func TestLinkCreateTwo(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	_, err = d.CreateEndpoint("dummy", "ep", nil)
+	te1 := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("dummy", "ep", te1, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to create a link: %s", err.Error())
 		t.Fatalf("Failed to create a link: %s", err.Error())
 	}
 	}
 
 
-	_, err = d.CreateEndpoint("dummy", "ep", nil)
+	te2 := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("dummy", "ep", te2, nil)
 	if err != nil {
 	if err != nil {
 		if err != driverapi.ErrEndpointExists {
 		if err != driverapi.ErrEndpointExists {
 			t.Fatalf("Failed with a wrong error :%s", err.Error())
 			t.Fatalf("Failed with a wrong error :%s", err.Error())
@@ -139,18 +147,19 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	sinfo, err := d.CreateEndpoint("dummy", "ep", nil)
+	te := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("dummy", "ep", te, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to create a link: %s", err.Error())
 		t.Fatalf("Failed to create a link: %s", err.Error())
 	}
 	}
 
 
-	interfaces := sinfo.Interfaces
-	if interfaces[0].AddressIPv6 != nil {
-		t.Fatalf("Expectd IPv6 address to be nil when IPv6 is not enabled. Got IPv6 = %s", interfaces[0].AddressIPv6.String())
+	interfaces := te.ifaces
+	if interfaces[0].addrv6.IP.To16() != nil {
+		t.Fatalf("Expectd IPv6 address to be nil when IPv6 is not enabled. Got IPv6 = %s", interfaces[0].addrv6.String())
 	}
 	}
 
 
-	if sinfo.GatewayIPv6 != nil {
-		t.Fatalf("Expected GatewayIPv6 to be nil when IPv6 is not enabled. Got GatewayIPv6 = %s", sinfo.GatewayIPv6.String())
+	if te.gw6.To16() != nil {
+		t.Fatalf("Expected GatewayIPv6 to be nil when IPv6 is not enabled. Got GatewayIPv6 = %s", te.gw6.String())
 	}
 	}
 }
 }
 
 
@@ -169,7 +178,8 @@ func TestLinkDelete(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	_, err = d.CreateEndpoint("dummy", "ep1", nil)
+	te := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("dummy", "ep1", te, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to create a link: %s", err.Error())
 		t.Fatalf("Failed to create a link: %s", err.Error())
 	}
 	}
@@ -187,9 +197,4 @@ func TestLinkDelete(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-
-	err = d.DeleteEndpoint("dummy", "ep1")
-	if err == nil {
-		t.Fatal(err)
-	}
 }
 }

+ 2 - 2
libnetwork/drivers/bridge/port_mapping.go

@@ -15,7 +15,7 @@ var (
 	defaultBindingIP = net.IPv4(0, 0, 0, 0)
 	defaultBindingIP = net.IPv4(0, 0, 0, 0)
 )
 )
 
 
-func allocatePorts(epConfig *EndpointConfiguration, sinfo *sandbox.Info, reqDefBindIP net.IP) ([]netutils.PortBinding, error) {
+func allocatePorts(epConfig *EndpointConfiguration, intf *sandbox.Interface, reqDefBindIP net.IP) ([]netutils.PortBinding, error) {
 	if epConfig == nil || epConfig.PortBindings == nil {
 	if epConfig == nil || epConfig.PortBindings == nil {
 		return nil, nil
 		return nil, nil
 	}
 	}
@@ -25,7 +25,7 @@ func allocatePorts(epConfig *EndpointConfiguration, sinfo *sandbox.Info, reqDefB
 		defHostIP = reqDefBindIP
 		defHostIP = reqDefBindIP
 	}
 	}
 
 
-	return allocatePortsInternal(epConfig.PortBindings, sinfo.Interfaces[0].Address.IP, defHostIP)
+	return allocatePortsInternal(epConfig.PortBindings, intf.Address.IP, defHostIP)
 }
 }
 
 
 func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHostIP net.IP) ([]netutils.PortBinding, error) {
 func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHostIP net.IP) ([]netutils.PortBinding, error) {

+ 2 - 1
libnetwork/drivers/bridge/port_mapping_test.go

@@ -39,7 +39,8 @@ func TestPortMappingConfig(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	_, err = d.CreateEndpoint("dummy", "ep1", epOptions)
+	te := &testEndpoint{ifaces: []*testInterface{}}
+	err = d.CreateEndpoint("dummy", "ep1", te, epOptions)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to create the endpoint: %s", err.Error())
 		t.Fatalf("Failed to create the endpoint: %s", err.Error())
 	}
 	}

+ 6 - 11
libnetwork/drivers/host/host.go

@@ -2,7 +2,6 @@ package host
 
 
 import (
 import (
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/driverapi"
-	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
 	"github.com/docker/libnetwork/types"
 )
 )
 
 
@@ -27,29 +26,25 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
 	return nil
 	return nil
 }
 }
 
 
-func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interface{}) (*sandbox.Info, error) {
-	return nil, nil
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
+	return nil
 }
 }
 
 
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 	return nil
 	return nil
 }
 }
 
 
-func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
+func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
 	return make(map[string]interface{}, 0), nil
 	return make(map[string]interface{}, 0), nil
 }
 }
 
 
 // Join method is invoked when a Sandbox is attached to an endpoint.
 // Join method is invoked when a Sandbox is attached to an endpoint.
-func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*driverapi.JoinInfo, error) {
-	jInfo := &driverapi.JoinInfo{
-		HostsPath: "/etc/hosts",
-	}
-
-	return jInfo, nil
+func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
+	return (jinfo.SetHostsPath("/etc/hosts"))
 }
 }
 
 
 // Leave method is invoked when a Sandbox detaches from an endpoint.
 // Leave method is invoked when a Sandbox detaches from an endpoint.
-func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) error {
+func (d *driver) Leave(nid, eid types.UUID) error {
 	return nil
 	return nil
 }
 }
 
 

+ 6 - 7
libnetwork/drivers/null/null.go

@@ -2,7 +2,6 @@ package null
 
 
 import (
 import (
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/driverapi"
-	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
 	"github.com/docker/libnetwork/types"
 )
 )
 
 
@@ -27,25 +26,25 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
 	return nil
 	return nil
 }
 }
 
 
-func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interface{}) (*sandbox.Info, error) {
-	return nil, nil
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
+	return nil
 }
 }
 
 
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 	return nil
 	return nil
 }
 }
 
 
-func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
+func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
 	return make(map[string]interface{}, 0), nil
 	return make(map[string]interface{}, 0), nil
 }
 }
 
 
 // Join method is invoked when a Sandbox is attached to an endpoint.
 // Join method is invoked when a Sandbox is attached to an endpoint.
-func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*driverapi.JoinInfo, error) {
-	return nil, nil
+func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
+	return nil
 }
 }
 
 
 // Leave method is invoked when a Sandbox detaches from an endpoint.
 // Leave method is invoked when a Sandbox detaches from an endpoint.
-func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) error {
+func (d *driver) Leave(nid, eid types.UUID) error {
 	return nil
 	return nil
 }
 }
 
 

+ 6 - 7
libnetwork/drivers/remote/driver.go

@@ -6,7 +6,6 @@ import (
 	log "github.com/Sirupsen/logrus"
 	log "github.com/Sirupsen/logrus"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/driverapi"
-	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
 	"github.com/docker/libnetwork/types"
 )
 )
 
 
@@ -43,25 +42,25 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
 	return driverapi.ErrNotImplemented
 	return driverapi.ErrNotImplemented
 }
 }
 
 
-func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interface{}) (*sandbox.Info, error) {
-	return nil, driverapi.ErrNotImplemented
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
+	return driverapi.ErrNotImplemented
 }
 }
 
 
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 	return driverapi.ErrNotImplemented
 	return driverapi.ErrNotImplemented
 }
 }
 
 
-func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
+func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
 	return nil, driverapi.ErrNotImplemented
 	return nil, driverapi.ErrNotImplemented
 }
 }
 
 
 // Join method is invoked when a Sandbox is attached to an endpoint.
 // Join method is invoked when a Sandbox is attached to an endpoint.
-func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*driverapi.JoinInfo, error) {
-	return nil, driverapi.ErrNotImplemented
+func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
+	return driverapi.ErrNotImplemented
 }
 }
 
 
 // Leave method is invoked when a Sandbox detaches from an endpoint.
 // Leave method is invoked when a Sandbox detaches from an endpoint.
-func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) error {
+func (d *driver) Leave(nid, eid types.UUID) error {
 	return driverapi.ErrNotImplemented
 	return driverapi.ErrNotImplemented
 }
 }
 
 

+ 40 - 76
libnetwork/endpoint.go

@@ -10,7 +10,6 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
-	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/etchosts"
 	"github.com/docker/libnetwork/etchosts"
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/netutils"
 	"github.com/docker/libnetwork/netutils"
@@ -39,11 +38,11 @@ type Endpoint interface {
 	// the network resources populated in the sandbox
 	// the network resources populated in the sandbox
 	Leave(containerID string, options ...EndpointOption) error
 	Leave(containerID string, options ...EndpointOption) error
 
 
-	// SandboxInfo returns the sandbox information for this endpoint.
-	SandboxInfo() *sandbox.Info
+	// Return certain operational data belonging to this endpoint
+	Info() EndpointInfo
 
 
-	// Info returns a collection of operational data related to this endpoint retrieved from the driver
-	Info() (map[string]interface{}, error)
+	// Info returns a collection of driver operational data related to this endpoint retrieved from the driver
+	DriverInfo() (map[string]interface{}, error)
 
 
 	// Delete and detaches this endpoint from the network.
 	// Delete and detaches this endpoint from the network.
 	Delete() error
 	Delete() error
@@ -104,12 +103,11 @@ type endpoint struct {
 	id            types.UUID
 	id            types.UUID
 	network       *network
 	network       *network
 	sandboxInfo   *sandbox.Info
 	sandboxInfo   *sandbox.Info
-	sandBox       sandbox.Sandbox
-	joinInfo      *driverapi.JoinInfo
+	iFaces        []*endpointInterface
+	joinInfo      *endpointJoinInfo
 	container     *containerInfo
 	container     *containerInfo
 	exposedPorts  []netutils.TransportPort
 	exposedPorts  []netutils.TransportPort
 	generic       map[string]interface{}
 	generic       map[string]interface{}
-	context       map[string]interface{}
 	joinLeaveDone chan struct{}
 	joinLeaveDone chan struct{}
 	sync.Mutex
 	sync.Mutex
 }
 }
@@ -137,30 +135,6 @@ func (ep *endpoint) Network() string {
 	return ep.network.name
 	return ep.network.name
 }
 }
 
 
-func (ep *endpoint) SandboxInfo() *sandbox.Info {
-	ep.Lock()
-	defer ep.Unlock()
-
-	if ep.sandboxInfo == nil {
-		return nil
-	}
-	return ep.sandboxInfo.GetCopy()
-}
-
-func (ep *endpoint) Info() (map[string]interface{}, error) {
-	ep.Lock()
-	network := ep.network
-	epid := ep.id
-	ep.Unlock()
-
-	network.Lock()
-	driver := network.driver
-	nid := network.id
-	network.Unlock()
-
-	return driver.EndpointInfo(nid, epid)
-}
-
 func (ep *endpoint) processOptions(options ...EndpointOption) {
 func (ep *endpoint) processOptions(options ...EndpointOption) {
 	ep.Lock()
 	ep.Lock()
 	defer ep.Unlock()
 	defer ep.Unlock()
@@ -255,9 +229,13 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
 			},
 			},
 		}}
 		}}
 
 
+	ep.joinInfo = &endpointJoinInfo{}
+
 	container := ep.container
 	container := ep.container
 	network := ep.network
 	network := ep.network
 	epid := ep.id
 	epid := ep.id
+	joinInfo := ep.joinInfo
+	ifaces := ep.iFaces
 
 
 	ep.Unlock()
 	ep.Unlock()
 	defer func() {
 	defer func() {
@@ -281,15 +259,11 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
 		sboxKey = sandbox.GenerateKey("default")
 		sboxKey = sandbox.GenerateKey("default")
 	}
 	}
 
 
-	joinInfo, err := driver.Join(nid, epid, sboxKey, container.config.generic)
+	err = driver.Join(nid, epid, sboxKey, ep, container.config.generic)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	ep.Lock()
-	ep.joinInfo = joinInfo
-	ep.Unlock()
-
 	err = ep.buildHostsFiles()
 	err = ep.buildHostsFiles()
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -315,26 +289,31 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
 		}
 		}
 	}()
 	}()
 
 
-	sinfo := ep.SandboxInfo()
-	if sinfo != nil {
-		for _, i := range sinfo.Interfaces {
-			err = sb.AddInterface(i)
-			if err != nil {
-				return nil, err
-			}
+	for _, i := range ifaces {
+		iface := &sandbox.Interface{
+			SrcName: i.srcName,
+			DstName: i.dstName,
+			Address: &i.addr,
 		}
 		}
-
-		err = sb.SetGateway(sinfo.Gateway)
-		if err != nil {
-			return nil, err
+		if i.addrv6.IP.To16() != nil {
+			iface.AddressIPv6 = &i.addrv6
 		}
 		}
-
-		err = sb.SetGatewayIPv6(sinfo.GatewayIPv6)
+		err = sb.AddInterface(iface)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 	}
 	}
 
 
+	err = sb.SetGateway(joinInfo.gw)
+	if err != nil {
+		return nil, err
+	}
+
+	err = sb.SetGatewayIPv6(joinInfo.gw6)
+	if err != nil {
+		return nil, err
+	}
+
 	container.data.SandboxKey = sb.Key()
 	container.data.SandboxKey = sb.Key()
 	cData := container.data
 	cData := container.data
 
 
@@ -352,7 +331,6 @@ func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error {
 	ep.Lock()
 	ep.Lock()
 	container := ep.container
 	container := ep.container
 	n := ep.network
 	n := ep.network
-	context := ep.context
 
 
 	if container == nil || container.id == "" ||
 	if container == nil || container.id == "" ||
 		containerID == "" || container.id != containerID {
 		containerID == "" || container.id != containerID {
@@ -366,7 +344,6 @@ func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error {
 		return err
 		return err
 	}
 	}
 	ep.container = nil
 	ep.container = nil
-	ep.context = nil
 	ep.Unlock()
 	ep.Unlock()
 
 
 	n.Lock()
 	n.Lock()
@@ -374,16 +351,13 @@ func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error {
 	ctrlr := n.ctrlr
 	ctrlr := n.ctrlr
 	n.Unlock()
 	n.Unlock()
 
 
-	err = driver.Leave(n.id, ep.id, context)
+	err = driver.Leave(n.id, ep.id)
 
 
-	sinfo := ep.SandboxInfo()
-	if sinfo != nil {
-		sb := ctrlr.sandboxGet(container.data.SandboxKey)
-		for _, i := range sinfo.Interfaces {
-			err = sb.RemoveInterface(i)
-			if err != nil {
-				logrus.Debugf("Remove interface failed: %v", err)
-			}
+	sb := ctrlr.sandboxGet(container.data.SandboxKey)
+	for _, i := range sb.Interfaces() {
+		err = sb.RemoveInterface(i)
+		if err != nil {
+			logrus.Debugf("Remove interface failed: %v", err)
 		}
 		}
 	}
 	}
 
 
@@ -435,6 +409,7 @@ func (ep *endpoint) buildHostsFiles() error {
 	ep.Lock()
 	ep.Lock()
 	container := ep.container
 	container := ep.container
 	joinInfo := ep.joinInfo
 	joinInfo := ep.joinInfo
+	ifaces := ep.iFaces
 	ep.Unlock()
 	ep.Unlock()
 
 
 	if container == nil {
 	if container == nil {
@@ -451,8 +426,8 @@ func (ep *endpoint) buildHostsFiles() error {
 		return err
 		return err
 	}
 	}
 
 
-	if joinInfo != nil && joinInfo.HostsPath != "" {
-		content, err := ioutil.ReadFile(joinInfo.HostsPath)
+	if joinInfo != nil && joinInfo.hostsPath != "" {
+		content, err := ioutil.ReadFile(joinInfo.hostsPath)
 		if err != nil && !os.IsNotExist(err) {
 		if err != nil && !os.IsNotExist(err) {
 			return err
 			return err
 		}
 		}
@@ -473,10 +448,8 @@ func (ep *endpoint) buildHostsFiles() error {
 	}
 	}
 
 
 	IP := ""
 	IP := ""
-	sinfo := ep.SandboxInfo()
-	if sinfo != nil && sinfo.Interfaces[0] != nil &&
-		sinfo.Interfaces[0].Address != nil {
-		IP = sinfo.Interfaces[0].Address.IP.String()
+	if len(ifaces) != 0 && ifaces[0] != nil {
+		IP = ifaces[0].addr.IP.String()
 	}
 	}
 
 
 	return etchosts.Build(container.config.hostsPath, IP, container.config.hostName,
 	return etchosts.Build(container.config.hostsPath, IP, container.config.hostName,
@@ -749,12 +722,3 @@ func JoinOptionGeneric(generic map[string]interface{}) EndpointOption {
 		ep.container.config.generic = generic
 		ep.container.config.generic = generic
 	}
 	}
 }
 }
-
-// LeaveOptionGeneric 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 leave method. Container Labels are a good example.
-func LeaveOptionGeneric(context map[string]interface{}) EndpointOption {
-	return func(ep *endpoint) {
-		ep.context = context
-	}
-}

+ 215 - 0
libnetwork/endpoint_info.go

@@ -0,0 +1,215 @@
+package libnetwork
+
+import (
+	"net"
+
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/netutils"
+)
+
+// EndpointInfo provides an interface to retrieve network resources bound to the endpoint.
+type EndpointInfo interface {
+	// InterfaceList returns an interface list which were assigned to the endpoint
+	// by the driver. This can be used after the endpoint has been created.
+	InterfaceList() []InterfaceInfo
+
+	// Gateway returns the IPv4 gateway assigned by the driver.
+	// This will only return a valid value if a container has joined the endpoint.
+	Gateway() net.IP
+
+	// GatewayIPv6 returns the IPv6 gateway assigned by the driver.
+	// This will only return a valid value if a container has joined the endpoint.
+	GatewayIPv6() net.IP
+
+	// SandboxKey returns the sanbox key for the container which has joined
+	// the endpoint. If there is no container joined then this will return an
+	// empty string.
+	SandboxKey() string
+}
+
+// InterfaceInfo provides an interface to retrieve interface addresses bound to the endpoint.
+type InterfaceInfo interface {
+	// MacAddress returns the MAC address assigned to the endpoint.
+	MacAddress() net.HardwareAddr
+
+	// Address returns the IPv4 address assigned to the endpoint.
+	Address() net.IPNet
+
+	// AddressIPv6 returns the IPv6 address assigned to the endpoint.
+	AddressIPv6() net.IPNet
+}
+
+type endpointInterface struct {
+	id      int
+	mac     net.HardwareAddr
+	addr    net.IPNet
+	addrv6  net.IPNet
+	srcName string
+	dstName string
+}
+
+type endpointJoinInfo struct {
+	gw             net.IP
+	gw6            net.IP
+	hostsPath      string
+	resolvConfPath string
+}
+
+func (ep *endpoint) Info() EndpointInfo {
+	return ep
+}
+
+func (ep *endpoint) DriverInfo() (map[string]interface{}, error) {
+	ep.Lock()
+	network := ep.network
+	epid := ep.id
+	ep.Unlock()
+
+	network.Lock()
+	driver := network.driver
+	nid := network.id
+	network.Unlock()
+
+	return driver.EndpointOperInfo(nid, epid)
+}
+
+func (ep *endpoint) InterfaceList() []InterfaceInfo {
+	ep.Lock()
+	defer ep.Unlock()
+
+	iList := make([]InterfaceInfo, len(ep.iFaces))
+
+	for i, iface := range ep.iFaces {
+		iList[i] = iface
+	}
+
+	return iList
+}
+
+func (ep *endpoint) Interfaces() []driverapi.InterfaceInfo {
+	ep.Lock()
+	defer ep.Unlock()
+
+	iList := make([]driverapi.InterfaceInfo, len(ep.iFaces))
+
+	for i, iface := range ep.iFaces {
+		iList[i] = iface
+	}
+
+	return iList
+}
+
+func (ep *endpoint) AddInterface(id int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
+	ep.Lock()
+	defer ep.Unlock()
+
+	iface := &endpointInterface{
+		id:     id,
+		addr:   *netutils.GetIPNetCopy(&ipv4),
+		addrv6: *netutils.GetIPNetCopy(&ipv6),
+	}
+	iface.mac = netutils.GetMacCopy(mac)
+
+	ep.iFaces = append(ep.iFaces, iface)
+	return nil
+}
+
+func (i *endpointInterface) ID() int {
+	return i.id
+}
+
+func (i *endpointInterface) MacAddress() net.HardwareAddr {
+	return netutils.GetMacCopy(i.mac)
+}
+
+func (i *endpointInterface) Address() net.IPNet {
+	return (*netutils.GetIPNetCopy(&i.addr))
+}
+
+func (i *endpointInterface) AddressIPv6() net.IPNet {
+	return (*netutils.GetIPNetCopy(&i.addrv6))
+}
+
+func (i *endpointInterface) SetNames(srcName string, dstName string) error {
+	i.srcName = srcName
+	i.dstName = dstName
+	return nil
+}
+
+func (ep *endpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
+	ep.Lock()
+	defer ep.Unlock()
+
+	iList := make([]driverapi.InterfaceNameInfo, len(ep.iFaces))
+
+	for i, iface := range ep.iFaces {
+		iList[i] = iface
+	}
+
+	return iList
+}
+
+func (ep *endpoint) SandboxKey() string {
+	ep.Lock()
+	defer ep.Unlock()
+
+	if ep.container == nil {
+		return ""
+	}
+
+	return ep.container.data.SandboxKey
+}
+
+func (ep *endpoint) Gateway() net.IP {
+	ep.Lock()
+	defer ep.Unlock()
+
+	if ep.joinInfo == nil {
+		return net.IP{}
+	}
+
+	return netutils.GetIPCopy(ep.joinInfo.gw)
+}
+
+func (ep *endpoint) GatewayIPv6() net.IP {
+	ep.Lock()
+	defer ep.Unlock()
+
+	if ep.joinInfo == nil {
+		return net.IP{}
+	}
+
+	return netutils.GetIPCopy(ep.joinInfo.gw6)
+}
+
+func (ep *endpoint) SetGateway(gw net.IP) error {
+	ep.Lock()
+	defer ep.Unlock()
+
+	ep.joinInfo.gw = netutils.GetIPCopy(gw)
+	return nil
+}
+
+func (ep *endpoint) SetGatewayIPv6(gw6 net.IP) error {
+	ep.Lock()
+	defer ep.Unlock()
+
+	ep.joinInfo.gw6 = netutils.GetIPCopy(gw6)
+	return nil
+}
+
+func (ep *endpoint) SetHostsPath(path string) error {
+	ep.Lock()
+	defer ep.Unlock()
+
+	ep.joinInfo.hostsPath = path
+	return nil
+}
+
+func (ep *endpoint) SetResolvConfPath(path string) error {
+	ep.Lock()
+	defer ep.Unlock()
+
+	ep.joinInfo.resolvConfPath = path
+	return nil
+}

+ 28 - 1
libnetwork/libnetwork_test.go

@@ -190,7 +190,7 @@ func TestBridge(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	epInfo, err := ep.Info()
+	epInfo, err := ep.DriverInfo()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -696,6 +696,23 @@ func TestEndpointJoin(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
+	// Validate if ep.Info() only gives me IP address info and not names and gateway during CreateEndpoint()
+	info := ep.Info()
+
+	for _, iface := range info.InterfaceList() {
+		if iface.Address().IP.To4() == nil {
+			t.Fatalf("Invalid IP address returned: %v", iface.Address())
+		}
+	}
+
+	if info.Gateway().To4() != nil {
+		t.Fatalf("Expected empty gateway for an empty endpoint. Instead found a gateway: %v", info.Gateway())
+	}
+
+	if info.SandboxKey() != "" {
+		t.Fatalf("Expected an empty sandbox key for an empty endpoint. Instead found a non-empty sandbox key: %s", info.SandboxKey())
+	}
+
 	_, err = ep.Join(containerID,
 	_, err = ep.Join(containerID,
 		libnetwork.JoinOptionHostname("test"),
 		libnetwork.JoinOptionHostname("test"),
 		libnetwork.JoinOptionDomainname("docker.io"),
 		libnetwork.JoinOptionDomainname("docker.io"),
@@ -710,6 +727,16 @@ func TestEndpointJoin(t *testing.T) {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 	}()
 	}()
+
+	// Validate if ep.Info() only gives valid gateway and sandbox key after has container has joined.
+	info = ep.Info()
+	if info.Gateway().To4() == nil {
+		t.Fatalf("Expected a valid gateway for a joined endpoint. Instead found an invalid gateway: %v", info.Gateway())
+	}
+
+	if info.SandboxKey() == "" {
+		t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found a empty sandbox key")
+	}
 }
 }
 
 
 func TestEndpointJoinInvalidContainerId(t *testing.T) {
 func TestEndpointJoinInvalidContainerId(t *testing.T) {

+ 7 - 0
libnetwork/netutils/utils.go

@@ -288,6 +288,13 @@ func GenerateRandomName(prefix string, size int) (string, error) {
 	return prefix + hex.EncodeToString(id)[:size], nil
 	return prefix + hex.EncodeToString(id)[:size], nil
 }
 }
 
 
+// GetMacCopy returns a copy of the passed MAC address
+func GetMacCopy(from net.HardwareAddr) net.HardwareAddr {
+	to := make(net.HardwareAddr, len(from))
+	copy(to, from)
+	return to
+}
+
 // GetIPCopy returns a copy of the passed IP address
 // GetIPCopy returns a copy of the passed IP address
 func GetIPCopy(from net.IP) net.IP {
 func GetIPCopy(from net.IP) net.IP {
 	to := make(net.IP, len(from))
 	to := make(net.IP, len(from))

+ 2 - 3
libnetwork/network.go

@@ -135,18 +135,17 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
 	if name == "" {
 	if name == "" {
 		return nil, ErrInvalidName
 		return nil, ErrInvalidName
 	}
 	}
-	ep := &endpoint{name: name, generic: make(map[string]interface{})}
+	ep := &endpoint{name: name, iFaces: []*endpointInterface{}, generic: make(map[string]interface{})}
 	ep.id = types.UUID(stringid.GenerateRandomID())
 	ep.id = types.UUID(stringid.GenerateRandomID())
 	ep.network = n
 	ep.network = n
 	ep.processOptions(options...)
 	ep.processOptions(options...)
 
 
 	d := n.driver
 	d := n.driver
-	sinfo, err := d.CreateEndpoint(n.id, ep.id, ep.generic)
+	err := d.CreateEndpoint(n.id, ep.id, ep, ep.generic)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	ep.sandboxInfo = sinfo
 	n.Lock()
 	n.Lock()
 	n.endpoints[ep.id] = ep
 	n.endpoints[ep.id] = ep
 	n.Unlock()
 	n.Unlock()