瀏覽代碼

Remove multiple interface in an endpoint

Currently the endpoint data model consists of multiple
interfaces per-endpoint. This seems to be an overkill
since there is no real use case for it. Removing it
to remove unnecessary complexity from the code.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
Jana Radhakrishnan 9 年之前
父節點
當前提交
a5bd12b963

+ 5 - 12
libnetwork/cmd/ovrouter/ovrouter.go

@@ -21,7 +21,6 @@ type endpoint struct {
 	addr net.IPNet
 	addr net.IPNet
 	mac  net.HardwareAddr
 	mac  net.HardwareAddr
 	name string
 	name string
-	id   int
 }
 }
 
 
 func (r *router) RegisterDriver(name string, driver driverapi.Driver, c driverapi.Capability) error {
 func (r *router) RegisterDriver(name string, driver driverapi.Driver, c driverapi.Capability) error {
@@ -29,20 +28,18 @@ func (r *router) RegisterDriver(name string, driver driverapi.Driver, c driverap
 	return nil
 	return nil
 }
 }
 
 
-func (ep *endpoint) Interfaces() []driverapi.InterfaceInfo {
+func (ep *endpoint) Interface() driverapi.InterfaceInfo {
 	return nil
 	return nil
 }
 }
 
 
-func (ep *endpoint) AddInterface(ID int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
-	ep.id = ID
+func (ep *endpoint) AddInterface(mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
 	ep.addr = ipv4
 	ep.addr = ipv4
 	ep.mac = mac
 	ep.mac = mac
 	return nil
 	return nil
 }
 }
 
 
-func (ep *endpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
-	return []driverapi.InterfaceNameInfo{ep}
-
+func (ep *endpoint) InterfaceName() driverapi.InterfaceNameInfo {
+	return ep
 }
 }
 
 
 func (ep *endpoint) SetNames(srcName, dstPrefix string) error {
 func (ep *endpoint) SetNames(srcName, dstPrefix string) error {
@@ -50,10 +47,6 @@ func (ep *endpoint) SetNames(srcName, dstPrefix string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (ep *endpoint) ID() int {
-	return ep.id
-}
-
 func (ep *endpoint) SetGateway(net.IP) error {
 func (ep *endpoint) SetGateway(net.IP) error {
 	return nil
 	return nil
 }
 }
@@ -63,7 +56,7 @@ func (ep *endpoint) SetGatewayIPv6(net.IP) error {
 }
 }
 
 
 func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int,
 func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int,
-	nextHop net.IP, interfaceID int) error {
+	nextHop net.IP) error {
 	return nil
 	return nil
 }
 }
 
 

+ 7 - 8
libnetwork/docs/design.md

@@ -20,7 +20,7 @@ Libnetwork implements Container Network Model (CNM) which formalizes the steps r
 **Sandbox**
 **Sandbox**
 
 
 A Sandbox contains the configuration of a container's network stack.
 A Sandbox contains the configuration of a container's network stack.
-This includes management of the container's interfaces, routing table and DNS settings. 
+This includes management of the container's interfaces, routing table and DNS settings.
 An implementation of a Sandbox could be a Linux Network Namespace, a FreeBSD Jail or other similar concept.
 An implementation of a Sandbox could be a Linux Network Namespace, a FreeBSD Jail or other similar concept.
 A Sandbox may contain *many* endpoints from *multiple* networks.
 A Sandbox may contain *many* endpoints from *multiple* networks.
 
 
@@ -84,7 +84,7 @@ Consumers of the CNM, like Docker for example, interact through the CNM Objects
 
 
 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`.
 
 
-8. `network.Delete()` is used to delete a network. LibNetwork will not allow the delete to proceed if there are any existing endpoints attached to the Network. 
+8. `network.Delete()` is used to delete a network. LibNetwork will not allow the delete to proceed if there are any existing endpoints attached to the Network.
 
 
 
 
 ## Implementation Details
 ## Implementation Details
@@ -95,7 +95,7 @@ LibNetwork's Network and Endpoint APIs are primarily for managing the correspond
 
 
 ### Sandbox
 ### Sandbox
 
 
-Libnetwork provides a framework to implement of a Sandbox in multiple operating systems. Currently we have implemented Sandbox for Linux using `namespace_linux.go` and `configure_linux.go` in `sandbox` package 
+Libnetwork provides a framework to implement of a Sandbox in multiple operating systems. Currently we have implemented Sandbox for Linux using `namespace_linux.go` and `configure_linux.go` in `sandbox` package
 This creates a Network Namespace for each sandbox which is uniquely identified by a path on the host filesystem.
 This creates a Network Namespace for each sandbox which is uniquely identified by a path on the host filesystem.
 Netlink calls are used to move interfaces from the global namespace to the Sandbox namespace.
 Netlink calls are used to move interfaces from the global namespace to the Sandbox namespace.
 Netlink is also used to manage the routing table in the namespace.
 Netlink is also used to manage the routing table in the namespace.
@@ -111,7 +111,7 @@ Drivers are essentially an extension of libnetwork and provides the actual imple
 * `driver.CreateEndpoint`
 * `driver.CreateEndpoint`
 * `driver.DeleteEndpoint`
 * `driver.DeleteEndpoint`
 * `driver.Join`
 * `driver.Join`
-* `driver.Leave` 
+* `driver.Leave`
 
 
 These Driver facing APIs makes use of unique identifiers (`networkid`,`endpointid`,...) instead of names (as seen in user-facing APIs).
 These Driver facing APIs makes use of unique identifiers (`networkid`,`endpointid`,...) instead of names (as seen in user-facing APIs).
 
 
@@ -121,11 +121,11 @@ The APIs are still work in progress and there can be changes to these based on t
 
 
  * `Driver.CreateEndpoint`
  * `Driver.CreateEndpoint`
 
 
-This method is passed an interface `EndpointInfo`, with methods `Interfaces` and `AddInterface`.
+This method is passed an interface `EndpointInfo`, with methods `Interface` and `AddInterface`.
 
 
-If the slice returned by `Interfaces` is non-empty, the driver is expected to make use of the interface information therein (e.g., treating the address or addresses as statically supplied), and must return an error if it cannot. If the slice is empty, the driver should allocate zero or more _fresh_ interfaces, and use `AddInterface` to record them; or return an error if it cannot.
+If the value returned by `Interface` is non-nil, the driver is expected to make use of the interface information therein (e.g., treating the address or addresses as statically supplied), and must return an error if it cannot. If the value is `nil`, the driver should allocate exactly one _fresh_ interface, and use `AddInterface` to record them; or return an error if it cannot.
 
 
-It is forbidden to use `AddInterface` if `Interfaces` is non-empty.
+It is forbidden to use `AddInterface` if `Interface` is non-nil.
 
 
 ## Implementations
 ## Implementations
 
 
@@ -155,4 +155,3 @@ For more details on its design, please see the [Overlay Driver Design](overlay.m
 The `remote` package does not provide a driver, but provides a means of supporting drivers over a remote transport.
 The `remote` package does not provide a driver, but provides a means of supporting drivers over a remote transport.
 This allows a driver to be written in a language of your choice.
 This allows a driver to be written in a language of your choice.
 For further details, please see the [Remote Driver Design](remote.md).
 For further details, please see the [Remote Driver Design](remote.md).
-

+ 50 - 57
libnetwork/docs/remote.md

@@ -34,7 +34,7 @@ If the remote process cannot decode, or otherwise detects a syntactic problem wi
 If the remote process can decode the request, but cannot complete the operation, it must send a response in the form
 If the remote process can decode the request, but cannot complete the operation, it must send a response in the form
 
 
     {
     {
-        "Err": string
+	"Err": string
     }
     }
 
 
 The string value supplied may appear in logs, so should not include confidential information.
 The string value supplied may appear in logs, so should not include confidential information.
@@ -44,7 +44,7 @@ The string value supplied may appear in logs, so should not include confidential
 When loaded, a remote driver process receives an HTTP POST on the URL `/Plugin.Activate` with no payload. It must respond with a manifest of the form
 When loaded, a remote driver process receives an HTTP POST on the URL `/Plugin.Activate` with no payload. It must respond with a manifest of the form
 
 
     {
     {
-        "Implements": ["NetworkDriver"]
+	"Implements": ["NetworkDriver"]
     }
     }
 
 
 Other entries in the list value are allowed; `"NetworkDriver"` indicates that the plugin should be registered with LibNetwork as a driver.
 Other entries in the list value are allowed; `"NetworkDriver"` indicates that the plugin should be registered with LibNetwork as a driver.
@@ -54,10 +54,10 @@ Other entries in the list value are allowed; `"NetworkDriver"` indicates that th
 When the proxy is asked to create a network, the remote process shall receive a POST to the URL `/NetworkDriver.CreateNetwork` of the form
 When the proxy is asked to create a network, the remote process shall receive a POST to the URL `/NetworkDriver.CreateNetwork` of the form
 
 
     {
     {
-        "NetworkID": string,
-        "Options": {
-            ...
-        }
+	"NetworkID": string,
+	"Options": {
+	    ...
+	}
     }
     }
 
 
 The `NetworkID` value is generated by LibNetwork. The `Options` value is the arbitrary map given to the proxy by LibNetwork.
 The `NetworkID` value is generated by LibNetwork. The `Options` value is the arbitrary map given to the proxy by LibNetwork.
@@ -71,7 +71,7 @@ The response indicating success is empty:
 When a network owned by the remote driver is deleted, the remote process shall receive a POST to the URL `/NetworkDriver.DeleteNetwork` of the form
 When a network owned by the remote driver is deleted, the remote process shall receive a POST to the URL `/NetworkDriver.DeleteNetwork` of the form
 
 
     {
     {
-        "NetworkID": string
+	"NetworkID": string
     }
     }
 
 
 The success response is empty:
 The success response is empty:
@@ -83,53 +83,51 @@ The success response is empty:
 When the proxy is asked to create an endpoint, the remote process shall receive a POST to the URL `/NetworkDriver.CreateEndpoint` of the form
 When the proxy is asked to create an endpoint, the remote process shall receive a POST to the URL `/NetworkDriver.CreateEndpoint` of the form
 
 
     {
     {
-        "NetworkID": string,
-        "EndpointID": string,
-        "Options": {
-            ...
-        },
-        "Interfaces": [{
-            "ID": int,
-            "Address": string,
-            "AddressIPv6": string,
-            "MacAddress": string
-        }, ...]
+	"NetworkID": string,
+	"EndpointID": string,
+	"Options": {
+	    ...
+	},
+	"Interface": {
+	    "Address": string,
+	    "AddressIPv6": string,
+	    "MacAddress": string
+	}
     }
     }
 
 
 The `NetworkID` is the generated identifier for the network to which the endpoint belongs; the `EndpointID` is a generated identifier for the endpoint.
 The `NetworkID` is the generated identifier for the network to which the endpoint belongs; the `EndpointID` is a generated identifier for the endpoint.
 
 
 `Options` is an arbitrary map as supplied to the proxy.
 `Options` is an arbitrary map as supplied to the proxy.
 
 
-The `Interfaces` value is a list with values of the form given. The fields in the `Interfaces` entries may be empty; and the `Interfaces` list itself may be empty. If supplied, `Address` is an IPv4 address and subnet in CIDR notation; e.g., `"192.168.34.12/16"`. If supplied, `AddressIPv6` is an IPv6 address and subnet in CIDR notation. `MacAddress` is a MAC address as a string; e.g., `"6e:75:32:60:44:c9"`.
+The `Interface` value is of the form given. The fields in the `Interface` may be empty; and the `Interface` itself may be empty. If supplied, `Address` is an IPv4 address and subnet in CIDR notation; e.g., `"192.168.34.12/16"`. If supplied, `AddressIPv6` is an IPv6 address and subnet in CIDR notation. `MacAddress` is a MAC address as a string; e.g., `"6e:75:32:60:44:c9"`.
 
 
 A success response is of the form
 A success response is of the form
 
 
     {
     {
-        "Interfaces": [{
-            "ID": int,
-            "Address": string,
-            "AddressIPv6": string,
-            "MacAddress": string
-        }, ...]
+	"Interface": {
+	    "Address": string,
+	    "AddressIPv6": string,
+	    "MacAddress": string
+	}
     }
     }
 
 
-with values in the `Interfaces` entries as above. For each entry, an `ID` and `MacAddress` and either or both of `Address` and `AddressIPv6` must be given. The `ID` is arbitrary but must differ among entries. It is used to identify, within the scope of the endpoint, an individual interface during a `Join` call.
+with values in the `Interface` as above. As far as the value of `Interface` is concerned, `MacAddress` and either or both of `Address` and `AddressIPv6` must be given.
 
 
-If the remote process was supplied entries in `Interfaces`, it must respond with an empty `Interfaces` list. LibNetwork will treat it as an error if it supplies a non-empty list and receives a non-empty list back, and roll back the operation.
+If the remote process was supplied a non-empty value in `Interface`, it must respond with an empty `Interface` value. LibNetwork will treat it as an error if it supplies a non-empty value and receives a non-empty value back, and roll back the operation.
 
 
 ### Endpoint operational info
 ### Endpoint operational info
 
 
 The proxy may be asked for "operational info" on an endpoint. When this happens, the remote process shall receive a POST to `/NetworkDriver.EndpointOperInfo` of the form
 The proxy may be asked for "operational info" on an endpoint. When this happens, the remote process shall receive a POST to `/NetworkDriver.EndpointOperInfo` of the form
 
 
     {
     {
-        "NetworkID": string,
-        "EndpointID": string
+	"NetworkID": string,
+	"EndpointID": string
     }
     }
 
 
 where `NetworkID` and `EndpointID` have meanings as above. It must send a response of the form
 where `NetworkID` and `EndpointID` have meanings as above. It must send a response of the form
 
 
     {
     {
-        "Value": { ... }
+	"Value": { ... }
     }
     }
 
 
 where the value of the `Value` field is an arbitrary (possibly empty) map.
 where the value of the `Value` field is an arbitrary (possibly empty) map.
@@ -139,8 +137,8 @@ where the value of the `Value` field is an arbitrary (possibly empty) map.
 When an endpoint is deleted, the remote process shall receive a POST to the URL `/NetworkDriver.DeleteEndpoint` with a body of the form
 When an endpoint is deleted, the remote process shall receive a POST to the URL `/NetworkDriver.DeleteEndpoint` with a body of the form
 
 
     {
     {
-        "NetworkID": string,
-        "EndpointID": string
+	"NetworkID": string,
+	"EndpointID": string
     }
     }
 
 
 where `NetworkID` and `EndpointID` have meanings as above. A success response is empty:
 where `NetworkID` and `EndpointID` have meanings as above. A success response is empty:
@@ -152,10 +150,10 @@ where `NetworkID` and `EndpointID` have meanings as above. A success response is
 When a sandbox is given an endpoint, the remote process shall receive a POST to the URL `NetworkDriver.Join` of the form
 When a sandbox is given an endpoint, the remote process shall receive a POST to the URL `NetworkDriver.Join` of the form
 
 
     {
     {
-        "NetworkID": string,
-        "EndpointID": string,
-        "SandboxKey": string,
-        "Options": { ... }
+	"NetworkID": string,
+	"EndpointID": string,
+	"SandboxKey": string,
+	"Options": { ... }
     }
     }
 
 
 The `NetworkID` and `EndpointID` have meanings as above. The `SandboxKey` identifies the sandbox. `Options` is an arbitrary map as supplied to the proxy.
 The `NetworkID` and `EndpointID` have meanings as above. The `SandboxKey` identifies the sandbox. `Options` is an arbitrary map as supplied to the proxy.
@@ -163,29 +161,24 @@ The `NetworkID` and `EndpointID` have meanings as above. The `SandboxKey` identi
 The response must have the form
 The response must have the form
 
 
     {
     {
-        "InterfaceNames": [{
-            SrcName: string,
-            DstPrefix: string
-        }, ...],
-        "Gateway": string,
-        "GatewayIPv6": string,
-        "StaticRoutes": [{
-            "Destination": string,
-            "RouteType": int,
-            "NextHop": string,
-            "InterfaceID": int
-        }, ...]
-        "HostsPath": string,
-        "ResolvConfPath": string
+	"InterfaceName": {
+	    SrcName: string,
+	    DstPrefix: string
+	},
+	"Gateway": string,
+	"GatewayIPv6": string,
+	"StaticRoutes": [{
+	    "Destination": string,
+	    "RouteType": int,
+	    "NextHop": string,
+	}, ...]
     }
     }
 
 
-`Gateway` is optional and if supplied is an IP address as a string; e.g., `"192.168.0.1"`. `GatewayIPv6` is optional and if supplied is an IPv6 address as a string; e.g., `"fe80::7809:baff:fec6:7744"`. `HostsPath` is optional, as is `ResolvConfPath`.
+`Gateway` is optional and if supplied is an IP address as a string; e.g., `"192.168.0.1"`. `GatewayIPv6` is optional and if supplied is an IPv6 address as a string; e.g., `"fe80::7809:baff:fec6:7744"`.
 
 
-The entries in `InterfaceNames` represent veths that should be moved by LibNetwork into the sandbox; the `SrcName` is the name of the veth that the remote process created, and the `DstPrefix` is a prefix for the name the veth should have after it has been moved into the sandbox (LibNetwork will append an index to make sure the actual name does not collide with others).
+The entries in `InterfaceName` represent actual OS level interfaces that should be moved by LibNetwork into the sandbox; the `SrcName` is the name of the OS level interface that the remote process created, and the `DstPrefix` is a prefix for the name the OS level interface should have after it has been moved into the sandbox (LibNetwork will append an index to make sure the actual name does not collide with others).
 
 
-The position of the entries in the list must correspond to the interface IDs given in the response to `/NetworkDriver.CreateEndpoint` as described above. For example, if there were two `Interfaces` in the create endpoint response, with IDs `0` and `1`, then the `InterfaceNames` list would have the interface names respectively in positions `0` and `1`of the list. (For this reason it is recommended that interfaces are given sequential IDs starting with `0`.)
-
-The entries in `"StaticRoutes"` represent routes that should be added to an interface once it has been moved into the sandbox. Since there may be zero or more routes for an interface, unlike the interface names they can be supplied in any order, and are marked with the `InterfaceID` of the corresponding interface.
+The entries in `"StaticRoutes"` represent routes that should be added to an interface once it has been moved into the sandbox. Since there may be zero or more routes for an interface, unlike the interface name they can be supplied in any order.
 
 
 Routes are either given a `RouteType` of `0` and a value for `NextHop`; or, a `RouteType` of `1` and no value for `NextHop`, meaning a connected route.
 Routes are either given a `RouteType` of `0` and a value for `NextHop`; or, a `RouteType` of `1` and no value for `NextHop`, meaning a connected route.
 
 
@@ -194,8 +187,8 @@ Routes are either given a `RouteType` of `0` and a value for `NextHop`; or, a `R
 If the proxy is asked to remove an endpoint from a sandbox, the remote process shall receive a POST to the URL `/NetworkDriver.Leave` of the form
 If the proxy is asked to remove an endpoint from a sandbox, the remote process shall receive a POST to the URL `/NetworkDriver.Leave` of the form
 
 
     {
     {
-        "NetworkID": string,
-        "EndpointID": string
+	"NetworkID": string,
+	"EndpointID": string
     }
     }
 
 
 where `NetworkID` and `EndpointID` have meanings as above. The success response is empty:
 where `NetworkID` and `EndpointID` have meanings as above. The success response is empty:

+ 14 - 24
libnetwork/driverapi/driverapi.go

@@ -45,18 +45,16 @@ type Driver interface {
 
 
 // EndpointInfo provides a go interface to fetch or populate endpoint assigned network resources.
 // EndpointInfo provides a go interface to fetch or populate endpoint assigned network resources.
 type EndpointInfo interface {
 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
+	// Interface returns the interface bound to the endpoint.
+	// If the value is not nil the driver is only expected to consume the interface.
+	// It is an error to try to add interface if the passed down value is non-nil
+	// If the value is nil the driver is expected to add an interface
+	Interface() InterfaceInfo
+
+	// AddInterface is used by the driver to add an interface for the endpoint.
+	// This method will return an error if the driver attempts to add interface
+	// if the Interface() method returned a non-nil value.
+	AddInterface(mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error
 }
 }
 
 
 // InterfaceInfo provides a go interface for drivers to retrive
 // InterfaceInfo provides a go interface for drivers to retrive
@@ -70,10 +68,6 @@ type InterfaceInfo interface {
 
 
 	// AddressIPv6 returns the IPv6 address.
 	// AddressIPv6 returns the IPv6 address.
 	AddressIPv6() net.IPNet
 	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
 // InterfaceNameInfo provides a go interface for the drivers to assign names
@@ -81,18 +75,14 @@ type InterfaceInfo interface {
 type InterfaceNameInfo interface {
 type InterfaceNameInfo interface {
 	// SetNames method assigns the srcName and dstPrefix for the interface.
 	// SetNames method assigns the srcName and dstPrefix for the interface.
 	SetNames(srcName, dstPrefix string) error
 	SetNames(srcName, dstPrefix 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 interface {
 type JoinInfo interface {
-	// InterfaceNames returns a list of InterfaceNameInfo go interface to facilitate
-	// setting the names for the interfaces.
-	InterfaceNames() []InterfaceNameInfo
+	// InterfaceName returns a InterfaceNameInfo go interface to facilitate
+	// setting the names for the interface.
+	InterfaceName() InterfaceNameInfo
 
 
 	// SetGateway sets the default IPv4 gateway when a container joins the endpoint.
 	// SetGateway sets the default IPv4 gateway when a container joins the endpoint.
 	SetGateway(net.IP) error
 	SetGateway(net.IP) error
@@ -102,7 +92,7 @@ type JoinInfo interface {
 
 
 	// AddStaticRoute adds a routes to the sandbox.
 	// AddStaticRoute adds a routes to the sandbox.
 	// It may be used in addtion to or instead of a default gateway (as above).
 	// It may be used in addtion to or instead of a default gateway (as above).
-	AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP, interfaceID int) error
+	AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error
 }
 }
 
 
 // DriverCallback provides a Callback interface for Drivers into LibNetwork
 // DriverCallback provides a Callback interface for Drivers into LibNetwork

+ 7 - 12
libnetwork/drivers/bridge/bridge.go

@@ -32,7 +32,6 @@ const (
 	vethLen                 = 7
 	vethLen                 = 7
 	containerVethPrefix     = "eth"
 	containerVethPrefix     = "eth"
 	maxAllocatePortAttempts = 10
 	maxAllocatePortAttempts = 10
-	ifaceID                 = 1
 )
 )
 
 
 var (
 var (
@@ -883,8 +882,8 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
 		return errors.New("invalid endpoint info passed")
 		return errors.New("invalid endpoint info passed")
 	}
 	}
 
 
-	if len(epInfo.Interfaces()) != 0 {
-		return errors.New("non empty interface list passed to bridge(local) driver")
+	if epInfo.Interface() != nil {
+		return errors.New("non-nil interface passed to bridge(local) driver")
 	}
 	}
 
 
 	// Get the network handler and make sure it exists
 	// Get the network handler and make sure it exists
@@ -1070,7 +1069,7 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
 		endpoint.addrv6 = ipv6Addr
 		endpoint.addrv6 = ipv6Addr
 	}
 	}
 
 
-	err = epInfo.AddInterface(ifaceID, endpoint.macAddress, *ipv4Addr, *ipv6Addr)
+	err = epInfo.AddInterface(endpoint.macAddress, *ipv4Addr, *ipv6Addr)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -1244,14 +1243,10 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 		return EndpointNotFoundError(eid)
 		return EndpointNotFoundError(eid)
 	}
 	}
 
 
-	for _, iNames := range jinfo.InterfaceNames() {
-		// Make sure to set names on the correct interface ID.
-		if iNames.ID() == ifaceID {
-			err = iNames.SetNames(endpoint.srcName, containerVethPrefix)
-			if err != nil {
-				return err
-			}
-		}
+	iNames := jinfo.InterfaceName()
+	err = iNames.SetNames(endpoint.srcName, containerVethPrefix)
+	if err != nil {
+		return err
 	}
 	}
 
 
 	err = jinfo.SetGateway(network.bridge.gatewayIPv4)
 	err = jinfo.SetGateway(network.bridge.gatewayIPv4)

+ 26 - 34
libnetwork/drivers/bridge/bridge_test.go

@@ -58,13 +58,14 @@ func TestCreateFullOptions(t *testing.T) {
 
 
 	// Verify the IP address allocated for the endpoint belongs to the container network
 	// Verify the IP address allocated for the endpoint belongs to the container network
 	epOptions := make(map[string]interface{})
 	epOptions := make(map[string]interface{})
-	te := &testEndpoint{ifaces: []*testInterface{}}
+	te := &testEndpoint{}
 	err = d.CreateEndpoint("dummy", "ep1", te, epOptions)
 	err = d.CreateEndpoint("dummy", "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())
 	}
 	}
-	if !cnw.Contains(te.Interfaces()[0].Address().IP) {
-		t.Fatalf("endpoint got assigned address outside of container network(%s): %s", cnw.String(), te.Interfaces()[0].Address())
+
+	if !cnw.Contains(te.Interface().Address().IP) {
+		t.Fatalf("endpoint got assigned address outside of container network(%s): %s", cnw.String(), te.Interface().Address())
 	}
 	}
 }
 }
 
 
