瀏覽代碼

Merge pull request #110 from aboch/ed

Provide API to retrieve Endpoint operational data
Madhu Venugopal 10 年之前
父節點
當前提交
def2a1192f

+ 11 - 0
libnetwork/README.md

@@ -61,6 +61,17 @@ There are many networking solutions available to suit a broad range of use-cases
         if err != nil {
                 return
         }
+
+		// libentwork client can check the endpoint's operational data via the Info() API
+		epInfo, err := ep.Info()
+		mapData, ok := epInfo[options.PortMap]
+		if ok {
+			portMapping, ok := mapData.([]netutils.PortBinding)
+			if ok {
+				fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping)
+			}
+		}
+
 ```
 
 ## Future

+ 13 - 0
libnetwork/cmd/readme_test/readme.go

@@ -1,7 +1,10 @@
 package main
 
 import (
+	"fmt"
+
 	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/docker/libnetwork/pkg/options"
 )
 
@@ -46,4 +49,14 @@ func main() {
 	if err != nil {
 		return
 	}
+
+	// libentwork client can check the endpoint's operational data via the Info() API
+	epInfo, err := ep.Info()
+	mapData, ok := epInfo[options.PortMap]
+	if ok {
+		portMapping, ok := mapData.([]netutils.PortBinding)
+		if ok {
+			fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping)
+		}
+	}
 }

+ 3 - 0
libnetwork/driverapi/driverapi.go

@@ -40,6 +40,9 @@ type Driver interface {
 	// passing the network id and endpoint id.
 	DeleteEndpoint(nid, eid types.UUID) error
 
+	// EndpointInfo retrieves from the driver the operational data related to the specified endpoint
+	EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error)
+
 	// Join method is invoked when a Sandbox is attached to an endpoint.
 	Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) error
 

+ 40 - 0
libnetwork/drivers/bridge/bridge.go

@@ -573,6 +573,46 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 	return nil
 }
 
+func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
+	// Get the network handler and make sure it exists
+	d.Lock()
+	n := d.network
+	d.Unlock()
+	if n == nil {
+		return nil, driverapi.ErrNoNetwork
+	}
+
+	// Sanity check
+	n.Lock()
+	if n.id != nid {
+		n.Unlock()
+		return nil, InvalidNetworkIDError(nid)
+	}
+	n.Unlock()
+
+	// Check if endpoint id is good and retrieve correspondent endpoint
+	ep, err := n.getEndpoint(eid)
+	if err != nil {
+		return nil, err
+	}
+	if ep == nil {
+		return nil, driverapi.ErrNoEndpoint
+	}
+
+	m := make(map[string]interface{})
+
+	if ep.portMapping != nil {
+		// Return a copy of the operational data
+		pmc := make([]netutils.PortBinding, 0, len(ep.portMapping))
+		for _, pm := range ep.portMapping {
+			pmc = append(pmc, pm.GetCopy())
+		}
+		m[options.PortMap] = pmc
+	}
+
+	return m, nil
+}
+
 // Join method is invoked when a Sandbox is attached to an endpoint.
 func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) error {
 	var err error

+ 62 - 0
libnetwork/drivers/bridge/bridge_test.go

@@ -72,6 +72,68 @@ func TestCreateFullOptions(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 }
+
+func TestQueryEndpointInfo(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	_, d := New()
+
+	config := &Configuration{
+		BridgeName:     DefaultBridgeName,
+		EnableIPTables: true,
+		EnableICC:      false,
+	}
+	genericOption := make(map[string]interface{})
+	genericOption[options.GenericData] = config
+
+	if err := d.Config(genericOption); err != nil {
+		t.Fatalf("Failed to setup driver config: %v", err)
+	}
+
+	err := d.CreateNetwork("net1", nil)
+	if err != nil {
+		t.Fatalf("Failed to create bridge: %v", err)
+	}
+
+	portMappings := getPortMapping()
+	epOptions := make(map[string]interface{})
+	epOptions[options.PortMap] = portMappings
+
+	_, err = d.CreateEndpoint("net1", "ep1", epOptions)
+	if err != nil {
+		t.Fatalf("Failed to create an endpoint : %s", err.Error())
+	}
+
+	dd := d.(*driver)
+	ep, _ := dd.network.endpoints["ep1"]
+	data, err := d.EndpointInfo(dd.network.id, ep.id)
+	if err != nil {
+		t.Fatalf("Failed to ask for endpoint operational data:  %v", err)
+	}
+	pmd, ok := data[options.PortMap]
+	if !ok {
+		t.Fatalf("Endpoint operational data does not contain port mapping data")
+	}
+	pm, ok := pmd.([]netutils.PortBinding)
+	if !ok {
+		t.Fatalf("Unexpected format for port mapping in endpoint operational data")
+	}
+	if len(ep.portMapping) != len(pm) {
+		t.Fatalf("Incomplete data for port mapping in endpoint operational data")
+	}
+	for i, pb := range ep.portMapping {
+		if !pb.Equal(&pm[i]) {
+			t.Fatalf("Unexpected data for port mapping in endpoint operational data")
+		}
+	}
+
+	// Cleanup as host ports are there
+	err = releasePorts(ep)
+	if err != nil {
+		t.Fatalf("Failed to release mapped ports: %v", err)
+	}
+}
+
 func TestCreateLinkWithOptions(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 

+ 4 - 0
libnetwork/drivers/null/null.go

@@ -35,6 +35,10 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 	return nil
 }
 
+func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
+	return make(map[string]interface{}, 0), nil
+}
+
 // Join method is invoked when a Sandbox is attached to an endpoint.
 func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) error {
 	return nil

+ 7 - 0
libnetwork/endpoint.go

@@ -34,6 +34,9 @@ type Endpoint interface {
 	// SandboxInfo returns the sandbox information for this endpoint.
 	SandboxInfo() *sandbox.Info
 
+	// Info returns a collection of operational data related to this endpoint retrieved from the driver
+	Info() (map[string]interface{}, error)
+
 	// Delete and detaches this endpoint from the network.
 	Delete() error
 }
@@ -94,6 +97,10 @@ func (ep *endpoint) SandboxInfo() *sandbox.Info {
 	return ep.sandboxInfo.GetCopy()
 }
 
+func (ep *endpoint) Info() (map[string]interface{}, error) {
+	return ep.network.driver.EndpointInfo(ep.network.id, ep.id)
+}
+
 func (ep *endpoint) processOptions(options ...EndpointOption) {
 	for _, opt := range options {
 		if opt != nil {

+ 16 - 0
libnetwork/libnetwork_test.go

@@ -131,6 +131,22 @@ func TestBridge(t *testing.T) {
 		t.Fatal(err)
 	}
 
+	epInfo, err := ep.Info()
+	if err != nil {
+		t.Fatal(err)
+	}
+	pmd, ok := epInfo[options.PortMap]
+	if !ok {
+		t.Fatalf("Could not find expected info in endpoint data")
+	}
+	pm, ok := pmd.([]netutils.PortBinding)
+	if !ok {
+		t.Fatalf("Unexpected format for port mapping in endpoint operational data")
+	}
+	if len(pm) != 3 {
+		t.Fatalf("Incomplete data for port mapping in endpoint operational data")
+	}
+
 	if err := ep.Delete(); err != nil {
 		t.Fatal(err)
 	}

+ 37 - 0
libnetwork/netutils/utils.go

@@ -83,6 +83,43 @@ func (p *PortBinding) GetCopy() PortBinding {
 	}
 }
 
+// Equal checks if this instance of PortBinding is equal to the passed one
+func (p *PortBinding) Equal(o *PortBinding) bool {
+	if p == o {
+		return true
+	}
+
+	if o == nil {
+		return false
+	}
+
+	if p.Proto != o.Proto || p.Port != o.Port || p.HostPort != o.HostPort {
+		return false
+	}
+
+	if p.IP != nil {
+		if !p.IP.Equal(o.IP) {
+			return false
+		}
+	} else {
+		if o.IP != nil {
+			return false
+		}
+	}
+
+	if p.HostIP != nil {
+		if !p.HostIP.Equal(o.HostIP) {
+			return false
+		}
+	} else {
+		if o.HostIP != nil {
+			return false
+		}
+	}
+
+	return true
+}
+
 const (
 	// ICMP is for the ICMP ip protocol
 	ICMP = 1