Browse code

Driver api refactor

Refactored the driver api so that is aligns well with the design
of endpoint lifecycle becoming decoupled from the container lifecycle.
Introduced go interfaces to obtain address information during CreateEndpoint.
Go interfaces are also used to get data from driver during join.
This sort of deisgn hides the libnetwork specific type details from drivers.

Another adjustment is to provide a list of interfaces during CreateEndpoint. The
goal of this is many-fold:
* To indicate to the driver that IP address has been assigned by some other
entity (like a user wanting to use their own static IP for an endpoint/container)
and asking the driver to honor this. Driver may reject this configuration
and return an error but it may not try to allocate an IP address and override
the passed one.
* To indicate to the driver that IP address has already been allocated once
for this endpoint by an instance of the same driver in some docker host
in the cluster and this is merely a notification about that endpoint and the
allocated resources.
* In case the list of interfaces is empty the driver is required to allocate and
assign IP addresses for this endpoint.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>

Jana Radhakrishnan authored on 2015/05/14 15:23:45
Showing 18 changed files
... ...
@@ -59,7 +59,7 @@ There are many networking solutions available to suit a broad range of use-cases
59 59
         }
60 60
 
61 61
 		// libentwork client can check the endpoint's operational data via the Info() API
62
-		epInfo, err := ep.Info()
62
+		epInfo, err := ep.DriverInfo()
63 63
 		mapData, ok := epInfo[netlabel.PortMap]
64 64
 		if ok {
65 65
 			portMapping, ok := mapData.([]netutils.PortBinding)
... ...
@@ -8,7 +8,6 @@ import (
8 8
 
9 9
 	"github.com/docker/libnetwork"
10 10
 	"github.com/docker/libnetwork/netutils"
11
-	"github.com/docker/libnetwork/sandbox"
12 11
 	"github.com/gorilla/mux"
13 12
 )
14 13
 
... ...
@@ -143,7 +142,6 @@ type endpointResource struct {
143 143
 	Name    string
144 144
 	ID      string
145 145
 	Network string
146
-	Info    sandbox.Info
147 146
 }
148 147
 
149 148
 func buildNetworkResource(nw libnetwork.Network) *networkResource {
... ...
@@ -168,11 +166,6 @@ func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource {
168 168
 		r.Name = ep.Name()
169 169
 		r.ID = ep.ID()
170 170
 		r.Network = ep.Network()
171
-
172
-		i := ep.SandboxInfo()
173
-		if i != nil {
174
-			r.Info = *i
175
-		}
176 171
 	}
177 172
 	return r
178 173
 }
... ...
@@ -430,7 +423,7 @@ func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, b
430 430
 		return nil, errRsp
431 431
 	}
432 432
 
433
-	err := ep.Leave(vars[urlCnID], nil)
433
+	err := ep.Leave(vars[urlCnID])
434 434
 	if err != nil {
435 435
 		return nil, convertNetworkError(err)
436 436
 	}
... ...
@@ -55,7 +55,7 @@ func main() {
55 55
 	}
56 56
 
57 57
 	// libentwork client can check the endpoint's operational data via the Info() API
58
-	epInfo, err := ep.Info()
58
+	epInfo, err := ep.DriverInfo()
59 59
 	mapData, ok := epInfo[netlabel.PortMap]