@@ -207,7 +208,6 @@ func verifyV4INCEntries(networks map[string]*bridgeNetwork, numEntries int, t *t
 }
 }
 
 
 type testInterface struct {
 type testInterface struct {
-	id      int
 	mac     net.HardwareAddr
 	mac     net.HardwareAddr
 	addr    net.IPNet
 	addr    net.IPNet
 	addrv6  net.IPNet
 	addrv6  net.IPNet
@@ -216,7 +216,7 @@ type testInterface struct {
 }
 }
 
 
 type testEndpoint struct {
 type testEndpoint struct {
-	ifaces         []*testInterface
+	iface          *testInterface
 	gw             net.IP
 	gw             net.IP
 	gw6            net.IP
 	gw6            net.IP
 	hostsPath      string
 	hostsPath      string
@@ -224,24 +224,18 @@ type testEndpoint struct {
 	routes         []types.StaticRoute
 	routes         []types.StaticRoute
 }
 }
 
 
-func (te *testEndpoint) Interfaces() []driverapi.InterfaceInfo {
-	iList := make([]driverapi.InterfaceInfo, len(te.ifaces))
-
-	for i, iface := range te.ifaces {
-		iList[i] = iface
+func (te *testEndpoint) Interface() driverapi.InterfaceInfo {
+	if te.iface != nil {
+		return te.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
 	return nil
 }
 }
 
 
-func (i *testInterface) ID() int {
-	return i.id
+func (te *testEndpoint) AddInterface(mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
+	iface := &testInterface{addr: ipv4, addrv6: ipv6}
+	te.iface = iface
+	return nil
 }
 }
 
 
 func (i *testInterface) MacAddress() net.HardwareAddr {
 func (i *testInterface) MacAddress() net.HardwareAddr {
@@ -262,14 +256,12 @@ func (i *testInterface) SetNames(srcName string, dstName string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (te *testEndpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
-	iList := make([]driverapi.InterfaceNameInfo, len(te.ifaces))
-
-	for i, iface := range te.ifaces {
-		iList[i] = iface
+func (te *testEndpoint) InterfaceName() driverapi.InterfaceNameInfo {
+	if te.iface != nil {
+		return te.iface
 	}
 	}
 
 
-	return iList
+	return nil
 }
 }
 
 
 func (te *testEndpoint) SetGateway(gw net.IP) error {
 func (te *testEndpoint) SetGateway(gw net.IP) error {
@@ -282,8 +274,8 @@ func (te *testEndpoint) SetGatewayIPv6(gw6 net.IP) error {
 	return nil
 	return nil
 }
 }
 
 
-func (te *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP, interfaceID int) error {
-	te.routes = append(te.routes, types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop, InterfaceID: interfaceID})
+func (te *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error {
+	te.routes = append(te.routes, types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop})
 	return nil
 	return nil
 }
 }
 
 
@@ -327,7 +319,7 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
 	epOptions := make(map[string]interface{})
 	epOptions := make(map[string]interface{})
 	epOptions[netlabel.PortMap] = portMappings
 	epOptions[netlabel.PortMap] = portMappings
 
 
-	te := &testEndpoint{ifaces: []*testInterface{}}
+	te := &testEndpoint{}
 	err = d.CreateEndpoint("net1", "ep1", te, epOptions)
 	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())
@@ -387,7 +379,7 @@ func TestCreateLinkWithOptions(t *testing.T) {
 	epOptions := make(map[string]interface{})
 	epOptions := make(map[string]interface{})
 	epOptions[netlabel.MacAddress] = mac
 	epOptions[netlabel.MacAddress] = mac
 
 
-	te := &testEndpoint{ifaces: []*testInterface{}}
+	te := &testEndpoint{}
 	err = d.CreateEndpoint("net1", "ep", te, epOptions)
 	err = d.CreateEndpoint("net1", "ep", 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())
@@ -398,7 +390,7 @@ func TestCreateLinkWithOptions(t *testing.T) {
 		t.Fatalf("Failed to join the endpoint: %v", err)
 		t.Fatalf("Failed to join the endpoint: %v", err)
 	}
 	}
 
 
-	ifaceName := te.ifaces[0].srcName
+	ifaceName := te.iface.srcName
 	veth, err := netlink.LinkByName(ifaceName)
 	veth, err := netlink.LinkByName(ifaceName)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -456,24 +448,24 @@ func TestLinkContainers(t *testing.T) {
 	epOptions := make(map[string]interface{})
 	epOptions := make(map[string]interface{})
 	epOptions[netlabel.ExposedPorts] = exposedPorts
 	epOptions[netlabel.ExposedPorts] = exposedPorts
 
 
-	te1 := &testEndpoint{ifaces: []*testInterface{}}
+	te1 := &testEndpoint{}
 	err = d.CreateEndpoint("net1", "ep1", te1, epOptions)
 	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 := te1.ifaces[0].addr
+	addr1 := te1.iface.addr
 	if addr1.IP.To4() == nil {
 	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")
 	}
 	}
 
 
-	te2 := &testEndpoint{ifaces: []*testInterface{}}
+	te2 := &testEndpoint{}
 	err = d.CreateEndpoint("net1", "ep2", te2, nil)
 	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 := te2.ifaces[0].addr
+	addr2 := te2.iface.addr
 	if addr2.IP.To4() == nil {
 	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")
 	}
 	}
@@ -683,7 +675,7 @@ func TestSetDefaultGw(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	te := &testEndpoint{ifaces: []*testInterface{}}
+	te := &testEndpoint{}
 	err = d.CreateEndpoint("dummy", "ep", te, nil)
 	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)

+ 15 - 19
libnetwork/drivers/bridge/network_test.go

@@ -32,7 +32,7 @@ func TestLinkCreate(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	te := &testEndpoint{ifaces: []*testInterface{}}
+	te := &testEndpoint{}
 	err = d.CreateEndpoint("dummy", "", te, nil)
 	err = d.CreateEndpoint("dummy", "", te, nil)
 	if err != nil {
 	if err != nil {
 		if _, ok := err.(InvalidEndpointIDError); !ok {
 		if _, ok := err.(InvalidEndpointIDError); !ok {
@@ -54,7 +54,7 @@ func TestLinkCreate(t *testing.T) {
 	}
 	}
 
 
 	// Verify sbox endoint interface inherited MTU value from bridge config
 	// Verify sbox endoint interface inherited MTU value from bridge config
-	sboxLnk, err := netlink.LinkByName(te.ifaces[0].srcName)
+	sboxLnk, err := netlink.LinkByName(te.iface.srcName)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -64,35 +64,31 @@ 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.
 
 
-	te1 := &testEndpoint{ifaces: []*testInterface{}}
+	te1 := &testEndpoint{iface: &testInterface{}}
 	err = d.CreateEndpoint("dummy", "ep", te1, nil)
 	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")
 	}
 	}
 
 
-	if len(te.ifaces) != 1 {
-		t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(te.ifaces))
-	}
-
-	if te.ifaces[0].dstName == "" {
+	if te.iface.dstName == "" {
 		t.Fatal("Invalid Dstname returned")
 		t.Fatal("Invalid Dstname returned")
 	}
 	}
 
 
-	_, err = netlink.LinkByName(te.ifaces[0].srcName)
+	_, err = netlink.LinkByName(te.iface.srcName)
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Could not find source link %s: %v", te.ifaces[0].srcName, err)
+		t.Fatalf("Could not find source link %s: %v", te.iface.srcName, err)
 	}
 	}
 
 
 	n, ok := dr.networks["dummy"]
 	n, ok := dr.networks["dummy"]
 	if !ok {
 	if !ok {
 		t.Fatalf("Cannot find network %s inside driver", "dummy")
 		t.Fatalf("Cannot find network %s inside driver", "dummy")
 	}
 	}
-	ip := te.ifaces[0].addr.IP
+	ip := te.iface.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 := te.ifaces[0].addrv6.IP
+	ip6 := te.iface.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())
 	}
 	}
@@ -127,13 +123,13 @@ func TestLinkCreateTwo(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	te1 := &testEndpoint{ifaces: []*testInterface{}}
+	te1 := &testEndpoint{}
 	err = d.CreateEndpoint("dummy", "ep", te1, nil)
 	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())
 	}
 	}
 
 
