From 56741e7d60607b0fd98f7790db389cc01da511f2 Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Mon, 4 May 2015 11:49:53 -0700 Subject: [PATCH] Provide API to retrieve Endpoint operational data - from the driver Signed-off-by: Alessandro Boch --- libnetwork/README.md | 11 +++++ libnetwork/cmd/readme_test/readme.go | 13 +++++ libnetwork/driverapi/driverapi.go | 3 ++ libnetwork/drivers/bridge/bridge.go | 40 +++++++++++++++ libnetwork/drivers/bridge/bridge_test.go | 62 ++++++++++++++++++++++++ libnetwork/drivers/null/null.go | 4 ++ libnetwork/endpoint.go | 7 +++ libnetwork/libnetwork_test.go | 16 ++++++ libnetwork/netutils/utils.go | 37 ++++++++++++++ 9 files changed, 193 insertions(+) diff --git a/libnetwork/README.md b/libnetwork/README.md index 3e4ebf5d10..7516b19f4d 100644 --- a/libnetwork/README.md +++ b/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 diff --git a/libnetwork/cmd/readme_test/readme.go b/libnetwork/cmd/readme_test/readme.go index ad57757a45..4cdd6fe5df 100644 --- a/libnetwork/cmd/readme_test/readme.go +++ b/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) + } + } } diff --git a/libnetwork/driverapi/driverapi.go b/libnetwork/driverapi/driverapi.go index b0c64a9280..963058d14e 100644 --- a/libnetwork/driverapi/driverapi.go +++ b/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 diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index e6756ed946..664811cd8e 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/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 diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index 2952687251..e623d40c03 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/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)() diff --git a/libnetwork/drivers/null/null.go b/libnetwork/drivers/null/null.go index c95dc820ea..57577c6d57 100644 --- a/libnetwork/drivers/null/null.go +++ b/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 diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 6bebf88bba..5f7170148d 100644 --- a/libnetwork/endpoint.go +++ b/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 { diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index d6ec1ffe8d..f663d453dc 100644 --- a/libnetwork/libnetwork_test.go +++ b/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) } diff --git a/libnetwork/netutils/utils.go b/libnetwork/netutils/utils.go index b24776ea09..cc4450b384 100644 --- a/libnetwork/netutils/utils.go +++ b/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