60 60
 	if ok {
61 61
 		portMapping, ok := mapData.([]netutils.PortBinding)
... ...
@@ -73,14 +73,14 @@ Consumers of the CNM, like Docker for example, interact through the CNM Objects
73 73
 
74 74
 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.
75 75
 
76
-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.
76
+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.
77 77
 
78 78
 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.
79 79
   * 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.
80 80
   * 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.
81 81
     - 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.
82 82
 
83
-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.
83
+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.
84 84
 
85 85
 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`.
86 86
 
... ...
@@ -3,8 +3,8 @@ package driverapi
3 3
 import (
4 4
 	"errors"
5 5
 	"fmt"
6
+	"net"
6 7
 
7
-	"github.com/docker/libnetwork/sandbox"
8 8
 	"github.com/docker/libnetwork/types"
9 9
 )
10 10
 
... ...
@@ -37,32 +37,91 @@ type Driver interface {
37 37
 	DeleteNetwork(nid types.UUID) error
38 38
 
39 39
 	// CreateEndpoint invokes the driver method to create an endpoint
40
-	// passing the network id, endpoint id and driver
41
-	// specific config. The config mechanism will eventually be replaced
42
-	// with labels which are yet to be introduced.
43
-	CreateEndpoint(nid, eid types.UUID, options map[string]interface{}) (*sandbox.Info, error)
40
+	// passing the network id, endpoint id endpoint information and driver
41
+	// specific config. The endpoint information can be either consumed by
42
+	// the driver or populated by the driver. The config mechanism will
43
+	// eventually be replaced with labels which are yet to be introduced.
44
+	CreateEndpoint(nid, eid types.UUID, epInfo EndpointInfo, options map[string]interface{}) error
44 45
 
45 46
 	// DeleteEndpoint invokes the driver method to delete an endpoint
46 47
 	// passing the network id and endpoint id.
47 48
 	DeleteEndpoint(nid, eid types.UUID) error
48 49
 
49
-	// EndpointInfo retrieves from the driver the operational data related to the specified endpoint
50
-	EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error)
50
+	// EndpointOperInfo retrieves from the driver the operational data related to the specified endpoint
51
+	EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error)
51 52
 
52 53
 	// Join method is invoked when a Sandbox is attached to an endpoint.
53
-	Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*JoinInfo, error)
54
+	Join(nid, eid types.UUID, sboxKey string, jinfo JoinInfo, options map[string]interface{}) error
54 55
 
55 56
 	// Leave method is invoked when a Sandbox detaches from an endpoint.
56
-	Leave(nid, eid types.UUID, options map[string]interface{}) error
57
+	Leave(nid, eid types.UUID) error
57 58
 
58 59
 	// Type returns the the type of this driver, the network type this driver manages
59 60
 	Type() string
60 61
 }
61 62
 
63
+// EndpointInfo provides a go interface to fetch or populate endpoint assigned network resources.
64
+type EndpointInfo interface {
65
+	// Interfaces returns a list of interfaces bound to the endpoint.
66
+	// If the list is not empty the driver is only expected to consume the interfaces.
67
+	// It is an error to try to add interfaces to a non-empty list.
68
+	// If the list is empty the driver is expected to populate with 0 or more interfaces.
69
+	Interfaces() []InterfaceInfo
70
+
71
+	// AddInterface is used by the driver to add an interface to the interface list.
72
+	// This method will return an error if the driver attempts to add interfaces
73
+	// if the Interfaces() method returned a non-empty list.
74
+	// ID field need only have significance within the endpoint so it can be a simple
75
+	// monotonically increasing number
76
+	AddInterface(ID int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error
77
+}
78
+
79
+// InterfaceInfo provides a go interface for drivers to retrive
80
+// network information to interface resources.
81
+type InterfaceInfo interface {
82
+	// MacAddress returns the MAC address.
83
+	MacAddress() net.HardwareAddr
84
+
85
+	// Address returns the IPv4 address.
86
+	Address() net.IPNet
87
+
88
+	// AddressIPv6 returns the IPv6 address.
89
+	AddressIPv6() net.IPNet
90
+
91
+	// ID returns the numerical id of the interface and has significance only within
92
+	// the endpoint.
93
+	ID() int
94
+}
95
+
96
+// InterfaceNameInfo provides a go interface for the drivers to assign names
97
+// to interfaces.
98
+type InterfaceNameInfo interface {
99
+	// SetNames method assigns the srcName and dstName for the interface.
100
+	SetNames(srcName, dstName string) error
101
+
102
+	// ID returns the numerical id that was assigned to the interface by the driver
103
+	// CreateEndpoint.
104
+	ID() int
105
+}
106
+
62 107
 // JoinInfo represents a set of resources that the driver has the ability to provide during
63 108
 // join time.
64
-type JoinInfo struct {
65
-	HostsPath string
109
+type JoinInfo interface {
110
+	// InterfaceNames returns a list of InterfaceNameInfo go interface to facilitate
111
+	// setting the names for the interfaces.
112
+	InterfaceNames() []InterfaceNameInfo
113
+
114
+	// SetGateway sets the default IPv4 gateway when a container joins the endpoint.
115
+	SetGateway(net.IP) error
116
+
117
+	// SetGatewayIPv6 sets the default IPv6 gateway when a container joins the endpoint.
118
+	SetGatewayIPv6(net.IP) error
119
+
120
+	// SetHostsPath sets the overriding /etc/hosts path to use for the container.
121
+	SetHostsPath(string) error
122
+
123
+	// SetResolvConfPath sets the overriding /etc/resolv.conf path to use for the container.
124
+	SetResolvConfPath(string) error
66 125
 }
67 126
 
68 127
 // ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered
... ...
@@ -1,6 +1,7 @@
1 1
 package bridge
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"net"
5 6
 	"strings"
6 7
 	"sync"
... ...
@@ -22,6 +23,7 @@ const (
22 22
 	vethLen                 = 7
23 23
 	containerVeth           = "eth0"
24 24
 	maxAllocatePortAttempts = 10
25
+	ifaceID                 = 1
25 26
 )
26 27
 
27 28
 var (
... ...
@@ -371,44 +373,52 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
371 371
 	return err
372 372
 }
373 373
 
374
-func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interface{}) (*sandbox.Info, error) {
374
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
375 375
 	var (
376 376
 		ipv6Addr *net.IPNet
377 377
 		err      error
378 378
 	)
379 379
 
380
+	if epInfo == nil {
381
+		return errors.New("invalid endpoint info passed")
382
+	}
383
+
384
+	if len(epInfo.Interfaces()) != 0 {
385
+		return errors.New("non empty interface list passed to bridge(local) driver")
386
+	}
387
+
380 388
 	// Get the network handler and make sure it exists
381 389
 	d.Lock()
382 390
 	n := d.network
383 391
 	config := n.config
384 392
 	d.Unlock()
385 393
 	if n == nil {
386
-		return nil, driverapi.ErrNoNetwork
394
+		return driverapi.ErrNoNetwork
387 395
 	}
388 396
 
389 397
 	// Sanity check
390 398
 	n.Lock()
391 399
 	if n.id != nid {
392 400
 		n.Unlock()
393
-		return nil, InvalidNetworkIDError(nid)
401
+		return InvalidNetworkIDError(nid)
394 402
 	}
395 403
 	n.Unlock()
396 404
 
397 405
 	// Check if endpoint id is good and retrieve correspondent endpoint
398 406
 	ep, err := n.getEndpoint(eid)
399 407
 	if err != nil {
400
-		return nil, err
408
+		return err
401 409
 	}
402 410
 
403 411
 	// Endpoint with that id exists either on desired or other sandbox
404 412
 	if ep != nil {
405
-		return nil, driverapi.ErrEndpointExists
413
+		return driverapi.ErrEndpointExists
406 414
 	}
407 415
 
408 416
 	// Try to convert the options to endpoint configuration
409 417
 	epConfig, err := parseEndpointOptions(epOptions)
410 418
 	if err != nil {
411
-		return nil, err
419
+		return err
412 420
 	}
413 421
 
414 422
 	// Create and add the endpoint
... ...
@@ -429,13 +439,13 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
429 429
 	// Generate a name for what will be the host side pipe interface
430 430
 	name1, err := generateIfaceName()
431 431
 	if err != nil {
432
-		return nil, err
432
+		return err
433 433
 	}
434 434
 
435 435
 	// Generate a name for what will be the sandbox side pipe interface
436 436
 	name2, err := generateIfaceName()
437 437
 	if err != nil {
438
-		return nil, err
438
+		return err
439 439
 	}
440 440
 
441 441
 	// Generate and add the interface pipe host <-> sandbox
... ...
@@ -443,13 +453,13 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
443 443
 		LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
444 444
 		PeerName:  name2}
445 445
 	if err = netlink.LinkAdd(veth); err != nil {
446
-		return nil, err
446
+		return err
447 447
 	}
448 448
 
449 449
 	// Get the host side pipe interface handler
450 450
 	host, err := netlink.LinkByName(name1)
451 451
 	if err != nil {
452
-		return nil, err
452
+		return err
453 453
 	}
454 454
 	defer func() {
455 455
 		if err != nil {
... ...
@@ -460,7 +470,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
460 460
 	// Get the sandbox side pipe interface handler
461 461
 	sbox, err := netlink.LinkByName(name2)
462 462
 	if err != nil {
463
-		return nil, err
463
+		return err
464 464
 	}
465 465
 	defer func() {
466 466
 		if err != nil {
... ...
@@ -472,7 +482,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
472 472
 	mac := electMacAddress(epConfig)
473 473
 	err = netlink.LinkSetHardwareAddr(sbox, mac)
474 474
 	if err != nil {
475
-		return nil, err
475
+		return err
476 476
 	}
477 477
 	endpoint.macAddress = mac
478 478
 
... ...
@@ -480,28 +490,29 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
480 480
 	if config.Mtu != 0 {
481 481
 		err = netlink.LinkSetMTU(host, config.Mtu)
482 482
 		if err != nil {
483
-			return nil, err
483
+			return err
484 484
 		}
485 485
 		err = netlink.LinkSetMTU(sbox, config.Mtu)
486 486
 		if err != nil {
487
-			return nil, err
487
+			return err
488 488
 		}
489 489
 	}
490 490
 
491 491
 	// Attach host side pipe interface into the bridge
492 492
 	if err = netlink.LinkSetMaster(host,
493 493
 		&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil {
494
-		return nil, err
494
+		return err
495 495
 	}
496 496
 
497 497
 	// v4 address for the sandbox side pipe interface
498 498
 	ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil)
499 499
 	if err != nil {
500
-		return nil, err
500
+		return err
501 501
 	}
502 502
 	ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
503 503
 
504 504
 	// v6 address for the sandbox side pipe interface
505
+	ipv6Addr = &net.IPNet{}
505 506
 	if config.EnableIPv6 {
506 507
 		var ip6 net.IP
507 508
 
... ...
@@ -521,7 +532,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
521 521
 
522 522
 		ip6, err := ipAllocator.RequestIP(network, ip6)
523 523
 		if err != nil {
524
-			return nil, err
524
+			return err
525 525
 		}
526 526
 
527 527
 		ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask}
... ...
@@ -533,26 +544,25 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
533 533
 	intf.DstName = containerVeth
534 534
 	intf.Address = ipv4Addr
535 535
 
536
+	if config.EnableIPv6 {
537
+		intf.AddressIPv6 = ipv6Addr
538
+	}
539
+
536 540
 	// Store the interface in endpoint, this is needed for cleanup on DeleteEndpoint()
537 541
 	endpoint.intf = intf
538 542
 
539
-	// Generate the sandbox info to return
540
-	sinfo := &sandbox.Info{Interfaces: []*sandbox.Interface{intf}}
541
-
542
-	// Set the default gateway(s) for the sandbox
543
-	sinfo.Gateway = n.bridge.gatewayIPv4
544
-	if config.EnableIPv6 {
545
-		intf.AddressIPv6 = ipv6Addr
546
-		sinfo.GatewayIPv6 = n.bridge.gatewayIPv6
543
+	err = epInfo.AddInterface(ifaceID, endpoint.macAddress, *ipv4Addr, *ipv6Addr)
544
+	if err != nil {
545
+		return err
547 546
 	}
548 547
 
549 548
 	// Program any required port mapping and store them in the endpoint
550
-	endpoint.portMapping, err = allocatePorts(epConfig, sinfo, config.DefaultBindingIP)
549
+	endpoint.portMapping, err = allocatePorts(epConfig, intf, config.DefaultBindingIP)
551 550
 	if err != nil {
552
-		return nil, err
551
+		return err
553 552
 	}
554 553
 
555
-	return sinfo, nil
554
+	return nil
556 555
 }
557 556
 
558 557
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
... ...
@@ -628,7 +638,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
628 628
 	return nil
629 629
 }
630 630
 
631
-func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
631
+func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
632 632
 	// Get the network handler and make sure it exists
633 633
 	d.Lock()
634 634
 	n := d.network
... ...
@@ -673,40 +683,55 @@ func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, erro
673 673
 }
674 674
 
675 675
 // Join method is invoked when a Sandbox is attached to an endpoint.
676
-func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*driverapi.JoinInfo, error) {
676
+func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
677 677
 	network, err := d.getNetwork(nid)
678 678
 	if err != nil {
679
-		return nil, err
679
+		return err
680 680
 	}
681 681
 
682
-	if !network.config.EnableICC {
683
-		return nil, d.link(nid, eid, options, true)
682
+	endpoint, err := network.getEndpoint(eid)
683
+	if err != nil {
684
+		return err
684 685
 	}
685 686
 
686
-	return nil, nil
687
-}
687
+	if endpoint == nil {
688
+		return EndpointNotFoundError(eid)
689
+	}
688 690
 
689
-// Leave method is invoked when a Sandbox detaches from an endpoint.
690
-func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) error {
691
-	network, err := d.getNetwork(nid)
691
+	for _, iNames := range jinfo.InterfaceNames() {
692
+		// Make sure to set names on the correct interface ID.
693
+		if iNames.ID() == ifaceID {
694
+			err = iNames.SetNames(endpoint.intf.SrcName, endpoint.intf.DstName)
695
+			if err != nil {
696
+				return err
697
+			}
698
+		}
699
+	}
700
+
701
+	err = jinfo.SetGateway(network.bridge.gatewayIPv4)
702
+	if err != nil {
703
+		return err
704
+	}
705
+
706
+	err = jinfo.SetGatewayIPv6(network.bridge.gatewayIPv6)
692 707
 	if err != nil {
693 708
 		return err
694 709
 	}
695 710
 
696 711
 	if !network.config.EnableICC {
697
-		return d.link(nid, eid, options, false)
712
+		return d.link(network, endpoint, options, true)
698 713
 	}
699 714
 
700 715
 	return nil
701 716
 }
702 717
 
703
-func (d *driver) link(nid, eid types.UUID, options map[string]interface{}, enable bool) error {
704
-	var cc *ContainerConfiguration
705
-
718
+// Leave method is invoked when a Sandbox detaches from an endpoint.
719
+func (d *driver) Leave(nid, eid types.UUID) error {
706 720
 	network, err := d.getNetwork(nid)
707 721
 	if err != nil {
708 722
 		return err
709 723
 	}
724
+
710 725
 	endpoint, err := network.getEndpoint(eid)
711 726
 	if err != nil {
712 727
 		return err
... ...
@@ -716,6 +741,19 @@ func (d *driver) link(nid, eid types.UUID, options map[string]interface{}, enabl
716 716
 		return EndpointNotFoundError(eid)
717 717
 	}
718 718
 
719
+	if !network.config.EnableICC {
720
+		return d.link(network, endpoint, nil, false)
721
+	}
722
+
723
+	return nil
724
+}
725
+
726
+func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options map[string]interface{}, enable bool) error {
727
+	var (
728
+		cc  *ContainerConfiguration
729
+		err error
730
+	)
731
+
719 732
 	if enable {
720 733
 		cc, err = parseContainerOptions(options)
721 734
 		if err != nil {
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"regexp"
8 8
 	"testing"
9 9
 
10
+	"github.com/docker/libnetwork/driverapi"
10 11
 	"github.com/docker/libnetwork/iptables"
11 12
 	"github.com/docker/libnetwork/netlabel"
12 13
 	"github.com/docker/libnetwork/netutils"
... ...
@@ -70,6 +71,91 @@ func TestCreateFail(t *testing.T) {
70 70
 	}
71 71
 }
72 72
 
73
+type testInterface struct {
74
+	id      int
75
+	mac     net.HardwareAddr
76
+	addr    net.IPNet
77
+	addrv6  net.IPNet
78
+	srcName string
79
+	dstName string
80
+}
81
+
82
+type testEndpoint struct {
83
+	ifaces         []*testInterface
84
+	gw             net.IP
85
+	gw6            net.IP
86
+	hostsPath      string
87
+	resolvConfPath string
88
+}
89
+
90
+func (te *testEndpoint) Interfaces() []driverapi.InterfaceInfo {
91
+	iList := make([]driverapi.InterfaceInfo, len(te.ifaces))
92
+
93
+	for i, iface := range te.ifaces {
94
+		iList[i] = iface
95
+	}
96
+
97
+	return iList
98
+}
99
+
100
+func (te *testEndpoint) AddInterface(id int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
101
+	iface := &testInterface{id: id, addr: ipv4, addrv6: ipv6}
102
+	te.ifaces = append(te.ifaces, iface)
103
+	return nil
104
+}
105
+
106
+func (i *testInterface) ID() int {
107
+	return i.id
108
+}
109
+
110
+func (i *testInterface) MacAddress() net.HardwareAddr {
111
+	return i.mac
112
+}
113
+
114
+func (i *testInterface) Address() net.IPNet {
115
+	return i.addr
116
+}
117
+
118
+func (i *testInterface) AddressIPv6() net.IPNet {
119
+	return i.addrv6
120
+}
121
+
122
+func (i *testInterface) SetNames(srcName string, dstName string) error {
123
+	i.srcName = srcName
124
+	i.dstName = dstName
125
+	return nil
126
+}
127
+
128
+func (te *testEndpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
129
+	iList := make([]driverapi.InterfaceNameInfo, len(te.ifaces))
130
+
131
+	for i, iface := range te.ifaces {
132
+		iList[i] = iface
133
+	}
134
+
135
+	return iList
136
+}
137
+
138
+func (te *testEndpoint) SetGateway(gw net.IP) error {
139
+	te.gw = gw
140
+	return nil
141
+}
142
+
143
+func (te *testEndpoint) SetGatewayIPv6(gw6 net.IP) error {
144
+	te.gw6 = gw6
145
+	return nil
146
+}
147
+
148
+func (te *testEndpoint) SetHostsPath(path string) error {
149
+	te.hostsPath = path
150
+	return nil
151
+}
152
+
153
+func (te *testEndpoint) SetResolvConfPath(path string) error {
154
+	te.resolvConfPath = path
155
+	return nil
156
+}
157
+
73 158
 func TestQueryEndpointInfo(t *testing.T) {
74 159
 	defer netutils.SetupTestNetNS(t)()
75 160
 	d := newDriver()
... ...
@@ -92,13 +178,14 @@ func TestQueryEndpointInfo(t *testing.T) {
92 92
 	epOptions := make(map[string]interface{})
93 93
 	epOptions[netlabel.PortMap] = portMappings
94 94
 
95
-	_, err = d.CreateEndpoint("net1", "ep1", epOptions)
95
+	te := &testEndpoint{ifaces: []*testInterface{}}
96
+	err = d.CreateEndpoint("net1", "ep1", te, epOptions)
96 97
 	if err != nil {
97 98
 		t.Fatalf("Failed to create an endpoint : %s", err.Error())
98 99
 	}
99 100
 
100 101
 	ep, _ := dd.network.endpoints["ep1"]
101
-	data, err := d.EndpointInfo(dd.network.id, ep.id)
102
+	data, err := d.EndpointOperInfo(dd.network.id, ep.id)
102 103
 	if err != nil {
103 104
 		t.Fatalf("Failed to ask for endpoint operational data:  %v", err)
104 105
 	}
... ...
@@ -143,12 +230,18 @@ func TestCreateLinkWithOptions(t *testing.T) {
143 143
 	epOptions := make(map[string]interface{})
144 144
 	epOptions[netlabel.MacAddress] = mac
145 145
 
146
-	sinfo, err := d.CreateEndpoint("net1", "ep", epOptions)
146
+	te := &testEndpoint{ifaces: []*testInterface{}}
147
+	err = d.CreateEndpoint("net1", "ep", te, epOptions)
148
+	if err != nil {
149
+		t.Fatalf("Failed to create an endpoint: %s", err.Error())
150
+	}
151
+
152
+	err = d.Join("net1", "ep", "sbox", te, nil)
147 153
 	if err != nil {
148
-		t.Fatalf("Failed to create a link: %s", err.Error())
154
+		t.Fatalf("Failed to join the endpoint: %v", err)
149 155
 	}
150 156
 
151
-	ifaceName := sinfo.Interfaces[0].SrcName
157
+	ifaceName := te.ifaces[0].srcName
152 158
 	veth, err := netlink.LinkByName(ifaceName)
153 159
 	if err != nil {
154 160
 		t.Fatal(err)
... ...
@@ -197,23 +290,25 @@ func TestLinkContainers(t *testing.T) {
197 197
 	epOptions := make(map[string]interface{})
198 198
 	epOptions[netlabel.ExposedPorts] = exposedPorts
199 199
 
200
-	sinfo, err := d.CreateEndpoint("net1", "ep1", epOptions)
200
+	te1 := &testEndpoint{ifaces: []*testInterface{}}
201
+	err = d.CreateEndpoint("net1", "ep1", te1, epOptions)
201 202
 	if err != nil {
202 203
 		t.Fatalf("Failed to create an endpoint : %s", err.Error())
203 204
 	}
204 205
 
205
-	addr1 := sinfo.Interfaces[0].Address
206
-	if addr1 == nil {
206
+	addr1 := te1.ifaces[0].addr
207
+	if addr1.IP.To4() == nil {
207 208
 		t.Fatalf("No Ipv4 address assigned to the endpoint:  ep1")
208 209
 	}
209 210
 
210
-	sinfo, err = d.CreateEndpoint("net1", "ep2", nil)
211
+	te2 := &testEndpoint{ifaces: []*testInterface{}}
212
+	err = d.CreateEndpoint("net1", "ep2", te2, nil)
211 213
 	if err != nil {
212 214
 		t.Fatalf("Failed to create an endpoint : %s", err.Error())
213 215
 	}
214 216
 
215
-	addr2 := sinfo.Interfaces[0].Address
216
-	if addr2 == nil {
217
+	addr2 := te2.ifaces[0].addr
218
+	if addr2.IP.To4() == nil {
217 219
 		t.Fatalf("No Ipv4 address assigned to the endpoint:  ep2")
218 220
 	}
219 221
 
... ...
@@ -222,7 +317,7 @@ func TestLinkContainers(t *testing.T) {
222 222
 	genericOption = make(map[string]interface{})
223 223
 	genericOption[netlabel.GenericData] = cConfig
224 224
 
225
-	_, err = d.Join("net1", "ep2", "", genericOption)
225
+	err = d.Join("net1", "ep2", "", te2, genericOption)
226 226
 	if err != nil {
227 227
 		t.Fatalf("Failed to link ep1 and ep2")
228 228
 	}
... ...
@@ -243,7 +338,7 @@ func TestLinkContainers(t *testing.T) {
243 243
 		}
244 244
 	}
245 245
 
246
-	err = d.Leave("net1", "ep2", genericOption)
246
+	err = d.Leave("net1", "ep2")
247 247
 	if err != nil {
248 248
 		t.Fatalf("Failed to unlink ep1 and ep2")
249 249
 	}
... ...
@@ -270,7 +365,7 @@ func TestLinkContainers(t *testing.T) {
270 270
 	genericOption = make(map[string]interface{})
271 271
 	genericOption[netlabel.GenericData] = cConfig
272 272
 
273
-	_, err = d.Join("net1", "ep2", "", genericOption)
273
+	err = d.Join("net1", "ep2", "", te2, genericOption)
274 274
 	if err != nil {
275 275
 		out, err = iptables.Raw("-L", DockerChain)
276 276
 		for _, pm := range exposedPorts {
... ...
@@ -406,16 +501,22 @@ func TestSetDefaultGw(t *testing.T) {
406 406
 		t.Fatalf("Failed to create bridge: %v", err)
407 407
 	}
408 408
 
409
-	sinfo, err := d.CreateEndpoint("dummy", "ep", nil)
409
+	te := &testEndpoint{ifaces: []*testInterface{}}
410
+	err = d.CreateEndpoint("dummy", "ep", te, nil)
410 411
 	if err != nil {
411 412
 		t.Fatalf("Failed to create endpoint: %v", err)
412 413
 	}
413 414
 
414
-	if !gw4.Equal(sinfo.Gateway) {
415
-		t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw4, sinfo.Gateway)
415
+	err = d.Join("dummy", "ep", "sbox", te, nil)
416
+	if err != nil {
417
+		t.Fatalf("Failed to join endpoint: %v", err)
418
+	}
419
+
420
+	if !gw4.Equal(te.gw) {
421
+		t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw4, te.gw)
416 422
 	}
417 423
 
418
-	if !gw6.Equal(sinfo.GatewayIPv6) {
419
-		t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw6, sinfo.GatewayIPv6)
424
+	if !gw6.Equal(te.gw6) {
425
+		t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw6, te.gw6)
420 426
 	}
421 427
 }
... ...
@@ -28,7 +28,8 @@ func TestLinkCreate(t *testing.T) {
28 28
 		t.Fatalf("Failed to create bridge: %v", err)
29 29
 	}
30 30
 
31
-	sinfo, err := d.CreateEndpoint("dummy", "", nil)
31
+	te := &testEndpoint{ifaces: []*testInterface{}}
32
+	err = d.CreateEndpoint("dummy", "", te, nil)
32 33
 	if err != nil {
33 34
 		if _, ok := err.(InvalidEndpointIDError); !ok {
34 35
 			t.Fatalf("Failed with a wrong error :%s", err.Error())
... ...
@@ -38,13 +39,18 @@ func TestLinkCreate(t *testing.T) {
38 38
 	}
39 39
 
40 40
 	// Good endpoint creation
41
-	sinfo, err = d.CreateEndpoint("dummy", "ep", nil)
41
+	err = d.CreateEndpoint("dummy", "ep", te, nil)
42
+	if err != nil {
43
+		t.Fatalf("Failed to create a link: %s", err.Error())
44
+	}
45
+
46
+	err = d.Join("dummy", "ep", "sbox", te, nil)
42 47
 	if err != nil {
43 48
 		t.Fatalf("Failed to create a link: %s", err.Error())
44 49
 	}
45 50
 
46 51
 	// Verify sbox endoint interface inherited MTU value from bridge config
47
-	sboxLnk, err := netlink.LinkByName(sinfo.Interfaces[0].SrcName)
52
+	sboxLnk, err := netlink.LinkByName(te.ifaces[0].srcName)
48 53
 	if err != nil {
49 54
 		t.Fatal(err)
50 55
 	}
... ...
@@ -54,44 +60,44 @@ func TestLinkCreate(t *testing.T) {
54 54
 	// TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName
55 55
 	// then we could check the MTU on hostLnk as well.
56 56
 
57
-	_, err = d.CreateEndpoint("dummy", "ep", nil)
57
+	te1 := &testEndpoint{ifaces: []*testInterface{}}
58
+	err = d.CreateEndpoint("dummy", "ep", te1, nil)
58 59
 	if err == nil {
59 60
 		t.Fatalf("Failed to detect duplicate endpoint id on same network")
60 61
 	}
61 62
 
62
-	interfaces := sinfo.Interfaces
63
-	if len(interfaces) != 1 {
64
-		t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(interfaces))
63
+	if len(te.ifaces) != 1 {
64
+		t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(te.ifaces))
65 65
 	}
66 66
 
67
-	if interfaces[0].DstName == "" {
67
+	if te.ifaces[0].dstName == "" {
68 68
 		t.Fatal("Invalid Dstname returned")
69 69
 	}
70 70
 
71
-	_, err = netlink.LinkByName(interfaces[0].SrcName)
71
+	_, err = netlink.LinkByName(te.ifaces[0].srcName)
72 72
 	if err != nil {
73
-		t.Fatalf("Could not find source link %s: %v", interfaces[0].SrcName, err)
73
+		t.Fatalf("Could not find source link %s: %v", te.ifaces[0].srcName, err)
74 74
 	}
75 75
 
76 76
 	n := dr.network
77
-	ip := interfaces[0].Address.IP
77
+	ip := te.ifaces[0].addr.IP
78 78
 	if !n.bridge.bridgeIPv4.Contains(ip) {
79 79
 		t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), n.bridge.bridgeIPv4.String())
80 80
 	}
81 81
 
82
-	ip6 := interfaces[0].AddressIPv6.IP
82
+	ip6 := te.ifaces[0].addrv6.IP
83 83
 	if !n.bridge.bridgeIPv6.Contains(ip6) {
84 84
 		t.Fatalf("IP %s is not a valid ip in the subnet %s", ip6.String(), bridgeIPv6.String())
85 85
 	}
86 86
 
87
-	if !sinfo.Gateway.Equal(n.bridge.bridgeIPv4.IP) {
87
+	if !te.gw.Equal(n.bridge.bridgeIPv4.IP) {
88 88
 		t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(),
89
-			sinfo.Gateway.String())
89
+			te.gw.String())
90 90
 	}
91 91
 
92
-	if !sinfo.GatewayIPv6.Equal(n.bridge.bridgeIPv6.IP) {
92
+	if !te.gw6.Equal(n.bridge.bridgeIPv6.IP) {
93 93
 		t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(),
94
-			sinfo.GatewayIPv6.String())
94
+			te.gw6.String())
95 95
 	}
96 96
 }
97 97
 
... ...
@@ -110,12 +116,14 @@ func TestLinkCreateTwo(t *testing.T) {
110 110
 		t.Fatalf("Failed to create bridge: %v", err)
111 111
 	}
112 112
 
113
-	_, err = d.CreateEndpoint("dummy", "ep", nil)
113
+	te1 := &testEndpoint{ifaces: []*testInterface{}}
114
+	err = d.CreateEndpoint("dummy", "ep", te1, nil)
114 115
 	if err != nil {
115 116
 		t.Fatalf("Failed to create a link: %s", err.Error())
116 117
 	}
117 118
 
118
-	_, err = d.CreateEndpoint("dummy", "ep", nil)
119
+	te2 := &testEndpoint{ifaces: []*testInterface{}}
120
+	err = d.CreateEndpoint("dummy", "ep", te2, nil)
119 121
 	if err != nil {
120 122
 		if err != driverapi.ErrEndpointExists {
121 123
 			t.Fatalf("Failed with a wrong error :%s", err.Error())
... ...
@@ -139,18 +147,19 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) {
139 139
 		t.Fatalf("Failed to create bridge: %v", err)
140 140
 	}
141 141
 
142
-	sinfo, err := d.CreateEndpoint("dummy", "ep", nil)
142
+	te := &testEndpoint{ifaces: []*testInterface{}}
143
+	err = d.CreateEndpoint("dummy", "ep", te, nil)
143 144
 	if err != nil {
144 145
 		t.Fatalf("Failed to create a link: %s", err.Error())
145 146
 	}
146 147
 
147
-	interfaces := sinfo.Interfaces
148
-	if interfaces[0].AddressIPv6 != nil {
149
-		t.Fatalf("Expectd IPv6 address to be nil when IPv6 is not enabled. Got IPv6 = %s", interfaces[0].AddressIPv6.String())
148
+	interfaces := te.ifaces
149
+	if interfaces[0].addrv6.IP.To16() != nil {
150
+		t.Fatalf("Expectd IPv6 address to be nil when IPv6 is not enabled. Got IPv6 = %s", interfaces[0].addrv6.String())
150 151
 	}
151 152
 
152
-	if sinfo.GatewayIPv6 != nil {
153
-		t.Fatalf("Expected GatewayIPv6 to be nil when IPv6 is not enabled. Got GatewayIPv6 = %s", sinfo.GatewayIPv6.String())
153
+	if te.gw6.To16() != nil {
154
+		t.Fatalf("Expected GatewayIPv6 to be nil when IPv6 is not enabled. Got GatewayIPv6 = %s", te.gw6.String())
154 155
 	}
155 156
 }
156 157
 
... ...
@@ -169,7 +178,8 @@ func TestLinkDelete(t *testing.T) {
169 169
 		t.Fatalf("Failed to create bridge: %v", err)
170 170
 	}
171 171
 
172
-	_, err = d.CreateEndpoint("dummy", "ep1", nil)
172
+	te := &testEndpoint{ifaces: []*testInterface{}}
173
+	err = d.CreateEndpoint("dummy", "ep1", te, nil)
173 174
 	if err != nil {
174 175
 		t.Fatalf("Failed to create a link: %s", err.Error())
175 176
 	}
... ...
@@ -187,9 +197,4 @@ func TestLinkDelete(t *testing.T) {
187 187
 	if err != nil {
188 188
 		t.Fatal(err)
189 189
 	}
190
-
191
-	err = d.DeleteEndpoint("dummy", "ep1")
192
-	if err == nil {
193
-		t.Fatal(err)
194
-	}
195 190
 }
... ...
@@ -15,7 +15,7 @@ var (
15 15
 	defaultBindingIP = net.IPv4(0, 0, 0, 0)
16 16
 )
17 17
 
18
-func allocatePorts(epConfig *EndpointConfiguration, sinfo *sandbox.Info, reqDefBindIP net.IP) ([]netutils.PortBinding, error) {
18
+func allocatePorts(epConfig *EndpointConfiguration, intf *sandbox.Interface, reqDefBindIP net.IP) ([]netutils.PortBinding, error) {
19 19
 	if epConfig == nil || epConfig.PortBindings == nil {
20 20
 		return nil, nil
21 21
 	}
... ...
@@ -25,7 +25,7 @@ func allocatePorts(epConfig *EndpointConfiguration, sinfo *sandbox.Info, reqDefB
25 25
 		defHostIP = reqDefBindIP
26 26
 	}
27 27
 
28
-	return allocatePortsInternal(epConfig.PortBindings, sinfo.Interfaces[0].Address.IP, defHostIP)
28
+	return allocatePortsInternal(epConfig.PortBindings, intf.Address.IP, defHostIP)
29 29
 }
30 30
 
31 31
 func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHostIP net.IP) ([]netutils.PortBinding, error) {
... ...
@@ -39,7 +39,8 @@ func TestPortMappingConfig(t *testing.T) {
39 39
 		t.Fatalf("Failed to create bridge: %v", err)
40 40
 	}
41 41
 
42
-	_, err = d.CreateEndpoint("dummy", "ep1", epOptions)
42
+	te := &testEndpoint{ifaces: []*testInterface{}}
43
+	err = d.CreateEndpoint("dummy", "ep1", te, epOptions)
43 44
 	if err != nil {
44 45
 		t.Fatalf("Failed to create the endpoint: %s", err.Error())
45 46
 	}
... ...
@@ -2,7 +2,6 @@ package host
2 2
 
3 3
 import (
4 4
 	"github.com/docker/libnetwork/driverapi"
5
-	"github.com/docker/libnetwork/sandbox"
6 5
 	"github.com/docker/libnetwork/types"
7 6
 )
8 7
 
... ...
@@ -27,29 +26,25 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
27 27
 	return nil
28 28
 }
29 29
 
30
-func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interface{}) (*sandbox.Info, error) {
31
-	return nil, nil
30
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
31
+	return nil
32 32
 }
33 33
 
34 34
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
35 35
 	return nil
36 36
 }
37 37
 
38
-func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
38
+func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
39 39
 	return make(map[string]interface{}, 0), nil
40 40
 }
41 41
 
42 42
 // Join method is invoked when a Sandbox is attached to an endpoint.
43
-func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*driverapi.JoinInfo, error) {
44
-	jInfo := &driverapi.JoinInfo{
45
-		HostsPath: "/etc/hosts",
46
-	}
47
-
48
-	return jInfo, nil
43
+func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
44
+	return (jinfo.SetHostsPath("/etc/hosts"))
49 45
 }
50 46
 
51 47
 // Leave method is invoked when a Sandbox detaches from an endpoint.
52
-func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) error {
48
+func (d *driver) Leave(nid, eid types.UUID) error {
53 49
 	return nil
54 50
 }
55 51
 
... ...
@@ -2,7 +2,6 @@ package null
2 2
 
3 3
 import (
4 4
 	"github.com/docker/libnetwork/driverapi"
5
-	"github.com/docker/libnetwork/sandbox"
6 5
 	"github.com/docker/libnetwork/types"
7 6
 )
8 7
 
... ...
@@ -27,25 +26,25 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
27 27
 	return nil
28 28
 }
29 29
 
30
-func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interface{}) (*sandbox.Info, error) {
31
-	return nil, nil
30
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
31
+	return nil
32 32
 }
33 33
 
34 34
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
35 35
 	return nil
36 36
 }
37 37
 
38
-func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
38
+func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
39 39
 	return make(map[string]interface{}, 0), nil
40 40
 }
41 41
 
42 42
 // Join method is invoked when a Sandbox is attached to an endpoint.
43
-func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*driverapi.JoinInfo, error) {
44
-	return nil, nil
43
+func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
44
+	return nil
45 45
 }
46 46
 
47 47
 // Leave method is invoked when a Sandbox detaches from an endpoint.
48
-func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) error {
48
+func (d *driver) Leave(nid, eid types.UUID) error {
49 49
 	return nil
50 50
 }
51 51
 
... ...
@@ -6,7 +6,6 @@ import (
6 6
 	log "github.com/Sirupsen/logrus"
7 7
 	"github.com/docker/docker/pkg/plugins"
8 8
 	"github.com/docker/libnetwork/driverapi"
9
-	"github.com/docker/libnetwork/sandbox"
10 9
 	"github.com/docker/libnetwork/types"
11 10
 )
12 11
 
... ...
@@ -43,25 +42,25 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
43 43
 	return driverapi.ErrNotImplemented
44 44
 }
45 45
 
46
-func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interface{}) (*sandbox.Info, error) {
47
-	return nil, driverapi.ErrNotImplemented
46
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
47
+	return driverapi.ErrNotImplemented
48 48
 }
49 49
 
50 50
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
51 51
 	return driverapi.ErrNotImplemented
52 52
 }
53 53
 
54
-func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
54
+func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
55 55
 	return nil, driverapi.ErrNotImplemented
56 56
 }
57 57
 
58 58
 // Join method is invoked when a Sandbox is attached to an endpoint.
59
-func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*driverapi.JoinInfo, error) {
60
-	return nil, driverapi.ErrNotImplemented
59
+func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
60
+	return driverapi.ErrNotImplemented
61 61
 }
62 62
 
63 63
 // Leave method is invoked when a Sandbox detaches from an endpoint.
64
-func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) error {
64
+func (d *driver) Leave(nid, eid types.UUID) error {
65 65
 	return driverapi.ErrNotImplemented
66 66
 }
67 67
 
... ...
@@ -10,7 +10,6 @@ import (
10 10
 
11 11
 	"github.com/Sirupsen/logrus"
12 12
 	"github.com/docker/docker/pkg/ioutils"
13
-	"github.com/docker/libnetwork/driverapi"
14 13
 	"github.com/docker/libnetwork/etchosts"
15 14
 	"github.com/docker/libnetwork/netlabel"
16 15
 	"github.com/docker/libnetwork/netutils"
... ...
@@ -39,11 +38,11 @@ type Endpoint interface {
39 39
 	// the network resources populated in the sandbox
40 40
 	Leave(containerID string, options ...EndpointOption) error
41 41
 
42
-	// SandboxInfo returns the sandbox information for this endpoint.
43
-	SandboxInfo() *sandbox.Info
42
+	// Return certain operational data belonging to this endpoint
43
+	Info() EndpointInfo
44 44
 
45
-	// Info returns a collection of operational data related to this endpoint retrieved from the driver
46
-	Info() (map[string]interface{}, error)
45
+	// Info returns a collection of driver operational data related to this endpoint retrieved from the driver
46
+	DriverInfo() (map[string]interface{}, error)
47 47
 
48 48
 	// Delete and detaches this endpoint from the network.
49 49
 	Delete() error
... ...
@@ -104,12 +103,11 @@ type endpoint struct {
104 104
 	id            types.UUID
105 105
 	network       *network
106 106
 	sandboxInfo   *sandbox.Info
107
-	sandBox       sandbox.Sandbox
108
-	joinInfo      *driverapi.JoinInfo
107
+	iFaces        []*endpointInterface
108
+	joinInfo      *endpointJoinInfo
109 109
 	container     *containerInfo
110 110
 	exposedPorts  []netutils.TransportPort
111 111
 	generic       map[string]interface{}
112
-	context       map[string]interface{}
113 112
 	joinLeaveDone chan struct{}
114 113
 	sync.Mutex
115 114
 }
... ...
@@ -137,30 +135,6 @@ func (ep *endpoint) Network() string {
137 137
 	return ep.network.name
138 138
 }
139 139
 
140
-func (ep *endpoint) SandboxInfo() *sandbox.Info {
141
-	ep.Lock()
142
-	defer ep.Unlock()
143
-
144
-	if ep.sandboxInfo == nil {
145
-		return nil
146
-	}
147
-	return ep.sandboxInfo.GetCopy()
148
-}
149
-
150
-func (ep *endpoint) Info() (map[string]interface{}, error) {
151
-	ep.Lock()
152
-	network := ep.network
153
-	epid := ep.id
154
-	ep.Unlock()
155
-
156
-	network.Lock()
157
-	driver := network.driver
158
-	nid := network.id
159
-	network.Unlock()
160
-
161
-	return driver.EndpointInfo(nid, epid)
162
-}
163
-
164 140
 func (ep *endpoint) processOptions(options ...EndpointOption) {
165 141
 	ep.Lock()
166 142
 	defer ep.Unlock()
... ...
@@ -255,9 +229,13 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
255 255
 			},
256 256
 		}}
257 257
 
258
+	ep.joinInfo = &endpointJoinInfo{}
259
+
258 260
 	container := ep.container
259 261
 	network := ep.network
260 262
 	epid := ep.id
263
+	joinInfo := ep.joinInfo
264
+	ifaces := ep.iFaces
261 265
 
262 266
 	ep.Unlock()
263 267
 	defer func() {
... ...
@@ -281,15 +259,11 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
281 281
 		sboxKey = sandbox.GenerateKey("default")
282 282
 	}
283 283
 
284
-	joinInfo, err := driver.Join(nid, epid, sboxKey, container.config.generic)
284
+	err = driver.Join(nid, epid, sboxKey, ep, container.config.generic)
285 285
 	if err != nil {
286 286
 		return nil, err
287 287
 	}
288 288
 
289
-	ep.Lock()
290
-	ep.joinInfo = joinInfo
291
-	ep.Unlock()
292
-
293 289
 	err = ep.buildHostsFiles()
294 290
 	if err != nil {
295 291
 		return nil, err
... ...
@@ -315,26 +289,31 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
315 315
 		}
316 316
 	}()
317 317
 
318
-	sinfo := ep.SandboxInfo()
319
-	if sinfo != nil {
320
-		for _, i := range sinfo.Interfaces {
321
-			err = sb.AddInterface(i)
322
-			if err != nil {
323
-				return nil, err
324
-			}
318
+	for _, i := range ifaces {
319
+		iface := &sandbox.Interface{
320
+			SrcName: i.srcName,
321
+			DstName: i.dstName,
322
+			Address: &i.addr,
325 323
 		}
326
-
327
-		err = sb.SetGateway(sinfo.Gateway)
328
-		if err != nil {
329
-			return nil, err
324
+		if i.addrv6.IP.To16() != nil {
325
+			iface.AddressIPv6 = &i.addrv6
330 326
 		}
331
-
332
-		err = sb.SetGatewayIPv6(sinfo.GatewayIPv6)
327
+		err = sb.AddInterface(iface)
333 328
 		if err != nil {
334 329
 			return nil, err
335 330
 		}
336 331
 	}
337 332
 
333
+	err = sb.SetGateway(joinInfo.gw)
334
+	if err != nil {
335
+		return nil, err
336
+	}
337
+
338
+	err = sb.SetGatewayIPv6(joinInfo.gw6)
339
+	if err != nil {
340
+		return nil, err
341
+	}
342
+
338 343
 	container.data.SandboxKey = sb.Key()
339 344
 	cData := container.data
340 345
 
... ...
@@ -352,7 +331,6 @@ func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error {
352 352
 	ep.Lock()
353 353
 	container := ep.container
354 354
 	n := ep.network
355
-	context := ep.context
356 355
 
357 356
 	if container == nil || container.id == "" ||
358 357
 		containerID == "" || container.id != containerID {
... ...
@@ -366,7 +344,6 @@ func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error {
366 366
 		return err
367 367
 	}
368 368
 	ep.container = nil
369
-	ep.context = nil
370 369
 	ep.Unlock()
371 370
 
372 371
 	n.Lock()
... ...
@@ -374,16 +351,13 @@ func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error {
374 374
 	ctrlr := n.ctrlr
375 375
 	n.Unlock()
376 376
 
377
-	err = driver.Leave(n.id, ep.id, context)
377
+	err = driver.Leave(n.id, ep.id)
378 378
 
379
-	sinfo := ep.SandboxInfo()
380
-	if sinfo != nil {
381
-		sb := ctrlr.sandboxGet(container.data.SandboxKey)
382
-		for _, i := range sinfo.Interfaces {
383
-			err = sb.RemoveInterface(i)
384
-			if err != nil {
385
-				logrus.Debugf("Remove interface failed: %v", err)
386
-			}
379
+	sb := ctrlr.sandboxGet(container.data.SandboxKey)
380
+	for _, i := range sb.Interfaces() {
381
+		err = sb.RemoveInterface(i)
382
+		if err != nil {
383
+			logrus.Debugf("Remove interface failed: %v", err)
387 384
 		}
388 385
 	}
389 386
 
... ...
@@ -435,6 +409,7 @@ func (ep *endpoint) buildHostsFiles() error {
435 435
 	ep.Lock()
436 436
 	container := ep.container
437 437
 	joinInfo := ep.joinInfo
438
+	ifaces := ep.iFaces
438 439
 	ep.Unlock()
439 440
 
440 441
 	if container == nil {
... ...
@@ -451,8 +426,8 @@ func (ep *endpoint) buildHostsFiles() error {
451 451
 		return err
452 452
 	}
453 453
 
454
-	if joinInfo != nil && joinInfo.HostsPath != "" {
455
-		content, err := ioutil.ReadFile(joinInfo.HostsPath)
454
+	if joinInfo != nil && joinInfo.hostsPath != "" {
455
+		content, err := ioutil.ReadFile(joinInfo.hostsPath)
456 456
 		if err != nil && !os.IsNotExist(err) {
457 457
 			return err
458 458
 		}
... ...
@@ -473,10 +448,8 @@ func (ep *endpoint) buildHostsFiles() error {
473 473
 	}
474 474
 
475 475
 	IP := ""
476
-	sinfo := ep.SandboxInfo()
477
-	if sinfo != nil && sinfo.Interfaces[0] != nil &&
478
-		sinfo.Interfaces[0].Address != nil {
479
-		IP = sinfo.Interfaces[0].Address.IP.String()
476
+	if len(ifaces) != 0 && ifaces[0] != nil {
477
+		IP = ifaces[0].addr.IP.String()
480 478
 	}
481 479
 
482 480
 	return etchosts.Build(container.config.hostsPath, IP, container.config.hostName,
... ...
@@ -749,12 +722,3 @@ func JoinOptionGeneric(generic map[string]interface{}) EndpointOption {
749 749
 		ep.container.config.generic = generic
750 750
 	}
751 751
 }
752
-
753
-// LeaveOptionGeneric function returns an option setter for Generic configuration
754
-// that is not managed by libNetwork but can be used by the Drivers during the call to
755
-// endpoint leave method. Container Labels are a good example.
756
-func LeaveOptionGeneric(context map[string]interface{}) EndpointOption {
757
-	return func(ep *endpoint) {
758
-		ep.context = context
759
-	}
760
-}
761 752
new file mode 100644
... ...
@@ -0,0 +1,215 @@
0
+package libnetwork
1
+
2
+import (
3
+	"net"
4
+
5
+	"github.com/docker/libnetwork/driverapi"
6
+	"github.com/docker/libnetwork/netutils"
7
+)
8
+
9
+// EndpointInfo provides an interface to retrieve network resources bound to the endpoint.
10
+type EndpointInfo interface {
11
+	// InterfaceList returns an interface list which were assigned to the endpoint
12
+	// by the driver. This can be used after the endpoint has been created.
13
+	InterfaceList() []InterfaceInfo
14
+
15
+	// Gateway returns the IPv4 gateway assigned by the driver.
16
+	// This will only return a valid value if a container has joined the endpoint.
17
+	Gateway() net.IP
18
+
19
+	// GatewayIPv6 returns the IPv6 gateway assigned by the driver.
20
+	// This will only return a valid value if a container has joined the endpoint.
21
+	GatewayIPv6() net.IP
22
+
23
+	// SandboxKey returns the sanbox key for the container which has joined
24
+	// the endpoint. If there is no container joined then this will return an
25
+	// empty string.
26
+	SandboxKey() string
27
+}
28
+
29
+// InterfaceInfo provides an interface to retrieve interface addresses bound to the endpoint.
30
+type InterfaceInfo interface {
31
+	// MacAddress returns the MAC address assigned to the endpoint.
32
+	MacAddress() net.HardwareAddr
33
+
34
+	// Address returns the IPv4 address assigned to the endpoint.
35
+	Address() net.IPNet
36
+
37
+	// AddressIPv6 returns the IPv6 address assigned to the endpoint.
38
+	AddressIPv6() net.IPNet
39
+}
40
+
41
+type endpointInterface struct {
42
+	id      int
43
+	mac     net.HardwareAddr
44
+	addr    net.IPNet
45
+	addrv6  net.IPNet
46
+	srcName string
47
+	dstName string
48
+}
49
+
50
+type endpointJoinInfo struct {
51
+	gw             net.IP
52
+	gw6            net.IP
53
+	hostsPath      string
54
+	resolvConfPath string
55
+}
56
+
57
+func (ep *endpoint) Info() EndpointInfo {
58
+	return ep
59
+}
60
+
61
+func (ep *endpoint) DriverInfo() (map[string]interface{}, error) {
62
+	ep.Lock()
63
+	network := ep.network
64
+	epid := ep.id
65
+	ep.Unlock()
66
+
67
+	network.Lock()
68
+	driver := network.driver
69
+	nid := network.id
70
+	network.Unlock()
71
+
72
+	return driver.EndpointOperInfo(nid, epid)
73
+}
74
+
75
+func (ep *endpoint) InterfaceList() []InterfaceInfo {
76
+	ep.Lock()
77
+	defer ep.Unlock()
78
+
79
+	iList := make([]InterfaceInfo, len(ep.iFaces))
80
+
81
+	for i, iface := range ep.iFaces {
82
+		iList[i] = iface
83
+	}
84
+
85
+	return iList
86
+}
87
+
88
+func (ep *endpoint) Interfaces() []driverapi.InterfaceInfo {
89
+	ep.Lock()
90
+	defer ep.Unlock()
91
+
92
+	iList := make([]driverapi.InterfaceInfo, len(ep.iFaces))
93
+
94
+	for i, iface := range ep.iFaces {
95
+		iList[i] = iface
96
+	}
97
+
98
+	return iList
99
+}
100
+
101
+func (ep *endpoint) AddInterface(id int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
102
+	ep.Lock()
103
+	defer ep.Unlock()
104
+
105
+	iface := &endpointInterface{
106
+		id:     id,
107
+		addr:   *netutils.GetIPNetCopy(&ipv4),
108
+		addrv6: *netutils.GetIPNetCopy(&ipv6),
109
+	}
110
+	iface.mac = netutils.GetMacCopy(mac)
111
+
112
+	ep.iFaces = append(ep.iFaces, iface)
113
+	return nil
114
+}
115
+
116
+func (i *endpointInterface) ID() int {
117
+	return i.id
118
+}
119
+
120
+func (i *endpointInterface) MacAddress() net.HardwareAddr {
121
+	return netutils.GetMacCopy(i.mac)
122
+}
123
+
124
+func (i *endpointInterface) Address() net.IPNet {
125
+	return (*netutils.GetIPNetCopy(&i.addr))
126
+}
127
+
128
+func (i *endpointInterface) AddressIPv6() net.IPNet {
129
+	return (*netutils.GetIPNetCopy(&i.addrv6))
130
+}
131
+
132
+func (i *endpointInterface) SetNames(srcName string, dstName string) error {
133
+	i.srcName = srcName
134
+	i.dstName = dstName
135
+	return nil
136
+}
137
+
138
+func (ep *endpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
139
+	ep.Lock()
140
+	defer ep.Unlock()
141
+
142
+	iList := make([]driverapi.InterfaceNameInfo, len(ep.iFaces))
143
+
144
+	for i, iface := range ep.iFaces {
145
+		iList[i] = iface
146
+	}
147
+
148
+	return iList
149
+}
150
+
151
+func (ep *endpoint) SandboxKey() string {
152
+	ep.Lock()
153
+	defer ep.Unlock()
154
+
155
+	if ep.container == nil {
156
+		return ""
157
+	}
158
+
159
+	return ep.container.data.SandboxKey
160
+}
161
+
162
+func (ep *endpoint) Gateway() net.IP {
163
+	ep.Lock()
164
+	defer ep.Unlock()
165
+
166
+	if ep.joinInfo == nil {
167
+		return net.IP{}
168
+	}
169
+
170
+	return netutils.GetIPCopy(ep.joinInfo.gw)
171
+}
172
+
173
+func (ep *endpoint) GatewayIPv6() net.IP {
174
+	ep.Lock()
175
+	defer ep.Unlock()
176
+
177
+	if ep.joinInfo == nil {
178
+		return net.IP{}
179
+	}
180
+
181
+	return netutils.GetIPCopy(ep.joinInfo.gw6)
182
+}
183
+
184
+func (ep *endpoint) SetGateway(gw net.IP) error {
185
+	ep.Lock()
186
+	defer ep.Unlock()
187
+
188
+	ep.joinInfo.gw = netutils.GetIPCopy(gw)
189
+	return nil
190
+}
191
+
192
+func (ep *endpoint) SetGatewayIPv6(gw6 net.IP) error {
193
+	ep.Lock()
194
+	defer ep.Unlock()
195
+
196
+	ep.joinInfo.gw6 = netutils.GetIPCopy(gw6)
197
+	return nil
198
+}
199
+
200
+func (ep *endpoint) SetHostsPath(path string) error {
201
+	ep.Lock()
202
+	defer ep.Unlock()
203
+
204
+	ep.joinInfo.hostsPath = path
205
+	return nil
206
+}
207
+
208
+func (ep *endpoint) SetResolvConfPath(path string) error {
209
+	ep.Lock()
210
+	defer ep.Unlock()
211
+
212
+	ep.joinInfo.resolvConfPath = path
213
+	return nil
214
+}
... ...
@@ -190,7 +190,7 @@ func TestBridge(t *testing.T) {
190 190
 		t.Fatal(err)
191 191
 	}
192 192
 
193
-	epInfo, err := ep.Info()
193
+	epInfo, err := ep.DriverInfo()
194 194
 	if err != nil {
195 195
 		t.Fatal(err)
196 196
 	}
... ...
@@ -696,6 +696,23 @@ func TestEndpointJoin(t *testing.T) {
696 696
 		t.Fatal(err)
697 697
 	}
698 698
 
699
+	// Validate if ep.Info() only gives me IP address info and not names and gateway during CreateEndpoint()
700
+	info := ep.Info()
701
+
702
+	for _, iface := range info.InterfaceList() {
703
+		if iface.Address().IP.To4() == nil {
704
+			t.Fatalf("Invalid IP address returned: %v", iface.Address())
705
+		}
706
+	}
707
+
708
+	if info.Gateway().To4() != nil {
709
+		t.Fatalf("Expected empty gateway for an empty endpoint. Instead found a gateway: %v", info.Gateway())
710
+	}
711
+
712
+	if info.SandboxKey() != "" {
713
+		t.Fatalf("Expected an empty sandbox key for an empty endpoint. Instead found a non-empty sandbox key: %s", info.SandboxKey())
714
+	}
715
+
699 716
 	_, err = ep.Join(containerID,
700 717
 		libnetwork.JoinOptionHostname("test"),
701 718
 		libnetwork.JoinOptionDomainname("docker.io"),
... ...
@@ -710,6 +727,16 @@ func TestEndpointJoin(t *testing.T) {
710 710
 			t.Fatal(err)
711 711
 		}
712 712
 	}()
713
+
714
+	// Validate if ep.Info() only gives valid gateway and sandbox key after has container has joined.
715
+	info = ep.Info()
716
+	if info.Gateway().To4() == nil {
717
+		t.Fatalf("Expected a valid gateway for a joined endpoint. Instead found an invalid gateway: %v", info.Gateway())
718
+	}
719
+
720
+	if info.SandboxKey() == "" {
721
+		t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found a empty sandbox key")
722
+	}
713 723
 }
714 724
 
715 725
 func TestEndpointJoinInvalidContainerId(t *testing.T) {
... ...
@@ -288,6 +288,13 @@ func GenerateRandomName(prefix string, size int) (string, error) {
288 288
 	return prefix + hex.EncodeToString(id)[:size], nil
289 289
 }
290 290
 
291
+// GetMacCopy returns a copy of the passed MAC address
292
+func GetMacCopy(from net.HardwareAddr) net.HardwareAddr {
293
+	to := make(net.HardwareAddr, len(from))
294
+	copy(to, from)
295
+	return to
296
+}
297
+
291 298
 // GetIPCopy returns a copy of the passed IP address
292 299
 func GetIPCopy(from net.IP) net.IP {
293 300
 	to := make(net.IP, len(from))
... ...
@@ -135,18 +135,17 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
135 135
 	if name == "" {
136 136
 		return nil, ErrInvalidName
137 137
 	}
138
-	ep := &endpoint{name: name, generic: make(map[string]interface{})}
138
+	ep := &endpoint{name: name, iFaces: []*endpointInterface{}, generic: make(map[string]interface{})}
139 139
 	ep.id = types.UUID(stringid.GenerateRandomID())
140 140
 	ep.network = n
141 141
 	ep.processOptions(options...)
142 142
 
143 143
 	d := n.driver
144
-	sinfo, err := d.CreateEndpoint(n.id, ep.id, ep.generic)
144
+	err := d.CreateEndpoint(n.id, ep.id, ep, ep.generic)
145 145
 	if err != nil {
146 146
 		return nil, err
147 147
 	}
148 148
 
149
-	ep.sandboxInfo = sinfo
150 149
 	n.Lock()
151 150
 	n.endpoints[ep.id] = ep
152 151
 	n.Unlock()