Browse code

Provide API to retrieve Endpoint operational data

- from the driver

Signed-off-by: Alessandro Boch <aboch@docker.com>

Alessandro Boch authored on 2015/05/05 03:49:53
Showing 9 changed files
... ...
@@ -61,6 +61,17 @@ There are many networking solutions available to suit a broad range of use-cases
61 61
         if err != nil {
62 62
                 return
63 63
         }
64
+
65
+		// libentwork client can check the endpoint's operational data via the Info() API
66
+		epInfo, err := ep.Info()
67
+		mapData, ok := epInfo[options.PortMap]
68
+		if ok {
69
+			portMapping, ok := mapData.([]netutils.PortBinding)
70
+			if ok {
71
+				fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping)
72
+			}
73
+		}
74
+
64 75
 ```
65 76
 
66 77
 ## Future
... ...
@@ -1,7 +1,10 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"fmt"
5
+
4 6
 	"github.com/docker/libnetwork"
7
+	"github.com/docker/libnetwork/netutils"
5 8
 	"github.com/docker/libnetwork/pkg/options"
6 9
 )
7 10
 
... ...
@@ -46,4 +49,14 @@ func main() {
46 46
 	if err != nil {
47 47
 		return
48 48
 	}
49
+
50
+	// libentwork client can check the endpoint's operational data via the Info() API
51
+	epInfo, err := ep.Info()
52
+	mapData, ok := epInfo[options.PortMap]
53
+	if ok {
54
+		portMapping, ok := mapData.([]netutils.PortBinding)
55
+		if ok {
56
+			fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping)
57
+		}
58
+	}
49 59
 }
... ...
@@ -40,6 +40,9 @@ type Driver interface {
40 40
 	// passing the network id and endpoint id.
41 41
 	DeleteEndpoint(nid, eid types.UUID) error
42 42
 
43
+	// EndpointInfo retrieves from the driver the operational data related to the specified endpoint
44
+	EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error)
45
+
43 46
 	// Join method is invoked when a Sandbox is attached to an endpoint.
44 47
 	Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) error
45 48
 
... ...
@@ -573,6 +573,46 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
573 573
 	return nil
574 574
 }
575 575
 
576
+func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
577
+	// Get the network handler and make sure it exists
578
+	d.Lock()
579
+	n := d.network
580
+	d.Unlock()
581
+	if n == nil {
582
+		return nil, driverapi.ErrNoNetwork
583
+	}
584
+
585
+	// Sanity check
586
+	n.Lock()
587
+	if n.id != nid {
588
+		n.Unlock()
589
+		return nil, InvalidNetworkIDError(nid)
590
+	}
591
+	n.Unlock()
592
+
593
+	// Check if endpoint id is good and retrieve correspondent endpoint
594
+	ep, err := n.getEndpoint(eid)
595
+	if err != nil {
596
+		return nil, err
597
+	}
598
+	if ep == nil {
599
+		return nil, driverapi.ErrNoEndpoint
600
+	}
601
+
602
+	m := make(map[string]interface{})
603
+
604
+	if ep.portMapping != nil {
605
+		// Return a copy of the operational data
606
+		pmc := make([]netutils.PortBinding, 0, len(ep.portMapping))
607
+		for _, pm := range ep.portMapping {
608
+			pmc = append(pmc, pm.GetCopy())
609
+		}
610
+		m[options.PortMap] = pmc
611
+	}
612
+
613
+	return m, nil
614
+}
615
+
576 616
 // Join method is invoked when a Sandbox is attached to an endpoint.
577 617
 func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) error {
578 618
 	var err error
... ...
@@ -72,6 +72,68 @@ func TestCreateFullOptions(t *testing.T) {
72 72
 		t.Fatalf("Failed to create bridge: %v", err)
73 73
 	}
74 74
 }
75
+
76
+func TestQueryEndpointInfo(t *testing.T) {
77
+	defer netutils.SetupTestNetNS(t)()
78
+
79
+	_, d := New()
80
+
81
+	config := &Configuration{
82
+		BridgeName:     DefaultBridgeName,
83
+		EnableIPTables: true,
84
+		EnableICC:      false,
85
+	}
86
+	genericOption := make(map[string]interface{})
87
+	genericOption[options.GenericData] = config
88
+
89
+	if err := d.Config(genericOption); err != nil {
90
+		t.Fatalf("Failed to setup driver config: %v", err)
91
+	}
92
+
93
+	err := d.CreateNetwork("net1", nil)
94
+	if err != nil {
95
+		t.Fatalf("Failed to create bridge: %v", err)
96
+	}
97
+
98
+	portMappings := getPortMapping()
99
+	epOptions := make(map[string]interface{})
100
+	epOptions[options.PortMap] = portMappings
101
+
102
+	_, err = d.CreateEndpoint("net1", "ep1", epOptions)
103
+	if err != nil {
104
+		t.Fatalf("Failed to create an endpoint : %s", err.Error())
105
+	}
106
+
107
+	dd := d.(*driver)
108
+	ep, _ := dd.network.endpoints["ep1"]
109
+	data, err := d.EndpointInfo(dd.network.id, ep.id)
110
+	if err != nil {
111
+		t.Fatalf("Failed to ask for endpoint operational data:  %v", err)
112
+	}
113
+	pmd, ok := data[options.PortMap]
114
+	if !ok {
115
+		t.Fatalf("Endpoint operational data does not contain port mapping data")
116
+	}
117
+	pm, ok := pmd.([]netutils.PortBinding)
118
+	if !ok {
119
+		t.Fatalf("Unexpected format for port mapping in endpoint operational data")
120
+	}
121
+	if len(ep.portMapping) != len(pm) {
122
+		t.Fatalf("Incomplete data for port mapping in endpoint operational data")
123
+	}
124
+	for i, pb := range ep.portMapping {
125
+		if !pb.Equal(&pm[i]) {
126
+			t.Fatalf("Unexpected data for port mapping in endpoint operational data")
127
+		}
128
+	}
129
+
130
+	// Cleanup as host ports are there
131
+	err = releasePorts(ep)
132
+	if err != nil {
133
+		t.Fatalf("Failed to release mapped ports: %v", err)
134
+	}
135
+}
136
+
75 137
 func TestCreateLinkWithOptions(t *testing.T) {
76 138
 	defer netutils.SetupTestNetNS(t)()
77 139
 
... ...
@@ -35,6 +35,10 @@ 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) {
39
+	return make(map[string]interface{}, 0), nil
40
+}
41
+
38 42
 // Join method is invoked when a Sandbox is attached to an endpoint.
39 43
 func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) error {
40 44
 	return nil
... ...
@@ -34,6 +34,9 @@ type Endpoint interface {
34 34
 	// SandboxInfo returns the sandbox information for this endpoint.
35 35
 	SandboxInfo() *sandbox.Info
36 36
 
37
+	// Info returns a collection of operational data related to this endpoint retrieved from the driver
38
+	Info() (map[string]interface{}, error)
39
+
37 40
 	// Delete and detaches this endpoint from the network.
38 41
 	Delete() error
39 42
 }
... ...
@@ -94,6 +97,10 @@ func (ep *endpoint) SandboxInfo() *sandbox.Info {
94 94
 	return ep.sandboxInfo.GetCopy()
95 95
 }
96 96
 
97
+func (ep *endpoint) Info() (map[string]interface{}, error) {
98
+	return ep.network.driver.EndpointInfo(ep.network.id, ep.id)
99
+}
100
+
97 101
 func (ep *endpoint) processOptions(options ...EndpointOption) {
98 102
 	for _, opt := range options {
99 103
 		if opt != nil {
... ...
@@ -131,6 +131,22 @@ func TestBridge(t *testing.T) {
131 131
 		t.Fatal(err)
132 132
 	}
133 133
 
134
+	epInfo, err := ep.Info()
135
+	if err != nil {
136
+		t.Fatal(err)
137
+	}
138
+	pmd, ok := epInfo[options.PortMap]
139
+	if !ok {
140
+		t.Fatalf("Could not find expected info in endpoint data")
141
+	}
142
+	pm, ok := pmd.([]netutils.PortBinding)
143
+	if !ok {
144
+		t.Fatalf("Unexpected format for port mapping in endpoint operational data")
145
+	}
146
+	if len(pm) != 3 {
147
+		t.Fatalf("Incomplete data for port mapping in endpoint operational data")
148
+	}
149
+
134 150
 	if err := ep.Delete(); err != nil {
135 151
 		t.Fatal(err)
136 152
 	}
... ...
@@ -83,6 +83,43 @@ func (p *PortBinding) GetCopy() PortBinding {
83 83
 	}
84 84
 }
85 85
 
86
+// Equal checks if this instance of PortBinding is equal to the passed one
87
+func (p *PortBinding) Equal(o *PortBinding) bool {
88
+	if p == o {
89
+		return true
90
+	}
91
+
92
+	if o == nil {
93
+		return false
94
+	}
95
+
96
+	if p.Proto != o.Proto || p.Port != o.Port || p.HostPort != o.HostPort {
97
+		return false
98
+	}
99
+
100
+	if p.IP != nil {
101
+		if !p.IP.Equal(o.IP) {
102
+			return false
103
+		}
104
+	} else {
105
+		if o.IP != nil {
106
+			return false
107
+		}
108
+	}
109
+
110
+	if p.HostIP != nil {
111
+		if !p.HostIP.Equal(o.HostIP) {
112
+			return false
113
+		}
114
+	} else {
115
+		if o.HostIP != nil {
116
+			return false
117
+		}
118
+	}
119
+
120
+	return true
121
+}
122
+
86 123
 const (
87 124
 	// ICMP is for the ICMP ip protocol
88 125
 	ICMP = 1