-	te2 := &testEndpoint{ifaces: []*testInterface{}}
+	te2 := &testEndpoint{}
 	err = d.CreateEndpoint("dummy", "ep", te2, nil)
 	err = d.CreateEndpoint("dummy", "ep", te2, nil)
 	if err != nil {
 	if err != nil {
 		if _, ok := err.(driverapi.ErrEndpointExists); !ok {
 		if _, ok := err.(driverapi.ErrEndpointExists); !ok {
@@ -162,15 +158,15 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	te := &testEndpoint{ifaces: []*testInterface{}}
+	te := &testEndpoint{}
 	err = d.CreateEndpoint("dummy", "ep", te, nil)
 	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 := 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())
+	iface := te.iface
+	if iface.addrv6.IP.To16() != nil {
+		t.Fatalf("Expectd IPv6 address to be nil when IPv6 is not enabled. Got IPv6 = %s", iface.addrv6.String())
 	}
 	}
 
 
 	if te.gw6.To16() != nil {
 	if te.gw6.To16() != nil {
@@ -197,7 +193,7 @@ func TestLinkDelete(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	te := &testEndpoint{ifaces: []*testInterface{}}
+	te := &testEndpoint{}
 	err = d.CreateEndpoint("dummy", "ep1", te, nil)
 	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())

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

@@ -49,7 +49,7 @@ func TestPortMappingConfig(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 	}
 
 
-	te := &testEndpoint{ifaces: []*testInterface{}}
+	te := &testEndpoint{}
 	err = d.CreateEndpoint("dummy", "ep1", te, epOptions)
 	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())

+ 4 - 7
libnetwork/drivers/overlay/joinleave.go

@@ -65,13 +65,10 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 		return fmt.Errorf("could not set mac address to the container interface: %v", err)
 		return fmt.Errorf("could not set mac address to the container interface: %v", err)
 	}
 	}
 
 
-	for _, iNames := range jinfo.InterfaceNames() {
-		// Make sure to set names on the correct interface ID.
-		if iNames.ID() == 1 {
-			err = iNames.SetNames(name2, "eth")
-			if err != nil {
-				return err
-			}
+	if iNames := jinfo.InterfaceName(); iNames != nil {
+		err = iNames.SetNames(name2, "eth")
+		if err != nil {
+			return err
 		}
 		}
 	}
 	}
 
 

+ 4 - 4
libnetwork/drivers/overlay/ov_endpoint.go

@@ -51,10 +51,10 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
 		id: eid,
 		id: eid,
 	}
 	}
 
 
-	if epInfo != nil && (len(epInfo.Interfaces()) > 0) {
-		addr := epInfo.Interfaces()[0].Address()
+	if epInfo != nil && epInfo.Interface() != nil {
+		addr := epInfo.Interface().Address()
 		ep.addr = &addr
 		ep.addr = &addr
-		ep.mac = epInfo.Interfaces()[0].MacAddress()
+		ep.mac = epInfo.Interface().MacAddress()
 		n.addEndpoint(ep)
 		n.addEndpoint(ep)
 		return nil
 		return nil
 	}
 	}
@@ -74,7 +74,7 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
 
 
 	ep.mac = netutils.GenerateMACFromIP(ep.addr.IP)
 	ep.mac = netutils.GenerateMACFromIP(ep.addr.IP)
 
 
-	err = epInfo.AddInterface(1, ep.mac, *ep.addr, net.IPNet{})
+	err = epInfo.AddInterface(ep.mac, *ep.addr, net.IPNet{})
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("could not add interface to endpoint info: %v", err)
 		return fmt.Errorf("could not add interface to endpoint info: %v", err)
 	}
 	}

+ 6 - 9
libnetwork/drivers/remote/api/api.go

@@ -48,13 +48,12 @@ type CreateEndpointRequest struct {
 	NetworkID string
 	NetworkID string
 	// The ID of the endpoint for later reference.
 	// The ID of the endpoint for later reference.
 	EndpointID string
 	EndpointID string
-	Interfaces []*EndpointInterface
+	Interface  *EndpointInterface
 	Options    map[string]interface{}
 	Options    map[string]interface{}
 }
 }
 
 
 // EndpointInterface represents an interface endpoint.
 // EndpointInterface represents an interface endpoint.
 type EndpointInterface struct {
 type EndpointInterface struct {
-	ID          int
 	Address     string
 	Address     string
 	AddressIPv6 string
 	AddressIPv6 string
 	MacAddress  string
 	MacAddress  string
@@ -63,12 +62,11 @@ type EndpointInterface struct {
 // CreateEndpointResponse is the response to the CreateEndpoint action.
 // CreateEndpointResponse is the response to the CreateEndpoint action.
 type CreateEndpointResponse struct {
 type CreateEndpointResponse struct {
 	Response
 	Response
-	Interfaces []*EndpointInterface
+	Interface *EndpointInterface
 }
 }
 
 
 // Interface is the representation of a linux interface.
 // Interface is the representation of a linux interface.
 type Interface struct {
 type Interface struct {
-	ID          int
 	Address     *net.IPNet
 	Address     *net.IPNet
 	AddressIPv6 *net.IPNet
 	AddressIPv6 *net.IPNet
 	MacAddress  net.HardwareAddr
 	MacAddress  net.HardwareAddr
@@ -118,16 +116,15 @@ type StaticRoute struct {
 	Destination string
 	Destination string
 	RouteType   int
 	RouteType   int
 	NextHop     string
 	NextHop     string
-	InterfaceID int
 }
 }
 
 
 // JoinResponse is the response to a JoinRequest.
 // JoinResponse is the response to a JoinRequest.
 type JoinResponse struct {
 type JoinResponse struct {
 	Response
 	Response
-	InterfaceNames []*InterfaceName
-	Gateway        string
-	GatewayIPv6    string
-	StaticRoutes   []StaticRoute
+	InterfaceName *InterfaceName
+	Gateway       string
+	GatewayIPv6   string
+	StaticRoutes  []StaticRoute
 }
 }
 
 
 // LeaveRequest describes the API for detaching an endpoint from a sandbox.
 // LeaveRequest describes the API for detaching an endpoint from a sandbox.

+ 36 - 39
libnetwork/drivers/remote/driver.go

@@ -71,16 +71,17 @@ func (d *driver) DeleteNetwork(nid string) error {
 }
 }
 
 
 func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
 func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
+	var reqIface *api.EndpointInterface
+
 	if epInfo == nil {
 	if epInfo == nil {
 		return fmt.Errorf("must not be called with nil EndpointInfo")
 		return fmt.Errorf("must not be called with nil EndpointInfo")
 	}
 	}
 
 
-	reqIfaces := make([]*api.EndpointInterface, len(epInfo.Interfaces()))
-	for i, iface := range epInfo.Interfaces() {
+	iface := epInfo.Interface()
+	if iface != nil {
 		addr4 := iface.Address()
 		addr4 := iface.Address()
 		addr6 := iface.AddressIPv6()
 		addr6 := iface.AddressIPv6()
-		reqIfaces[i] = &api.EndpointInterface{
-			ID:          iface.ID(),
+		reqIface = &api.EndpointInterface{
 			Address:     addr4.String(),
 			Address:     addr4.String(),
 			AddressIPv6: addr6.String(),
 			AddressIPv6: addr6.String(),
 			MacAddress:  iface.MacAddress().String(),
 			MacAddress:  iface.MacAddress().String(),
@@ -89,7 +90,7 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
 	create := &api.CreateEndpointRequest{
 	create := &api.CreateEndpointRequest{
 		NetworkID:  nid,
 		NetworkID:  nid,
 		EndpointID: eid,
 		EndpointID: eid,
-		Interfaces: reqIfaces,
+		Interface:  reqIface,
 		Options:    epOptions,
 		Options:    epOptions,
 	}
 	}
 	var res api.CreateEndpointResponse
 	var res api.CreateEndpointResponse
@@ -97,25 +98,26 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
 		return err
 		return err
 	}
 	}
 
 
-	ifaces, err := parseInterfaces(res)
+	inIface, err := parseInterface(res)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	if len(reqIfaces) > 0 && len(ifaces) > 0 {
-		// We're not supposed to add interfaces if there already are
-		// some. Attempt to roll back
-		return errorWithRollback("driver attempted to add more interfaces", d.DeleteEndpoint(nid, eid))
+	if reqIface != nil && inIface != nil {
+		// We're not supposed to add interface if there is already
+		// one. Attempt to roll back
+		return errorWithRollback("driver attempted to add interface ignoring the one provided", d.DeleteEndpoint(nid, eid))
 	}
 	}
-	for _, iface := range ifaces {
+
+	if inIface != nil {
 		var addr4, addr6 net.IPNet
 		var addr4, addr6 net.IPNet
-		if iface.Address != nil {
-			addr4 = *(iface.Address)
+		if inIface.Address != nil {
+			addr4 = *(inIface.Address)
 		}
 		}
-		if iface.AddressIPv6 != nil {
-			addr6 = *(iface.AddressIPv6)
+		if inIface.AddressIPv6 != nil {
+			addr6 = *(inIface.AddressIPv6)
 		}
 		}
-		if err := epInfo.AddInterface(iface.ID, iface.MacAddress, addr4, addr6); err != nil {
-			return errorWithRollback(fmt.Sprintf("failed to AddInterface %v: %s", iface, err), d.DeleteEndpoint(nid, eid))
+		if err := epInfo.AddInterface(inIface.MacAddress, addr4, addr6); err != nil {
+			return errorWithRollback(fmt.Sprintf("failed to AddInterface %v: %s", inIface, err), d.DeleteEndpoint(nid, eid))
 		}
 		}
 	}
 	}
 	return nil
 	return nil
@@ -165,18 +167,13 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 		return err
 		return err
 	}
 	}
 
 
-	// Expect each interface ID given by CreateEndpoint to have an
-	// entry at that index in the names supplied here. In other words,
-	// if you supply 0..n interfaces with IDs 0..n above, you should
-	// supply the names in the same order.
-	ifaceNames := res.InterfaceNames
-	for _, iface := range jinfo.InterfaceNames() {
-		i := iface.ID()
-		if i >= len(ifaceNames) || i < 0 {
-			return fmt.Errorf("no correlating interface %d in supplied interface names", i)
-		}
-		supplied := ifaceNames[i]
-		if err := iface.SetNames(supplied.SrcName, supplied.DstPrefix); err != nil {
+	ifaceName := res.InterfaceName
+	if ifaceName == nil {
+		return fmt.Errorf("no interface name information received")
+	}
+
+	if iface := jinfo.InterfaceName(); iface != nil {
+		if err := iface.SetNames(ifaceName.SrcName, ifaceName.DstPrefix); err != nil {
 			return errorWithRollback(fmt.Sprintf("failed to set interface name: %s", err), d.Leave(nid, eid))
 			return errorWithRollback(fmt.Sprintf("failed to set interface name: %s", err), d.Leave(nid, eid))
 		}
 		}
 	}
 	}
@@ -204,7 +201,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 			return err
 			return err
 		}
 		}
 		for _, route := range routes {
 		for _, route := range routes {
-			if jinfo.AddStaticRoute(route.Destination, route.RouteType, route.NextHop, route.InterfaceID) != nil {
+			if jinfo.AddStaticRoute(route.Destination, route.RouteType, route.NextHop) != nil {
 				return errorWithRollback(fmt.Sprintf("failed to set static route: %v", route), d.Leave(nid, eid))
 				return errorWithRollback(fmt.Sprintf("failed to set static route: %v", route), d.Leave(nid, eid))
 			}
 			}
 		}
 		}
@@ -229,7 +226,7 @@ func parseStaticRoutes(r api.JoinResponse) ([]*types.StaticRoute, error) {
 	var routes = make([]*types.StaticRoute, len(r.StaticRoutes))
 	var routes = make([]*types.StaticRoute, len(r.StaticRoutes))
 	for i, inRoute := range r.StaticRoutes {
 	for i, inRoute := range r.StaticRoutes {
 		var err error
 		var err error
-		outRoute := &types.StaticRoute{InterfaceID: inRoute.InterfaceID, RouteType: inRoute.RouteType}
+		outRoute := &types.StaticRoute{RouteType: inRoute.RouteType}
 
 
 		if inRoute.Destination != "" {
 		if inRoute.Destination != "" {
 			if outRoute.Destination, err = toAddr(inRoute.Destination); err != nil {
 			if outRoute.Destination, err = toAddr(inRoute.Destination); err != nil {
@@ -250,13 +247,13 @@ func parseStaticRoutes(r api.JoinResponse) ([]*types.StaticRoute, error) {
 }
 }
 
 
 // parseInterfaces validates all the parameters of an Interface and returns them.
 // parseInterfaces validates all the parameters of an Interface and returns them.
-func parseInterfaces(r api.CreateEndpointResponse) ([]*api.Interface, error) {
-	var (
-		Interfaces = make([]*api.Interface, len(r.Interfaces))
-	)
-	for i, inIf := range r.Interfaces {
+func parseInterface(r api.CreateEndpointResponse) (*api.Interface, error) {
+	var outIf *api.Interface
+
+	inIf := r.Interface
+	if inIf != nil {
 		var err error
 		var err error
-		outIf := &api.Interface{ID: inIf.ID}
+		outIf = &api.Interface{}
 		if inIf.Address != "" {
 		if inIf.Address != "" {
 			if outIf.Address, err = toAddr(inIf.Address); err != nil {
 			if outIf.Address, err = toAddr(inIf.Address); err != nil {
 				return nil, err
 				return nil, err
@@ -272,9 +269,9 @@ func parseInterfaces(r api.CreateEndpointResponse) ([]*api.Interface, error) {
 				return nil, err
 				return nil, err
 			}
 			}
 		}
 		}
-		Interfaces[i] = outIf
 	}
 	}
-	return Interfaces, nil
+
+	return outIf, nil
 }
 }
 
 
 func toAddr(ipAddr string) (*net.IPNet, error) {
 func toAddr(ipAddr string) (*net.IPNet, error) {

+ 15 - 36
libnetwork/drivers/remote/driver_test.go

@@ -59,7 +59,6 @@ func setupPlugin(t *testing.T, name string, mux *http.ServeMux) func() {
 
 
 type testEndpoint struct {
 type testEndpoint struct {
 	t              *testing.T
 	t              *testing.T
-	id             int
 	src            string
 	src            string
 	dst            string
 	dst            string
 	address        string
 	address        string
@@ -74,16 +73,11 @@ type testEndpoint struct {
 	routeType      int
 	routeType      int
 }
 }
 
 
-func (test *testEndpoint) Interfaces() []driverapi.InterfaceInfo {
-	// return an empty one so we don't trip the check for existing
-	// interfaces; we don't care about this after that
-	return []driverapi.InterfaceInfo{}
+func (test *testEndpoint) Interface() driverapi.InterfaceInfo {
+	return nil
 }
 }
 
 
-func (test *testEndpoint) AddInterface(ID int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
-	if ID != test.id {
-		test.t.Fatalf("Wrong ID passed to AddInterface: %d", ID)
-	}
+func (test *testEndpoint) AddInterface(mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
 	ip4, net4, _ := net.ParseCIDR(test.address)
 	ip4, net4, _ := net.ParseCIDR(test.address)
 	ip6, net6, _ := net.ParseCIDR(test.addressIPv6)
 	ip6, net6, _ := net.ParseCIDR(test.addressIPv6)
 	if ip4 != nil {
 	if ip4 != nil {
@@ -104,8 +98,8 @@ func (test *testEndpoint) AddInterface(ID int, mac net.HardwareAddr, ipv4 net.IP
 	return nil
 	return nil
 }
 }
 
 
-func (test *testEndpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
-	return []driverapi.InterfaceNameInfo{test}
+func (test *testEndpoint) InterfaceName() driverapi.InterfaceNameInfo {
+	return test
 }
 }
 
 
 func compareIPs(t *testing.T, kind string, shouldBe string, supplied net.IP) {
 func compareIPs(t *testing.T, kind string, shouldBe string, supplied net.IP) {
@@ -148,7 +142,7 @@ func (test *testEndpoint) SetNames(src string, dst string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (test *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP, interfaceID int) error {
+func (test *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error {
 	compareIPNets(test.t, "Destination", test.destination, *destination)
 	compareIPNets(test.t, "Destination", test.destination, *destination)
 	compareIPs(test.t, "NextHop", test.nextHop, nextHop)
 	compareIPs(test.t, "NextHop", test.nextHop, nextHop)
 
 
@@ -156,17 +150,9 @@ func (test *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int,
 		test.t.Fatalf(`Wrong RouteType; expected "%d", got "%d"`, test.routeType, routeType)
 		test.t.Fatalf(`Wrong RouteType; expected "%d", got "%d"`, test.routeType, routeType)
 	}
 	}
 
 
-	if test.id != interfaceID {
-		test.t.Fatalf(`Wrong InterfaceID; expected "%d", got "%d"`, test.id, interfaceID)
-	}
-
 	return nil
 	return nil
 }
 }
 
 
-func (test *testEndpoint) ID() int {
-	return test.id
-}
-
 func TestRemoteDriver(t *testing.T) {
 func TestRemoteDriver(t *testing.T) {
 	var plugin = "test-net-driver"
 	var plugin = "test-net-driver"
 
 
@@ -207,13 +193,12 @@ func TestRemoteDriver(t *testing.T) {
 	})
 	})
 	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
 	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
 		iface := map[string]interface{}{
 		iface := map[string]interface{}{
-			"ID":          ep.id,
 			"Address":     ep.address,
 			"Address":     ep.address,
 			"AddressIPv6": ep.addressIPv6,
 			"AddressIPv6": ep.addressIPv6,
 			"MacAddress":  ep.macAddress,
 			"MacAddress":  ep.macAddress,
 		}
 		}
 		return map[string]interface{}{
 		return map[string]interface{}{
-			"Interfaces": []interface{}{iface},
+			"Interface": iface,
 		}
 		}
 	})
 	})
 	handle(t, mux, "Join", func(msg map[string]interface{}) interface{} {
 	handle(t, mux, "Join", func(msg map[string]interface{}) interface{} {
@@ -227,17 +212,14 @@ func TestRemoteDriver(t *testing.T) {
 			"GatewayIPv6":    ep.gatewayIPv6,
 			"GatewayIPv6":    ep.gatewayIPv6,
 			"HostsPath":      ep.hostsPath,
 			"HostsPath":      ep.hostsPath,
 			"ResolvConfPath": ep.resolvConfPath,
 			"ResolvConfPath": ep.resolvConfPath,
-			"InterfaceNames": []map[string]interface{}{
-				map[string]interface{}{
-					"SrcName":   ep.src,
-					"DstPrefix": ep.dst,
-				},
+			"InterfaceName": map[string]interface{}{
+				"SrcName":   ep.src,
+				"DstPrefix": ep.dst,
 			},
 			},
 			"StaticRoutes": []map[string]interface{}{
 			"StaticRoutes": []map[string]interface{}{
 				map[string]interface{}{
 				map[string]interface{}{
 					"Destination": ep.destination,
 					"Destination": ep.destination,
 					"RouteType":   ep.routeType,
 					"RouteType":   ep.routeType,
-					"InterfaceID": ep.id,
 					"NextHop":     ep.nextHop,
 					"NextHop":     ep.nextHop,
 				},
 				},
 			},
 			},
@@ -343,13 +325,11 @@ func TestMissingValues(t *testing.T) {
 	defer setupPlugin(t, plugin, mux)()
 	defer setupPlugin(t, plugin, mux)()
 
 
 	ep := &testEndpoint{
 	ep := &testEndpoint{
-		t:  t,
-		id: 0,
+		t: t,
 	}
 	}
 
 
 	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
 	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
 		iface := map[string]interface{}{
 		iface := map[string]interface{}{
-			"ID":          ep.id,
 			"Address":     ep.address,
 			"Address":     ep.address,
 			"AddressIPv6": ep.addressIPv6,
 			"AddressIPv6": ep.addressIPv6,
 			"MacAddress":  ep.macAddress,
 			"MacAddress":  ep.macAddress,
@@ -373,11 +353,11 @@ func TestMissingValues(t *testing.T) {
 type rollbackEndpoint struct {
 type rollbackEndpoint struct {
 }
 }
 
 
-func (r *rollbackEndpoint) Interfaces() []driverapi.InterfaceInfo {
-	return []driverapi.InterfaceInfo{}
+func (r *rollbackEndpoint) Interface() driverapi.InterfaceInfo {
+	return nil
 }
 }
 
 
-func (r *rollbackEndpoint) AddInterface(_ int, _ net.HardwareAddr, _ net.IPNet, _ net.IPNet) error {
+func (r *rollbackEndpoint) AddInterface(_ net.HardwareAddr, _ net.IPNet, _ net.IPNet) error {
 	return fmt.Errorf("fail this to trigger a rollback")
 	return fmt.Errorf("fail this to trigger a rollback")
 }
 }
 
 
@@ -391,13 +371,12 @@ func TestRollback(t *testing.T) {
 
 
 	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
 	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
 		iface := map[string]interface{}{
 		iface := map[string]interface{}{
-			"ID":          0,
 			"Address":     "192.168.4.5/16",
 			"Address":     "192.168.4.5/16",
 			"AddressIPv6": "",
 			"AddressIPv6": "",
 			"MacAddress":  "7a:12:34:56:78:90",
 			"MacAddress":  "7a:12:34:56:78:90",
 		}
 		}
 		return map[string]interface{}{
 		return map[string]interface{}{
-			"Interfaces": []interface{}{iface},
+			"Interface": interface{}(iface),
 		}
 		}
 	})
 	})
 	handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} {
 	handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} {

+ 6 - 17
libnetwork/endpoint.go

@@ -50,7 +50,7 @@ type endpoint struct {
 	name          string
 	name          string
 	id            string
 	id            string
 	network       *network
 	network       *network
-	iFaces        []*endpointInterface
+	iface         *endpointInterface
 	joinInfo      *endpointJoinInfo
 	joinInfo      *endpointJoinInfo
 	sandboxID     string
 	sandboxID     string
 	exposedPorts  []types.TransportPort
 	exposedPorts  []types.TransportPort
@@ -68,7 +68,7 @@ func (ep *endpoint) MarshalJSON() ([]byte, error) {
 	epMap := make(map[string]interface{})
 	epMap := make(map[string]interface{})
 	epMap["name"] = ep.name
 	epMap["name"] = ep.name
 	epMap["id"] = ep.id
 	epMap["id"] = ep.id
-	epMap["ep_iface"] = ep.iFaces
+	epMap["ep_iface"] = ep.iface
 	epMap["exposed_ports"] = ep.exposedPorts
 	epMap["exposed_ports"] = ep.exposedPorts
 	epMap["generic"] = ep.generic
 	epMap["generic"] = ep.generic
 	epMap["sandbox"] = ep.sandboxID
 	epMap["sandbox"] = ep.sandboxID
@@ -87,12 +87,7 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) {
 	ep.id = epMap["id"].(string)
 	ep.id = epMap["id"].(string)
 
 
 	ib, _ := json.Marshal(epMap["ep_iface"])
 	ib, _ := json.Marshal(epMap["ep_iface"])
-	var ifaces []endpointInterface
-	json.Unmarshal(ib, &ifaces)
-	ep.iFaces = make([]*endpointInterface, 0)
-	for _, iface := range ifaces {
-		ep.iFaces = append(ep.iFaces, &iface)
-	}
+	json.Unmarshal(ib, ep.iface)
 
 
 	tb, _ := json.Marshal(epMap["exposed_ports"])
 	tb, _ := json.Marshal(epMap["exposed_ports"])
 	var tPorts []types.TransportPort
 	var tPorts []types.TransportPort
@@ -316,13 +311,7 @@ func (ep *endpoint) hasInterface(iName string) bool {
 	ep.Lock()
 	ep.Lock()
 	defer ep.Unlock()
 	defer ep.Unlock()
 
 
-	for _, iface := range ep.iFaces {
-		if iface.srcName == iName {
-			return true
-		}
-	}
-
-	return false
+	return ep.iface != nil && ep.iface.srcName == iName
 }
 }
 
 
 func (ep *endpoint) Leave(sbox Sandbox, options ...EndpointOption) error {
 func (ep *endpoint) Leave(sbox Sandbox, options ...EndpointOption) error {
@@ -479,8 +468,8 @@ func (ep *endpoint) getFirstInterfaceAddress() net.IP {
 	ep.Lock()
 	ep.Lock()
 	defer ep.Unlock()
 	defer ep.Unlock()
 
 
-	if len(ep.iFaces) != 0 && ep.iFaces[0] != nil {
-		return ep.iFaces[0].addr.IP
+	if ep.iface != nil {
+		return ep.iface.addr.IP
 	}
 	}
 
 
 	return nil
 	return nil

+ 27 - 41
libnetwork/endpoint_info.go

@@ -10,9 +10,11 @@ import (
 
 
 // EndpointInfo provides an interface to retrieve network resources bound to the endpoint.
 // EndpointInfo provides an interface to retrieve network resources bound to the endpoint.
 type EndpointInfo interface {
 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
+	// Iface returns InterfaceInfo, go interface that can be used
+	// to get more information on the interface which was assigned to
+	// the endpoint by the driver. This can be used after the
+	// endpoint has been created.
+	Iface() InterfaceInfo
 
 
 	// Gateway returns the IPv4 gateway assigned by the driver.
 	// Gateway returns the IPv4 gateway assigned by the driver.
 	// This will only return a valid value if a container has joined the endpoint.
 	// This will only return a valid value if a container has joined the endpoint.
@@ -39,7 +41,6 @@ type InterfaceInfo interface {
 }
 }
 
 
 type endpointInterface struct {
 type endpointInterface struct {
-	id        int
 	mac       net.HardwareAddr
 	mac       net.HardwareAddr
 	addr      net.IPNet
 	addr      net.IPNet
 	addrv6    net.IPNet
 	addrv6    net.IPNet
@@ -50,7 +51,6 @@ type endpointInterface struct {
 
 
 func (epi *endpointInterface) MarshalJSON() ([]byte, error) {
 func (epi *endpointInterface) MarshalJSON() ([]byte, error) {
 	epMap := make(map[string]interface{})
 	epMap := make(map[string]interface{})
-	epMap["id"] = epi.id
 	epMap["mac"] = epi.mac.String()
 	epMap["mac"] = epi.mac.String()
 	epMap["addr"] = epi.addr.String()
 	epMap["addr"] = epi.addr.String()
 	epMap["addrv6"] = epi.addrv6.String()
 	epMap["addrv6"] = epi.addrv6.String()
@@ -69,7 +69,6 @@ func (epi *endpointInterface) UnmarshalJSON(b []byte) (err error) {
 	if err := json.Unmarshal(b, &epMap); err != nil {
 	if err := json.Unmarshal(b, &epMap); err != nil {
 		return err
 		return err
 	}
 	}
-	epi.id = int(epMap["id"].(float64))
 
 
 	mac, _ := net.ParseMAC(epMap["mac"].(string))
 	mac, _ := net.ParseMAC(epMap["mac"].(string))
 	epi.mac = mac
 	epi.mac = mac
@@ -128,51 +127,42 @@ func (ep *endpoint) DriverInfo() (map[string]interface{}, error) {
 	return driver.EndpointOperInfo(nid, epid)
 	return driver.EndpointOperInfo(nid, epid)
 }
 }
 
 
-func (ep *endpoint) InterfaceList() []InterfaceInfo {
+func (ep *endpoint) Iface() InterfaceInfo {
 	ep.Lock()
 	ep.Lock()
 	defer ep.Unlock()
 	defer ep.Unlock()
 
 
-	iList := make([]InterfaceInfo, len(ep.iFaces))
-
-	for i, iface := range ep.iFaces {
-		iList[i] = iface
+	if ep.iface != nil {
+		return ep.iface
 	}
 	}
 
 
-	return iList
+	return nil
 }
 }
 
 
-func (ep *endpoint) Interfaces() []driverapi.InterfaceInfo {
+func (ep *endpoint) Interface() driverapi.InterfaceInfo {
 	ep.Lock()
 	ep.Lock()
 	defer ep.Unlock()
 	defer ep.Unlock()
 
 
-	iList := make([]driverapi.InterfaceInfo, len(ep.iFaces))
-
-	for i, iface := range ep.iFaces {
-		iList[i] = iface
+	if ep.iface != nil {
+		return ep.iface
 	}
 	}
 
 
-	return iList
+	return nil
 }
 }
 
 
-func (ep *endpoint) AddInterface(id int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
+func (ep *endpoint) AddInterface(mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
 	ep.Lock()
 	ep.Lock()
 	defer ep.Unlock()
 	defer ep.Unlock()
 
 
 	iface := &endpointInterface{
 	iface := &endpointInterface{
-		id:     id,
 		addr:   *types.GetIPNetCopy(&ipv4),
 		addr:   *types.GetIPNetCopy(&ipv4),
 		addrv6: *types.GetIPNetCopy(&ipv6),
 		addrv6: *types.GetIPNetCopy(&ipv6),
 	}
 	}
 	iface.mac = types.GetMacCopy(mac)
 	iface.mac = types.GetMacCopy(mac)
 
 
-	ep.iFaces = append(ep.iFaces, iface)
+	ep.iface = iface
 	return nil
 	return nil
 }
 }
 
 
-func (epi *endpointInterface) ID() int {
-	return epi.id
-}
-
 func (epi *endpointInterface) MacAddress() net.HardwareAddr {
 func (epi *endpointInterface) MacAddress() net.HardwareAddr {
 	return types.GetMacCopy(epi.mac)
 	return types.GetMacCopy(epi.mac)
 }
 }
@@ -191,24 +181,22 @@ func (epi *endpointInterface) SetNames(srcName string, dstPrefix string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (ep *endpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
+func (ep *endpoint) InterfaceName() driverapi.InterfaceNameInfo {
 	ep.Lock()
 	ep.Lock()
 	defer ep.Unlock()
 	defer ep.Unlock()
 
 
-	iList := make([]driverapi.InterfaceNameInfo, len(ep.iFaces))
-
-	for i, iface := range ep.iFaces {
-		iList[i] = iface
+	if ep.iface != nil {
+		return ep.iface
 	}
 	}
 
 
-	return iList
+	return nil
 }
 }
 
 
-func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP, interfaceID int) error {
+func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error {
 	ep.Lock()
 	ep.Lock()
 	defer ep.Unlock()
 	defer ep.Unlock()
 
 
-	r := types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop, InterfaceID: interfaceID}
+	r := types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop}
 
 
 	if routeType == types.NEXTHOP {
 	if routeType == types.NEXTHOP {
 		// If the route specifies a next-hop, then it's loosely routed (i.e. not bound to a particular interface).
 		// If the route specifies a next-hop, then it's loosely routed (i.e. not bound to a particular interface).
@@ -223,14 +211,12 @@ func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHo
 }
 }
 
 
 func (ep *endpoint) addInterfaceRoute(route *types.StaticRoute) error {
 func (ep *endpoint) addInterfaceRoute(route *types.StaticRoute) error {
-	for _, iface := range ep.iFaces {
-		if iface.id == route.InterfaceID {
-			iface.routes = append(iface.routes, route.Destination)
-			return nil
-		}
-	}
-	return types.BadRequestErrorf("Interface with ID %d doesn't exist.",
-		route.InterfaceID)
+	ep.Lock()
+	defer ep.Unlock()
+
+	iface := ep.iface
+	iface.routes = append(iface.routes, route.Destination)
+	return nil
 }
 }
 
 
 func (ep *endpoint) Sandbox() Sandbox {
 func (ep *endpoint) Sandbox() Sandbox {

+ 1 - 1
libnetwork/libnetwork_test.go

@@ -1029,7 +1029,7 @@ func TestEndpointJoin(t *testing.T) {
 
 
 	// Validate if ep.Info() only gives me IP address info and not names and gateway during CreateEndpoint()
 	// Validate if ep.Info() only gives me IP address info and not names and gateway during CreateEndpoint()
 	info := ep1.Info()
 	info := ep1.Info()
-	for _, iface := range info.InterfaceList() {
+	if iface := info.Iface(); iface != nil {
 		if iface.Address().IP.To4() == nil {
 		if iface.Address().IP.To4() == nil {
 			t.Fatalf("Invalid IP address returned: %v", iface.Address())
 			t.Fatalf("Invalid IP address returned: %v", iface.Address())
 		}
 		}

+ 1 - 2
libnetwork/network.go

@@ -305,7 +305,6 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
 	}
 	}
 
 
 	ep := &endpoint{name: name,
 	ep := &endpoint{name: name,
-		iFaces:  []*endpointInterface{},
 		generic: make(map[string]interface{})}
 		generic: make(map[string]interface{})}
 	ep.id = stringid.GenerateRandomID()
 	ep.id = stringid.GenerateRandomID()
 	ep.network = n
 	ep.network = n
@@ -409,7 +408,7 @@ func (n *network) isGlobalScoped() (bool, error) {
 func (n *network) updateSvcRecord(ep *endpoint, isAdd bool) {
 func (n *network) updateSvcRecord(ep *endpoint, isAdd bool) {
 	n.Lock()
 	n.Lock()
 	var recs []etchosts.Record
 	var recs []etchosts.Record
-	for _, iface := range ep.InterfaceList() {
+	if iface := ep.Iface(); iface != nil {
 		if isAdd {
 		if isAdd {
 			n.svcRecords[ep.Name()] = iface.Address().IP
 			n.svcRecords[ep.Name()] = iface.Address().IP
 			n.svcRecords[ep.Name()+"."+n.name] = iface.Address().IP
 			n.svcRecords[ep.Name()+"."+n.name] = iface.Address().IP

+ 2 - 2
libnetwork/sandbox.go

@@ -317,10 +317,10 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
 
 
 	ep.Lock()
 	ep.Lock()
 	joinInfo := ep.joinInfo
 	joinInfo := ep.joinInfo
-	ifaces := ep.iFaces
+	i := ep.iface
 	ep.Unlock()
 	ep.Unlock()
 
 
-	for _, i := range ifaces {
+	if i != nil {
 		var ifaceOptions []osl.IfaceOption
 		var ifaceOptions []osl.IfaceOption
 
 
 		ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(&i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes))
 		ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(&i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes))

+ 0 - 7
libnetwork/sandbox_test.go

@@ -13,13 +13,6 @@ func createEmptyCtrlr() *controller {
 	return &controller{sandboxes: sandboxTable{}}
 	return &controller{sandboxes: sandboxTable{}}
 }
 }
 
 
-func createEmptyEndpoint() *endpoint {
-	return &endpoint{
-		joinInfo: &endpointJoinInfo{},
-		iFaces:   []*endpointInterface{},
-	}
-}
-
 func getTestEnv(t *testing.T) (NetworkController, Network, Network) {
 func getTestEnv(t *testing.T) (NetworkController, Network, Network) {
 	c, err := New()
 	c, err := New()
 	if err != nil {
 	if err != nil {

+ 3 - 9
libnetwork/types/types.go

@@ -266,12 +266,6 @@ type StaticRoute struct {
 
 
 	// NextHop will be resolved by the kernel (i.e. as a loose hop).
 	// NextHop will be resolved by the kernel (i.e. as a loose hop).
 	NextHop net.IP
 	NextHop net.IP
-
-	// InterfaceID must refer to a defined interface on the
-	// Endpoint to which the routes are specified.  Routes specified this way
-	// are interpreted as directly connected to the specified interface (no
-	// next hop will be used).
-	InterfaceID int
 }
 }
 
 
 // GetCopy returns a copy of this StaticRoute structure
 // GetCopy returns a copy of this StaticRoute structure
@@ -279,9 +273,9 @@ func (r *StaticRoute) GetCopy() *StaticRoute {
 	d := GetIPNetCopy(r.Destination)
 	d := GetIPNetCopy(r.Destination)
 	nh := GetIPCopy(r.NextHop)
 	nh := GetIPCopy(r.NextHop)
 	return &StaticRoute{Destination: d,
 	return &StaticRoute{Destination: d,
-		RouteType:   r.RouteType,
-		NextHop:     nh,
-		InterfaceID: r.InterfaceID}
+		RouteType: r.RouteType,
+		NextHop:   nh,
+	}
 }
 }
 
 
 /******************************
 /******************************