Browse code

Vendoring in libnetwork 67438080724b17b641b411322822c00d0d3c3201

This version brings in upto-date important bug-fixes from libnetwork

Signed-off-by: Madhu Venugopal <madhu@docker.com>

Madhu Venugopal authored on 2015/05/23 15:00:03
Showing 57 changed files
... ...
@@ -30,8 +30,8 @@ import (
30 30
 	"github.com/docker/libcontainer/devices"
31 31
 	"github.com/docker/libnetwork"
32 32
 	"github.com/docker/libnetwork/netlabel"
33
-	"github.com/docker/libnetwork/netutils"
34 33
 	"github.com/docker/libnetwork/options"
34
+	"github.com/docker/libnetwork/types"
35 35
 )
36 36
 
37 37
 const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
... ...
@@ -502,7 +502,7 @@ func (container *Container) buildPortMapInfo(n libnetwork.Network, ep libnetwork
502 502
 		return networkSettings, nil
503 503
 	}
504 504
 
505
-	if portMapping, ok := mapData.([]netutils.PortBinding); ok {
505
+	if portMapping, ok := mapData.([]types.PortBinding); ok {
506 506
 		networkSettings.Ports = nat.PortMap{}
507 507
 		for _, pp := range portMapping {
508 508
 			natPort := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port)))
... ...
@@ -641,8 +641,8 @@ func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointO
641 641
 	var (
642 642
 		portSpecs     = make(nat.PortSet)
643 643
 		bindings      = make(nat.PortMap)
644
-		pbList        []netutils.PortBinding
645
-		exposeList    []netutils.TransportPort
644
+		pbList        []types.PortBinding
645
+		exposeList    []types.TransportPort
646 646
 		createOptions []libnetwork.EndpointOption
647 647
 	)
648 648
 
... ...
@@ -682,12 +682,12 @@ func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointO
682 682
 	}
683 683
 	nat.SortPortMap(ports, bindings)
684 684
 	for _, port := range ports {
685
-		expose := netutils.TransportPort{}
686
-		expose.Proto = netutils.ParseProtocol(port.Proto())
685
+		expose := types.TransportPort{}
686
+		expose.Proto = types.ParseProtocol(port.Proto())
687 687
 		expose.Port = uint16(port.Int())
688 688
 		exposeList = append(exposeList, expose)
689 689
 
690
-		pb := netutils.PortBinding{Port: expose.Port, Proto: expose.Proto}
690
+		pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto}
691 691
 		binding := bindings[port]
692 692
 		for i := 0; i < len(binding); i++ {
693 693
 			pbCopy := pb.GetCopy()
... ...
@@ -55,7 +55,7 @@ clone hg code.google.com/p/go.net 84a4013f96e0
55 55
 clone hg code.google.com/p/gosqlite 74691fb6f837
56 56
 
57 57
 #get libnetwork packages
58
-clone git github.com/docker/libnetwork b39597744b0978fe4aeb9f3a099ba42f7b6c4a1f
58
+clone git github.com/docker/libnetwork 67438080724b17b641b411322822c00d0d3c3201
59 59
 clone git github.com/vishvananda/netns 008d17ae001344769b031375bdb38a86219154c6
60 60
 clone git github.com/vishvananda/netlink 8eb64238879fed52fd51c5b30ad20b928fb4c36c
61 61
 
... ...
@@ -22,7 +22,7 @@ build: ${build_image}.created
22 22
 	${docker} make build-local
23 23
 
24 24
 build-local:
25
-	$(shell which godep) go build ./...
25
+	$(shell which godep) go build -tags experimental ./...
26 26
 
27 27
 check: ${build_image}.created
28 28
 	${docker} make check-local
... ...
@@ -5,8 +5,10 @@ import (
5 5
 	"fmt"
6 6
 	"io/ioutil"
7 7
 	"net/http"
8
+	"strings"
8 9
 
9 10
 	"github.com/docker/libnetwork"
11
+	"github.com/docker/libnetwork/types"
10 12
 	"github.com/gorilla/mux"
11 13
 )
12 14
 
... ...
@@ -14,13 +16,28 @@ var (
14 14
 	successResponse  = responseStatus{Status: "Success", StatusCode: http.StatusOK}
15 15
 	createdResponse  = responseStatus{Status: "Created", StatusCode: http.StatusCreated}
16 16
 	mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest}
17
+	badQueryresponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest}
17 18
 )
18 19
 
19 20
 const (
20
-	urlNwName = "name"
21
-	urlNwID   = "id"
21
+	// Resource name regex
22
+	regex = "[a-zA-Z_0-9-]+"
23
+	// Router URL variable definition
24
+	nwName = "{" + urlNwName + ":" + regex + "}"
25
+	nwID   = "{" + urlNwID + ":" + regex + "}"
26
+	nwPID  = "{" + urlNwPID + ":" + regex + "}"
27
+	epName = "{" + urlEpName + ":" + regex + "}"
28
+	epID   = "{" + urlEpID + ":" + regex + "}"
29
+	epPID  = "{" + urlEpPID + ":" + regex + "}"
30
+	cnID   = "{" + urlCnID + ":" + regex + "}"
31
+
32
+	// Internal URL variable name, they can be anything
33
+	urlNwName = "network-name"
34
+	urlNwID   = "network-id"
35
+	urlNwPID  = "network-partial-id"
22 36
 	urlEpName = "endpoint-name"
23 37
 	urlEpID   = "endpoint-id"
38
+	urlEpPID  = "endpoint-partial-id"
24 39
 	urlCnID   = "container-id"
25 40
 )
26 41
 
... ...
@@ -59,42 +76,41 @@ func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) {
59 59
 }
60 60
 
61 61
 func (h *httpHandler) initRouter() {
62
-	m := map[string]map[string]processor{
62
+	m := map[string][]struct {
63
+		url string
64
+		qrs []string
65
+		fct processor
66
+	}{
63 67
 		"GET": {
64
-			"/networks":                                                                   procGetNetworks,
65
-			"/networks/name/{" + urlNwName + ":.*}":                                       procGetNetwork,
66
-			"/networks/id/{" + urlNwID + ":.*}":                                           procGetNetwork,
67
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/":                            procGetEndpoints,
68
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/":                                procGetEndpoints,
69
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procGetEndpoint,
70
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}":     procGetEndpoint,
71
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}":     procGetEndpoint,
72
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}":         procGetEndpoint,
68
+			// Order matters
69
+			{"/networks", []string{"name", nwName}, procGetNetworks},
70
+			{"/networks", []string{"partial-id", nwPID}, procGetNetworks},
71
+			{"/networks", nil, procGetNetworks},
72
+			{"/networks/" + nwID, nil, procGetNetwork},
73
+			{"/networks/" + nwID + "/endpoints", []string{"name", epName}, procGetEndpoints},
74
+			{"/networks/" + nwID + "/endpoints", []string{"partial-id", epPID}, procGetEndpoints},
75
+			{"/networks/" + nwID + "/endpoints", nil, procGetEndpoints},
76
+			{"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint},
73 77
 		},
74 78
 		"POST": {
75
-			"/networks/name/{" + urlNwName + ":.*}":                                                                     procCreateNetwork,
76
-			"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}":                                procCreateEndpoint,
77
-			"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procJoinEndpoint,
79
+			{"/networks", nil, procCreateNetwork},
80
+			{"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint},
81
+			{"/networks/" + nwID + "/endpoints/" + epID + "/containers", nil, procJoinEndpoint},
78 82
 		},
79 83
 		"DELETE": {
80
-			"/networks/name/{" + urlNwName + ":.*}":                                                                     procDeleteNetwork,
81
-			"/networks/id/{" + urlNwID + ":.*}":                                                                         procDeleteNetwork,
82
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}":                               procDeleteEndpoint,
83
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}":                                   procDeleteEndpoint,
84
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}":                                   procDeleteEndpoint,
85
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}":                                       procDeleteEndpoint,
86
-			"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
87
-			"/networks/name/{" + urlNwName + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}":     procLeaveEndpoint,
88
-			"/networks/id/{" + urlNwID + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}":     procLeaveEndpoint,
89
-			"/networks/id/{" + urlNwID + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}":         procLeaveEndpoint,
84
+			{"/networks/" + nwID, nil, procDeleteNetwork},
85
+			{"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint},
86
+			{"/networks/id/" + nwID + "/endpoints/" + epID + "/containers/" + cnID, nil, procLeaveEndpoint},
90 87
 		},
91 88
 	}
92 89
 
93 90
 	h.r = mux.NewRouter()
94 91
 	for method, routes := range m {
95
-		for route, fct := range routes {
96
-			f := makeHandler(h.c, fct)
97
-			h.r.Path(route).Methods(method).HandlerFunc(f)
92
+		for _, route := range routes {
93
+			r := h.r.Path("/{.*}" + route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct))
94
+			if route.qrs != nil {
95
+				r.Queries(route.qrs...)
96
+			}
98 97
 		}
99 98
 	}
100 99
 }
... ...
@@ -208,12 +224,7 @@ func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, b
208 208
 		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
209 209
 	}
210 210
 
211
-	name := vars[urlNwName]
212
-	if name != create.Name {
213
-		return "", &mismatchResponse
214
-	}
215
-
216
-	nw, err := c.NewNetwork(create.NetworkType, name, nil)
211
+	nw, err := c.NewNetwork(create.NetworkType, create.Name, nil)
217 212
 	if err != nil {
218 213
 		return "", convertNetworkError(err)
219 214
 	}
... ...
@@ -232,10 +243,33 @@ func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body
232 232
 
233 233
 func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
234 234
 	var list []*networkResource
235
-	for _, nw := range c.Networks() {
236
-		nwr := buildNetworkResource(nw)
237
-		list = append(list, nwr)
235
+
236
+	// Look for query filters and validate
237
+	name, queryByName := vars[urlNwName]
238
+	shortID, queryByPid := vars[urlNwPID]
239
+	if queryByName && queryByPid {
240
+		return nil, &badQueryresponse
241
+	}
242
+
243
+	if queryByName {
244
+		if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() {
245
+			list = append(list, buildNetworkResource(nw))
246
+		}
247
+	} else if queryByPid {
248
+		// Return all the prefix-matching networks
249
+		l := func(nw libnetwork.Network) bool {
250
+			if strings.HasPrefix(nw.ID(), shortID) {
251
+				list = append(list, buildNetworkResource(nw))
252
+			}
253
+			return false
254
+		}
255
+		c.WalkNetworks(l)
256
+	} else {
257
+		for _, nw := range c.Networks() {
258
+			list = append(list, buildNetworkResource(nw))
259
+		}
238 260
 	}
261
+
239 262
 	return list, &successResponse
240 263
 }
241 264
 
... ...
@@ -250,21 +284,12 @@ func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string,
250 250
 		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
251 251
 	}
252 252
 
253
-	epn := vars[urlEpName]
254
-	if ec.Name != epn {
255
-		return "", &mismatchResponse
256
-	}
257
-
258 253
 	nwT, nwBy := detectNetworkTarget(vars)
259 254
 	n, errRsp := findNetwork(c, nwT, nwBy)
260 255
 	if !errRsp.isOK() {
261 256
 		return "", errRsp
262 257
 	}
263 258
 
264
-	if ec.NetworkID != n.ID() {
265
-		return "", &mismatchResponse
266
-	}
267
-
268 259
 	var setFctList []libnetwork.EndpointOption
269 260
 	if ec.ExposedPorts != nil {
270 261
 		setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts))
... ...
@@ -273,7 +298,7 @@ func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string,
273 273
 		setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping))
274 274
 	}
275 275
 
276
-	ep, err := n.CreateEndpoint(epn, setFctList...)
276
+	ep, err := n.CreateEndpoint(ec.Name, setFctList...)
277 277
 	if err != nil {
278 278
 		return "", convertNetworkError(err)
279 279
 	}
... ...
@@ -294,17 +319,40 @@ func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, bod
294 294
 }
295 295
 
296 296
 func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
297
-	target, by := detectNetworkTarget(vars)
297
+	// Look for query filters and validate
298
+	name, queryByName := vars[urlEpName]
299
+	shortID, queryByPid := vars[urlEpPID]
300
+	if queryByName && queryByPid {
301
+		return nil, &badQueryresponse
302
+	}
298 303
 
299
-	nw, errRsp := findNetwork(c, target, by)
304
+	nwT, nwBy := detectNetworkTarget(vars)
305
+	nw, errRsp := findNetwork(c, nwT, nwBy)
300 306
 	if !errRsp.isOK() {
301 307
 		return nil, errRsp
302 308
 	}
303 309
 
304 310
 	var list []*endpointResource
305
-	for _, ep := range nw.Endpoints() {
306
-		epr := buildEndpointResource(ep)
307
-		list = append(list, epr)
311
+
312
+	// If query parameter is specified, return a filtered collection
313
+	if queryByName {
314
+		if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() {
315
+			list = append(list, buildEndpointResource(ep))
316
+		}
317
+	} else if queryByPid {
318
+		// Return all the prefix-matching networks
319
+		l := func(ep libnetwork.Endpoint) bool {
320
+			if strings.HasPrefix(ep.ID(), shortID) {
321
+				list = append(list, buildEndpointResource(ep))
322
+			}
323
+			return false
324
+		}
325
+		nw.WalkEndpoints(l)
326
+	} else {
327
+		for _, ep := range nw.Endpoints() {
328
+			epr := buildEndpointResource(ep)
329
+			list = append(list, epr)
330
+		}
308 331
 	}
309 332
 
310 333
 	return list, &successResponse
... ...
@@ -336,11 +384,6 @@ func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, bo
336 336
 		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
337 337
 	}
338 338
 
339
-	cid := vars[urlCnID]
340
-	if ej.ContainerID != cid {
341
-		return "", &mismatchResponse
342
-	}
343
-
344 339
 	nwT, nwBy := detectNetworkTarget(vars)
345 340
 	epT, epBy := detectEndpointTarget(vars)
346 341
 
... ...
@@ -434,7 +477,7 @@ func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.N
434 434
 		panic(fmt.Sprintf("unexpected selector for network search: %d", by))
435 435
 	}
436 436
 	if err != nil {
437
-		if err == libnetwork.ErrNoSuchNetwork {
437
+		if _, ok := err.(libnetwork.ErrNoSuchNetwork); ok {
438 438
 			return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound}
439 439
 		}
440 440
 		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
... ...
@@ -460,7 +503,7 @@ func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int)
460 460
 		panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy))
461 461
 	}
462 462
 	if err != nil {
463
-		if err == libnetwork.ErrNoSuchEndpoint {
463
+		if _, ok := err.(libnetwork.ErrNoSuchEndpoint); ok {
464 464
 			return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound}
465 465
 		}
466 466
 		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
... ...
@@ -469,9 +512,26 @@ func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int)
469 469
 }
470 470
 
471 471
 func convertNetworkError(err error) *responseStatus {
472
-	// No real libnetwork error => http error code conversion for now.
473
-	// Will came in later when new interface for libnetwork error is vailable
474
-	return &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
472
+	var code int
473
+	switch err.(type) {
474
+	case types.BadRequestError:
475
+		code = http.StatusBadRequest
476
+	case types.ForbiddenError:
477
+		code = http.StatusForbidden
478
+	case types.NotFoundError:
479
+		code = http.StatusNotFound
480
+	case types.TimeoutError:
481
+		code = http.StatusRequestTimeout
482
+	case types.NotImplementedError:
483
+		code = http.StatusNotImplemented
484
+	case types.NoServiceError:
485
+		code = http.StatusServiceUnavailable
486
+	case types.InternalError:
487
+		code = http.StatusInternalServerError
488
+	default:
489
+		code = http.StatusInternalServerError
490
+	}
491
+	return &responseStatus{Status: err.Error(), StatusCode: code}
475 492
 }
476 493
 
477 494
 func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
... ...
@@ -16,6 +16,7 @@ import (
16 16
 	"github.com/docker/libnetwork/netlabel"
17 17
 	"github.com/docker/libnetwork/netutils"
18 18
 	"github.com/docker/libnetwork/options"
19
+	"github.com/docker/libnetwork/types"
19 20
 )
20 21
 
21 22
 const (
... ...
@@ -111,7 +112,7 @@ func TestJoinOptionParser(t *testing.T) {
111 111
 }
112 112
 
113 113
 func TestJson(t *testing.T) {
114
-	nc := networkCreate{Name: "mynet", NetworkType: bridgeNetType}
114
+	nc := networkCreate{NetworkType: bridgeNetType}
115 115
 	b, err := json.Marshal(nc)
116 116
 	if err != nil {
117 117
 		t.Fatal(err)
... ...
@@ -123,26 +124,10 @@ func TestJson(t *testing.T) {
123 123
 		t.Fatal(err)
124 124
 	}
125 125
 
126
-	if nc.Name != ncp.Name || nc.NetworkType != ncp.NetworkType {
126
+	if nc.NetworkType != ncp.NetworkType {
127 127
 		t.Fatalf("Incorrect networkCreate after json encoding/deconding: %v", ncp)
128 128
 	}
129 129
 
130
-	ec := endpointCreate{Name: "mioEp", NetworkID: "0xabcde"}
131
-	b, err = json.Marshal(ec)
132
-	if err != nil {
133
-		t.Fatal(err)
134
-	}
135
-
136
-	var ecp endpointCreate
137
-	err = json.Unmarshal(b, &ecp)
138
-	if err != nil {
139
-		t.Fatal(err)
140
-	}
141
-
142
-	if ec.Name != ecp.Name || ec.NetworkID != ecp.NetworkID {
143
-		t.Fatalf("Incorrect endpointCreate after json encoding/deconding: %v", ecp)
144
-	}
145
-
146 130
 	jl := endpointJoin{ContainerID: "abcdef456789"}
147 131
 	b, err = json.Marshal(jl)
148 132
 	if err != nil {
... ...
@@ -156,7 +141,7 @@ func TestJson(t *testing.T) {
156 156
 	}
157 157
 
158 158
 	if jl.ContainerID != jld.ContainerID {
159
-		t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", ecp)
159
+		t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", jld)
160 160
 	}
161 161
 }
162 162
 
... ...
@@ -177,68 +162,55 @@ func TestCreateDeleteNetwork(t *testing.T) {
177 177
 		t.Fatal(err)
178 178
 	}
179 179
 
180
-	goodVars := map[string]string{urlNwName: "myNet"}
181
-	_, errRsp := procCreateNetwork(c, goodVars, badBody)
180
+	vars := make(map[string]string)
181
+	_, errRsp := procCreateNetwork(c, nil, badBody)
182 182
 	if errRsp == &createdResponse {
183 183
 		t.Fatalf("Expected to fail but succeeded")
184 184
 	}
185
+	if errRsp.StatusCode != http.StatusBadRequest {
186
+		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp)
187
+	}
185 188
 
186
-	incompleteBody, err := json.Marshal(networkCreate{Name: "myNet"})
189
+	incompleteBody, err := json.Marshal(networkCreate{})
187 190
 	if err != nil {
188 191
 		t.Fatal(err)
189 192
 	}
190 193
 
191
-	_, errRsp = procCreateNetwork(c, goodVars, incompleteBody)
194
+	_, errRsp = procCreateNetwork(c, vars, incompleteBody)
192 195
 	if errRsp == &createdResponse {
193 196
 		t.Fatalf("Expected to fail but succeeded")
194 197
 	}
195 198
 	if errRsp.StatusCode != http.StatusBadRequest {
196
-		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
199
+		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp)
197 200
 	}
198 201
 
199 202
 	ops := make(map[string]interface{})
200 203
 	ops[netlabel.GenericData] = options.Generic{}
201
-	nc := networkCreate{Name: "myNet", NetworkType: bridgeNetType, Options: ops}
204
+	nc := networkCreate{Name: "network_1", NetworkType: bridgeNetType, Options: ops}
202 205
 	goodBody, err := json.Marshal(nc)
203 206
 	if err != nil {
204 207
 		t.Fatal(err)
205 208
 	}
206 209
 
207
-	badVars := map[string]string{urlNwName: ""}
208
-	_, errRsp = procCreateNetwork(c, badVars, goodBody)
209
-	if errRsp == &createdResponse {
210
-		t.Fatalf("Expected to fail but succeeded")
211
-	}
212
-	if errRsp.StatusCode != http.StatusBadRequest {
213
-		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
214
-	}
215
-
216
-	badVars[urlNwName] = "badNetworkName"
217
-	_, errRsp = procCreateNetwork(c, badVars, goodBody)
218
-	if errRsp == &createdResponse {
219
-		t.Fatalf("Expected to fail but succeeded")
220
-	}
221
-	if errRsp.StatusCode != http.StatusBadRequest {
222
-		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
223
-	}
224
-
225
-	_, errRsp = procCreateNetwork(c, goodVars, goodBody)
210
+	_, errRsp = procCreateNetwork(c, vars, goodBody)
226 211
 	if errRsp != &createdResponse {
227 212
 		t.Fatalf("Unexepected failure: %v", errRsp)
228 213
 	}
229 214
 
230
-	_, errRsp = procDeleteNetwork(c, badVars, nil)
215
+	vars[urlNwName] = ""
216
+	_, errRsp = procDeleteNetwork(c, vars, nil)
231 217
 	if errRsp == &successResponse {
232 218
 		t.Fatalf("Expected to fail but succeeded")
233 219
 	}
234 220
 
235
-	badVars[urlNwName] = ""
236
-	_, errRsp = procDeleteNetwork(c, badVars, nil)
221
+	vars[urlNwName] = "abc"
222
+	_, errRsp = procDeleteNetwork(c, vars, nil)
237 223
 	if errRsp == &successResponse {
238 224
 		t.Fatalf("Expected to fail but succeeded")
239 225
 	}
240 226
 
241
-	_, errRsp = procDeleteNetwork(c, goodVars, nil)
227
+	vars[urlNwName] = "network_1"
228
+	_, errRsp = procDeleteNetwork(c, vars, nil)
242 229
 	if errRsp != &successResponse {
243 230
 		t.Fatalf("Unexepected failure: %v", errRsp)
244 231
 	}
... ...
@@ -262,7 +234,7 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
262 262
 		t.Fatal(err)
263 263
 	}
264 264
 
265
-	vars := map[string]string{urlNwName: "sh"}
265
+	vars := make(map[string]string)
266 266
 	inid, errRsp := procCreateNetwork(c, vars, body)
267 267
 	if errRsp != &createdResponse {
268 268
 		t.Fatalf("Unexepected failure: %v", errRsp)
... ...
@@ -273,29 +245,29 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
273 273
 	}
274 274
 
275 275
 	ec1 := endpointCreate{
276
-		Name:      "ep1",
277
-		NetworkID: string(nid),
278
-		ExposedPorts: []netutils.TransportPort{
279
-			netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)},
280
-			netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)},
281
-			netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)},
276
+		Name: "ep1",
277
+		ExposedPorts: []types.TransportPort{
278
+			types.TransportPort{Proto: types.TCP, Port: uint16(5000)},
279
+			types.TransportPort{Proto: types.UDP, Port: uint16(400)},
280
+			types.TransportPort{Proto: types.TCP, Port: uint16(600)},
282 281
 		},
283
-		PortMapping: []netutils.PortBinding{
284
-			netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)},
285
-			netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)},
286
-			netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)},
282
+		PortMapping: []types.PortBinding{
283
+			types.PortBinding{Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
284
+			types.PortBinding{Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
285
+			types.PortBinding{Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
287 286
 		},
288 287
 	}
289 288
 	b1, err := json.Marshal(ec1)
290 289
 	if err != nil {
291 290
 		t.Fatal(err)
292 291
 	}
293
-	ec2 := endpointCreate{Name: "ep2", NetworkID: nid}
292
+	ec2 := endpointCreate{Name: "ep2"}
294 293
 	b2, err := json.Marshal(ec2)
295 294
 	if err != nil {
296 295
 		t.Fatal(err)
297 296
 	}
298 297
 
298
+	vars[urlNwName] = "sh"
299 299
 	vars[urlEpName] = "ep1"
300 300
 	ieid1, errRsp := procCreateEndpoint(c, vars, b1)
301 301
 	if errRsp != &createdResponse {
... ...
@@ -471,6 +443,7 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
471 471
 	if errRsp != &successResponse {
472 472
 		t.Fatalf("Unexepected failure: %v", errRsp)
473 473
 	}
474
+	delete(vars, urlEpName)
474 475
 	iepList, errRsp = procGetEndpoints(c, vars, nil)
475 476
 	if errRsp != &successResponse {
476 477
 		t.Fatalf("Unexepected failure: %v", errRsp)
... ...
@@ -509,6 +482,43 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
509 509
 	}
510 510
 }
511 511
 
512
+func TestDetectGetNetworksInvalidQueryComposition(t *testing.T) {
513
+	c, err := libnetwork.New()
514
+	if err != nil {
515
+		t.Fatal(err)
516
+	}
517
+
518
+	vars := map[string]string{urlNwName: "x", urlNwPID: "y"}
519
+	_, errRsp := procGetNetworks(c, vars, nil)
520
+	if errRsp.StatusCode != http.StatusBadRequest {
521
+		t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp)
522
+	}
523
+}
524
+
525
+func TestDetectGetEndpointsInvalidQueryComposition(t *testing.T) {
526
+	defer netutils.SetupTestNetNS(t)()
527
+
528
+	c, err := libnetwork.New()
529
+	if err != nil {
530
+		t.Fatal(err)
531
+	}
532
+	err = c.ConfigureNetworkDriver(bridgeNetType, nil)
533
+	if err != nil {
534
+		t.Fatal(err)
535
+	}
536
+
537
+	_, err = c.NewNetwork(bridgeNetType, "network", nil)
538
+	if err != nil {
539
+		t.Fatal(err)
540
+	}
541
+
542
+	vars := map[string]string{urlNwName: "network", urlEpName: "x", urlEpPID: "y"}
543
+	_, errRsp := procGetEndpoints(c, vars, nil)
544
+	if errRsp.StatusCode != http.StatusBadRequest {
545
+		t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp)
546
+	}
547
+}
548
+
512 549
 func TestFindNetworkUtil(t *testing.T) {
513 550
 	defer netutils.SetupTestNetNS(t)()
514 551
 
... ...
@@ -603,85 +613,46 @@ func TestCreateDeleteEndpoints(t *testing.T) {
603 603
 		t.Fatal(err)
604 604
 	}
605 605
 
606
-	vars := map[string]string{urlNwName: "firstNet"}
606
+	vars := make(map[string]string)
607 607
 	i, errRsp := procCreateNetwork(c, vars, body)
608 608
 	if errRsp != &createdResponse {
609 609
 		t.Fatalf("Unexepected failure: %v", errRsp)
610 610
 	}
611 611
 	nid := i2s(i)
612 612
 
613
-	vbad, err := json.Marshal("bad endppint create data")
613
+	vbad, err := json.Marshal("bad endppoint create data")
614 614
 	if err != nil {
615 615
 		t.Fatal(err)
616 616
 	}
617 617
 
618
-	vars[urlEpName] = "ep1"
618
+	vars[urlNwName] = "firstNet"
619 619
 	_, errRsp = procCreateEndpoint(c, vars, vbad)
620 620
 	if errRsp == &createdResponse {
621 621
 		t.Fatalf("Expected to fail but succeeded")
622 622
 	}
623 623
 
624
-	bad, err := json.Marshal(endpointCreate{Name: "ep1", NetworkID: "123456"})
625
-	if err != nil {
626
-		t.Fatal(err)
627
-	}
628
-	_, errRsp = procCreateEndpoint(c, vars, bad)
629
-	if errRsp == &createdResponse {
630
-		t.Fatalf("Expected to fail but succeeded")
631
-	}
632
-
633
-	soso, err := json.Marshal(endpointCreate{Name: "ep11", NetworkID: nid})
634
-	if err != nil {
635
-		t.Fatal(err)
636
-	}
637
-	_, errRsp = procCreateEndpoint(c, vars, soso)
638
-	if errRsp != &mismatchResponse {
639
-		t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp)
640
-	}
641
-
642
-	bla, err := json.Marshal(endpointCreate{Name: "", NetworkID: nid})
643
-	if err != nil {
644
-		t.Fatal(err)
645
-	}
646
-	vars[urlNwName] = "firstNet"
647
-	vars[urlEpName] = ""
648
-	_, errRsp = procCreateEndpoint(c, vars, bla)
649
-	if errRsp == &createdResponse {
650
-		t.Fatalf("Expected to fail but succeeded: %v", errRsp)
651
-	}
652
-
653
-	b, err := json.Marshal(endpointCreate{Name: "firstEp", NetworkID: nid})
624
+	b, err := json.Marshal(endpointCreate{Name: ""})
654 625
 	if err != nil {
655 626
 		t.Fatal(err)
656 627
 	}
657 628
 
658 629
 	vars[urlNwName] = "secondNet"
659
-	vars[urlEpName] = "firstEp"
660 630
 	_, errRsp = procCreateEndpoint(c, vars, b)
661 631
 	if errRsp == &createdResponse {
662 632
 		t.Fatalf("Expected to fail but succeeded")
663 633
 	}
664 634
 
665 635
 	vars[urlNwName] = "firstNet"
666
-	vars[urlEpName] = "ep1"
667
-	_, errRsp = procCreateEndpoint(c, vars, b)
668
-	if errRsp != &mismatchResponse {
669
-		t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp)
670
-	}
671
-
672
-	vars = make(map[string]string)
673 636
 	_, errRsp = procCreateEndpoint(c, vars, b)
674 637
 	if errRsp == &successResponse {
675 638
 		t.Fatalf("Expected failure but succeeded: %v", errRsp)
676 639
 	}
677 640
 
678
-	vars[urlNwName] = "firstNet"
679
-	_, errRsp = procCreateEndpoint(c, vars, b)
680
-	if errRsp == &successResponse {
681
-		t.Fatalf("Expected failure but succeeded: %v", errRsp)
641
+	b, err = json.Marshal(endpointCreate{Name: "firstEp"})
642
+	if err != nil {
643
+		t.Fatal(err)
682 644
 	}
683 645
 
684
-	vars[urlEpName] = "firstEp"
685 646
 	i, errRsp = procCreateEndpoint(c, vars, b)
686 647
 	if errRsp != &createdResponse {
687 648
 		t.Fatalf("Unexepected failure: %v", errRsp)
... ...
@@ -713,8 +684,8 @@ func TestCreateDeleteEndpoints(t *testing.T) {
713 713
 		t.Fatalf("Unexepected failure: %v", errRsp)
714 714
 	}
715 715
 
716
-	if ep0 != ep1 || ep0 != ep2 || ep0 != ep3 {
717
-		t.Fatalf("Diffenrent queries returned different endpoints")
716
+	if ep0.ID() != ep1.ID() || ep0.ID() != ep2.ID() || ep0.ID() != ep3.ID() {
717
+		t.Fatalf("Diffenrent queries returned different endpoints: \nep0: %v\nep1: %v\nep2: %v\nep3: %v", ep0, ep1, ep2, ep3)
718 718
 	}
719 719
 
720 720
 	vars = make(map[string]string)
... ...
@@ -766,18 +737,17 @@ func TestJoinLeave(t *testing.T) {
766 766
 	if err != nil {
767 767
 		t.Fatal(err)
768 768
 	}
769
-	vars := map[string]string{urlNwName: "network"}
770
-	i, errRsp := procCreateNetwork(c, vars, nb)
769
+	vars := make(map[string]string)
770
+	_, errRsp := procCreateNetwork(c, vars, nb)
771 771
 	if errRsp != &createdResponse {
772 772
 		t.Fatalf("Unexepected failure: %v", errRsp)
773 773
 	}
774
-	nid := i2s(i)
775 774
 
776
-	vars[urlEpName] = "epoint"
777
-	eb, err := json.Marshal(endpointCreate{Name: "epoint", NetworkID: nid})
775
+	eb, err := json.Marshal(endpointCreate{Name: "endpoint"})
778 776
 	if err != nil {
779 777
 		t.Fatal(err)
780 778
 	}
779
+	vars[urlNwName] = "network"
781 780
 	_, errRsp = procCreateEndpoint(c, vars, eb)
782 781
 	if errRsp != &createdResponse {
783 782
 		t.Fatalf("Unexepected failure: %v", errRsp)
... ...
@@ -792,6 +762,7 @@ func TestJoinLeave(t *testing.T) {
792 792
 		t.Fatalf("Expected failure, got: %v", errRsp)
793 793
 	}
794 794
 
795
+	vars[urlEpName] = "endpoint"
795 796
 	bad, err := json.Marshal(endpointJoin{})
796 797
 	if err != nil {
797 798
 		t.Fatal(err)
... ...
@@ -811,44 +782,30 @@ func TestJoinLeave(t *testing.T) {
811 811
 	vars = make(map[string]string)
812 812
 	vars[urlNwName] = ""
813 813
 	vars[urlEpName] = ""
814
-	vars[urlCnID] = cid
815
-	_, errRsp = procJoinEndpoint(c, vars, jlb)
816
-	if errRsp == &successResponse {
817
-		t.Fatalf("Expected failure, got: %v", errRsp)
818
-	}
819
-
820
-	vars[urlNwName] = "network1"
821
-	vars[urlEpName] = ""
822 814
 	_, errRsp = procJoinEndpoint(c, vars, jlb)
823 815
 	if errRsp == &successResponse {
824 816
 		t.Fatalf("Expected failure, got: %v", errRsp)
825 817
 	}
826 818
 
827 819
 	vars[urlNwName] = "network"
828
-	vars[urlEpName] = "endpoint"
820
+	vars[urlEpName] = ""
829 821
 	_, errRsp = procJoinEndpoint(c, vars, jlb)
830 822
 	if errRsp == &successResponse {
831 823
 		t.Fatalf("Expected failure, got: %v", errRsp)
832 824
 	}
833 825
 
834 826
 	vars[urlEpName] = "epoint"
835
-	delete(vars, urlCnID)
836 827
 	_, errRsp = procJoinEndpoint(c, vars, jlb)
837 828
 	if errRsp == &successResponse {
838 829
 		t.Fatalf("Expected failure, got: %v", errRsp)
839 830
 	}
840 831
 
841
-	vars[urlCnID] = "who?"
842
-	_, errRsp = procJoinEndpoint(c, vars, jlb)
843
-	if errRsp == &successResponse {
844
-		t.Fatalf("Expected failure, got: %v", errRsp)
845
-	}
846
-
847
-	vars[urlCnID] = cid
832
+	vars[urlEpName] = "endpoint"
848 833
 	cdi, errRsp := procJoinEndpoint(c, vars, jlb)
849 834
 	if errRsp != &successResponse {
850
-		t.Fatalf("Unexpected failure, got: %v", errRsp)
835
+		t.Fatalf("Expected failure, got: %v", errRsp)
851 836
 	}
837
+
852 838
 	cd := i2c(cdi)
853 839
 	if cd.SandboxKey == "" {
854 840
 		t.Fatalf("Empty sandbox key")
... ...
@@ -897,6 +854,7 @@ func TestJoinLeave(t *testing.T) {
897 897
 	}
898 898
 
899 899
 	delete(vars, urlCnID)
900
+	vars[urlEpName] = "endpoint"
900 901
 	_, errRsp = procLeaveEndpoint(c, vars, jlb)
901 902
 	if errRsp == &successResponse {
902 903
 		t.Fatalf("Expected failure, got: %v", errRsp)
... ...
@@ -1178,7 +1136,7 @@ func TestHttpHandlerUninit(t *testing.T) {
1178 1178
 	}
1179 1179
 
1180 1180
 	rsp := newWriter()
1181
-	req, err := http.NewRequest("GET", "/networks", nil)
1181
+	req, err := http.NewRequest("GET", "/v1.19/networks", nil)
1182 1182
 	if err != nil {
1183 1183
 		t.Fatal(err)
1184 1184
 	}
... ...
@@ -1193,15 +1151,24 @@ func TestHttpHandlerUninit(t *testing.T) {
1193 1193
 
1194 1194
 	handleRequest(rsp, req)
1195 1195
 	if rsp.statusCode != http.StatusOK {
1196
-		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
1196
+		t.Fatalf("Expected (%d). Got: (%d): %s", http.StatusOK, rsp.statusCode, rsp.body)
1197 1197
 	}
1198 1198
 
1199
-	n, err := c.NewNetwork(bridgeNetType, "onenet", nil)
1199
+	var list []*networkResource
1200
+	err = json.Unmarshal(rsp.body, &list)
1201
+	if err != nil {
1202
+		t.Fatal(err)
1203
+	}
1204
+	if len(list) != 0 {
1205
+		t.Fatalf("Expected empty list. Got %v", list)
1206
+	}
1207
+
1208
+	n, err := c.NewNetwork(bridgeNetType, "didietro", nil)
1200 1209
 	if err != nil {
1201 1210
 		t.Fatal(err)
1202 1211
 	}
1203 1212
 	nwr := buildNetworkResource(n)
1204
-	expected, err := json.Marshal([]networkResource{*nwr})
1213
+	expected, err := json.Marshal([]*networkResource{nwr})
1205 1214
 	if err != nil {
1206 1215
 		t.Fatal(err)
1207 1216
 	}
... ...
@@ -1229,7 +1196,7 @@ func TestHttpHandlerBadBody(t *testing.T) {
1229 1229
 	}
1230 1230
 	handleRequest := NewHTTPHandler(c)
1231 1231
 
1232
-	req, err := http.NewRequest("POST", "/networks/name/zero-network", &localReader{beBad: true})
1232
+	req, err := http.NewRequest("POST", "/v1.19/networks", &localReader{beBad: true})
1233 1233
 	if err != nil {
1234 1234
 		t.Fatal(err)
1235 1235
 	}
... ...
@@ -1240,7 +1207,7 @@ func TestHttpHandlerBadBody(t *testing.T) {
1240 1240
 
1241 1241
 	body := []byte{}
1242 1242
 	lr := newLocalReader(body)
1243
-	req, err = http.NewRequest("POST", "/networks/name/zero-network", lr)
1243
+	req, err = http.NewRequest("POST", "/v1.19/networks", lr)
1244 1244
 	if err != nil {
1245 1245
 		t.Fatal(err)
1246 1246
 	}
... ...
@@ -1250,7 +1217,7 @@ func TestHttpHandlerBadBody(t *testing.T) {
1250 1250
 	}
1251 1251
 }
1252 1252
 
1253
-func TestHttpHandlerGood(t *testing.T) {
1253
+func TestEndToEnd(t *testing.T) {
1254 1254
 	defer netutils.SetupTestNetNS(t)()
1255 1255
 
1256 1256
 	rsp := newWriter()
... ...
@@ -1261,14 +1228,14 @@ func TestHttpHandlerGood(t *testing.T) {
1261 1261
 	}
1262 1262
 	handleRequest := NewHTTPHandler(c)
1263 1263
 
1264
-	nc := networkCreate{Name: "zero-network", NetworkType: bridgeNetType}
1264
+	// Create network
1265
+	nc := networkCreate{Name: "network-fiftyfive", NetworkType: bridgeNetType}
1265 1266
 	body, err := json.Marshal(nc)
1266 1267
 	if err != nil {
1267 1268
 		t.Fatal(err)
1268 1269
 	}
1269
-
1270 1270
 	lr := newLocalReader(body)
1271
-	req, err := http.NewRequest("POST", "/networks/name/zero-network", lr)
1271
+	req, err := http.NewRequest("POST", "/v1.19/networks", lr)
1272 1272
 	if err != nil {
1273 1273
 		t.Fatal(err)
1274 1274
 	}
... ...
@@ -1280,13 +1247,102 @@ func TestHttpHandlerGood(t *testing.T) {
1280 1280
 		t.Fatalf("Empty response body")
1281 1281
 	}
1282 1282
 
1283
-	var id string
1284
-	err = json.Unmarshal(rsp.body, &id)
1283
+	var nid string
1284
+	err = json.Unmarshal(rsp.body, &nid)
1285
+	if err != nil {
1286
+		t.Fatal(err)
1287
+	}
1288
+
1289
+	// Query networks collection
1290
+	req, err = http.NewRequest("GET", "/v1.19/networks", nil)
1291
+	if err != nil {
1292
+		t.Fatal(err)
1293
+	}
1294
+	handleRequest(rsp, req)
1295
+	if rsp.statusCode != http.StatusOK {
1296
+		t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
1297
+	}
1298
+
1299
+	b0 := make([]byte, len(rsp.body))
1300
+	copy(b0, rsp.body)
1301
+
1302
+	req, err = http.NewRequest("GET", "/v1.19/networks?name=network-fiftyfive", nil)
1303
+	if err != nil {
1304
+		t.Fatal(err)
1305
+	}
1306
+	handleRequest(rsp, req)
1307
+	if rsp.statusCode != http.StatusOK {
1308
+		t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
1309
+	}
1310
+
1311
+	if !bytes.Equal(b0, rsp.body) {
1312
+		t.Fatalf("Expected same body from GET /networks and GET /networks?name=<nw> when only network <nw> exist.")
1313
+	}
1314
+
1315
+	// Query network by name
1316
+	req, err = http.NewRequest("GET", "/v1.19/networks?name=culo", nil)
1317
+	if err != nil {
1318
+		t.Fatal(err)
1319
+	}
1320
+	handleRequest(rsp, req)
1321
+	if rsp.statusCode != http.StatusOK {
1322
+		t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
1323
+	}
1324
+
1325
+	var list []*networkResource
1326
+	err = json.Unmarshal(rsp.body, &list)
1327
+	if err != nil {
1328
+		t.Fatal(err)
1329
+	}
1330
+	if len(list) != 0 {
1331
+		t.Fatalf("Expected empty list. Got %v", list)
1332
+	}
1333
+
1334
+	req, err = http.NewRequest("GET", "/v1.19/networks?name=network-fiftyfive", nil)
1335
+	if err != nil {
1336
+		t.Fatal(err)
1337
+	}
1338
+	handleRequest(rsp, req)
1339
+	if rsp.statusCode != http.StatusOK {
1340
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
1341
+	}
1342
+
1343
+	err = json.Unmarshal(rsp.body, &list)
1285 1344
 	if err != nil {
1286 1345
 		t.Fatal(err)
1287 1346
 	}
1347
+	if len(list) == 0 {
1348
+		t.Fatalf("Expected non empty list")
1349
+	}
1350
+	if list[0].Name != "network-fiftyfive" || nid != list[0].ID {
1351
+		t.Fatalf("Incongruent resource found: %v", list[0])
1352
+	}
1288 1353
 
1289
-	req, err = http.NewRequest("GET", "/networks/id/"+id, nil)
1354
+	// Query network by partial id
1355
+	chars := []byte(nid)
1356
+	partial := string(chars[0 : len(chars)/2])
1357
+	req, err = http.NewRequest("GET", "/v1.19/networks?partial-id="+partial, nil)
1358
+	if err != nil {
1359
+		t.Fatal(err)
1360
+	}
1361
+	handleRequest(rsp, req)
1362
+	if rsp.statusCode != http.StatusOK {
1363
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
1364
+	}
1365
+
1366
+	err = json.Unmarshal(rsp.body, &list)
1367
+	if err != nil {
1368
+		t.Fatal(err)
1369
+	}
1370
+	if len(list) == 0 {
1371
+		t.Fatalf("Expected non empty list")
1372
+	}
1373
+	if list[0].Name != "network-fiftyfive" || nid != list[0].ID {
1374
+		t.Fatalf("Incongruent resource found: %v", list[0])
1375
+	}
1376
+
1377
+	// Get network by id
1378
+	req, err = http.NewRequest("GET", "/v1.19/networks/"+nid, nil)
1290 1379
 	if err != nil {
1291 1380
 		t.Fatal(err)
1292 1381
 	}
... ...
@@ -1300,7 +1356,211 @@ func TestHttpHandlerGood(t *testing.T) {
1300 1300
 	if err != nil {
1301 1301
 		t.Fatal(err)
1302 1302
 	}
1303
-	if nwr.Name != "zero-network" || id != nwr.ID {
1304
-		t.Fatalf("Incongruent resource found")
1303
+	if nwr.Name != "network-fiftyfive" || nid != nwr.ID {
1304
+		t.Fatalf("Incongruent resource found: %v", nwr)
1305
+	}
1306
+
1307
+	// Create endpoint
1308
+	eb, err := json.Marshal(endpointCreate{Name: "ep-TwentyTwo"})
1309
+	if err != nil {
1310
+		t.Fatal(err)
1311
+	}
1312
+
1313
+	lr = newLocalReader(eb)
1314
+	req, err = http.NewRequest("POST", "/v1.19/networks/"+nid+"/endpoints", lr)
1315
+	if err != nil {
1316
+		t.Fatal(err)
1317
+	}
1318
+	handleRequest(rsp, req)
1319
+	if rsp.statusCode != http.StatusCreated {
1320
+		t.Fatalf("Unexpectded status code. Expected (%d). Got (%d): %s.", http.StatusCreated, rsp.statusCode, string(rsp.body))
1321
+	}
1322
+	if len(rsp.body) == 0 {
1323
+		t.Fatalf("Empty response body")
1324
+	}
1325
+
1326
+	var eid string
1327
+	err = json.Unmarshal(rsp.body, &eid)
1328
+	if err != nil {
1329
+		t.Fatal(err)
1330
+	}
1331
+
1332
+	// Query endpoint(s)
1333
+	req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints", nil)
1334
+	if err != nil {
1335
+		t.Fatal(err)
1336
+	}
1337
+	handleRequest(rsp, req)
1338
+	if rsp.statusCode != http.StatusOK {
1339
+		t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
1340
+	}
1341
+
1342
+	req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints?name=bla", nil)
1343
+	if err != nil {
1344
+		t.Fatal(err)
1345
+	}
1346
+	handleRequest(rsp, req)
1347
+	if rsp.statusCode != http.StatusOK {
1348
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
1349
+	}
1350
+	var epList []*endpointResource
1351
+	err = json.Unmarshal(rsp.body, &epList)
1352
+	if err != nil {
1353
+		t.Fatal(err)
1354
+	}
1355
+	if len(epList) != 0 {
1356
+		t.Fatalf("Expected empty list. Got %v", epList)
1357
+	}
1358
+
1359
+	// Query endpoint by name
1360
+	req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints?name=ep-TwentyTwo", nil)
1361
+	if err != nil {
1362
+		t.Fatal(err)
1363
+	}
1364
+	handleRequest(rsp, req)
1365
+	if rsp.statusCode != http.StatusOK {
1366
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
1367
+	}
1368
+
1369
+	err = json.Unmarshal(rsp.body, &epList)
1370
+	if err != nil {
1371
+		t.Fatal(err)
1372
+	}
1373
+	if len(epList) == 0 {
1374
+		t.Fatalf("Empty response body")
1375
+	}
1376
+	if epList[0].Name != "ep-TwentyTwo" || eid != epList[0].ID {
1377
+		t.Fatalf("Incongruent resource found: %v", epList[0])
1378
+	}
1379
+
1380
+	// Query endpoint by partial id
1381
+	chars = []byte(eid)
1382
+	partial = string(chars[0 : len(chars)/2])
1383
+	req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints?partial-id="+partial, nil)
1384
+	if err != nil {
1385
+		t.Fatal(err)
1386
+	}
1387
+	handleRequest(rsp, req)
1388
+	if rsp.statusCode != http.StatusOK {
1389
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
1390
+	}
1391
+
1392
+	err = json.Unmarshal(rsp.body, &epList)
1393
+	if err != nil {
1394
+		t.Fatal(err)
1395
+	}
1396
+	if len(epList) == 0 {
1397
+		t.Fatalf("Empty response body")
1398
+	}
1399
+	if epList[0].Name != "ep-TwentyTwo" || eid != epList[0].ID {
1400
+		t.Fatalf("Incongruent resource found: %v", epList[0])
1401
+	}
1402
+
1403
+	// Get endpoint by id
1404
+	req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints/"+eid, nil)
1405
+	if err != nil {
1406
+		t.Fatal(err)
1407
+	}
1408
+	handleRequest(rsp, req)
1409
+	if rsp.statusCode != http.StatusOK {
1410
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
1411
+	}
1412
+
1413
+	var epr endpointResource
1414
+	err = json.Unmarshal(rsp.body, &epr)
1415
+	if err != nil {
1416
+		t.Fatal(err)
1417
+	}
1418
+	if epr.Name != "ep-TwentyTwo" || epr.ID != eid {
1419
+		t.Fatalf("Incongruent resource found: %v", epr)
1420
+	}
1421
+}
1422
+
1423
+type bre struct{}
1424
+
1425
+func (b *bre) Error() string {
1426
+	return "I am a bad request error"
1427
+}
1428
+func (b *bre) BadRequest() {}
1429
+
1430
+type nfe struct{}
1431
+
1432
+func (n *nfe) Error() string {
1433
+	return "I am a not found error"
1434
+}
1435
+func (n *nfe) NotFound() {}
1436
+
1437
+type forb struct{}
1438
+
1439
+func (f *forb) Error() string {
1440
+	return "I am a bad request error"
1441
+}
1442
+func (f *forb) Forbidden() {}
1443
+
1444
+type notimpl struct{}
1445
+
1446
+func (nip *notimpl) Error() string {
1447
+	return "I am a not implemented error"
1448
+}
1449
+func (nip *notimpl) NotImplemented() {}
1450
+
1451
+type inter struct{}
1452
+
1453
+func (it *inter) Error() string {
1454
+	return "I am a internal error"
1455
+}
1456
+func (it *inter) Internal() {}
1457
+
1458
+type tout struct{}
1459
+
1460
+func (to *tout) Error() string {
1461
+	return "I am a timeout error"
1462
+}
1463
+func (to *tout) Timeout() {}
1464
+
1465
+type noserv struct{}
1466
+
1467
+func (nos *noserv) Error() string {
1468
+	return "I am a no service error"
1469
+}
1470
+func (nos *noserv) NoService() {}
1471
+
1472
+type notclassified struct{}
1473
+
1474
+func (noc *notclassified) Error() string {
1475
+	return "I am a non classified error"
1476
+}
1477
+
1478
+func TestErrorConversion(t *testing.T) {
1479
+	if convertNetworkError(new(bre)).StatusCode != http.StatusBadRequest {
1480
+		t.Fatalf("Failed to recognize BadRequest error")
1481
+	}
1482
+
1483
+	if convertNetworkError(new(nfe)).StatusCode != http.StatusNotFound {
1484
+		t.Fatalf("Failed to recognize NotFound error")
1485
+	}
1486
+
1487
+	if convertNetworkError(new(forb)).StatusCode != http.StatusForbidden {
1488
+		t.Fatalf("Failed to recognize Forbidden error")
1489
+	}
1490
+
1491
+	if convertNetworkError(new(notimpl)).StatusCode != http.StatusNotImplemented {
1492
+		t.Fatalf("Failed to recognize NotImplemented error")
1493
+	}
1494
+
1495
+	if convertNetworkError(new(inter)).StatusCode != http.StatusInternalServerError {
1496
+		t.Fatalf("Failed to recognize Internal error")
1497
+	}
1498
+
1499
+	if convertNetworkError(new(tout)).StatusCode != http.StatusRequestTimeout {
1500
+		t.Fatalf("Failed to recognize Timeout error")
1501
+	}
1502
+
1503
+	if convertNetworkError(new(noserv)).StatusCode != http.StatusServiceUnavailable {
1504
+		t.Fatalf("Failed to recognize No Service error")
1505
+	}
1506
+
1507
+	if convertNetworkError(new(notclassified)).StatusCode != http.StatusInternalServerError {
1508
+		t.Fatalf("Failed to recognize not classified error as Internal error")
1305 1509
 	}
1306 1510
 }
... ...
@@ -1,6 +1,6 @@
1 1
 package api
2 2
 
3
-import "github.com/docker/libnetwork/netutils"
3
+import "github.com/docker/libnetwork/types"
4 4
 
5 5
 /***********
6 6
  Resources
... ...
@@ -35,9 +35,8 @@ type networkCreate struct {
35 35
 // endpointCreate represents the body of the "create endpoint" http request message
36 36
 type endpointCreate struct {
37 37
 	Name         string
38
-	NetworkID    string
39
-	ExposedPorts []netutils.TransportPort
40
-	PortMapping  []netutils.PortBinding
38
+	ExposedPorts []types.TransportPort
39
+	PortMapping  []types.PortBinding
41 40
 }
42 41
 
43 42
 // endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages
... ...
@@ -49,6 +49,12 @@ func (cli *NetworkCli) getMethod(args ...string) (func(string, ...string) error,
49 49
 // Cmd is borrowed from Docker UI and acts as the entry point for network UI commands.
50 50
 // network UI commands are designed to be invoked from multiple parent chains
51 51
 func (cli *NetworkCli) Cmd(chain string, args ...string) error {
52
+	if len(args) > 2 {
53
+		method, exists := cli.getMethod(args[:3]...)
54
+		if exists {
55
+			return method(chain+" "+args[0]+" "+args[1], args[3:]...)
56
+		}
57
+	}
52 58
 	if len(args) > 1 {
53 59
 		method, exists := cli.getMethod(args[:2]...)
54 60
 		if exists {
55 61
new file mode 100644
... ...
@@ -0,0 +1,124 @@
0
+// +build experimental
1
+
2
+package client
3
+
4
+import (
5
+	"bytes"
6
+	"testing"
7
+
8
+	_ "github.com/docker/libnetwork/netutils"
9
+)
10
+
11
+func TestClientNetworkServiceInvalidCommand(t *testing.T) {
12
+	var out, errOut bytes.Buffer
13
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
14
+
15
+	err := cli.Cmd("docker", "network", "service", "invalid")
16
+	if err == nil {
17
+		t.Fatalf("Passing invalid commands must fail")
18
+	}
19
+}
20
+
21
+func TestClientNetworkServiceCreate(t *testing.T) {
22
+	var out, errOut bytes.Buffer
23
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
24
+
25
+	err := cli.Cmd("docker", "network", "service", "create", mockServiceName, mockNwName)
26
+	if err != nil {
27
+		t.Fatal(err.Error())
28
+	}
29
+}
30
+
31
+func TestClientNetworkServiceRm(t *testing.T) {
32
+	var out, errOut bytes.Buffer
33
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
34
+
35
+	err := cli.Cmd("docker", "network", "service", "rm", mockServiceName, mockNwName)
36
+	if err != nil {
37
+		t.Fatal(err.Error())
38
+	}
39
+}
40
+
41
+func TestClientNetworkServiceLs(t *testing.T) {
42
+	var out, errOut bytes.Buffer
43
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
44
+
45
+	err := cli.Cmd("docker", "network", "service", "ls", mockNwName)
46
+	if err != nil {
47
+		t.Fatal(err.Error())
48
+	}
49
+}
50
+
51
+func TestClientNetworkServiceInfo(t *testing.T) {
52
+	var out, errOut bytes.Buffer
53
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
54
+
55
+	err := cli.Cmd("docker", "network", "service", "info", mockServiceName, mockNwName)
56
+	if err != nil {
57
+		t.Fatal(err.Error())
58
+	}
59
+}
60
+
61
+func TestClientNetworkServiceInfoById(t *testing.T) {
62
+	var out, errOut bytes.Buffer
63
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
64
+
65
+	err := cli.Cmd("docker", "network", "service", "info", mockServiceID, mockNwID)
66
+	if err != nil {
67
+		t.Fatal(err.Error())
68
+	}
69
+}
70
+
71
+func TestClientNetworkServiceJoin(t *testing.T) {
72
+	var out, errOut bytes.Buffer
73
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
74
+
75
+	err := cli.Cmd("docker", "network", "service", "join", mockContainerID, mockServiceName, mockNwName)
76
+	if err != nil {
77
+		t.Fatal(err.Error())
78
+	}
79
+}
80
+
81
+func TestClientNetworkServiceLeave(t *testing.T) {
82
+	var out, errOut bytes.Buffer
83
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
84
+
85
+	err := cli.Cmd("docker", "network", "service", "leave", mockContainerID, mockServiceName, mockNwName)
86
+	if err != nil {
87
+		t.Fatal(err.Error())
88
+	}
89
+}
90
+
91
+// Docker Flag processing in flag.go uses os.Exit() frequently, even for --help
92
+// TODO : Handle the --help test-case in the IT when CLI is available
93
+/*
94
+func TestClientNetworkServiceCreateHelp(t *testing.T) {
95
+	var out, errOut bytes.Buffer
96
+	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
97
+		return nil, 0, nil
98
+	}
99
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
100
+
101
+	err := cli.Cmd("docker", "network", "create", "--help")
102
+	if err != nil {
103
+		t.Fatalf(err.Error())
104
+	}
105
+}
106
+*/
107
+
108
+// Docker flag processing in flag.go uses os.Exit(1) for incorrect parameter case.
109
+// TODO : Handle the missing argument case in the IT when CLI is available
110
+/*
111
+func TestClientNetworkServiceCreateMissingArgument(t *testing.T) {
112
+	var out, errOut bytes.Buffer
113
+	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
114
+		return nil, 0, nil
115
+	}
116
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
117
+
118
+	err := cli.Cmd("docker", "network", "create")
119
+	if err != nil {
120
+		t.Fatal(err.Error())
121
+	}
122
+}
123
+*/
... ...
@@ -2,7 +2,11 @@ package client
2 2
 
3 3
 import (
4 4
 	"bytes"
5
+	"encoding/json"
6
+	"fmt"
5 7
 	"io"
8
+	"os"
9
+	"strings"
6 10
 	"testing"
7 11
 
8 12
 	_ "github.com/docker/libnetwork/netutils"
... ...
@@ -15,12 +19,82 @@ type nopCloser struct {
15 15
 
16 16
 func (nopCloser) Close() error { return nil }
17 17
 
18
+func TestMain(m *testing.M) {
19
+	setupMockHTTPCallback()
20
+	os.Exit(m.Run())
21
+}
22
+
23
+var callbackFunc func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error)
24
+var mockNwJSON, mockNwListJSON, mockServiceJSON, mockServiceListJSON []byte
25
+var mockNwName = "test"
26
+var mockNwID = "2a3456789"
27
+var mockServiceName = "testSrv"
28
+var mockServiceID = "2a3456789"
29
+var mockContainerID = "2a3456789"
30
+
31
+func setupMockHTTPCallback() {
32
+	var list []networkResource
33
+	nw := networkResource{Name: mockNwName, ID: mockNwID}
34
+	mockNwJSON, _ = json.Marshal(nw)
35
+	list = append(list, nw)
36
+	mockNwListJSON, _ = json.Marshal(list)
37
+
38
+	var srvList []endpointResource
39
+	ep := endpointResource{Name: mockServiceName, ID: mockServiceID, Network: mockNwName}
40
+	mockServiceJSON, _ = json.Marshal(ep)
41
+	srvList = append(srvList, ep)
42
+	mockServiceListJSON, _ = json.Marshal(srvList)
43
+
44
+	callbackFunc = func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
45
+		var rsp string
46
+		switch method {
47
+		case "GET":
48
+			if strings.Contains(path, fmt.Sprintf("networks?name=%s", mockNwName)) {
49
+				rsp = string(mockNwListJSON)
50
+			} else if strings.Contains(path, "networks?name=") {
51
+				rsp = "[]"
52
+			} else if strings.Contains(path, fmt.Sprintf("networks?partial-id=%s", mockNwID)) {
53
+				rsp = string(mockNwListJSON)
54
+			} else if strings.Contains(path, "networks?partial-id=") {
55
+				rsp = "[]"
56
+			} else if strings.HasSuffix(path, "networks") {
57
+				rsp = string(mockNwListJSON)
58
+			} else if strings.HasSuffix(path, "networks/"+mockNwID) {
59
+				rsp = string(mockNwJSON)
60
+			} else if strings.Contains(path, fmt.Sprintf("endpoints?name=%s", mockServiceName)) {
61
+				rsp = string(mockServiceListJSON)
62
+			} else if strings.Contains(path, "endpoints?name=") {
63
+				rsp = "[]"
64
+			} else if strings.Contains(path, fmt.Sprintf("endpoints?partial-id=%s", mockServiceID)) {
65
+				rsp = string(mockServiceListJSON)
66
+			} else if strings.Contains(path, "endpoints?partial-id=") {
67
+				rsp = "[]"
68
+			} else if strings.HasSuffix(path, "endpoints") {
69
+				rsp = string(mockServiceListJSON)
70
+			} else if strings.HasSuffix(path, "endpoints/"+mockServiceID) {
71
+				rsp = string(mockServiceJSON)
72
+			}
73
+		case "POST":
74
+			var data []byte
75
+			if strings.HasSuffix(path, "networks") {
76
+				data, _ = json.Marshal(mockNwID)
77
+			} else if strings.HasSuffix(path, "endpoints") {
78
+				data, _ = json.Marshal(mockServiceID)
79
+			} else if strings.HasSuffix(path, "containers") {
80
+				data, _ = json.Marshal(mockContainerID)
81
+			}
82
+			rsp = string(data)
83
+		case "PUT":
84
+		case "DELETE":
85
+			rsp = ""
86
+		}
87
+		return nopCloser{bytes.NewBufferString(rsp)}, 200, nil
88
+	}
89
+}
90
+
18 91
 func TestClientDummyCommand(t *testing.T) {
19 92
 	var out, errOut bytes.Buffer
20
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
21
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
22
-	}
23
-	cli := NewNetworkCli(&out, &errOut, cFunc)
93
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
24 94
 
25 95
 	err := cli.Cmd("docker", "dummy")
26 96
 	if err == nil {
... ...
@@ -30,10 +104,7 @@ func TestClientDummyCommand(t *testing.T) {
30 30
 
31 31
 func TestClientNetworkInvalidCommand(t *testing.T) {
32 32
 	var out, errOut bytes.Buffer
33
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
34
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
35
-	}
36
-	cli := NewNetworkCli(&out, &errOut, cFunc)
33
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
37 34
 
38 35
 	err := cli.Cmd("docker", "network", "invalid")
39 36
 	if err == nil {
... ...
@@ -43,12 +114,9 @@ func TestClientNetworkInvalidCommand(t *testing.T) {
43 43
 
44 44
 func TestClientNetworkCreate(t *testing.T) {
45 45
 	var out, errOut bytes.Buffer
46
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
47
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
48
-	}
49
-	cli := NewNetworkCli(&out, &errOut, cFunc)
46
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
50 47
 
51
-	err := cli.Cmd("docker", "network", "create", "test")
48
+	err := cli.Cmd("docker", "network", "create", mockNwName)
52 49
 	if err != nil {
53 50
 		t.Fatal(err.Error())
54 51
 	}
... ...
@@ -56,17 +124,14 @@ func TestClientNetworkCreate(t *testing.T) {
56 56
 
57 57
 func TestClientNetworkCreateWithDriver(t *testing.T) {
58 58
 	var out, errOut bytes.Buffer
59
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
60
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
61
-	}
62
-	cli := NewNetworkCli(&out, &errOut, cFunc)
59
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
63 60
 
64
-	err := cli.Cmd("docker", "network", "create", "-f=dummy", "test")
61
+	err := cli.Cmd("docker", "network", "create", "-f=dummy", mockNwName)
65 62
 	if err == nil {
66 63
 		t.Fatalf("Passing incorrect flags to the create command must fail")
67 64
 	}
68 65
 
69
-	err = cli.Cmd("docker", "network", "create", "-d=dummy", "test")
66
+	err = cli.Cmd("docker", "network", "create", "-d=dummy", mockNwName)
70 67
 	if err != nil {
71 68
 		t.Fatalf(err.Error())
72 69
 	}
... ...
@@ -74,12 +139,9 @@ func TestClientNetworkCreateWithDriver(t *testing.T) {
74 74
 
75 75
 func TestClientNetworkRm(t *testing.T) {
76 76
 	var out, errOut bytes.Buffer
77
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
78
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
79
-	}
80
-	cli := NewNetworkCli(&out, &errOut, cFunc)
77
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
81 78
 
82
-	err := cli.Cmd("docker", "network", "rm", "test")
79
+	err := cli.Cmd("docker", "network", "rm", mockNwName)
83 80
 	if err != nil {
84 81
 		t.Fatal(err.Error())
85 82
 	}
... ...
@@ -87,47 +149,43 @@ func TestClientNetworkRm(t *testing.T) {
87 87
 
88 88
 func TestClientNetworkLs(t *testing.T) {
89 89
 	var out, errOut bytes.Buffer
90
-	networks := "db,web,test"
91
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
92
-		return nopCloser{bytes.NewBufferString(networks)}, 200, nil
93
-	}
94
-	cli := NewNetworkCli(&out, &errOut, cFunc)
90
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
95 91
 
96 92
 	err := cli.Cmd("docker", "network", "ls")
97 93
 	if err != nil {
98 94
 		t.Fatal(err.Error())
99 95
 	}
100
-	if out.String() != networks {
101
-		t.Fatal("Network List command fail to return the intended list")
102
-	}
103 96
 }
104 97
 
105 98
 func TestClientNetworkInfo(t *testing.T) {
106 99
 	var out, errOut bytes.Buffer
107
-	info := "dummy info"
108
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
109
-		return nopCloser{bytes.NewBufferString(info)}, 200, nil
110
-	}
111
-	cli := NewNetworkCli(&out, &errOut, cFunc)
100
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
112 101
 
113
-	err := cli.Cmd("docker", "network", "info", "test")
102
+	err := cli.Cmd("docker", "network", "info", mockNwName)
114 103
 	if err != nil {
115 104
 		t.Fatal(err.Error())
116 105
 	}
117
-	if out.String() != info {
118
-		t.Fatal("Network List command fail to return the intended list")
106
+}
107
+
108
+func TestClientNetworkInfoById(t *testing.T) {
109
+	var out, errOut bytes.Buffer
110
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
111
+
112
+	err := cli.Cmd("docker", "network", "info", mockNwID)
113
+	if err != nil {
114
+		t.Fatal(err.Error())
119 115
 	}
120 116
 }
121 117
 
122 118
 // Docker Flag processing in flag.go uses os.Exit() frequently, even for --help
123 119
 // TODO : Handle the --help test-case in the IT when CLI is available
124 120
 /*
125
-func TestClientNetworkCreateHelp(t *testing.T) {
121
+func TestClientNetworkServiceCreateHelp(t *testing.T) {
126 122
 	var out, errOut bytes.Buffer
127 123
 	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
128 124
 		return nil, 0, nil
129 125
 	}
130
-	cli := NewNetworkCli(&out, &errOut, cFunc)
126
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
131 127
 
132 128
 	err := cli.Cmd("docker", "network", "create", "--help")
133 129
 	if err != nil {
... ...
@@ -139,12 +197,12 @@ func TestClientNetworkCreateHelp(t *testing.T) {
139 139
 // Docker flag processing in flag.go uses os.Exit(1) for incorrect parameter case.
140 140
 // TODO : Handle the missing argument case in the IT when CLI is available
141 141
 /*
142
-func TestClientNetworkCreateMissingArgument(t *testing.T) {
142
+func TestClientNetworkServiceCreateMissingArgument(t *testing.T) {
143 143
 	var out, errOut bytes.Buffer
144 144
 	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
145 145
 		return nil, 0, nil
146 146
 	}
147
-	cli := NewNetworkCli(&out, &errOut, cFunc)
147
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
148 148
 
149 149
 	err := cli.Cmd("docker", "network", "create")
150 150
 	if err != nil {
... ...
@@ -2,10 +2,13 @@ package client
2 2
 
3 3
 import (
4 4
 	"bytes"
5
+	"encoding/json"
5 6
 	"fmt"
6
-	"io"
7
+	"net/http"
8
+	"text/tabwriter"
7 9
 
8 10
 	flag "github.com/docker/docker/pkg/mflag"
11
+	"github.com/docker/docker/pkg/stringid"
9 12
 )
10 13
 
11 14
 const (
... ...
@@ -33,7 +36,7 @@ func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error {
33 33
 	err := cmd.ParseFlags(args, true)
34 34
 	if err == nil {
35 35
 		cmd.Usage()
36
-		return fmt.Errorf("Invalid command : %v", args)
36
+		return fmt.Errorf("invalid command : %v", args)
37 37
 	}
38 38
 	return err
39 39
 }
... ...
@@ -53,31 +56,33 @@ func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
53 53
 
54 54
 	nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver}
55 55
 
56
-	obj, _, err := readBody(cli.call("POST", "/networks/name/"+cmd.Arg(0), nc, nil))
56
+	obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
57 57
 	if err != nil {
58
-		fmt.Fprintf(cli.err, "%s", err.Error())
59 58
 		return err
60 59
 	}
61
-	if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
60
+	var replyID string
61
+	err = json.Unmarshal(obj, &replyID)
62
+	if err != nil {
62 63
 		return err
63 64
 	}
65
+	fmt.Fprintf(cli.out, "%s\n", replyID)
64 66
 	return nil
65 67
 }
66 68
 
67 69
 // CmdNetworkRm handles Network Delete UI
68 70
 func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error {
69
-	cmd := cli.Subcmd(chain, "rm", "NETWORK-NAME", "Deletes a network", false)
71
+	cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false)
70 72
 	cmd.Require(flag.Min, 1)
71 73
 	err := cmd.ParseFlags(args, true)
72 74
 	if err != nil {
73 75
 		return err
74 76
 	}
75
-	obj, _, err := readBody(cli.call("DELETE", "/networks/name/"+cmd.Arg(0), nil, nil))
77
+	id, err := lookupNetworkID(cli, cmd.Arg(0))
76 78
 	if err != nil {
77
-		fmt.Fprintf(cli.err, "%s", err.Error())
78 79
 		return err
79 80
 	}
80
-	if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
81
+	_, _, err = readBody(cli.call("DELETE", "/networks/"+id, nil, nil))
82
+	if err != nil {
81 83
 		return err
82 84
 	}
83 85
 	return nil
... ...
@@ -86,45 +91,149 @@ func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error {
86 86
 // CmdNetworkLs handles Network List UI
87 87
 func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error {
88 88
 	cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false)
89
+	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
90
+	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
91
+	nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
92
+	last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
89 93
 	err := cmd.ParseFlags(args, true)
90 94
 	if err != nil {
91 95
 		return err
92 96
 	}
93 97
 	obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
94 98
 	if err != nil {
95
-		fmt.Fprintf(cli.err, "%s", err.Error())
96 99
 		return err
97 100
 	}
98
-	if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
101
+	if *last == -1 && *nLatest {
102
+		*last = 1
103
+	}
104
+
105
+	var networkResources []networkResource
106
+	err = json.Unmarshal(obj, &networkResources)
107
+	if err != nil {
99 108
 		return err
100 109
 	}
110
+
111
+	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
112
+
113
+	// unless quiet (-q) is specified, print field titles
114
+	if !*quiet {
115
+		fmt.Fprintln(wr, "NETWORK ID\tNAME\tTYPE")
116
+	}
117
+
118
+	for _, networkResource := range networkResources {
119
+		ID := networkResource.ID
120
+		netName := networkResource.Name
121
+		if !*noTrunc {
122
+			ID = stringid.TruncateID(ID)
123
+		}
124
+		if *quiet {
125
+			fmt.Fprintln(wr, ID)
126
+			continue
127
+		}
128
+		netType := networkResource.Type
129
+		fmt.Fprintf(wr, "%s\t%s\t%s\t",
130
+			ID,
131
+			netName,
132
+			netType)
133
+		fmt.Fprint(wr, "\n")
134
+	}
135
+	wr.Flush()
101 136
 	return nil
102 137
 }
103 138
 
104 139
 // CmdNetworkInfo handles Network Info UI
105 140
 func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error {
106
-	cmd := cli.Subcmd(chain, "info", "NETWORK-NAME", "Displays detailed information on a network", false)
141
+	cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false)
107 142
 	cmd.Require(flag.Min, 1)
108 143
 	err := cmd.ParseFlags(args, true)
109 144
 	if err != nil {
110 145
 		return err
111 146
 	}
112
-	obj, _, err := readBody(cli.call("GET", "/networks/name/"+cmd.Arg(0), nil, nil))
147
+
148
+	id, err := lookupNetworkID(cli, cmd.Arg(0))
149
+	if err != nil {
150
+		return err
151
+	}
152
+
153
+	obj, _, err := readBody(cli.call("GET", "/networks/"+id, nil, nil))
113 154
 	if err != nil {
114
-		fmt.Fprintf(cli.err, "%s", err.Error())
115 155
 		return err
116 156
 	}
117
-	if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
157
+	networkResource := &networkResource{}
158
+	if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
118 159
 		return err
119 160
 	}
161
+	fmt.Fprintf(cli.out, "Network Id: %s\n", networkResource.ID)
162
+	fmt.Fprintf(cli.out, "Name: %s\n", networkResource.Name)
163
+	fmt.Fprintf(cli.out, "Type: %s\n", networkResource.Type)
164
+	if networkResource.Endpoints != nil {
165
+		for _, endpointResource := range networkResource.Endpoints {
166
+			fmt.Fprintf(cli.out, "  Service Id: %s\n", endpointResource.ID)
167
+			fmt.Fprintf(cli.out, "\tName: %s\n", endpointResource.Name)
168
+		}
169
+	}
170
+
120 171
 	return nil
121 172
 }
122 173
 
174
+// Helper function to predict if a string is a name or id or partial-id
175
+// This provides a best-effort mechanism to identify a id with the help of GET Filter APIs
176
+// Being a UI, its most likely that name will be used by the user, which is used to lookup
177
+// the corresponding ID. If ID is not found, this function will assume that the passed string
178
+// is an ID by itself.
179
+
180
+func lookupNetworkID(cli *NetworkCli, nameID string) (string, error) {
181
+	obj, statusCode, err := readBody(cli.call("GET", "/networks?name="+nameID, nil, nil))
182
+	if err != nil {
183
+		return "", err
184
+	}
185
+
186
+	if statusCode != http.StatusOK {
187
+		return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
188
+	}
189
+
190
+	var list []*networkResource
191
+	err = json.Unmarshal(obj, &list)
192
+	if err != nil {
193
+		return "", err
194
+	}
195
+	if len(list) > 0 {
196
+		// name query filter will always return a single-element collection
197
+		return list[0].ID, nil
198
+	}
199
+
200
+	// Check for Partial-id
201
+	obj, statusCode, err = readBody(cli.call("GET", "/networks?partial-id="+nameID, nil, nil))
202
+	if err != nil {
203
+		return "", err
204
+	}
205
+
206
+	if statusCode != http.StatusOK {
207
+		return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
208
+	}
209
+
210
+	err = json.Unmarshal(obj, &list)
211
+	if err != nil {
212
+		return "", err
213
+	}
214
+	if len(list) == 0 {
215
+		return "", fmt.Errorf("resource not found %s", nameID)
216
+	}
217
+	if len(list) > 1 {
218
+		return "", fmt.Errorf("multiple Networks matching the partial identifier (%s). Please use full identifier", nameID)
219
+	}
220
+	return list[0].ID, nil
221
+}
222
+
123 223
 func networkUsage(chain string) string {
124 224
 	help := "Commands:\n"
125 225
 
126 226
 	for _, cmd := range networkCommands {
127
-		help += fmt.Sprintf("    %-10.10s%s\n", cmd.name, cmd.description)
227
+		help += fmt.Sprintf("    %-25.25s%s\n", cmd.name, cmd.description)
228
+	}
229
+
230
+	for _, cmd := range serviceCommands {
231
+		help += fmt.Sprintf("    %-25.25s%s\n", "service "+cmd.name, cmd.description)
128 232
 	}
129 233
 
130 234
 	help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain)
131 235
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build !experimental
1
+
2
+package client
3
+
4
+var (
5
+	serviceCommands = []command{}
6
+)
0 7
new file mode 100644
... ...
@@ -0,0 +1,317 @@
0
+// +build experimental
1
+
2
+package client
3
+
4
+import (
5
+	"bytes"
6
+	"encoding/json"
7
+	"fmt"
8
+	"net/http"
9
+	"text/tabwriter"
10
+
11
+	flag "github.com/docker/docker/pkg/mflag"
12
+	"github.com/docker/docker/pkg/stringid"
13
+)
14
+
15
+var (
16
+	serviceCommands = []command{
17
+		{"create", "Create a service endpoint"},
18
+		{"rm", "Remove a service endpoint"},
19
+		{"join", "Join a container to a service endpoint"},
20
+		{"leave", "Leave a container from a service endpoint"},
21
+		{"ls", "Lists all service endpoints on a network"},
22
+		{"info", "Display information of a service endpoint"},
23
+	}
24
+)
25
+
26
+func lookupServiceID(cli *NetworkCli, networkID string, nameID string) (string, error) {
27
+	obj, statusCode, err := readBody(cli.call("GET", fmt.Sprintf("/networks/%s/endpoints?name=%s", networkID, nameID), nil, nil))
28
+	if err != nil {
29
+		return "", err
30
+	}
31
+
32
+	if statusCode != http.StatusOK {
33
+		return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
34
+	}
35
+
36
+	var list []*networkResource
37
+	err = json.Unmarshal(obj, &list)
38
+	if err != nil {
39
+		return "", err
40
+	}
41
+	if len(list) > 0 {
42
+		// name query filter will always return a single-element collection
43
+		return list[0].ID, nil
44
+	}
45
+
46
+	// Check for Partial-id
47
+	obj, statusCode, err = readBody(cli.call("GET", fmt.Sprintf("/networks/%s/endpoints?partial-id=%s", networkID, nameID), nil, nil))
48
+	if err != nil {
49
+		return "", err
50
+	}
51
+
52
+	if statusCode != http.StatusOK {
53
+		return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
54
+	}
55
+
56
+	err = json.Unmarshal(obj, &list)
57
+	if err != nil {
58
+		return "", err
59
+	}
60
+	if len(list) == 0 {
61
+		return "", fmt.Errorf("resource not found %s", nameID)
62
+	}
63
+	if len(list) > 1 {
64
+		return "", fmt.Errorf("multiple services matching the partial identifier (%s). Please use full identifier", nameID)
65
+	}
66
+	return list[0].ID, nil
67
+}
68
+
69
+func lookupContainerID(cli *NetworkCli, nameID string) (string, error) {
70
+	// TODO : containerID to sandbox-key ?
71
+	return nameID, nil
72
+}
73
+
74
+// CmdNetworkService handles the network service UI
75
+func (cli *NetworkCli) CmdNetworkService(chain string, args ...string) error {
76
+	cmd := cli.Subcmd(chain, "service", "COMMAND [OPTIONS] [arg...]", serviceUsage(chain), false)
77
+	cmd.Require(flag.Min, 1)
78
+	err := cmd.ParseFlags(args, true)
79
+	if err == nil {
80
+		cmd.Usage()
81
+		return fmt.Errorf("Invalid command : %v", args)
82
+	}
83
+	return err
84
+}
85
+
86
+// CmdNetworkServiceCreate handles service create UI
87
+func (cli *NetworkCli) CmdNetworkServiceCreate(chain string, args ...string) error {
88
+	cmd := cli.Subcmd(chain, "create", "SERVICE NETWORK", "Creates a new service on a network", false)
89
+	cmd.Require(flag.Min, 2)
90
+	err := cmd.ParseFlags(args, true)
91
+	if err != nil {
92
+		return err
93
+	}
94
+
95
+	networkID, err := lookupNetworkID(cli, cmd.Arg(1))
96
+	if err != nil {
97
+		return err
98
+	}
99
+
100
+	ec := endpointCreate{Name: cmd.Arg(0), NetworkID: networkID}
101
+
102
+	obj, _, err := readBody(cli.call("POST", "/networks/"+networkID+"/endpoints", ec, nil))
103
+	if err != nil {
104
+		return err
105
+	}
106
+
107
+	var replyID string
108
+	err = json.Unmarshal(obj, &replyID)
109
+	if err != nil {
110
+		return err
111
+	}
112
+
113
+	fmt.Fprintf(cli.out, "%s\n", replyID)
114
+	return nil
115
+}
116
+
117
+// CmdNetworkServiceRm handles service delete UI
118
+func (cli *NetworkCli) CmdNetworkServiceRm(chain string, args ...string) error {
119
+	cmd := cli.Subcmd(chain, "rm", "SERVICE NETWORK", "Deletes a service", false)
120
+	cmd.Require(flag.Min, 2)
121
+	err := cmd.ParseFlags(args, true)
122
+	if err != nil {
123
+		return err
124
+	}
125
+
126
+	networkID, err := lookupNetworkID(cli, cmd.Arg(1))
127
+	if err != nil {
128
+		return err
129
+	}
130
+
131
+	serviceID, err := lookupServiceID(cli, networkID, cmd.Arg(0))
132
+	if err != nil {
133
+		return err
134
+	}
135
+
136
+	_, _, err = readBody(cli.call("DELETE", "/networks/"+networkID+"/endpoints/"+serviceID, nil, nil))
137
+	if err != nil {
138
+		return err
139
+	}
140
+	return nil
141
+}
142
+
143
+// CmdNetworkServiceLs handles service list UI
144
+func (cli *NetworkCli) CmdNetworkServiceLs(chain string, args ...string) error {
145
+	cmd := cli.Subcmd(chain, "ls", "NETWORK", "Lists all the services on a network", false)
146
+	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
147
+	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
148
+	nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
149
+	last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
150
+	err := cmd.ParseFlags(args, true)
151
+	if err != nil {
152
+		return err
153
+	}
154
+
155
+	cmd.Require(flag.Min, 1)
156
+
157
+	networkID, err := lookupNetworkID(cli, cmd.Arg(0))
158
+	if err != nil {
159
+		return err
160
+	}
161
+
162
+	obj, _, err := readBody(cli.call("GET", "/networks/"+networkID+"/endpoints", nil, nil))
163
+	if err != nil {
164
+		fmt.Fprintf(cli.err, "%s", err.Error())
165
+		return err
166
+	}
167
+	if *last == -1 && *nLatest {
168
+		*last = 1
169
+	}
170
+
171
+	var endpointResources []endpointResource
172
+	err = json.Unmarshal(obj, &endpointResources)
173
+	if err != nil {
174
+		return err
175
+	}
176
+
177
+	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
178
+	// unless quiet (-q) is specified, print field titles
179
+	if !*quiet {
180
+		fmt.Fprintln(wr, "NETWORK SERVICE ID\tNAME\tNETWORK")
181
+	}
182
+
183
+	for _, networkResource := range endpointResources {
184
+		ID := networkResource.ID
185
+		netName := networkResource.Name
186
+		if !*noTrunc {
187
+			ID = stringid.TruncateID(ID)
188
+		}
189
+		if *quiet {
190
+			fmt.Fprintln(wr, ID)
191
+			continue
192
+		}
193
+		network := networkResource.Network
194
+		fmt.Fprintf(wr, "%s\t%s\t%s",
195
+			ID,
196
+			netName,
197
+			network)
198
+		fmt.Fprint(wr, "\n")
199
+	}
200
+	wr.Flush()
201
+
202
+	return nil
203
+}
204
+
205
+// CmdNetworkServiceInfo handles service info UI
206
+func (cli *NetworkCli) CmdNetworkServiceInfo(chain string, args ...string) error {
207
+	cmd := cli.Subcmd(chain, "info", "SERVICE NETWORK", "Displays detailed information on a service", false)
208
+	cmd.Require(flag.Min, 2)
209
+	err := cmd.ParseFlags(args, true)
210
+	if err != nil {
211
+		return err
212
+	}
213
+
214
+	networkID, err := lookupNetworkID(cli, cmd.Arg(1))
215
+	if err != nil {
216
+		return err
217
+	}
218
+
219
+	serviceID, err := lookupServiceID(cli, networkID, cmd.Arg(0))
220
+	if err != nil {
221
+		return err
222
+	}
223
+
224
+	obj, _, err := readBody(cli.call("GET", "/networks/"+networkID+"/endpoints/"+serviceID, nil, nil))
225
+	if err != nil {
226
+		fmt.Fprintf(cli.err, "%s", err.Error())
227
+		return err
228
+	}
229
+
230
+	endpointResource := &endpointResource{}
231
+	if err := json.NewDecoder(bytes.NewReader(obj)).Decode(endpointResource); err != nil {
232
+		return err
233
+	}
234
+	fmt.Fprintf(cli.out, "Service Id: %s\n", endpointResource.ID)
235
+	fmt.Fprintf(cli.out, "\tName: %s\n", endpointResource.Name)
236
+	fmt.Fprintf(cli.out, "\tNetwork: %s\n", endpointResource.Network)
237
+
238
+	return nil
239
+}
240
+
241
+// CmdNetworkServiceJoin handles service join UI
242
+func (cli *NetworkCli) CmdNetworkServiceJoin(chain string, args ...string) error {
243
+	cmd := cli.Subcmd(chain, "join", "CONTAINER SERVICE NETWORK", "Sets a container as a service backend", false)
244
+	cmd.Require(flag.Min, 3)
245
+	err := cmd.ParseFlags(args, true)
246
+	if err != nil {
247
+		return err
248
+	}
249
+
250
+	containerID, err := lookupContainerID(cli, cmd.Arg(0))
251
+	if err != nil {
252
+		return err
253
+	}
254
+
255
+	networkID, err := lookupNetworkID(cli, cmd.Arg(2))
256
+	if err != nil {
257
+		return err
258
+	}
259
+
260
+	serviceID, err := lookupServiceID(cli, networkID, cmd.Arg(1))
261
+	if err != nil {
262
+		return err
263
+	}
264
+
265
+	nc := endpointJoin{ContainerID: containerID}
266
+
267
+	_, _, err = readBody(cli.call("POST", "/networks/"+networkID+"/endpoints/"+serviceID+"/containers", nc, nil))
268
+	if err != nil {
269
+		fmt.Fprintf(cli.err, "%s", err.Error())
270
+		return err
271
+	}
272
+	return nil
273
+}
274
+
275
+// CmdNetworkServiceLeave handles service leave UI
276
+func (cli *NetworkCli) CmdNetworkServiceLeave(chain string, args ...string) error {
277
+	cmd := cli.Subcmd(chain, "leave", "CONTAINER SERVICE NETWORK", "Removes a container from service backend", false)
278
+	cmd.Require(flag.Min, 3)
279
+	err := cmd.ParseFlags(args, true)
280
+	if err != nil {
281
+		return err
282
+	}
283
+
284
+	containerID, err := lookupContainerID(cli, cmd.Arg(0))
285
+	if err != nil {
286
+		return err
287
+	}
288
+
289
+	networkID, err := lookupNetworkID(cli, cmd.Arg(2))
290
+	if err != nil {
291
+		return err
292
+	}
293
+
294
+	serviceID, err := lookupServiceID(cli, networkID, cmd.Arg(1))
295
+	if err != nil {
296
+		return err
297
+	}
298
+
299
+	_, _, err = readBody(cli.call("DELETE", "/networks/"+networkID+"/endpoints/"+serviceID+"/containers/"+containerID, nil, nil))
300
+	if err != nil {
301
+		fmt.Fprintf(cli.err, "%s", err.Error())
302
+		return err
303
+	}
304
+	return nil
305
+}
306
+
307
+func serviceUsage(chain string) string {
308
+	help := "Commands:\n"
309
+
310
+	for _, cmd := range serviceCommands {
311
+		help += fmt.Sprintf("    %-10.10s%s\n", cmd, cmd.description)
312
+	}
313
+
314
+	help += fmt.Sprintf("\nRun '%s service COMMAND --help' for more information on a command.", chain)
315
+	return help
316
+}
... ...
@@ -1,6 +1,6 @@
1 1
 package client
2 2
 
3
-import "github.com/docker/libnetwork/sandbox"
3
+import "github.com/docker/libnetwork/types"
4 4
 
5 5
 /***********
6 6
  Resources
... ...
@@ -19,7 +19,6 @@ type endpointResource struct {
19 19
 	Name    string
20 20
 	ID      string
21 21
 	Network string
22
-	Info    sandbox.Info
23 22
 }
24 23
 
25 24
 /***********
... ...
@@ -32,3 +31,38 @@ type networkCreate struct {
32 32
 	NetworkType string
33 33
 	Options     map[string]interface{}
34 34
 }
35
+
36
+// endpointCreate represents the body of the "create endpoint" http request message
37
+type endpointCreate struct {
38
+	Name         string
39
+	NetworkID    string
40
+	ExposedPorts []types.TransportPort
41
+	PortMapping  []types.PortBinding
42
+}
43
+
44
+// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages
45
+type endpointJoin struct {
46
+	ContainerID       string
47
+	HostName          string
48
+	DomainName        string
49
+	HostsPath         string
50
+	ResolvConfPath    string
51
+	DNS               []string
52
+	ExtraHosts        []endpointExtraHost
53
+	ParentUpdates     []endpointParentUpdate
54
+	UseDefaultSandbox bool
55
+}
56
+
57
+// EndpointExtraHost represents the extra host object
58
+type endpointExtraHost struct {
59
+	Name    string
60
+	Address string
61
+}
62
+
63
+// EndpointParentUpdate is the object carrying the information about the
64
+// endpoint parent that needs to be updated
65
+type endpointParentUpdate struct {
66
+	EndpointID string
67
+	Name       string
68
+	Address    string
69
+}
... ...
@@ -113,10 +113,8 @@ func (d *dnetConnection) dnetDaemon() error {
113 113
 	}
114 114
 	httpHandler := api.NewHTTPHandler(controller)
115 115
 	r := mux.NewRouter().StrictSlash(false)
116
-	post := r.PathPrefix("/networks").Subrouter()
117
-	post.Methods("GET").HandlerFunc(httpHandler)
118
-	post.Methods("PUT", "POST").HandlerFunc(httpHandler)
119
-	post.Methods("DELETE").HandlerFunc(httpHandler)
116
+	post := r.PathPrefix("/{.*}/networks").Subrouter()
117
+	post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
120 118
 	return http.ListenAndServe(d.addr, r)
121 119
 }
122 120
 
... ...
@@ -143,7 +141,7 @@ func (d *dnetConnection) httpCall(method, path string, data interface{}, headers
143 143
 		return nil, -1, err
144 144
 	}
145 145
 
146
-	req, err := http.NewRequest(method, fmt.Sprintf("%s", path), in)
146
+	req, err := http.NewRequest(method, fmt.Sprintf("/dnet%s", path), in)
147 147
 	if err != nil {
148 148
 		return nil, -1, err
149 149
 	}
... ...
@@ -160,7 +158,7 @@ func (d *dnetConnection) httpCall(method, path string, data interface{}, headers
160 160
 		statusCode = resp.StatusCode
161 161
 	}
162 162
 	if err != nil {
163
-		return nil, statusCode, fmt.Errorf("An error occurred trying to connect: %v", err)
163
+		return nil, statusCode, fmt.Errorf("error when trying to connect: %v", err)
164 164
 	}
165 165
 
166 166
 	if statusCode < 200 || statusCode >= 400 {
... ...
@@ -168,7 +166,7 @@ func (d *dnetConnection) httpCall(method, path string, data interface{}, headers
168 168
 		if err != nil {
169 169
 			return nil, statusCode, err
170 170
 		}
171
-		return nil, statusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
171
+		return nil, statusCode, fmt.Errorf("error : %s", bytes.TrimSpace(body))
172 172
 	}
173 173
 
174 174
 	return resp.Body, statusCode, nil
... ...
@@ -16,7 +16,7 @@ type byName []command
16 16
 
17 17
 var (
18 18
 	flDaemon   = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
19
-	flHost     = flag.String([]string{"H", "-Host"}, "", "Daemon socket to connect to")
19
+	flHost     = flag.String([]string{"H", "-host"}, "", "Daemon socket to connect to")
20 20
 	flLogLevel = flag.String([]string{"l", "-log-level"}, "info", "Set the logging level")
21 21
 	flDebug    = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
22 22
 	flHelp     = flag.Bool([]string{"h", "-help"}, false, "Print usage")
... ...
@@ -5,8 +5,8 @@ import (
5 5
 
6 6
 	"github.com/docker/libnetwork"
7 7
 	"github.com/docker/libnetwork/netlabel"
8
-	"github.com/docker/libnetwork/netutils"
9 8
 	"github.com/docker/libnetwork/options"
9
+	"github.com/docker/libnetwork/types"
10 10
 )
11 11
 
12 12
 func main() {
... ...
@@ -58,7 +58,7 @@ func main() {
58 58
 	epInfo, err := ep.DriverInfo()
59 59
 	mapData, ok := epInfo[netlabel.PortMap]
60 60
 	if ok {
61
-		portMapping, ok := mapData.([]netutils.PortBinding)
61
+		portMapping, ok := mapData.([]types.PortBinding)
62 62
 		if ok {
63 63
 			fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping)
64 64
 		}
... ...
@@ -134,7 +134,7 @@ func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver)
134 134
 // are network specific and modeled in a generic way.
135 135
 func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) {
136 136
 	if name == "" {
137
-		return nil, ErrInvalidName
137
+		return nil, ErrInvalidName(name)
138 138
 	}
139 139
 	// Check if a driver for the specified network type is available
140 140
 	c.Lock()
... ...
@@ -203,7 +203,7 @@ func (c *controller) WalkNetworks(walker NetworkWalker) {
203 203
 
204 204
 func (c *controller) NetworkByName(name string) (Network, error) {
205 205
 	if name == "" {
206
-		return nil, ErrInvalidName
206
+		return nil, ErrInvalidName(name)
207 207
 	}
208 208
 	var n Network
209 209
 
... ...
@@ -218,7 +218,7 @@ func (c *controller) NetworkByName(name string) (Network, error) {
218 218
 	c.WalkNetworks(s)
219 219
 
220 220
 	if n == nil {
221
-		return nil, ErrNoSuchNetwork
221
+		return nil, ErrNoSuchNetwork(name)
222 222
 	}
223 223
 
224 224
 	return n, nil
... ...
@@ -226,14 +226,14 @@ func (c *controller) NetworkByName(name string) (Network, error) {
226 226
 
227 227
 func (c *controller) NetworkByID(id string) (Network, error) {
228 228
 	if id == "" {
229
-		return nil, ErrInvalidID
229
+		return nil, ErrInvalidID(id)
230 230
 	}
231 231
 	c.Lock()
232 232
 	defer c.Unlock()
233 233
 	if n, ok := c.networks[types.UUID(id)]; ok {
234 234
 		return n, nil
235 235
 	}
236
-	return nil, ErrNoSuchNetwork
236
+	return nil, ErrNoSuchNetwork(id)
237 237
 }
238 238
 
239 239
 func (c *controller) sandboxAdd(key string, create bool) (sandbox.Sandbox, error) {
... ...
@@ -286,13 +286,16 @@ func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) {
286 286
 	// As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available.
287 287
 	_, err := plugins.Get(networkType, driverapi.NetworkPluginEndpointType)
288 288
 	if err != nil {
289
+		if err == plugins.ErrNotFound {
290
+			return nil, types.NotFoundErrorf(err.Error())
291
+		}
289 292
 		return nil, err
290 293
 	}
291 294
 	c.Lock()
292 295
 	defer c.Unlock()
293 296
 	d, ok := c.drivers[networkType]
294 297
 	if !ok {
295
-		return nil, ErrInvalidNetworkDriver
298
+		return nil, ErrInvalidNetworkDriver(networkType)
296 299
 	}
297 300
 	return d, nil
298 301
 }
... ...
@@ -6,7 +6,7 @@ This document describes how libnetwork has been designed in order to acheive thi
6 6
 Requirements for individual releases can be found on the [Project Page](https://github.com/docker/libnetwork/wiki)
7 7
 
8 8
 Many of the design decisions are inspired by the learnings from the Docker networking design as of Docker v1.6.
9
-Please refer to this [Docker v1.6 Design](https://github.com/docker/libnetwork/blob/docs/legacy.md) document for more information on networking design as of Docker v1.6.
9
+Please refer to this [Docker v1.6 Design](legacy.md) document for more information on networking design as of Docker v1.6.
10 10
 
11 11
 ## Goal
12 12
 
... ...
@@ -1,24 +1,11 @@
1 1
 package driverapi
2 2
 
3 3
 import (
4
-	"errors"
5
-	"fmt"
6 4
 	"net"
7 5
 
8 6
 	"github.com/docker/libnetwork/types"
9 7
 )
10 8
 
11
-var (
12
-	// ErrEndpointExists is returned if more than one endpoint is added to the network
13
-	ErrEndpointExists = errors.New("Endpoint already exists (Only one endpoint allowed)")
14
-	// ErrNoNetwork is returned if no network with the specified id exists
15
-	ErrNoNetwork = errors.New("No network exists")
16
-	// ErrNoEndpoint is returned if no endpoint with the specified id exists
17
-	ErrNoEndpoint = errors.New("No endpoint exists")
18
-	// ErrNotImplemented is returned when a Driver has not implemented an API yet
19
-	ErrNotImplemented = errors.New("The API is not implemented yet")
20
-)
21
-
22 9
 // NetworkPluginEndpointType represents the Endpoint Type used by Plugin system
23 10
 const NetworkPluginEndpointType = "NetworkDriver"
24 11
 
... ...
@@ -96,8 +83,8 @@ type InterfaceInfo interface {
96 96
 // InterfaceNameInfo provides a go interface for the drivers to assign names
97 97
 // to interfaces.
98 98
 type InterfaceNameInfo interface {
99
-	// SetNames method assigns the srcName and dstName for the interface.
100
-	SetNames(srcName, dstName string) error
99
+	// SetNames method assigns the srcName and dstPrefix for the interface.
100
+	SetNames(srcName, dstPrefix string) error
101 101
 
102 102
 	// ID returns the numerical id that was assigned to the interface by the driver
103 103
 	// CreateEndpoint.
... ...
@@ -124,14 +111,6 @@ type JoinInfo interface {
124 124
 	SetResolvConfPath(string) error
125 125
 }
126 126
 
127
-// ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered
128
-type ErrActiveRegistration string
129
-
130
-// Error interface for ErrActiveRegistration
131
-func (ar ErrActiveRegistration) Error() string {
132
-	return fmt.Sprintf("Driver already registered for type %q", string(ar))
133
-}
134
-
135 127
 // DriverCallback provides a Callback interface for Drivers into LibNetwork
136 128
 type DriverCallback interface {
137 129
 	// RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a driver instance
138 130
new file mode 100644
... ...
@@ -0,0 +1,56 @@
0
+package driverapi
1
+
2
+import (
3
+	"fmt"
4
+)
5
+
6
+// ErrNoNetwork is returned if no network with the specified id exists
7
+type ErrNoNetwork string
8
+
9
+func (enn ErrNoNetwork) Error() string {
10
+	return fmt.Sprintf("No network (%s) exists", string(enn))
11
+}
12
+
13
+// NotFound denotes the type of this error
14
+func (enn ErrNoNetwork) NotFound() {}
15
+
16
+// ErrEndpointExists is returned if more than one endpoint is added to the network
17
+type ErrEndpointExists string
18
+
19
+func (ee ErrEndpointExists) Error() string {
20
+	return fmt.Sprintf("Endpoint (%s) already exists (Only one endpoint allowed)", string(ee))
21
+}
22
+
23
+// Forbidden denotes the type of this error
24
+func (ee ErrEndpointExists) Forbidden() {}
25
+
26
+// ErrNotImplemented is returned when a Driver has not implemented an API yet
27
+type ErrNotImplemented struct{}
28
+
29
+func (eni *ErrNotImplemented) Error() string {
30
+	return "The API is not implemented yet"
31
+}
32
+
33
+// NotImplemented denotes the type of this error
34
+func (eni *ErrNotImplemented) NotImplemented() {}
35
+
36
+// ErrNoEndpoint is returned if no endpoint with the specified id exists
37
+type ErrNoEndpoint string
38
+
39
+func (ene ErrNoEndpoint) Error() string {
40
+	return fmt.Sprintf("No endpoint (%s) exists", string(ene))
41
+}
42
+
43
+// NotFound denotes the type of this error
44
+func (ene ErrNoEndpoint) NotFound() {}
45
+
46
+// ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered
47
+type ErrActiveRegistration string
48
+
49
+// Error interface for ErrActiveRegistration
50
+func (ar ErrActiveRegistration) Error() string {
51
+	return fmt.Sprintf("Driver already registered for type %q", string(ar))
52
+}
53
+
54
+// Forbidden denotes the type of this error
55
+func (ar ErrActiveRegistration) Forbidden() {}
... ...
@@ -21,7 +21,7 @@ const (
21 21
 	networkType             = "bridge"
22 22
 	vethPrefix              = "veth"
23 23
 	vethLen                 = 7
24
-	containerVeth           = "eth0"
24
+	containerVethPrefix     = "eth"
25 25
 	maxAllocatePortAttempts = 10
26 26
 	ifaceID                 = 1
27 27
 )
... ...
@@ -57,8 +57,8 @@ type NetworkConfiguration struct {
57 57
 // EndpointConfiguration represents the user specified configuration for the sandbox endpoint
58 58
 type EndpointConfiguration struct {
59 59
 	MacAddress   net.HardwareAddr
60
-	PortBindings []netutils.PortBinding
61
-	ExposedPorts []netutils.TransportPort
60
+	PortBindings []types.PortBinding
61
+	ExposedPorts []types.TransportPort
62 62
 }
63 63
 
64 64
 // ContainerConfiguration represents the user specified configuration for a container
... ...
@@ -73,7 +73,7 @@ type bridgeEndpoint struct {
73 73
 	macAddress      net.HardwareAddr
74 74
 	config          *EndpointConfiguration // User specified parameters
75 75
 	containerConfig *ContainerConfiguration
76
-	portMapping     []netutils.PortBinding // Operation port bindings
76
+	portMapping     []types.PortBinding // Operation port bindings
77 77
 }
78 78
 
79 79
 type bridgeNetwork struct {
... ...
@@ -109,7 +109,7 @@ func Init(dc driverapi.DriverCallback) error {
109 109
 // Whatever can be assessed a priori before attempting any programming.
110 110
 func (c *NetworkConfiguration) Validate() error {
111 111
 	if c.Mtu < 0 {
112
-		return ErrInvalidMtu
112
+		return ErrInvalidMtu(c.Mtu)
113 113
 	}
114 114
 
115 115
 	// If bridge v4 subnet is specified
... ...
@@ -118,19 +118,19 @@ func (c *NetworkConfiguration) Validate() error {
118 118
 		if c.FixedCIDR != nil {
119 119
 			// Check Network address
120 120
 			if !c.AddressIPv4.Contains(c.FixedCIDR.IP) {
121
-				return ErrInvalidContainerSubnet
121
+				return &ErrInvalidContainerSubnet{}
122 122
 			}
123 123
 			// Check it is effectively a subset
124 124
 			brNetLen, _ := c.AddressIPv4.Mask.Size()
125 125
 			cnNetLen, _ := c.FixedCIDR.Mask.Size()
126 126
 			if brNetLen > cnNetLen {
127
-				return ErrInvalidContainerSubnet
127
+				return &ErrInvalidContainerSubnet{}
128 128
 			}
129 129
 		}
130 130
 		// If default gw is specified, it must be part of bridge subnet
131 131
 		if c.DefaultGatewayIPv4 != nil {
132 132
 			if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) {
133
-				return ErrInvalidGateway
133
+				return &ErrInvalidGateway{}
134 134
 			}
135 135
 		}
136 136
 	}
... ...
@@ -138,7 +138,7 @@ func (c *NetworkConfiguration) Validate() error {
138 138
 	// If default v6 gw is specified, FixedCIDRv6 must be specified and gw must belong to FixedCIDRv6 subnet
139 139
 	if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil {
140 140
 		if c.FixedCIDRv6 == nil || !c.FixedCIDRv6.Contains(c.DefaultGatewayIPv6) {
141
-			return ErrInvalidGateway
141
+			return &ErrInvalidGateway{}
142 142
 		}
143 143
 	}
144 144
 
... ...
@@ -167,7 +167,7 @@ func (d *driver) Config(option map[string]interface{}) error {
167 167
 	defer d.Unlock()
168 168
 
169 169
 	if d.config != nil {
170
-		return ErrConfigExists
170
+		return &ErrConfigExists{}
171 171
 	}
172 172
 
173 173
 	genericData, ok := option[netlabel.GenericData]
... ...
@@ -182,7 +182,7 @@ func (d *driver) Config(option map[string]interface{}) error {
182 182
 		case *Configuration:
183 183
 			config = opt
184 184
 		default:
185
-			return ErrInvalidDriverConfig
185
+			return &ErrInvalidDriverConfig{}
186 186
 		}
187 187
 
188 188
 		d.config = config
... ...
@@ -220,7 +220,7 @@ func parseNetworkOptions(option options.Generic) (*NetworkConfiguration, error)
220 220
 		case *NetworkConfiguration:
221 221
 			config = opt
222 222
 		default:
223
-			return nil, ErrInvalidNetworkConfig
223
+			return nil, &ErrInvalidNetworkConfig{}
224 224
 		}
225 225
 
226 226
 		if err := config.Validate(); err != nil {
... ...
@@ -247,7 +247,7 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
247 247
 	// Sanity checks
248 248
 	if d.network != nil {
249 249
 		d.Unlock()
250
-		return ErrNetworkExists
250
+		return &ErrNetworkExists{}
251 251
 	}
252 252
 
253 253
 	// Create and set network handler in driver
... ...
@@ -361,7 +361,7 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
361 361
 
362 362
 	// Sanity check
363 363
 	if n == nil {
364
-		err = driverapi.ErrNoNetwork
364
+		err = driverapi.ErrNoNetwork(nid)
365 365
 		return err
366 366
 	}
367 367
 
... ...
@@ -397,7 +397,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
397 397
 	config := n.config
398 398
 	d.Unlock()
399 399
 	if n == nil {
400
-		return driverapi.ErrNoNetwork
400
+		return driverapi.ErrNoNetwork(nid)
401 401
 	}
402 402
 
403 403
 	// Sanity check
... ...
@@ -416,7 +416,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
416 416
 
417 417
 	// Endpoint with that id exists either on desired or other sandbox
418 418
 	if ep != nil {
419
-		return driverapi.ErrEndpointExists
419
+		return driverapi.ErrEndpointExists(eid)
420 420
 	}
421 421
 
422 422
 	// Try to convert the options to endpoint configuration
... ...
@@ -545,7 +545,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
545 545
 	// Create the sandbox side pipe interface
546 546
 	intf := &sandbox.Interface{}
547 547
 	intf.SrcName = name2
548
-	intf.DstName = containerVeth
548
+	intf.DstName = containerVethPrefix
549 549
 	intf.Address = ipv4Addr
550 550
 
551 551
 	if config.EnableIPv6 {
... ...
@@ -578,7 +578,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
578 578
 	config := n.config
579 579
 	d.Unlock()
580 580
 	if n == nil {
581
-		return driverapi.ErrNoNetwork
581
+		return driverapi.ErrNoNetwork(nid)
582 582
 	}
583 583
 
584 584
 	// Sanity Check
... ...
@@ -648,7 +648,7 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
648 648
 	n := d.network
649 649
 	d.Unlock()
650 650
 	if n == nil {
651
-		return nil, driverapi.ErrNoNetwork
651
+		return nil, driverapi.ErrNoNetwork(nid)
652 652
 	}
653 653
 
654 654
 	// Sanity check
... ...
@@ -665,14 +665,14 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
665 665
 		return nil, err
666 666
 	}
667 667
 	if ep == nil {
668
-		return nil, driverapi.ErrNoEndpoint
668
+		return nil, driverapi.ErrNoEndpoint(eid)
669 669
 	}
670 670
 
671 671
 	m := make(map[string]interface{})
672 672
 
673 673
 	if ep.portMapping != nil {
674 674
 		// Return a copy of the operational data
675
-		pmc := make([]netutils.PortBinding, 0, len(ep.portMapping))
675
+		pmc := make([]types.PortBinding, 0, len(ep.portMapping))
676 676
 		for _, pm := range ep.portMapping {
677 677
 			pmc = append(pmc, pm.GetCopy())
678 678
 		}
... ...
@@ -856,23 +856,23 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*EndpointConfigurat
856 856
 		if mac, ok := opt.(net.HardwareAddr); ok {
857 857
 			ec.MacAddress = mac
858 858
 		} else {
859
-			return nil, ErrInvalidEndpointConfig
859
+			return nil, &ErrInvalidEndpointConfig{}
860 860
 		}
861 861
 	}
862 862
 
863 863
 	if opt, ok := epOptions[netlabel.PortMap]; ok {
864
-		if bs, ok := opt.([]netutils.PortBinding); ok {
864
+		if bs, ok := opt.([]types.PortBinding); ok {
865 865
 			ec.PortBindings = bs
866 866
 		} else {
867
-			return nil, ErrInvalidEndpointConfig
867
+			return nil, &ErrInvalidEndpointConfig{}
868 868
 		}
869 869
 	}
870 870
 
871 871
 	if opt, ok := epOptions[netlabel.ExposedPorts]; ok {
872
-		if ports, ok := opt.([]netutils.TransportPort); ok {
872
+		if ports, ok := opt.([]types.TransportPort); ok {
873 873
 			ec.ExposedPorts = ports
874 874
 		} else {
875
-			return nil, ErrInvalidEndpointConfig
875
+			return nil, &ErrInvalidEndpointConfig{}
876 876
 		}
877 877
 	}
878 878
 
... ...
@@ -924,5 +924,5 @@ func generateIfaceName() (string, error) {
924 924
 			return "", err
925 925
 		}
926 926
 	}
927
-	return "", ErrIfaceName
927
+	return "", &ErrIfaceName{}
928 928
 }
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"github.com/docker/libnetwork/iptables"
12 12
 	"github.com/docker/libnetwork/netlabel"
13 13
 	"github.com/docker/libnetwork/netutils"
14
+	"github.com/docker/libnetwork/types"
14 15
 	"github.com/vishvananda/netlink"
15 16
 )
16 17
 
... ...
@@ -202,7 +203,7 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
202 202
 	if !ok {
203 203
 		t.Fatalf("Endpoint operational data does not contain port mapping data")
204 204
 	}
205
-	pm, ok := pmd.([]netutils.PortBinding)
205
+	pm, ok := pmd.([]types.PortBinding)
206 206
 	if !ok {
207 207
 		t.Fatalf("Unexpected format for port mapping in endpoint operational data")
208 208
 	}
... ...
@@ -261,19 +262,19 @@ func TestCreateLinkWithOptions(t *testing.T) {
261 261
 	}
262 262
 }
263 263
 
264
-func getExposedPorts() []netutils.TransportPort {
265
-	return []netutils.TransportPort{
266
-		netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)},
267
-		netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)},
268
-		netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)},
264
+func getExposedPorts() []types.TransportPort {
265
+	return []types.TransportPort{
266
+		types.TransportPort{Proto: types.TCP, Port: uint16(5000)},
267
+		types.TransportPort{Proto: types.UDP, Port: uint16(400)},
268
+		types.TransportPort{Proto: types.TCP, Port: uint16(600)},
269 269
 	}
270 270
 }
271 271
 
272
-func getPortMapping() []netutils.PortBinding {
273
-	return []netutils.PortBinding{
274
-		netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)},
275
-		netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)},
276
-		netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)},
272
+func getPortMapping() []types.PortBinding {
273
+	return []types.PortBinding{
274
+		types.PortBinding{Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
275
+		types.PortBinding{Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
276
+		types.PortBinding{Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
277 277
 	}
278 278
 }
279 279
 
280 280
deleted file mode 100644
... ...
@@ -1,201 +0,0 @@
1
-package bridge
2
-
3
-import (
4
-	"errors"
5
-	"fmt"
6
-	"net"
7
-)
8
-
9
-var (
10
-	// ErrConfigExists error is returned when driver already has a config applied.
11
-	ErrConfigExists = errors.New("configuration already exists, bridge configuration can be applied only once")
12
-
13
-	// ErrInvalidDriverConfig error is returned when Bridge Driver is passed an invalid config
14
-	ErrInvalidDriverConfig = errors.New("Invalid configuration passed to Bridge Driver")
15
-
16
-	// ErrInvalidNetworkConfig error is returned when a network is created on a driver without valid config.
17
-	ErrInvalidNetworkConfig = errors.New("trying to create a network on a driver without valid config")
18
-
19
-	// ErrInvalidContainerConfig error is returned when a endpoint create is attempted with an invalid configuration.
20
-	ErrInvalidContainerConfig = errors.New("Error in joining a container due to invalid configuration")
21
-
22
-	// ErrInvalidEndpointConfig error is returned when a endpoint create is attempted with an invalid endpoint configuration.
23
-	ErrInvalidEndpointConfig = errors.New("trying to create an endpoint with an invalid endpoint configuration")
24
-
25
-	// ErrNetworkExists error is returned when a network already exists and another network is created.
26
-	ErrNetworkExists = errors.New("network already exists, bridge can only have one network")
27
-
28
-	// ErrIfaceName error is returned when a new name could not be generated.
29
-	ErrIfaceName = errors.New("failed to find name for new interface")
30
-
31
-	// ErrNoIPAddr error is returned when bridge has no IPv4 address configured.
32
-	ErrNoIPAddr = errors.New("bridge has no IPv4 address configured")
33
-
34
-	// ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid.
35
-	ErrInvalidGateway = errors.New("default gateway ip must be part of the network")
36
-
37
-	// ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid.
38
-	ErrInvalidContainerSubnet = errors.New("container subnet must be a subset of bridge network")
39
-
40
-	// ErrInvalidMtu is returned when the user provided MTU is not valid.
41
-	ErrInvalidMtu = errors.New("invalid MTU number")
42
-
43
-	// ErrIPFwdCfg is returned when ip forwarding setup is invoked when the configuration
44
-	// not enabled.
45
-	ErrIPFwdCfg = errors.New("unexpected request to enable IP Forwarding")
46
-)
47
-
48
-// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid.
49
-type ErrInvalidPort string
50
-
51
-func (ip ErrInvalidPort) Error() string {
52
-	return fmt.Sprintf("invalid transport port: %s", string(ip))
53
-}
54
-
55
-// ErrUnsupportedAddressType is returned when the specified address type is not supported.
56
-type ErrUnsupportedAddressType string
57
-
58
-func (uat ErrUnsupportedAddressType) Error() string {
59
-	return fmt.Sprintf("unsupported address type: %s", string(uat))
60
-}
61
-
62
-// ErrInvalidAddressBinding is returned when the host address specified in the port binding is not valid.
63
-type ErrInvalidAddressBinding string
64
-
65
-func (iab ErrInvalidAddressBinding) Error() string {
66
-	return fmt.Sprintf("invalid host address in port binding: %s", string(iab))
67
-}
68
-
69
-// ActiveEndpointsError is returned when there are
70
-// still active endpoints in the network being deleted.
71
-type ActiveEndpointsError string
72
-
73
-func (aee ActiveEndpointsError) Error() string {
74
-	return fmt.Sprintf("network %s has active endpoint", string(aee))
75
-}
76
-
77
-// InvalidNetworkIDError is returned when the passed
78
-// network id for an existing network is not a known id.
79
-type InvalidNetworkIDError string
80
-
81
-func (inie InvalidNetworkIDError) Error() string {
82
-	return fmt.Sprintf("invalid network id %s", string(inie))
83
-}
84
-
85
-// InvalidEndpointIDError is returned when the passed
86
-// endpoint id is not valid.
87
-type InvalidEndpointIDError string
88
-
89
-func (ieie InvalidEndpointIDError) Error() string {
90
-	return fmt.Sprintf("invalid endpoint id: %s", string(ieie))
91
-}
92
-
93
-// InvalidSandboxIDError is returned when the passed
94
-// sandbox id valid.
95
-type InvalidSandboxIDError string
96
-
97
-func (isie InvalidSandboxIDError) Error() string {
98
-	return fmt.Sprintf("invalid sanbox id: %s", string(isie))
99
-}
100
-
101
-// EndpointNotFoundError is returned when the no endpoint
102
-// with the passed endpoint id is found.
103
-type EndpointNotFoundError string
104
-
105
-func (enfe EndpointNotFoundError) Error() string {
106
-	return fmt.Sprintf("endpoint not found: %s", string(enfe))
107
-}
108
-
109
-// NonDefaultBridgeExistError is returned when a non-default
110
-// bridge config is passed but it does not already exist.
111
-type NonDefaultBridgeExistError string
112
-
113
-func (ndbee NonDefaultBridgeExistError) Error() string {
114
-	return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee))
115
-}
116
-
117
-// FixedCIDRv4Error is returned when fixed-cidrv4 configuration
118
-// failed.
119
-type FixedCIDRv4Error struct {
120
-	net    *net.IPNet
121
-	subnet *net.IPNet
122
-	err    error
123
-}
124
-
125
-func (fcv4 *FixedCIDRv4Error) Error() string {
126
-	return fmt.Sprintf("setup FixedCIDRv4 failed for subnet %s in %s: %v", fcv4.subnet, fcv4.net, fcv4.err)
127
-}
128
-
129
-// FixedCIDRv6Error is returned when fixed-cidrv6 configuration
130
-// failed.
131
-type FixedCIDRv6Error struct {
132
-	net *net.IPNet
133
-	err error
134
-}
135
-
136
-func (fcv6 *FixedCIDRv6Error) Error() string {
137
-	return fmt.Sprintf("setup FixedCIDRv6 failed for subnet %s in %s: %v", fcv6.net, fcv6.net, fcv6.err)
138
-}
139
-
140
-type ipTableCfgError string
141
-
142
-func (name ipTableCfgError) Error() string {
143
-	return fmt.Sprintf("unexpected request to set IP tables for interface: %s", string(name))
144
-}
145
-
146
-type invalidIPTablesCfgError string
147
-
148
-func (action invalidIPTablesCfgError) Error() string {
149
-	return fmt.Sprintf("Invalid IPTables action '%s'", string(action))
150
-}
151
-
152
-// IPv4AddrRangeError is returned when a valid IP address range couldn't be found.
153
-type IPv4AddrRangeError string
154
-
155
-func (name IPv4AddrRangeError) Error() string {
156
-	return fmt.Sprintf("can't find an address range for interface %q", string(name))
157
-}
158
-
159
-// IPv4AddrAddError is returned when IPv4 address could not be added to the bridge.
160
-type IPv4AddrAddError struct {
161
-	ip  *net.IPNet
162
-	err error
163
-}
164
-
165
-func (ipv4 *IPv4AddrAddError) Error() string {
166
-	return fmt.Sprintf("failed to add IPv4 address %s to bridge: %v", ipv4.ip, ipv4.err)
167
-}
168
-
169
-// IPv6AddrAddError is returned when IPv6 address could not be added to the bridge.
170
-type IPv6AddrAddError struct {
171
-	ip  *net.IPNet
172
-	err error
173
-}
174
-
175
-func (ipv6 *IPv6AddrAddError) Error() string {
176
-	return fmt.Sprintf("failed to add IPv6 address %s to bridge: %v", ipv6.ip, ipv6.err)
177
-}
178
-
179
-// IPv4AddrNoMatchError is returned when the bridge's IPv4 address does not match configured.
180
-type IPv4AddrNoMatchError struct {
181
-	ip    net.IP
182
-	cfgIP net.IP
183
-}
184
-
185
-func (ipv4 *IPv4AddrNoMatchError) Error() string {
186
-	return fmt.Sprintf("bridge IPv4 (%s) does not match requested configuration %s", ipv4.ip, ipv4.cfgIP)
187
-}
188
-
189
-// IPv6AddrNoMatchError is returned when the bridge's IPv6 address does not match configured.
190
-type IPv6AddrNoMatchError net.IPNet
191
-
192
-func (ipv6 *IPv6AddrNoMatchError) Error() string {
193
-	return fmt.Sprintf("bridge IPv6 addresses do not match the expected bridge configuration %s", (*net.IPNet)(ipv6).String())
194
-}
195
-
196
-// InvalidLinkIPAddrError is returned when a link is configured to a container with an invalid ip address
197
-type InvalidLinkIPAddrError string
198
-
199
-func (address InvalidLinkIPAddrError) Error() string {
200
-	return fmt.Sprintf("Cannot link to a container with Invalid IP Address '%s'", string(address))
201
-}
202 1
new file mode 100644
... ...
@@ -0,0 +1,341 @@
0
+package bridge
1
+
2
+import (
3
+	"fmt"
4
+	"net"
5
+)
6
+
7
+// ErrConfigExists error is returned when driver already has a config applied.
8
+type ErrConfigExists struct{}
9
+
10
+func (ece *ErrConfigExists) Error() string {
11
+	return "configuration already exists, bridge configuration can be applied only once"
12
+}
13
+
14
+// Forbidden denotes the type of this error
15
+func (ece *ErrConfigExists) Forbidden() {}
16
+
17
+// ErrInvalidDriverConfig error is returned when Bridge Driver is passed an invalid config
18
+type ErrInvalidDriverConfig struct{}
19
+
20
+func (eidc *ErrInvalidDriverConfig) Error() string {
21
+	return "Invalid configuration passed to Bridge Driver"
22
+}
23
+
24
+// BadRequest denotes the type of this error
25
+func (eidc *ErrInvalidDriverConfig) BadRequest() {}
26
+
27
+// ErrInvalidNetworkConfig error is returned when a network is created on a driver without valid config.
28
+type ErrInvalidNetworkConfig struct{}
29
+
30
+func (einc *ErrInvalidNetworkConfig) Error() string {
31
+	return "trying to create a network on a driver without valid config"
32
+}
33
+
34
+// Forbidden denotes the type of this error
35
+func (einc *ErrInvalidNetworkConfig) Forbidden() {}
36
+
37
+// ErrInvalidContainerConfig error is returned when a endpoint create is attempted with an invalid configuration.
38
+type ErrInvalidContainerConfig struct{}
39
+
40
+func (eicc *ErrInvalidContainerConfig) Error() string {
41
+	return "Error in joining a container due to invalid configuration"
42
+}
43
+
44
+// BadRequest denotes the type of this error
45
+func (eicc *ErrInvalidContainerConfig) BadRequest() {}
46
+
47
+// ErrInvalidEndpointConfig error is returned when a endpoint create is attempted with an invalid endpoint configuration.
48
+type ErrInvalidEndpointConfig struct{}
49
+
50
+func (eiec *ErrInvalidEndpointConfig) Error() string {
51
+	return "trying to create an endpoint with an invalid endpoint configuration"
52
+}
53
+
54
+// BadRequest denotes the type of this error
55
+func (eiec *ErrInvalidEndpointConfig) BadRequest() {}
56
+
57
+// ErrNetworkExists error is returned when a network already exists and another network is created.
58
+type ErrNetworkExists struct{}
59
+
60
+func (ene *ErrNetworkExists) Error() string {
61
+	return "network already exists, bridge can only have one network"
62
+}
63
+
64
+// Forbidden denotes the type of this error
65
+func (ene *ErrNetworkExists) Forbidden() {}
66
+
67
+// ErrIfaceName error is returned when a new name could not be generated.
68
+type ErrIfaceName struct{}
69
+
70
+func (ein *ErrIfaceName) Error() string {
71
+	return "failed to find name for new interface"
72
+}
73
+
74
+// InternalError denotes the type of this error
75
+func (ein *ErrIfaceName) InternalError() {}
76
+
77
+// ErrNoIPAddr error is returned when bridge has no IPv4 address configured.
78
+type ErrNoIPAddr struct{}
79
+
80
+func (enip *ErrNoIPAddr) Error() string {
81
+	return "bridge has no IPv4 address configured"
82
+}
83
+
84
+// InternalError denotes the type of this error
85
+func (enip *ErrNoIPAddr) InternalError() {}
86
+
87
+// ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid.
88
+type ErrInvalidGateway struct{}
89
+
90
+func (eig *ErrInvalidGateway) Error() string {
91
+	return "default gateway ip must be part of the network"
92
+}
93
+
94
+// BadRequest denotes the type of this error
95
+func (eig *ErrInvalidGateway) BadRequest() {}
96
+
97
+// ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid.
98
+type ErrInvalidContainerSubnet struct{}
99
+
100
+func (eis *ErrInvalidContainerSubnet) Error() string {
101
+	return "container subnet must be a subset of bridge network"
102
+}
103
+
104
+// BadRequest denotes the type of this error
105
+func (eis *ErrInvalidContainerSubnet) BadRequest() {}
106
+
107
+// ErrInvalidMtu is returned when the user provided MTU is not valid.
108
+type ErrInvalidMtu int
109
+
110
+func (eim ErrInvalidMtu) Error() string {
111
+	return fmt.Sprintf("invalid MTU number: %d", int(eim))
112
+}
113
+
114
+// BadRequest denotes the type of this error
115
+func (eim ErrInvalidMtu) BadRequest() {}
116
+
117
+// ErrIPFwdCfg is returned when ip forwarding setup is invoked when the configuration
118
+// not enabled.
119
+type ErrIPFwdCfg struct{}
120
+
121
+func (eipf *ErrIPFwdCfg) Error() string {
122
+	return "unexpected request to enable IP Forwarding"
123
+}
124
+
125
+// BadRequest denotes the type of this error
126
+func (eipf *ErrIPFwdCfg) BadRequest() {}
127
+
128
+// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid.
129
+type ErrInvalidPort string
130
+
131
+func (ip ErrInvalidPort) Error() string {
132
+	return fmt.Sprintf("invalid transport port: %s", string(ip))
133
+}
134
+
135
+// BadRequest denotes the type of this error
136
+func (ip ErrInvalidPort) BadRequest() {}
137
+
138
+// ErrUnsupportedAddressType is returned when the specified address type is not supported.
139
+type ErrUnsupportedAddressType string
140
+
141
+func (uat ErrUnsupportedAddressType) Error() string {
142
+	return fmt.Sprintf("unsupported address type: %s", string(uat))
143
+}
144
+
145
+// BadRequest denotes the type of this error
146
+func (uat ErrUnsupportedAddressType) BadRequest() {}
147
+
148
+// ErrInvalidAddressBinding is returned when the host address specified in the port binding is not valid.
149
+type ErrInvalidAddressBinding string
150
+
151
+func (iab ErrInvalidAddressBinding) Error() string {
152
+	return fmt.Sprintf("invalid host address in port binding: %s", string(iab))
153
+}
154
+
155
+// BadRequest denotes the type of this error
156
+func (iab ErrInvalidAddressBinding) BadRequest() {}
157
+
158
+// ActiveEndpointsError is returned when there are
159
+// still active endpoints in the network being deleted.
160
+type ActiveEndpointsError string
161
+
162
+func (aee ActiveEndpointsError) Error() string {
163
+	return fmt.Sprintf("network %s has active endpoint", string(aee))
164
+}
165
+
166
+// Forbidden denotes the type of this error
167
+func (aee ActiveEndpointsError) Forbidden() {}
168
+
169
+// InvalidNetworkIDError is returned when the passed
170
+// network id for an existing network is not a known id.
171
+type InvalidNetworkIDError string
172
+
173
+func (inie InvalidNetworkIDError) Error() string {
174
+	return fmt.Sprintf("invalid network id %s", string(inie))
175
+}
176
+
177
+// NotFound denotes the type of this error
178
+func (inie InvalidNetworkIDError) NotFound() {}
179
+
180
+// InvalidEndpointIDError is returned when the passed
181
+// endpoint id is not valid.
182
+type InvalidEndpointIDError string
183
+
184
+func (ieie InvalidEndpointIDError) Error() string {
185
+	return fmt.Sprintf("invalid endpoint id: %s", string(ieie))
186
+}
187
+
188
+// BadRequest denotes the type of this error
189
+func (ieie InvalidEndpointIDError) BadRequest() {}
190
+
191
+// InvalidSandboxIDError is returned when the passed
192
+// sandbox id is not valid.
193
+type InvalidSandboxIDError string
194
+
195
+func (isie InvalidSandboxIDError) Error() string {
196
+	return fmt.Sprintf("invalid sanbox id: %s", string(isie))
197
+}
198
+
199
+// BadRequest denotes the type of this error
200
+func (isie InvalidSandboxIDError) BadRequest() {}
201
+
202
+// EndpointNotFoundError is returned when the no endpoint
203
+// with the passed endpoint id is found.
204
+type EndpointNotFoundError string
205
+
206
+func (enfe EndpointNotFoundError) Error() string {
207
+	return fmt.Sprintf("endpoint not found: %s", string(enfe))
208
+}
209
+
210
+// NotFound denotes the type of this error
211
+func (enfe EndpointNotFoundError) NotFound() {}
212
+
213
+// NonDefaultBridgeExistError is returned when a non-default
214
+// bridge config is passed but it does not already exist.
215
+type NonDefaultBridgeExistError string
216
+
217
+func (ndbee NonDefaultBridgeExistError) Error() string {
218
+	return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee))
219
+}
220
+
221
+// Forbidden denotes the type of this error
222
+func (ndbee NonDefaultBridgeExistError) Forbidden() {}
223
+
224
+// FixedCIDRv4Error is returned when fixed-cidrv4 configuration
225
+// failed.
226
+type FixedCIDRv4Error struct {
227
+	Net    *net.IPNet
228
+	Subnet *net.IPNet
229
+	Err    error
230
+}
231
+
232
+func (fcv4 *FixedCIDRv4Error) Error() string {
233
+	return fmt.Sprintf("setup FixedCIDRv4 failed for subnet %s in %s: %v", fcv4.Subnet, fcv4.Net, fcv4.Err)
234
+}
235
+
236
+// InternalError denotes the type of this error
237
+func (fcv4 *FixedCIDRv4Error) InternalError() {}
238
+
239
+// FixedCIDRv6Error is returned when fixed-cidrv6 configuration
240
+// failed.
241
+type FixedCIDRv6Error struct {
242
+	Net *net.IPNet
243
+	Err error
244
+}
245
+
246
+func (fcv6 *FixedCIDRv6Error) Error() string {
247
+	return fmt.Sprintf("setup FixedCIDRv6 failed for subnet %s in %s: %v", fcv6.Net, fcv6.Net, fcv6.Err)
248
+}
249
+
250
+// InternalError denotes the type of this error
251
+func (fcv6 *FixedCIDRv6Error) InternalError() {}
252
+
253
+// IPTableCfgError is returned when an unexpected ip tables configuration is entered
254
+type IPTableCfgError string
255
+
256
+func (name IPTableCfgError) Error() string {
257
+	return fmt.Sprintf("unexpected request to set IP tables for interface: %s", string(name))
258
+}
259
+
260
+// BadRequest denotes the type of this error
261
+func (name IPTableCfgError) BadRequest() {}
262
+
263
+// InvalidIPTablesCfgError is returned when an invalid ip tables configuration is entered
264
+type InvalidIPTablesCfgError string
265
+
266
+func (action InvalidIPTablesCfgError) Error() string {
267
+	return fmt.Sprintf("Invalid IPTables action '%s'", string(action))
268
+}
269
+
270
+// BadRequest denotes the type of this error
271
+func (action InvalidIPTablesCfgError) BadRequest() {}
272
+
273
+// IPv4AddrRangeError is returned when a valid IP address range couldn't be found.
274
+type IPv4AddrRangeError string
275
+
276
+func (name IPv4AddrRangeError) Error() string {
277
+	return fmt.Sprintf("can't find an address range for interface %q", string(name))
278
+}
279
+
280
+// BadRequest denotes the type of this error
281
+func (name IPv4AddrRangeError) BadRequest() {}
282
+
283
+// IPv4AddrAddError is returned when IPv4 address could not be added to the bridge.
284
+type IPv4AddrAddError struct {
285
+	IP  *net.IPNet
286
+	Err error
287
+}
288
+
289
+func (ipv4 *IPv4AddrAddError) Error() string {
290
+	return fmt.Sprintf("failed to add IPv4 address %s to bridge: %v", ipv4.IP, ipv4.Err)
291
+}
292
+
293
+// InternalError denotes the type of this error
294
+func (ipv4 *IPv4AddrAddError) InternalError() {}
295
+
296
+// IPv6AddrAddError is returned when IPv6 address could not be added to the bridge.
297
+type IPv6AddrAddError struct {
298
+	IP  *net.IPNet
299
+	Err error
300
+}
301
+
302
+func (ipv6 *IPv6AddrAddError) Error() string {
303
+	return fmt.Sprintf("failed to add IPv6 address %s to bridge: %v", ipv6.IP, ipv6.Err)
304
+}
305
+
306
+// InternalError denotes the type of this error
307
+func (ipv6 *IPv6AddrAddError) InternalError() {}
308
+
309
+// IPv4AddrNoMatchError is returned when the bridge's IPv4 address does not match configured.
310
+type IPv4AddrNoMatchError struct {
311
+	IP    net.IP
312
+	CfgIP net.IP
313
+}
314
+
315
+func (ipv4 *IPv4AddrNoMatchError) Error() string {
316
+	return fmt.Sprintf("bridge IPv4 (%s) does not match requested configuration %s", ipv4.IP, ipv4.CfgIP)
317
+}
318
+
319
+// BadRequest denotes the type of this error
320
+func (ipv4 *IPv4AddrNoMatchError) BadRequest() {}
321
+
322
+// IPv6AddrNoMatchError is returned when the bridge's IPv6 address does not match configured.
323
+type IPv6AddrNoMatchError net.IPNet
324
+
325
+func (ipv6 *IPv6AddrNoMatchError) Error() string {
326
+	return fmt.Sprintf("bridge IPv6 addresses do not match the expected bridge configuration %s", (*net.IPNet)(ipv6).String())
327
+}
328
+
329
+// BadRequest denotes the type of this error
330
+func (ipv6 *IPv6AddrNoMatchError) BadRequest() {}
331
+
332
+// InvalidLinkIPAddrError is returned when a link is configured to a container with an invalid ip address
333
+type InvalidLinkIPAddrError string
334
+
335
+func (address InvalidLinkIPAddrError) Error() string {
336
+	return fmt.Sprintf("Cannot link to a container with Invalid IP Address '%s'", string(address))
337
+}
338
+
339
+// BadRequest denotes the type of this error
340
+func (address InvalidLinkIPAddrError) BadRequest() {}
... ...
@@ -6,13 +6,13 @@ import (
6 6
 
7 7
 	log "github.com/Sirupsen/logrus"
8 8
 	"github.com/docker/libnetwork/iptables"
9
-	"github.com/docker/libnetwork/netutils"
9
+	"github.com/docker/libnetwork/types"
10 10
 )
11 11
 
12 12
 type link struct {
13 13
 	parentIP string
14 14
 	childIP  string
15
-	ports    []netutils.TransportPort
15
+	ports    []types.TransportPort
16 16
 	bridge   string
17 17
 }
18 18
 
... ...
@@ -20,7 +20,7 @@ func (l *link) String() string {
20 20
 	return fmt.Sprintf("%s <-> %s [%v] on %s", l.parentIP, l.childIP, l.ports, l.bridge)
21 21
 }
22 22
 
23
-func newLink(parentIP, childIP string, ports []netutils.TransportPort, bridge string) *link {
23
+func newLink(parentIP, childIP string, ports []types.TransportPort, bridge string) *link {
24 24
 	return &link{
25 25
 		childIP:  childIP,
26 26
 		parentIP: parentIP,
... ...
@@ -45,7 +45,7 @@ func (l *link) Disable() {
45 45
 	// that returns typed errors
46 46
 }
47 47
 
48
-func linkContainers(action, parentIP, childIP string, ports []netutils.TransportPort, bridge string,
48
+func linkContainers(action, parentIP, childIP string, ports []types.TransportPort, bridge string,
49 49
 	ignoreErrors bool) error {
50 50
 	var nfAction iptables.Action
51 51
 
... ...
@@ -57,7 +57,7 @@ func linkContainers(action, parentIP, childIP string, ports []netutils.Transport
57 57
 	case "-D":
58 58
 		nfAction = iptables.Delete
59 59
 	default:
60
-		return invalidIPTablesCfgError(action)
60
+		return InvalidIPTablesCfgError(action)
61 61
 	}
62 62
 
63 63
 	ip1 := net.ParseIP(parentIP)
... ...
@@ -3,14 +3,14 @@ package bridge
3 3
 import (
4 4
 	"testing"
5 5
 
6
-	"github.com/docker/libnetwork/netutils"
6
+	"github.com/docker/libnetwork/types"
7 7
 )
8 8
 
9
-func getPorts() []netutils.TransportPort {
10
-	return []netutils.TransportPort{
11
-		netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)},
12
-		netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)},
13
-		netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)},
9
+func getPorts() []types.TransportPort {
10
+	return []types.TransportPort{
11
+		types.TransportPort{Proto: types.TCP, Port: uint16(5000)},
12
+		types.TransportPort{Proto: types.UDP, Port: uint16(400)},
13
+		types.TransportPort{Proto: types.TCP, Port: uint16(600)},
14 14
 	}
15 15
 }
16 16
 
... ...
@@ -125,8 +125,8 @@ func TestLinkCreateTwo(t *testing.T) {
125 125
 	te2 := &testEndpoint{ifaces: []*testInterface{}}
126 126
 	err = d.CreateEndpoint("dummy", "ep", te2, nil)
127 127
 	if err != nil {
128
-		if err != driverapi.ErrEndpointExists {
129
-			t.Fatalf("Failed with a wrong error :%s", err.Error())
128
+		if _, ok := err.(driverapi.ErrEndpointExists); !ok {
129
+			t.Fatalf("Failed with a wrong error: %s", err.Error())
130 130
 		}
131 131
 	} else {
132 132
 		t.Fatalf("Expected to fail while trying to add same endpoint twice")
... ...
@@ -7,15 +7,15 @@ import (
7 7
 	"net"
8 8
 
9 9
 	"github.com/Sirupsen/logrus"
10
-	"github.com/docker/libnetwork/netutils"
11 10
 	"github.com/docker/libnetwork/sandbox"
11
+	"github.com/docker/libnetwork/types"
12 12
 )
13 13
 
14 14
 var (
15 15
 	defaultBindingIP = net.IPv4(0, 0, 0, 0)
16 16
 )
17 17
 
18
-func allocatePorts(epConfig *EndpointConfiguration, intf *sandbox.Interface, reqDefBindIP net.IP, ulPxyEnabled bool) ([]netutils.PortBinding, error) {
18
+func allocatePorts(epConfig *EndpointConfiguration, intf *sandbox.Interface, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
19 19
 	if epConfig == nil || epConfig.PortBindings == nil {
20 20
 		return nil, nil
21 21
 	}
... ...
@@ -28,8 +28,8 @@ func allocatePorts(epConfig *EndpointConfiguration, intf *sandbox.Interface, req
28 28
 	return allocatePortsInternal(epConfig.PortBindings, intf.Address.IP, defHostIP, ulPxyEnabled)
29 29
 }
30 30
 
31
-func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]netutils.PortBinding, error) {
32
-	bs := make([]netutils.PortBinding, 0, len(bindings))
31
+func allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
32
+	bs := make([]types.PortBinding, 0, len(bindings))
33 33
 	for _, c := range bindings {
34 34
 		b := c.GetCopy()
35 35
 		if err := allocatePort(&b, containerIP, defHostIP, ulPxyEnabled); err != nil {
... ...
@@ -44,7 +44,7 @@ func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHost
44 44
 	return bs, nil
45 45
 }
46 46
 
47
-func allocatePort(bnd *netutils.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error {
47
+func allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error {
48 48
 	var (
49 49
 		host net.Addr
50 50
 		err  error
... ...
@@ -98,7 +98,7 @@ func releasePorts(ep *bridgeEndpoint) error {
98 98
 	return releasePortsInternal(ep.portMapping)
99 99
 }
100 100
 
101
-func releasePortsInternal(bindings []netutils.PortBinding) error {
101
+func releasePortsInternal(bindings []types.PortBinding) error {
102 102
 	var errorBuf bytes.Buffer
103 103
 
104 104
 	// Attempt to release all port bindings, do not stop on failure
... ...
@@ -114,7 +114,7 @@ func releasePortsInternal(bindings []netutils.PortBinding) error {
114 114
 	return nil
115 115
 }
116 116
 
117
-func releasePort(bnd netutils.PortBinding) error {
117
+func releasePort(bnd types.PortBinding) error {
118 118
 	// Construct the host side transport address
119 119
 	host, err := bnd.HostAddr()
120 120
 	if err != nil {
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"github.com/docker/docker/pkg/reexec"
8 8
 	"github.com/docker/libnetwork/netlabel"
9 9
 	"github.com/docker/libnetwork/netutils"
10
+	"github.com/docker/libnetwork/types"
10 11
 )
11 12
 
12 13
 func TestMain(m *testing.M) {
... ...
@@ -20,9 +21,9 @@ func TestPortMappingConfig(t *testing.T) {
20 20
 	defer netutils.SetupTestNetNS(t)()
21 21
 	d := newDriver()
22 22
 
23
-	binding1 := netutils.PortBinding{Proto: netutils.UDP, Port: uint16(400), HostPort: uint16(54000)}
24
-	binding2 := netutils.PortBinding{Proto: netutils.TCP, Port: uint16(500), HostPort: uint16(65000)}
25
-	portBindings := []netutils.PortBinding{binding1, binding2}
23
+	binding1 := types.PortBinding{Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)}
24
+	binding2 := types.PortBinding{Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)}
25
+	portBindings := []types.PortBinding{binding1, binding2}
26 26
 
27 27
 	epOptions := make(map[string]interface{})
28 28
 	epOptions[netlabel.PortMap] = portBindings
... ...
@@ -1,6 +1,8 @@
1 1
 package bridge
2 2
 
3
-import log "github.com/Sirupsen/logrus"
3
+import (
4
+	log "github.com/Sirupsen/logrus"
5
+)
4 6
 
5 7
 func setupFixedCIDRv4(config *NetworkConfiguration, i *bridgeInterface) error {
6 8
 	addrv4, _, err := i.addresses()
... ...
@@ -10,7 +12,7 @@ func setupFixedCIDRv4(config *NetworkConfiguration, i *bridgeInterface) error {
10 10
 
11 11
 	log.Debugf("Using IPv4 subnet: %v", config.FixedCIDR)
12 12
 	if err := ipAllocator.RegisterSubnet(addrv4.IPNet, config.FixedCIDR); err != nil {
13
-		return &FixedCIDRv4Error{subnet: config.FixedCIDR, net: addrv4.IPNet, err: err}
13
+		return &FixedCIDRv4Error{Subnet: config.FixedCIDR, Net: addrv4.IPNet, Err: err}
14 14
 	}
15 15
 
16 16
 	return nil
... ...
@@ -1,11 +1,13 @@
1 1
 package bridge
2 2
 
3
-import log "github.com/Sirupsen/logrus"
3
+import (
4
+	log "github.com/Sirupsen/logrus"
5
+)
4 6
 
5 7
 func setupFixedCIDRv6(config *NetworkConfiguration, i *bridgeInterface) error {
6 8
 	log.Debugf("Using IPv6 subnet: %v", config.FixedCIDRv6)
7 9
 	if err := ipAllocator.RegisterSubnet(config.FixedCIDRv6, config.FixedCIDRv6); err != nil {
8
-		return &FixedCIDRv6Error{net: config.FixedCIDRv6, err: err}
10
+		return &FixedCIDRv6Error{Net: config.FixedCIDRv6, Err: err}
9 11
 	}
10 12
 
11 13
 	return nil
... ...
@@ -13,7 +13,7 @@ const (
13 13
 func setupIPForwarding(config *Configuration) error {
14 14
 	// Sanity Check
15 15
 	if config.EnableIPForwarding == false {
16
-		return ErrIPFwdCfg
16
+		return &ErrIPFwdCfg{}
17 17
 	}
18 18
 
19 19
 	// Enable IPv4 forwarding
... ...
@@ -47,7 +47,7 @@ func TestUnexpectedSetupIPForwarding(t *testing.T) {
47 47
 		t.Fatal("Setup IP forwarding was expected to fail")
48 48
 	}
49 49
 
50
-	if err != ErrIPFwdCfg {
50
+	if _, ok := err.(*ErrIPFwdCfg); !ok {
51 51
 		t.Fatalf("Setup IP forwarding failed with unexpected error: %v", err)
52 52
 	}
53 53
 }
... ...
@@ -16,7 +16,7 @@ const (
16 16
 func setupIPTables(config *NetworkConfiguration, i *bridgeInterface) error {
17 17
 	// Sanity check.
18 18
 	if config.EnableIPTables == false {
19
-		return ipTableCfgError(config.BridgeName)
19
+		return IPTableCfgError(config.BridgeName)
20 20
 	}
21 21
 
22 22
 	hairpinMode := !config.EnableUserlandProxy
... ...
@@ -71,7 +71,7 @@ func setupBridgeIPv4(config *NetworkConfiguration, i *bridgeInterface) error {
71 71
 
72 72
 	log.Debugf("Creating bridge interface %q with network %s", config.BridgeName, bridgeIPv4)
73 73
 	if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv4}); err != nil {
74
-		return &IPv4AddrAddError{ip: bridgeIPv4, err: err}
74
+		return &IPv4AddrAddError{IP: bridgeIPv4, Err: err}
75 75
 	}
76 76
 
77 77
 	// Store bridge network and default gateway
... ...
@@ -114,7 +114,7 @@ func electBridgeIPv4(config *NetworkConfiguration) (*net.IPNet, error) {
114 114
 
115 115
 func setupGatewayIPv4(config *NetworkConfiguration, i *bridgeInterface) error {
116 116
 	if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) {
117
-		return ErrInvalidGateway
117
+		return &ErrInvalidGateway{}
118 118
 	}
119 119
 	if _, err := ipAllocator.RequestIP(i.bridgeIPv4, config.DefaultGatewayIPv4); err != nil {
120 120
 		return err
... ...
@@ -37,7 +37,7 @@ func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
37 37
 	// Add the default link local ipv6 address if it doesn't exist
38 38
 	if !findIPv6Address(netlink.Addr{IPNet: bridgeIPv6}, addrsv6) {
39 39
 		if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil {
40
-			return &IPv6AddrAddError{ip: bridgeIPv6, err: err}
40
+			return &IPv6AddrAddError{IP: bridgeIPv6, Err: err}
41 41
 		}
42 42
 	}
43 43
 
... ...
@@ -50,10 +50,10 @@ func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
50 50
 
51 51
 func setupGatewayIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
52 52
 	if config.FixedCIDRv6 == nil {
53
-		return ErrInvalidContainerSubnet
53
+		return &ErrInvalidContainerSubnet{}
54 54
 	}
55 55
 	if !config.FixedCIDRv6.Contains(config.DefaultGatewayIPv6) {
56
-		return ErrInvalidGateway
56
+		return &ErrInvalidGateway{}
57 57
 	}
58 58
 	if _, err := ipAllocator.RequestIP(config.FixedCIDRv6, config.DefaultGatewayIPv6); err != nil {
59 59
 		return err
... ...
@@ -1,6 +1,8 @@
1 1
 package bridge
2 2
 
3
-import "github.com/vishvananda/netlink"
3
+import (
4
+	"github.com/vishvananda/netlink"
5
+)
4 6
 
5 7
 func setupVerifyAndReconcile(config *NetworkConfiguration, i *bridgeInterface) error {
6 8
 	// Fetch a single IPv4 and a slice of IPv6 addresses from the bridge.
... ...
@@ -11,12 +13,12 @@ func setupVerifyAndReconcile(config *NetworkConfiguration, i *bridgeInterface) e
11 11
 
12 12
 	// Verify that the bridge does have an IPv4 address.
13 13
 	if addrv4.IPNet == nil {
14
-		return ErrNoIPAddr
14
+		return &ErrNoIPAddr{}
15 15
 	}
16 16
 
17 17
 	// Verify that the bridge IPv4 address matches the requested configuration.
18 18
 	if config.AddressIPv4 != nil && !addrv4.IP.Equal(config.AddressIPv4.IP) {
19
-		return &IPv4AddrNoMatchError{ip: addrv4.IP, cfgIP: config.AddressIPv4.IP}
19
+		return &IPv4AddrNoMatchError{IP: addrv4.IP, CfgIP: config.AddressIPv4.IP}
20 20
 	}
21 21
 
22 22
 	// Verify that one of the bridge IPv6 addresses matches the requested
... ...
@@ -1,7 +1,8 @@
1 1
 package remote
2 2
 
3 3
 import (
4
-	"errors"
4
+	"fmt"
5
+	"net"
5 6
 
6 7
 	log "github.com/Sirupsen/logrus"
7 8
 	"github.com/docker/docker/pkg/plugins"
... ...
@@ -9,59 +10,202 @@ import (
9 9
 	"github.com/docker/libnetwork/types"
10 10
 )
11 11
 
12
-var errNoCallback = errors.New("No Callback handler registered with Driver")
13
-
14 12
 type driver struct {
15 13
 	endpoint    *plugins.Client
16 14
 	networkType string
17 15
 }
18 16
 
19
-// Init does the necessary work to register remote drivers
17
+func newDriver(name string, client *plugins.Client) driverapi.Driver {
18
+	return &driver{networkType: name, endpoint: client}
19
+}
20
+
21
+// Init makes sure a remote driver is registered when a network driver
22
+// plugin is activated.
20 23
 func Init(dc driverapi.DriverCallback) error {
21 24
 	plugins.Handle(driverapi.NetworkPluginEndpointType, func(name string, client *plugins.Client) {
22
-
23
-		// TODO : Handhake with the Remote Plugin goes here
24
-
25
-		newDriver := &driver{networkType: name, endpoint: client}
26
-		if err := dc.RegisterDriver(name, newDriver); err != nil {
27
-			log.Errorf("Error registering Driver for %s due to %v", name, err)
25
+		if err := dc.RegisterDriver(name, newDriver(name, client)); err != nil {
26
+			log.Errorf("error registering driver for %s due to %v", name, err)
28 27
 		}
29 28
 	})
30 29
 	return nil
31 30
 }
32 31
 
32
+// Config is not implemented for remote drivers, since it is assumed
33
+// to be supplied to the remote process out-of-band (e.g., as command
34
+// line arguments).
33 35
 func (d *driver) Config(option map[string]interface{}) error {
34
-	return driverapi.ErrNotImplemented
36
+	return &driverapi.ErrNotImplemented{}
37
+}
38
+
39
+func (d *driver) call(methodName string, arg interface{}, retVal maybeError) error {
40
+	method := driverapi.NetworkPluginEndpointType + "." + methodName
41
+	err := d.endpoint.Call(method, arg, retVal)
42
+	if err != nil {
43
+		return err
44
+	}
45
+	if e := retVal.getError(); e != "" {
46
+		return fmt.Errorf("remote: %s", e)
47
+	}
48
+	return nil
35 49
 }
36 50
 
37
-func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error {
38
-	return driverapi.ErrNotImplemented
51
+func (d *driver) CreateNetwork(id types.UUID, options map[string]interface{}) error {
52
+	create := &createNetworkRequest{
53
+		NetworkID: string(id),
54
+		Options:   options,
55
+	}
56
+	return d.call("CreateNetwork", create, &createNetworkResponse{})
39 57
 }
40 58
 
41 59
 func (d *driver) DeleteNetwork(nid types.UUID) error {
42
-	return driverapi.ErrNotImplemented
60
+	delete := &deleteNetworkRequest{NetworkID: string(nid)}
61
+	return d.call("DeleteNetwork", delete, &deleteNetworkResponse{})
43 62
 }
44 63
 
45 64
 func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
46
-	return driverapi.ErrNotImplemented
65
+	if epInfo == nil {
66
+		return fmt.Errorf("must not be called with nil EndpointInfo")
67
+	}
68
+
69
+	reqIfaces := make([]*endpointInterface, len(epInfo.Interfaces()))
70
+	for i, iface := range epInfo.Interfaces() {
71
+		addr4 := iface.Address()
72
+		addr6 := iface.AddressIPv6()
73
+		reqIfaces[i] = &endpointInterface{
74
+			ID:          iface.ID(),
75
+			Address:     addr4.String(),
76
+			AddressIPv6: addr6.String(),
77
+			MacAddress:  iface.MacAddress().String(),
78
+		}
79
+	}
80
+	create := &createEndpointRequest{
81
+		NetworkID:  string(nid),
82
+		EndpointID: string(eid),
83
+		Interfaces: reqIfaces,
84
+		Options:    epOptions,
85
+	}
86
+	var res createEndpointResponse
87
+	if err := d.call("CreateEndpoint", create, &res); err != nil {
88
+		return err
89
+	}
90
+
91
+	ifaces, err := res.parseInterfaces()
92
+	if err != nil {
93
+		return err
94
+	}
95
+	if len(reqIfaces) > 0 && len(ifaces) > 0 {
96
+		// We're not supposed to add interfaces if there already are
97
+		// some. Attempt to roll back
98
+		return errorWithRollback("driver attempted to add more interfaces", d.DeleteEndpoint(nid, eid))
99
+	}
100
+	for _, iface := range ifaces {
101
+		var addr4, addr6 net.IPNet
102
+		if iface.Address != nil {
103
+			addr4 = *(iface.Address)
104
+		}
105
+		if iface.AddressIPv6 != nil {
106
+			addr6 = *(iface.AddressIPv6)
107
+		}
108
+		if err := epInfo.AddInterface(iface.ID, iface.MacAddress, addr4, addr6); err != nil {
109
+			return errorWithRollback(fmt.Sprintf("failed to AddInterface %v: %s", iface, err), d.DeleteEndpoint(nid, eid))
110
+		}
111
+	}
112
+	return nil
113
+}
114
+
115
+func errorWithRollback(msg string, err error) error {
116
+	rollback := "rolled back"
117
+	if err != nil {
118
+		rollback = "failed to roll back: " + err.Error()
119
+	}
120
+	return fmt.Errorf("%s; %s", msg, rollback)
47 121
 }
48 122
 
49 123
 func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
50
-	return driverapi.ErrNotImplemented
124
+	delete := &deleteEndpointRequest{
125
+		NetworkID:  string(nid),
126
+		EndpointID: string(eid),
127
+	}
128
+	return d.call("DeleteEndpoint", delete, &deleteEndpointResponse{})
51 129
 }
52 130
 
53 131
 func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
54
-	return nil, driverapi.ErrNotImplemented
132
+	info := &endpointInfoRequest{
133
+		NetworkID:  string(nid),
134
+		EndpointID: string(eid),
135
+	}
136
+	var res endpointInfoResponse
137
+	if err := d.call("EndpointOperInfo", info, &res); err != nil {
138
+		return nil, err
139
+	}
140
+	return res.Value, nil
55 141
 }
56 142
 
57 143
 // Join method is invoked when a Sandbox is attached to an endpoint.
58 144
 func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
59
-	return driverapi.ErrNotImplemented
145
+	join := &joinRequest{
146
+		NetworkID:  string(nid),
147
+		EndpointID: string(eid),
148
+		SandboxKey: sboxKey,
149
+		Options:    options,
150
+	}
151
+	var (
152
+		res joinResponse
153
+		err error
154
+	)
155
+	if err = d.call("Join", join, &res); err != nil {
156
+		return err
157
+	}
158
+
159
+	// Expect each interface ID given by CreateEndpoint to have an
160
+	// entry at that index in the names supplied here. In other words,
161
+	// if you supply 0..n interfaces with IDs 0..n above, you should
162
+	// supply the names in the same order.
163
+	ifaceNames := res.InterfaceNames
164
+	for _, iface := range jinfo.InterfaceNames() {
165
+		i := iface.ID()
166
+		if i >= len(ifaceNames) || i < 0 {
167
+			return fmt.Errorf("no correlating interface %d in supplied interface names", i)
168
+		}
169
+		supplied := ifaceNames[i]
170
+		if err := iface.SetNames(supplied.SrcName, supplied.DstName); err != nil {
171
+			return errorWithRollback(fmt.Sprintf("failed to set interface name: %s", err), d.Leave(nid, eid))
172
+		}
173
+	}
174
+
175
+	var addr net.IP
176
+	if res.Gateway != "" {
177
+		if addr = net.ParseIP(res.Gateway); addr == nil {
178
+			return fmt.Errorf(`unable to parse Gateway "%s"`, res.Gateway)
179
+		}
180
+		if jinfo.SetGateway(addr) != nil {
181
+			return errorWithRollback(fmt.Sprintf("failed to set gateway: %v", addr), d.Leave(nid, eid))
182
+		}
183
+	}
184
+	if res.GatewayIPv6 != "" {
185
+		if addr = net.ParseIP(res.GatewayIPv6); addr == nil {
186
+			return fmt.Errorf(`unable to parse GatewayIPv6 "%s"`, res.GatewayIPv6)
187
+		}
188
+		if jinfo.SetGatewayIPv6(addr) != nil {
189
+			return errorWithRollback(fmt.Sprintf("failed to set gateway IPv6: %v", addr), d.Leave(nid, eid))
190
+		}
191
+	}
192
+	if jinfo.SetHostsPath(res.HostsPath) != nil {
193
+		return errorWithRollback(fmt.Sprintf("failed to set hosts path: %s", res.HostsPath), d.Leave(nid, eid))
194
+	}
195
+	if jinfo.SetResolvConfPath(res.ResolvConfPath) != nil {
196
+		return errorWithRollback(fmt.Sprintf("failed to set resolv.conf path: %s", res.ResolvConfPath), d.Leave(nid, eid))
197
+	}
198
+	return nil
60 199
 }
61 200
 
62 201
 // Leave method is invoked when a Sandbox detaches from an endpoint.
63 202
 func (d *driver) Leave(nid, eid types.UUID) error {
64
-	return driverapi.ErrNotImplemented
203
+	leave := &leaveRequest{
204
+		NetworkID:  string(nid),
205
+		EndpointID: string(eid),
206
+	}
207
+	return d.call("Leave", leave, &leaveResponse{})
65 208
 }
66 209
 
67 210
 func (d *driver) Type() string {
68 211
new file mode 100644
... ...
@@ -0,0 +1,397 @@
0
+package remote
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"net"
6
+	"net/http"
7
+	"os"
8
+	"testing"
9
+
10
+	"github.com/docker/docker/pkg/plugins"
11
+	"github.com/docker/libnetwork/driverapi"
12
+	_ "github.com/docker/libnetwork/netutils"
13
+	"github.com/docker/libnetwork/types"
14
+)
15
+
16
+func decodeToMap(r *http.Request) (res map[string]interface{}, err error) {
17
+	err = json.NewDecoder(r.Body).Decode(&res)
18
+	return
19
+}
20
+
21
+func handle(t *testing.T, mux *http.ServeMux, method string, h func(map[string]interface{}) interface{}) {
22
+	mux.HandleFunc(fmt.Sprintf("/%s.%s", driverapi.NetworkPluginEndpointType, method), func(w http.ResponseWriter, r *http.Request) {
23
+		ask, err := decodeToMap(r)
24
+		if err != nil {
25
+			t.Fatal(err)
26
+		}
27
+		answer := h(ask)
28
+		err = json.NewEncoder(w).Encode(&answer)
29
+		if err != nil {
30
+			t.Fatal(err)
31
+		}
32
+	})
33
+}
34
+
35
+func setupPlugin(t *testing.T, name string, mux *http.ServeMux) func() {
36
+	if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil {
37
+		t.Fatal(err)
38
+	}
39
+
40
+	listener, err := net.Listen("unix", fmt.Sprintf("/usr/share/docker/plugins/%s.sock", name))
41
+	if err != nil {
42
+		t.Fatal("Could not listen to the plugin socket")
43
+	}
44
+
45
+	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
46
+		fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType)
47
+	})
48
+
49
+	go http.Serve(listener, mux)
50
+
51
+	return func() {
52
+		listener.Close()
53
+		if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil {
54
+			t.Fatal(err)
55
+		}
56
+	}
57
+}
58
+
59
+type testEndpoint struct {
60
+	t              *testing.T
61
+	id             int
62
+	src            string
63
+	dst            string
64
+	address        string
65
+	addressIPv6    string
66
+	macAddress     string
67
+	gateway        string
68
+	gatewayIPv6    string
69
+	resolvConfPath string
70
+	hostsPath      string
71
+}
72
+
73
+func (test *testEndpoint) Interfaces() []driverapi.InterfaceInfo {
74
+	// return an empty one so we don't trip the check for existing
75
+	// interfaces; we don't care about this after that
76
+	return []driverapi.InterfaceInfo{}
77
+}
78
+
79
+func (test *testEndpoint) AddInterface(ID int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
80
+	if ID != test.id {
81
+		test.t.Fatalf("Wrong ID passed to AddInterface: %d", ID)
82
+	}
83
+	ip4, net4, _ := net.ParseCIDR(test.address)
84
+	ip6, net6, _ := net.ParseCIDR(test.addressIPv6)
85
+	if ip4 != nil {
86
+		net4.IP = ip4
87
+		if !types.CompareIPNet(net4, &ipv4) {
88
+			test.t.Fatalf("Wrong address given %+v", ipv4)
89
+		}
90
+	}
91
+	if ip6 != nil {
92
+		net6.IP = ip6
93
+		if !types.CompareIPNet(net6, &ipv6) {
94
+			test.t.Fatalf("Wrong address (IPv6) given %+v", ipv6)
95
+		}
96
+	}
97
+	if test.macAddress != "" && mac.String() != test.macAddress {
98
+		test.t.Fatalf("Wrong MAC address given %v", mac)
99
+	}
100
+	return nil
101
+}
102
+
103
+func (test *testEndpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
104
+	return []driverapi.InterfaceNameInfo{test}
105
+}
106
+
107
+func compareIPs(t *testing.T, kind string, shouldBe string, supplied net.IP) {
108
+	ip := net.ParseIP(shouldBe)
109
+	if ip == nil {
110
+		t.Fatalf(`Invalid IP to test against: "%s"`, shouldBe)
111
+	}
112
+	if !ip.Equal(supplied) {
113
+		t.Fatalf(`%s IPs are not equal: expected "%s", got %v`, kind, shouldBe, supplied)
114
+	}
115
+}
116
+
117
+func (test *testEndpoint) SetGateway(ipv4 net.IP) error {
118
+	compareIPs(test.t, "Gateway", test.gateway, ipv4)
119
+	return nil
120
+}
121
+
122
+func (test *testEndpoint) SetGatewayIPv6(ipv6 net.IP) error {
123
+	compareIPs(test.t, "GatewayIPv6", test.gatewayIPv6, ipv6)
124
+	return nil
125
+}
126
+
127
+func (test *testEndpoint) SetHostsPath(p string) error {
128
+	if p != test.hostsPath {
129
+		test.t.Fatalf(`Wrong HostsPath; expected "%s", got "%s"`, test.hostsPath, p)
130
+	}
131
+	return nil
132
+}
133
+
134
+func (test *testEndpoint) SetResolvConfPath(p string) error {
135
+	if p != test.resolvConfPath {
136
+		test.t.Fatalf(`Wrong ResolvConfPath; expected "%s", got "%s"`, test.resolvConfPath, p)
137
+	}
138
+	return nil
139
+}
140
+
141
+func (test *testEndpoint) SetNames(src string, dst string) error {
142
+	if test.src != src {
143
+		test.t.Fatalf(`Wrong SrcName; expected "%s", got "%s"`, test.src, src)
144
+	}
145
+	if test.dst != dst {
146
+		test.t.Fatalf(`Wrong DstName; expected "%s", got "%s"`, test.dst, dst)
147
+	}
148
+	return nil
149
+}
150
+
151
+func (test *testEndpoint) ID() int {
152
+	return test.id
153
+}
154
+
155
+func TestRemoteDriver(t *testing.T) {
156
+	var plugin = "test-net-driver"
157
+
158
+	ep := &testEndpoint{
159
+		t:              t,
160
+		src:            "vethsrc",
161
+		dst:            "vethdst",
162
+		address:        "192.168.5.7/16",
163
+		addressIPv6:    "2001:DB8::5:7/48",
164
+		macAddress:     "7a:56:78:34:12:da",
165
+		gateway:        "192.168.0.1",
166
+		gatewayIPv6:    "2001:DB8::1",
167
+		hostsPath:      "/here/comes/the/host/path",
168
+		resolvConfPath: "/there/goes/the/resolv/conf",
169
+	}
170
+
171
+	mux := http.NewServeMux()
172
+	defer setupPlugin(t, plugin, mux)()
173
+
174
+	var networkID string
175
+
176
+	handle(t, mux, "CreateNetwork", func(msg map[string]interface{}) interface{} {
177
+		nid := msg["NetworkID"]
178
+		var ok bool
179
+		if networkID, ok = nid.(string); !ok {
180
+			t.Fatal("RPC did not include network ID string")
181
+		}
182
+		return map[string]interface{}{}
183
+	})
184
+	handle(t, mux, "DeleteNetwork", func(msg map[string]interface{}) interface{} {
185
+		if nid, ok := msg["NetworkID"]; !ok || nid != networkID {
186
+			t.Fatal("Network ID missing or does not match that created")
187
+		}
188
+		return map[string]interface{}{}
189
+	})
190
+	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
191
+		iface := map[string]interface{}{
192
+			"ID":          ep.id,
193
+			"Address":     ep.address,
194
+			"AddressIPv6": ep.addressIPv6,
195
+			"MacAddress":  ep.macAddress,
196
+		}
197
+		return map[string]interface{}{
198
+			"Interfaces": []interface{}{iface},
199
+		}
200
+	})
201
+	handle(t, mux, "Join", func(msg map[string]interface{}) interface{} {
202
+		options := msg["Options"].(map[string]interface{})
203
+		foo, ok := options["foo"].(string)
204
+		if !ok || foo != "fooValue" {
205
+			t.Fatalf("Did not receive expected foo string in request options: %+v", msg)
206
+		}
207
+		return map[string]interface{}{
208
+			"Gateway":        ep.gateway,
209
+			"GatewayIPv6":    ep.gatewayIPv6,
210
+			"HostsPath":      ep.hostsPath,
211
+			"ResolvConfPath": ep.resolvConfPath,
212
+			"InterfaceNames": []map[string]interface{}{
213
+				map[string]interface{}{
214
+					"SrcName": ep.src,
215
+					"DstName": ep.dst,
216
+				},
217
+			},
218
+		}
219
+	})
220
+	handle(t, mux, "Leave", func(msg map[string]interface{}) interface{} {
221
+		return map[string]string{}
222
+	})
223
+	handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} {
224
+		return map[string]interface{}{}
225
+	})
226
+	handle(t, mux, "EndpointOperInfo", func(msg map[string]interface{}) interface{} {
227
+		return map[string]interface{}{
228
+			"Value": map[string]string{
229
+				"Arbitrary": "key",
230
+				"Value":     "pairs?",
231
+			},
232
+		}
233
+	})
234
+
235
+	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
236
+	if err != nil {
237
+		t.Fatal(err)
238
+	}
239
+
240
+	driver := newDriver(plugin, p.Client)
241
+	if driver.Type() != plugin {
242
+		t.Fatal("Driver type does not match that given")
243
+	}
244
+
245
+	netID := types.UUID("dummy-network")
246
+	err = driver.CreateNetwork(netID, map[string]interface{}{})
247
+	if err != nil {
248
+		t.Fatal(err)
249
+	}
250
+
251
+	endID := types.UUID("dummy-endpoint")
252
+	err = driver.CreateEndpoint(netID, endID, ep, map[string]interface{}{})
253
+	if err != nil {
254
+		t.Fatal(err)
255
+	}
256
+
257
+	joinOpts := map[string]interface{}{"foo": "fooValue"}
258
+	err = driver.Join(netID, endID, "sandbox-key", ep, joinOpts)
259
+	if err != nil {
260
+		t.Fatal(err)
261
+	}
262
+	if _, err = driver.EndpointOperInfo(netID, endID); err != nil {
263
+		t.Fatal(err)
264
+	}
265
+	if err = driver.Leave(netID, endID); err != nil {
266
+		t.Fatal(err)
267
+	}
268
+	if err = driver.DeleteEndpoint(netID, endID); err != nil {
269
+		t.Fatal(err)
270
+	}
271
+	if err = driver.DeleteNetwork(netID); err != nil {
272
+		t.Fatal(err)
273
+	}
274
+}
275
+
276
+type failEndpoint struct {
277
+	t *testing.T
278
+}
279
+
280
+func (f *failEndpoint) Interfaces() []*driverapi.InterfaceInfo {
281
+	f.t.Fatal("Unexpected call of Interfaces")
282
+	return nil
283
+}
284
+func (f *failEndpoint) AddInterface(int, net.HardwareAddr, net.IPNet, net.IPNet) error {
285
+	f.t.Fatal("Unexpected call of AddInterface")
286
+	return nil
287
+}
288
+
289
+func TestDriverError(t *testing.T) {
290
+	var plugin = "test-net-driver-error"
291
+
292
+	mux := http.NewServeMux()
293
+	defer setupPlugin(t, plugin, mux)()
294
+
295
+	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
296
+		return map[string]interface{}{
297
+			"Err": "this should get raised as an error",
298
+		}
299
+	})
300
+
301
+	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
302
+	if err != nil {
303
+		t.Fatal(err)
304
+	}
305
+
306
+	driver := newDriver(plugin, p.Client)
307
+
308
+	if err := driver.CreateEndpoint(types.UUID("dummy"), types.UUID("dummy"), &testEndpoint{t: t}, map[string]interface{}{}); err == nil {
309
+		t.Fatalf("Expected error from driver")
310
+	}
311
+}
312
+
313
+func TestMissingValues(t *testing.T) {
314
+	var plugin = "test-net-driver-missing"
315
+
316
+	mux := http.NewServeMux()
317
+	defer setupPlugin(t, plugin, mux)()
318
+
319
+	ep := &testEndpoint{
320
+		t:  t,
321
+		id: 0,
322
+	}
323
+
324
+	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
325
+		iface := map[string]interface{}{
326
+			"ID":          ep.id,
327
+			"Address":     ep.address,
328
+			"AddressIPv6": ep.addressIPv6,
329
+			"MacAddress":  ep.macAddress,
330
+		}
331
+		return map[string]interface{}{
332
+			"Interfaces": []interface{}{iface},
333
+		}
334
+	})
335
+
336
+	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
337
+	if err != nil {
338
+		t.Fatal(err)
339
+	}
340
+	driver := newDriver(plugin, p.Client)
341
+
342
+	if err := driver.CreateEndpoint(types.UUID("dummy"), types.UUID("dummy"), ep, map[string]interface{}{}); err != nil {
343
+		t.Fatal(err)
344
+	}
345
+}
346
+
347
+type rollbackEndpoint struct {
348
+}
349
+
350
+func (r *rollbackEndpoint) Interfaces() []driverapi.InterfaceInfo {
351
+	return []driverapi.InterfaceInfo{}
352
+}
353
+
354
+func (r *rollbackEndpoint) AddInterface(_ int, _ net.HardwareAddr, _ net.IPNet, _ net.IPNet) error {
355
+	return fmt.Errorf("fail this to trigger a rollback")
356
+}
357
+
358
+func TestRollback(t *testing.T) {
359
+	var plugin = "test-net-driver-rollback"
360
+
361
+	mux := http.NewServeMux()
362
+	defer setupPlugin(t, plugin, mux)()
363
+
364
+	rolledback := false
365
+
366
+	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
367
+		iface := map[string]interface{}{
368
+			"ID":          0,
369
+			"Address":     "192.168.4.5/16",
370
+			"AddressIPv6": "",
371
+			"MacAddress":  "7a:12:34:56:78:90",
372
+		}
373
+		return map[string]interface{}{
374
+			"Interfaces": []interface{}{iface},
375
+		}
376
+	})
377
+	handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} {
378
+		rolledback = true
379
+		return map[string]interface{}{}
380
+	})
381
+
382
+	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
383
+	if err != nil {
384
+		t.Fatal(err)
385
+	}
386
+	driver := newDriver(plugin, p.Client)
387
+
388
+	ep := &rollbackEndpoint{}
389
+
390
+	if err := driver.CreateEndpoint(types.UUID("dummy"), types.UUID("dummy"), ep, map[string]interface{}{}); err == nil {
391
+		t.Fatalf("Expected error from driver")
392
+	}
393
+	if !rolledback {
394
+		t.Fatalf("Expected to have had DeleteEndpoint called")
395
+	}
396
+}
0 397
new file mode 100644
... ...
@@ -0,0 +1,143 @@
0
+package remote
1
+
2
+import "net"
3
+
4
+type response struct {
5
+	Err string
6
+}
7
+
8
+type maybeError interface {
9
+	getError() string
10
+}
11
+
12
+func (r *response) getError() string {
13
+	return r.Err
14
+}
15
+
16
+type createNetworkRequest struct {
17
+	NetworkID string
18
+	Options   map[string]interface{}
19
+}
20
+
21
+type createNetworkResponse struct {
22
+	response
23
+}
24
+
25
+type deleteNetworkRequest struct {
26
+	NetworkID string
27
+}
28
+
29
+type deleteNetworkResponse struct {
30
+	response
31
+}
32
+
33
+type createEndpointRequest struct {
34
+	NetworkID  string
35
+	EndpointID string
36
+	Interfaces []*endpointInterface
37
+	Options    map[string]interface{}
38
+}
39
+
40
+type endpointInterface struct {
41
+	ID          int
42
+	Address     string
43
+	AddressIPv6 string
44
+	MacAddress  string
45
+}
46
+
47
+type createEndpointResponse struct {
48
+	response
49
+	Interfaces []*endpointInterface
50
+}
51
+
52
+func toAddr(ipAddr string) (*net.IPNet, error) {
53
+	ip, ipnet, err := net.ParseCIDR(ipAddr)
54
+	if err != nil {
55
+		return nil, err
56
+	}
57
+	ipnet.IP = ip
58
+	return ipnet, nil
59
+}
60
+
61
+type iface struct {
62
+	ID          int
63
+	Address     *net.IPNet
64
+	AddressIPv6 *net.IPNet
65
+	MacAddress  net.HardwareAddr
66
+}
67
+
68
+func (r *createEndpointResponse) parseInterfaces() ([]*iface, error) {
69
+	var (
70
+		ifaces = make([]*iface, len(r.Interfaces))
71
+	)
72
+	for i, inIf := range r.Interfaces {
73
+		var err error
74
+		outIf := &iface{ID: inIf.ID}
75
+		if inIf.Address != "" {
76
+			if outIf.Address, err = toAddr(inIf.Address); err != nil {
77
+				return nil, err
78
+			}
79
+		}
80
+		if inIf.AddressIPv6 != "" {
81
+			if outIf.AddressIPv6, err = toAddr(inIf.AddressIPv6); err != nil {
82
+				return nil, err
83
+			}
84
+		}
85
+		if inIf.MacAddress != "" {
86
+			if outIf.MacAddress, err = net.ParseMAC(inIf.MacAddress); err != nil {
87
+				return nil, err
88
+			}
89
+		}
90
+		ifaces[i] = outIf
91
+	}
92
+	return ifaces, nil
93
+}
94
+
95
+type deleteEndpointRequest struct {
96
+	NetworkID  string
97
+	EndpointID string
98
+}
99
+
100
+type deleteEndpointResponse struct {
101
+	response
102
+}
103
+
104
+type endpointInfoRequest struct {
105
+	NetworkID  string
106
+	EndpointID string
107
+}
108
+
109
+type endpointInfoResponse struct {
110
+	response
111
+	Value map[string]interface{}
112
+}
113
+
114
+type joinRequest struct {
115
+	NetworkID  string
116
+	EndpointID string
117
+	SandboxKey string
118
+	Options    map[string]interface{}
119
+}
120
+
121
+type ifaceName struct {
122
+	SrcName string
123
+	DstName string
124
+}
125
+
126
+type joinResponse struct {
127
+	response
128
+	InterfaceNames []*ifaceName
129
+	Gateway        string
130
+	GatewayIPv6    string
131
+	HostsPath      string
132
+	ResolvConfPath string
133
+}
134
+
135
+type leaveRequest struct {
136
+	NetworkID  string
137
+	EndpointID string
138
+}
139
+
140
+type leaveResponse struct {
141
+	response
142
+}
... ...
@@ -12,7 +12,6 @@ import (
12 12
 	"github.com/docker/docker/pkg/ioutils"
13 13
 	"github.com/docker/libnetwork/etchosts"
14 14
 	"github.com/docker/libnetwork/netlabel"
15
-	"github.com/docker/libnetwork/netutils"
16 15
 	"github.com/docker/libnetwork/resolvconf"
17 16
 	"github.com/docker/libnetwork/sandbox"
18 17
 	"github.com/docker/libnetwork/types"
... ...
@@ -106,7 +105,7 @@ type endpoint struct {
106 106
 	iFaces        []*endpointInterface
107 107
 	joinInfo      *endpointJoinInfo
108 108
 	container     *containerInfo
109
-	exposedPorts  []netutils.TransportPort
109
+	exposedPorts  []types.TransportPort
110 110
 	generic       map[string]interface{}
111 111
 	joinLeaveDone chan struct{}
112 112
 	sync.Mutex
... ...
@@ -217,7 +216,7 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
217 217
 	ep.Lock()
218 218
 	if ep.container != nil {
219 219
 		ep.Unlock()
220
-		return nil, ErrInvalidJoin
220
+		return nil, ErrInvalidJoin{}
221 221
 	}
222 222
 
223 223
 	ep.container = &containerInfo{
... ...
@@ -292,7 +291,7 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
292 292
 	for _, i := range ifaces {
293 293
 		iface := &sandbox.Interface{
294 294
 			SrcName: i.srcName,
295
-			DstName: i.dstName,
295
+			DstName: i.dstPrefix,
296 296
 			Address: &i.addr,
297 297
 		}
298 298
 		if i.addrv6.IP.To16() != nil {
... ...
@@ -335,7 +334,7 @@ func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error {
335 335
 	if container == nil || container.id == "" ||
336 336
 		containerID == "" || container.id != containerID {
337 337
 		if container == nil {
338
-			err = ErrNoContainer
338
+			err = ErrNoContainer{}
339 339
 		} else {
340 340
 			err = InvalidContainerIDError(containerID)
341 341
 		}
... ...
@@ -413,7 +412,7 @@ func (ep *endpoint) buildHostsFiles() error {
413 413
 	ep.Unlock()
414 414
 
415 415
 	if container == nil {
416
-		return ErrNoContainer
416
+		return ErrNoContainer{}
417 417
 	}
418 418
 
419 419
 	if container.config.hostsPath == "" {
... ...
@@ -463,7 +462,7 @@ func (ep *endpoint) updateParentHosts() error {
463 463
 	ep.Unlock()
464 464
 
465 465
 	if container == nil {
466
-		return ErrNoContainer
466
+		return ErrNoContainer{}
467 467
 	}
468 468
 
469 469
 	for _, update := range container.config.parentUpdates {
... ...
@@ -496,7 +495,7 @@ func (ep *endpoint) updateDNS(resolvConf []byte) error {
496 496
 	ep.Unlock()
497 497
 
498 498
 	if container == nil {
499
-		return ErrNoContainer
499
+		return ErrNoContainer{}
500 500
 	}
501 501
 
502 502
 	oldHash := []byte{}
... ...
@@ -574,7 +573,7 @@ func (ep *endpoint) setupDNS() error {
574 574
 	ep.Unlock()
575 575
 
576 576
 	if container == nil {
577
-		return ErrNoContainer
577
+		return ErrNoContainer{}
578 578
 	}
579 579
 
580 580
 	if container.config.resolvConfPath == "" {
... ...
@@ -697,10 +696,10 @@ func JoinOptionUseDefaultSandbox() EndpointOption {
697 697
 
698 698
 // CreateOptionExposedPorts function returns an option setter for the container exposed
699 699
 // ports option to be passed to network.CreateEndpoint() method.
700
-func CreateOptionExposedPorts(exposedPorts []netutils.TransportPort) EndpointOption {
700
+func CreateOptionExposedPorts(exposedPorts []types.TransportPort) EndpointOption {
701 701
 	return func(ep *endpoint) {
702 702
 		// Defensive copy
703
-		eps := make([]netutils.TransportPort, len(exposedPorts))
703
+		eps := make([]types.TransportPort, len(exposedPorts))
704 704
 		copy(eps, exposedPorts)
705 705
 		// Store endpoint label and in generic because driver needs it
706 706
 		ep.exposedPorts = eps
... ...
@@ -710,10 +709,10 @@ func CreateOptionExposedPorts(exposedPorts []netutils.TransportPort) EndpointOpt
710 710
 
711 711
 // CreateOptionPortMapping function returns an option setter for the mapping
712 712
 // ports option to be passed to network.CreateEndpoint() method.
713
-func CreateOptionPortMapping(portBindings []netutils.PortBinding) EndpointOption {
713
+func CreateOptionPortMapping(portBindings []types.PortBinding) EndpointOption {
714 714
 	return func(ep *endpoint) {
715 715
 		// Store a copy of the bindings as generic data to pass to the driver
716
-		pbs := make([]netutils.PortBinding, len(portBindings))
716
+		pbs := make([]types.PortBinding, len(portBindings))
717 717
 		copy(pbs, portBindings)
718 718
 		ep.generic[netlabel.PortMap] = pbs
719 719
 	}
... ...
@@ -4,7 +4,7 @@ import (
4 4
 	"net"
5 5
 
6 6
 	"github.com/docker/libnetwork/driverapi"
7
-	"github.com/docker/libnetwork/netutils"
7
+	"github.com/docker/libnetwork/types"
8 8
 )
9 9
 
10 10
 // EndpointInfo provides an interface to retrieve network resources bound to the endpoint.
... ...
@@ -40,12 +40,12 @@ type InterfaceInfo interface {
40 40
 }
41 41
 
42 42
 type endpointInterface struct {
43
-	id      int
44
-	mac     net.HardwareAddr
45
-	addr    net.IPNet
46
-	addrv6  net.IPNet
47
-	srcName string
48
-	dstName string
43
+	id        int
44
+	mac       net.HardwareAddr
45
+	addr      net.IPNet
46
+	addrv6    net.IPNet
47
+	srcName   string
48
+	dstPrefix string
49 49
 }
50 50
 
51 51
 type endpointJoinInfo struct {
... ...
@@ -105,10 +105,10 @@ func (ep *endpoint) AddInterface(id int, mac net.HardwareAddr, ipv4 net.IPNet, i
105 105
 
106 106
 	iface := &endpointInterface{
107 107
 		id:     id,
108
-		addr:   *netutils.GetIPNetCopy(&ipv4),
109
-		addrv6: *netutils.GetIPNetCopy(&ipv6),
108
+		addr:   *types.GetIPNetCopy(&ipv4),
109
+		addrv6: *types.GetIPNetCopy(&ipv6),
110 110
 	}
111
-	iface.mac = netutils.GetMacCopy(mac)
111
+	iface.mac = types.GetMacCopy(mac)
112 112
 
113 113
 	ep.iFaces = append(ep.iFaces, iface)
114 114
 	return nil
... ...
@@ -119,20 +119,20 @@ func (i *endpointInterface) ID() int {
119 119
 }
120 120
 
121 121
 func (i *endpointInterface) MacAddress() net.HardwareAddr {
122
-	return netutils.GetMacCopy(i.mac)
122
+	return types.GetMacCopy(i.mac)
123 123
 }
124 124
 
125 125
 func (i *endpointInterface) Address() net.IPNet {
126
-	return (*netutils.GetIPNetCopy(&i.addr))
126
+	return (*types.GetIPNetCopy(&i.addr))
127 127
 }
128 128
 
129 129
 func (i *endpointInterface) AddressIPv6() net.IPNet {
130
-	return (*netutils.GetIPNetCopy(&i.addrv6))
130
+	return (*types.GetIPNetCopy(&i.addrv6))
131 131
 }
132 132
 
133
-func (i *endpointInterface) SetNames(srcName string, dstName string) error {
133
+func (i *endpointInterface) SetNames(srcName string, dstPrefix string) error {
134 134
 	i.srcName = srcName
135
-	i.dstName = dstName
135
+	i.dstPrefix = dstPrefix
136 136
 	return nil
137 137
 }
138 138
 
... ...
@@ -168,7 +168,7 @@ func (ep *endpoint) Gateway() net.IP {
168 168
 		return net.IP{}
169 169
 	}
170 170
 
171
-	return netutils.GetIPCopy(ep.joinInfo.gw)
171
+	return types.GetIPCopy(ep.joinInfo.gw)
172 172
 }
173 173
 
174 174
 func (ep *endpoint) GatewayIPv6() net.IP {
... ...
@@ -179,14 +179,14 @@ func (ep *endpoint) GatewayIPv6() net.IP {
179 179
 		return net.IP{}
180 180
 	}
181 181
 
182
-	return netutils.GetIPCopy(ep.joinInfo.gw6)
182
+	return types.GetIPCopy(ep.joinInfo.gw6)
183 183
 }
184 184
 
185 185
 func (ep *endpoint) SetGateway(gw net.IP) error {
186 186
 	ep.Lock()
187 187
 	defer ep.Unlock()
188 188
 
189
-	ep.joinInfo.gw = netutils.GetIPCopy(gw)
189
+	ep.joinInfo.gw = types.GetIPCopy(gw)
190 190
 	return nil
191 191
 }
192 192
 
... ...
@@ -194,7 +194,7 @@ func (ep *endpoint) SetGatewayIPv6(gw6 net.IP) error {
194 194
 	ep.Lock()
195 195
 	defer ep.Unlock()
196 196
 
197
-	ep.joinInfo.gw6 = netutils.GetIPCopy(gw6)
197
+	ep.joinInfo.gw6 = types.GetIPCopy(gw6)
198 198
 	return nil
199 199
 }
200 200
 
... ...
@@ -1,34 +1,83 @@
1 1
 package libnetwork
2 2
 
3 3
 import (
4
-	"errors"
5 4
 	"fmt"
6 5
 )
7 6
 
8
-var (
9
-	// ErrNoSuchNetwork is returned when a network query finds no result
10
-	ErrNoSuchNetwork = errors.New("network not found")
11
-	// ErrNoSuchEndpoint is returned when a endpoint query finds no result
12
-	ErrNoSuchEndpoint = errors.New("endpoint not found")
13
-	// ErrNilNetworkDriver is returned if a nil network driver
14
-	// is passed to NewNetwork api.
15
-	ErrNilNetworkDriver = errors.New("nil NetworkDriver instance")
16
-	// ErrInvalidNetworkDriver is returned if an invalid driver
17
-	// instance is passed.
18
-	ErrInvalidNetworkDriver = errors.New("invalid driver bound to network")
19
-	// ErrInvalidJoin is returned if a join is attempted on an endpoint
20
-	// which already has a container joined.
21
-	ErrInvalidJoin = errors.New("a container has already joined the endpoint")
22
-	// ErrNoContainer is returned when the endpoint has no container
23
-	// attached to it.
24
-	ErrNoContainer = errors.New("no container attached to the endpoint")
25
-	// ErrInvalidID is returned when a query-by-id method is being invoked
26
-	// with an empty id parameter
27
-	ErrInvalidID = errors.New("invalid ID")
28
-	// ErrInvalidName is returned when a query-by-name or resource create method is
29
-	// invoked with an empty name parameter
30
-	ErrInvalidName = errors.New("invalid Name")
31
-)
7
+// ErrNoSuchNetwork is returned when a network query finds no result
8
+type ErrNoSuchNetwork string
9
+
10
+func (nsn ErrNoSuchNetwork) Error() string {
11
+	return fmt.Sprintf("network %s not found", string(nsn))
12
+}
13
+
14
+// BadRequest denotes the type of this error
15
+func (nsn ErrNoSuchNetwork) BadRequest() {}
16
+
17
+// ErrNoSuchEndpoint is returned when a endpoint query finds no result
18
+type ErrNoSuchEndpoint string
19
+
20
+func (nse ErrNoSuchEndpoint) Error() string {
21
+	return fmt.Sprintf("endpoint %s not found", string(nse))
22
+}
23
+
24
+// BadRequest denotes the type of this error
25
+func (nse ErrNoSuchEndpoint) BadRequest() {}
26
+
27
+// ErrInvalidNetworkDriver is returned if an invalid driver
28
+// name is passed.
29
+type ErrInvalidNetworkDriver string
30
+
31
+func (ind ErrInvalidNetworkDriver) Error() string {
32
+	return fmt.Sprintf("invalid driver bound to network: %s", string(ind))
33
+}
34
+
35
+// BadRequest denotes the type of this error
36
+func (ind ErrInvalidNetworkDriver) BadRequest() {}
37
+
38
+// ErrInvalidJoin is returned if a join is attempted on an endpoint
39
+// which already has a container joined.
40
+type ErrInvalidJoin struct{}
41
+
42
+func (ij ErrInvalidJoin) Error() string {
43
+	return "a container has already joined the endpoint"
44
+}
45
+
46
+// BadRequest denotes the type of this error
47
+func (ij ErrInvalidJoin) BadRequest() {}
48
+
49
+// ErrNoContainer is returned when the endpoint has no container
50
+// attached to it.
51
+type ErrNoContainer struct{}
52
+
53
+func (nc ErrNoContainer) Error() string {
54
+	return "a container has already joined the endpoint"
55
+}
56
+
57
+// Maskable denotes the type of this error
58
+func (nc ErrNoContainer) Maskable() {}
59
+
60
+// ErrInvalidID is returned when a query-by-id method is being invoked
61
+// with an empty id parameter
62
+type ErrInvalidID string
63
+
64
+func (ii ErrInvalidID) Error() string {
65
+	return fmt.Sprintf("invalid id: %s", string(ii))
66
+}
67
+
68
+// BadRequest denotes the type of this error
69
+func (ii ErrInvalidID) BadRequest() {}
70
+
71
+// ErrInvalidName is returned when a query-by-name or resource create method is
72
+// invoked with an empty name parameter
73
+type ErrInvalidName string
74
+
75
+func (in ErrInvalidName) Error() string {
76
+	return fmt.Sprintf("invalid name: %s", string(in))
77
+}
78
+
79
+// BadRequest denotes the type of this error
80
+func (in ErrInvalidName) BadRequest() {}
32 81
 
33 82
 // NetworkTypeError type is returned when the network type string is not
34 83
 // known to libnetwork.
... ...
@@ -38,13 +87,19 @@ func (nt NetworkTypeError) Error() string {
38 38
 	return fmt.Sprintf("unknown driver %q", string(nt))
39 39
 }
40 40
 
41
+// NotFound denotes the type of this error
42
+func (nt NetworkTypeError) NotFound() {}
43
+
41 44
 // NetworkNameError is returned when a network with the same name already exists.
42 45
 type NetworkNameError string
43 46
 
44
-func (name NetworkNameError) Error() string {
45
-	return fmt.Sprintf("network with name %s already exists", string(name))
47
+func (nnr NetworkNameError) Error() string {
48
+	return fmt.Sprintf("network with name %s already exists", string(nnr))
46 49
 }
47 50
 
51
+// Forbidden denotes the type of this error
52
+func (nnr NetworkNameError) Forbidden() {}
53
+
48 54
 // UnknownNetworkError is returned when libnetwork could not find in it's database
49 55
 // a network with the same name and id.
50 56
 type UnknownNetworkError struct {
... ...
@@ -56,6 +111,9 @@ func (une *UnknownNetworkError) Error() string {
56 56
 	return fmt.Sprintf("unknown network %s id %s", une.name, une.id)
57 57
 }
58 58
 
59
+// NotFound denotes the type of this error
60
+func (une *UnknownNetworkError) NotFound() {}
61
+
59 62
 // ActiveEndpointsError is returned when a network is deleted which has active
60 63
 // endpoints in it.
61 64
 type ActiveEndpointsError struct {
... ...
@@ -67,6 +125,9 @@ func (aee *ActiveEndpointsError) Error() string {
67 67
 	return fmt.Sprintf("network with name %s id %s has active endpoints", aee.name, aee.id)
68 68
 }
69 69
 
70
+// Forbidden denotes the type of this error
71
+func (aee *ActiveEndpointsError) Forbidden() {}
72
+
70 73
 // UnknownEndpointError is returned when libnetwork could not find in it's database
71 74
 // an endpoint with the same name and id.
72 75
 type UnknownEndpointError struct {
... ...
@@ -78,6 +139,9 @@ func (uee *UnknownEndpointError) Error() string {
78 78
 	return fmt.Sprintf("unknown endpoint %s id %s", uee.name, uee.id)
79 79
 }
80 80
 
81
+// NotFound denotes the type of this error
82
+func (uee *UnknownEndpointError) NotFound() {}
83
+
81 84
 // ActiveContainerError is returned when an endpoint is deleted which has active
82 85
 // containers attached to it.
83 86
 type ActiveContainerError struct {
... ...
@@ -89,6 +153,9 @@ func (ace *ActiveContainerError) Error() string {
89 89
 	return fmt.Sprintf("endpoint with name %s id %s has active containers", ace.name, ace.id)
90 90
 }
91 91
 
92
+// Forbidden denotes the type of this error
93
+func (ace *ActiveContainerError) Forbidden() {}
94
+
92 95
 // InvalidContainerIDError is returned when an invalid container id is passed
93 96
 // in Join/Leave
94 97
 type InvalidContainerIDError string
... ...
@@ -96,3 +163,6 @@ type InvalidContainerIDError string
96 96
 func (id InvalidContainerIDError) Error() string {
97 97
 	return fmt.Sprintf("invalid container id %s", string(id))
98 98
 }
99
+
100
+// BadRequest denotes the type of this error
101
+func (id InvalidContainerIDError) BadRequest() {}
99 102
new file mode 100644
... ...
@@ -0,0 +1,51 @@
0
+package libnetwork
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/docker/libnetwork/types"
6
+)
7
+
8
+func TestErrorInterfaces(t *testing.T) {
9
+
10
+	badRequestErrorList := []error{ErrInvalidID(""), ErrInvalidName(""), ErrInvalidJoin{}, ErrInvalidNetworkDriver(""), InvalidContainerIDError(""), ErrNoSuchNetwork(""), ErrNoSuchEndpoint("")}
11
+	for _, err := range badRequestErrorList {
12
+		switch u := err.(type) {
13
+		case types.BadRequestError:
14
+			return
15
+		default:
16
+			t.Fatalf("Failed to detect err %v is of type BadRequestError. Got type: %T", err, u)
17
+		}
18
+	}
19
+
20
+	maskableErrorList := []error{ErrNoContainer{}}
21
+	for _, err := range maskableErrorList {
22
+		switch u := err.(type) {
23
+		case types.MaskableError:
24
+			return
25
+		default:
26
+			t.Fatalf("Failed to detect err %v is of type MaskableError. Got type: %T", err, u)
27
+		}
28
+	}
29
+
30
+	notFoundErrorList := []error{NetworkTypeError(""), &UnknownNetworkError{}, &UnknownEndpointError{}}
31
+	for _, err := range notFoundErrorList {
32
+		switch u := err.(type) {
33
+		case types.NotFoundError:
34
+			return
35
+		default:
36
+			t.Fatalf("Failed to detect err %v is of type NotFoundError. Got type: %T", err, u)
37
+		}
38
+	}
39
+
40
+	forbiddenErrorList := []error{NetworkTypeError(""), &UnknownNetworkError{}, &UnknownEndpointError{}}
41
+	for _, err := range forbiddenErrorList {
42
+		switch u := err.(type) {
43
+		case types.ForbiddenError:
44
+			return
45
+		default:
46
+			t.Fatalf("Failed to detect err %v is of type ForbiddenError. Got type: %T", err, u)
47
+		}
48
+	}
49
+
50
+}
... ...
@@ -11,7 +11,7 @@ import (
11 11
 	_ "github.com/docker/libnetwork/netutils"
12 12
 )
13 13
 
14
-const chainName = "DOCKER-TEST"
14
+const chainName = "DOCKEREST"
15 15
 
16 16
 var natChain *Chain
17 17
 var filterChain *Chain
... ...
@@ -22,6 +22,8 @@ import (
22 22
 	"github.com/docker/libnetwork/netlabel"
23 23
 	"github.com/docker/libnetwork/netutils"
24 24
 	"github.com/docker/libnetwork/options"
25
+	"github.com/docker/libnetwork/types"
26
+	"github.com/vishvananda/netlink"
25 27
 	"github.com/vishvananda/netns"
26 28
 )
27 29
 
... ...
@@ -65,11 +67,11 @@ func getEmptyGenericOption() map[string]interface{} {
65 65
 	return genericOption
66 66
 }
67 67
 
68
-func getPortMapping() []netutils.PortBinding {
69
-	return []netutils.PortBinding{
70
-		netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)},
71
-		netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)},
72
-		netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)},
68
+func getPortMapping() []types.PortBinding {
69
+	return []types.PortBinding{
70
+		types.PortBinding{Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
71
+		types.PortBinding{Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
72
+		types.PortBinding{Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
73 73
 	}
74 74
 }
75 75
 
... ...
@@ -245,7 +247,7 @@ func TestBridge(t *testing.T) {
245 245
 	if !ok {
246 246
 		t.Fatalf("Could not find expected info in endpoint data")
247 247
 	}
248
-	pm, ok := pmd.([]netutils.PortBinding)
248
+	pm, ok := pmd.([]types.PortBinding)
249 249
 	if !ok {
250 250
 		t.Fatalf("Unexpected format for port mapping in endpoint operational data")
251 251
 	}
... ...
@@ -289,7 +291,7 @@ func TestNilRemoteDriver(t *testing.T) {
289 289
 		t.Fatal("Expected to fail. But instead succeeded")
290 290
 	}
291 291
 
292
-	if err != plugins.ErrNotFound {
292
+	if _, ok := err.(types.NotFoundError); !ok {
293 293
 		t.Fatalf("Did not fail with expected error. Actual error: %v", err)
294 294
 	}
295 295
 }
... ...
@@ -337,8 +339,9 @@ func TestNetworkName(t *testing.T) {
337 337
 	if err == nil {
338 338
 		t.Fatal("Expected to fail. But instead succeeded")
339 339
 	}
340
-	if err != libnetwork.ErrInvalidName {
341
-		t.Fatal("Expected to fail with ErrInvalidName error")
340
+
341
+	if _, ok := err.(libnetwork.ErrInvalidName); !ok {
342
+		t.Fatalf("Expected to fail with ErrInvalidName error. Got %v", err)
342 343
 	}
343 344
 
344 345
 	networkName := "testnetwork"
... ...
@@ -474,8 +477,8 @@ func TestUnknownEndpoint(t *testing.T) {
474 474
 	if err == nil {
475 475
 		t.Fatal("Expected to fail. But instead succeeded")
476 476
 	}
477
-	if err != libnetwork.ErrInvalidName {
478
-		t.Fatal("Expected to fail with ErrInvalidName error")
477
+	if _, ok := err.(libnetwork.ErrInvalidName); !ok {
478
+		t.Fatalf("Expected to fail with ErrInvalidName error. Actual error: %v", err)
479 479
 	}
480 480
 
481 481
 	ep, err := network.CreateEndpoint("testep")
... ...
@@ -612,15 +615,15 @@ func TestControllerQuery(t *testing.T) {
612 612
 	if err == nil {
613 613
 		t.Fatalf("NetworkByName() succeeded with invalid target name")
614 614
 	}
615
-	if err != libnetwork.ErrInvalidName {
616
-		t.Fatalf("NetworkByName() failed with unexpected error: %v", err)
615
+	if _, ok := err.(libnetwork.ErrInvalidName); !ok {
616
+		t.Fatalf("Expected NetworkByName() to fail with ErrInvalidName error. Got: %v", err)
617 617
 	}
618 618
 
619 619
 	_, err = controller.NetworkByID("")
620 620
 	if err == nil {
621 621
 		t.Fatalf("NetworkByID() succeeded with invalid target id")
622 622
 	}
623
-	if err != libnetwork.ErrInvalidID {
623
+	if _, ok := err.(libnetwork.ErrInvalidID); !ok {
624 624
 		t.Fatalf("NetworkByID() failed with unexpected error: %v", err)
625 625
 	}
626 626
 
... ...
@@ -628,7 +631,7 @@ func TestControllerQuery(t *testing.T) {
628 628
 	if err == nil {
629 629
 		t.Fatalf("Unexpected success for NetworkByID(): %v", g)
630 630
 	}
631
-	if err != libnetwork.ErrNoSuchNetwork {
631
+	if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok {
632 632
 		t.Fatalf("NetworkByID() failed with unexpected error: %v", err)
633 633
 	}
634 634
 
... ...
@@ -694,15 +697,15 @@ func TestNetworkQuery(t *testing.T) {
694 694
 	if err == nil {
695 695
 		t.Fatalf("EndpointByName() succeeded with invalid target name")
696 696
 	}
697
-	if err != libnetwork.ErrInvalidName {
698
-		t.Fatalf("EndpointByName() failed with unexpected error: %v", err)
697
+	if _, ok := err.(libnetwork.ErrInvalidName); !ok {
698
+		t.Fatalf("Expected EndpointByName() to fail with ErrInvalidName error. Got: %v", err)
699 699
 	}
700 700
 
701 701
 	e, err = net1.EndpointByName("IamNotAnEndpoint")
702 702
 	if err == nil {
703 703
 		t.Fatalf("EndpointByName() succeeded with unknown target name")
704 704
 	}
705
-	if err != libnetwork.ErrNoSuchEndpoint {
705
+	if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok {
706 706
 		t.Fatal(err)
707 707
 	}
708 708
 	if e != nil {
... ...
@@ -721,13 +724,42 @@ func TestNetworkQuery(t *testing.T) {
721 721
 	if err == nil {
722 722
 		t.Fatalf("EndpointByID() succeeded with invalid target id")
723 723
 	}
724
-	if err != libnetwork.ErrInvalidID {
724
+	if _, ok := err.(libnetwork.ErrInvalidID); !ok {
725 725
 		t.Fatalf("EndpointByID() failed with unexpected error: %v", err)
726 726
 	}
727 727
 }
728 728
 
729 729
 const containerID = "valid_container"
730 730
 
731
+func checkSandbox(t *testing.T, info libnetwork.EndpointInfo) {
732
+	origns, err := netns.Get()
733
+	if err != nil {
734
+		t.Fatalf("Could not get the current netns: %v", err)
735
+	}
736
+	defer origns.Close()
737
+
738
+	key := info.SandboxKey()
739
+	f, err := os.OpenFile(key, os.O_RDONLY, 0)
740
+	if err != nil {
741
+		t.Fatalf("Failed to open network namespace path %q: %v", key, err)
742
+	}
743
+	defer f.Close()
744
+
745
+	runtime.LockOSThread()
746
+	defer runtime.UnlockOSThread()
747
+
748
+	nsFD := f.Fd()
749
+	if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
750
+		t.Fatalf("Setting to the namespace pointed to by the sandbox %s failed: %v", key, err)
751
+	}
752
+	defer netns.Set(origns)
753
+
754
+	_, err = netlink.LinkByName("eth0")
755
+	if err != nil {
756
+		t.Fatalf("Could not find the interface eth0 inside the sandbox: %v", err)
757
+	}
758
+}
759
+
731 760
 func TestEndpointJoin(t *testing.T) {
732 761
 	if !netutils.IsRunningInContainer() {
733 762
 		defer netutils.SetupTestNetNS(t)()
... ...
@@ -784,6 +816,8 @@ func TestEndpointJoin(t *testing.T) {
784 784
 	if info.SandboxKey() == "" {
785 785
 		t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found a empty sandbox key")
786 786
 	}
787
+
788
+	checkSandbox(t, info)
787 789
 }
788 790
 
789 791
 func TestEndpointJoinInvalidContainerId(t *testing.T) {
... ...
@@ -890,7 +924,7 @@ func TestEndpointMultipleJoins(t *testing.T) {
890 890
 		t.Fatal("Expected to fail multiple joins for the same endpoint")
891 891
 	}
892 892
 
893
-	if err != libnetwork.ErrInvalidJoin {
893
+	if _, ok := err.(libnetwork.ErrInvalidJoin); !ok {
894 894
 		t.Fatalf("Failed for unexpected reason: %v", err)
895 895
 	}
896 896
 }
... ...
@@ -916,7 +950,7 @@ func TestEndpointInvalidLeave(t *testing.T) {
916 916
 	}
917 917
 
918 918
 	if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
919
-		if err != libnetwork.ErrNoContainer {
919
+		if _, ok := err.(libnetwork.ErrNoContainer); !ok {
920 920
 			t.Fatalf("Failed for unexpected reason: %v", err)
921 921
 		}
922 922
 	}
... ...
@@ -1275,6 +1309,10 @@ func TestValidRemoteDriver(t *testing.T) {
1275 1275
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
1276 1276
 		fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType)
1277 1277
 	})
1278
+	mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
1279
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
1280
+		fmt.Fprintf(w, "null")
1281
+	})
1278 1282
 
1279 1283
 	if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil {
1280 1284
 		t.Fatal(err)
... ...
@@ -1296,7 +1334,7 @@ func TestValidRemoteDriver(t *testing.T) {
1296 1296
 
1297 1297
 	_, err = controller.NewNetwork("valid-network-driver", "dummy",
1298 1298
 		libnetwork.NetworkOptionGeneric(getEmptyGenericOption()))
1299
-	if err != nil && err != driverapi.ErrNotImplemented {
1299
+	if err != nil {
1300 1300
 		t.Fatal(err)
1301 1301
 	}
1302 1302
 }
... ...
@@ -1370,8 +1408,10 @@ func parallelJoin(t *testing.T, ep libnetwork.Endpoint, thrNumber int) {
1370 1370
 	_, err := ep.Join("racing_container")
1371 1371
 	runtime.LockOSThread()
1372 1372
 	if err != nil {
1373
-		if err != libnetwork.ErrNoContainer && err != libnetwork.ErrInvalidJoin {
1374
-			t.Fatal(err)
1373
+		if _, ok := err.(libnetwork.ErrNoContainer); !ok {
1374
+			if _, ok := err.(libnetwork.ErrInvalidJoin); !ok {
1375
+				t.Fatal(err)
1376
+			}
1375 1377
 		}
1376 1378
 		debugf("JE%d(%v).", thrNumber, err)
1377 1379
 	}
... ...
@@ -1383,8 +1423,10 @@ func parallelLeave(t *testing.T, ep libnetwork.Endpoint, thrNumber int) {
1383 1383
 	err := ep.Leave("racing_container")
1384 1384
 	runtime.LockOSThread()
1385 1385
 	if err != nil {
1386
-		if err != libnetwork.ErrNoContainer && err != libnetwork.ErrInvalidJoin {
1387
-			t.Fatal(err)
1386
+		if _, ok := err.(libnetwork.ErrNoContainer); !ok {
1387
+			if _, ok := err.(libnetwork.ErrInvalidJoin); !ok {
1388
+				t.Fatal(err)
1389
+			}
1388 1390
 		}
1389 1391
 		debugf("LE%d(%v).", thrNumber, err)
1390 1392
 	}
... ...
@@ -3,14 +3,12 @@
3 3
 package netutils
4 4
 
5 5
 import (
6
-	"bytes"
7 6
 	"crypto/rand"
8 7
 	"encoding/hex"
9 8
 	"errors"
10 9
 	"fmt"
11 10
 	"io"
12 11
 	"net"
13
-	"strings"
14 12
 
15 13
 	"github.com/vishvananda/netlink"
16 14
 )
... ...
@@ -26,144 +24,6 @@ var (
26 26
 	networkGetRoutesFct = netlink.RouteList
27 27
 )
28 28
 
29
-// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid.
30
-type ErrInvalidProtocolBinding string
31
-
32
-func (ipb ErrInvalidProtocolBinding) Error() string {
33
-	return fmt.Sprintf("invalid transport protocol: %s", string(ipb))
34
-}
35
-
36
-// TransportPort represent a local Layer 4 endpoint
37
-type TransportPort struct {
38
-	Proto Protocol
39
-	Port  uint16
40
-}
41
-
42
-// GetCopy returns a copy of this TransportPort structure instance
43
-func (t *TransportPort) GetCopy() TransportPort {
44
-	return TransportPort{Proto: t.Proto, Port: t.Port}
45
-}
46
-
47
-// PortBinding represent a port binding between the container an the host
48
-type PortBinding struct {
49
-	Proto    Protocol
50
-	IP       net.IP
51
-	Port     uint16
52
-	HostIP   net.IP
53
-	HostPort uint16
54
-}
55
-
56
-// HostAddr returns the host side transport address
57
-func (p PortBinding) HostAddr() (net.Addr, error) {
58
-	switch p.Proto {
59
-	case UDP:
60
-		return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
61
-	case TCP:
62
-		return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
63
-	default:
64
-		return nil, ErrInvalidProtocolBinding(p.Proto.String())
65
-	}
66
-}
67
-
68
-// ContainerAddr returns the container side transport address
69
-func (p PortBinding) ContainerAddr() (net.Addr, error) {
70
-	switch p.Proto {
71
-	case UDP:
72
-		return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil
73
-	case TCP:
74
-		return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
75
-	default:
76
-		return nil, ErrInvalidProtocolBinding(p.Proto.String())
77
-	}
78
-}
79
-
80
-// GetCopy returns a copy of this PortBinding structure instance
81
-func (p *PortBinding) GetCopy() PortBinding {
82
-	return PortBinding{
83
-		Proto:    p.Proto,
84
-		IP:       GetIPCopy(p.IP),
85
-		Port:     p.Port,
86
-		HostIP:   GetIPCopy(p.HostIP),
87
-		HostPort: p.HostPort,
88
-	}
89
-}
90
-
91
-// Equal checks if this instance of PortBinding is equal to the passed one
92
-func (p *PortBinding) Equal(o *PortBinding) bool {
93
-	if p == o {
94
-		return true
95
-	}
96
-
97
-	if o == nil {
98
-		return false
99
-	}
100
-
101
-	if p.Proto != o.Proto || p.Port != o.Port || p.HostPort != o.HostPort {
102
-		return false
103
-	}
104
-
105
-	if p.IP != nil {
106
-		if !p.IP.Equal(o.IP) {
107
-			return false
108
-		}
109
-	} else {
110
-		if o.IP != nil {
111
-			return false
112
-		}
113
-	}
114
-
115
-	if p.HostIP != nil {
116
-		if !p.HostIP.Equal(o.HostIP) {
117
-			return false
118
-		}
119
-	} else {
120
-		if o.HostIP != nil {
121
-			return false
122
-		}
123
-	}
124
-
125
-	return true
126
-}
127
-
128
-const (
129
-	// ICMP is for the ICMP ip protocol
130
-	ICMP = 1
131
-	// TCP is for the TCP ip protocol
132
-	TCP = 6
133
-	// UDP is for the UDP ip protocol
134
-	UDP = 17
135
-)
136
-
137
-// Protocol represents a IP protocol number
138
-type Protocol uint8
139
-
140
-func (p Protocol) String() string {
141
-	switch p {
142
-	case ICMP:
143
-		return "icmp"
144
-	case TCP:
145
-		return "tcp"
146
-	case UDP:
147
-		return "udp"
148
-	default:
149
-		return fmt.Sprintf("%d", p)
150
-	}
151
-}
152
-
153
-// ParseProtocol returns the respective Protocol type for the passed string
154
-func ParseProtocol(s string) Protocol {
155
-	switch strings.ToLower(s) {
156
-	case "icmp":
157
-		return ICMP
158
-	case "udp":
159
-		return UDP
160
-	case "tcp":
161
-		return TCP
162
-	default:
163
-		return 0
164
-	}
165
-}
166
-
167 29
 // CheckNameserverOverlaps checks whether the passed network overlaps with any of the nameservers
168 30
 func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
169 31
 	if len(nameservers) > 0 {
... ...
@@ -287,38 +147,3 @@ func GenerateRandomName(prefix string, size int) (string, error) {
287 287
 	}
288 288
 	return prefix + hex.EncodeToString(id)[:size], nil
289 289
 }
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
-
298
-// GetIPCopy returns a copy of the passed IP address
299
-func GetIPCopy(from net.IP) net.IP {
300
-	to := make(net.IP, len(from))
301
-	copy(to, from)
302
-	return to
303
-}
304
-
305
-// GetIPNetCopy returns a copy of the passed IP Network
306
-func GetIPNetCopy(from *net.IPNet) *net.IPNet {
307
-	if from == nil {
308
-		return nil
309
-	}
310
-	bm := make(net.IPMask, len(from.Mask))
311
-	copy(bm, from.Mask)
312
-	return &net.IPNet{IP: GetIPCopy(from.IP), Mask: bm}
313
-}
314
-
315
-// CompareIPNet returns equal if the two IP Networks are equal
316
-func CompareIPNet(a, b *net.IPNet) bool {
317
-	if a == b {
318
-		return true
319
-	}
320
-	if a == nil || b == nil {
321
-		return false
322
-	}
323
-	return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
324
-}
... ...
@@ -209,135 +209,3 @@ func TestUtilGenerateRandomMAC(t *testing.T) {
209 209
 		t.Fatalf("mac1 %s should not equal mac2 %s", mac1, mac2)
210 210
 	}
211 211
 }
212
-
213
-func TestCompareIPNet(t *testing.T) {
214
-	if CompareIPNet(nil, nil) == false {
215
-		t.Fatalf("Failed to detect two nil net.IPNets are equal")
216
-	}
217
-
218
-	_, net1, _ := net.ParseCIDR("192.168.30.22/24")
219
-	if CompareIPNet(net1, net1) == false {
220
-		t.Fatalf("Failed to detect same net.IPNet pointers equality")
221
-	}
222
-
223
-	_, net2, _ := net.ParseCIDR("192.168.30.22/24")
224
-	if CompareIPNet(net1, net2) == false {
225
-		t.Fatalf("Failed to detect same net.IPNet object equality")
226
-	}
227
-
228
-	_, net3, _ := net.ParseCIDR("192.168.30.33/24")
229
-	if CompareIPNet(net1, net3) == false {
230
-		t.Fatalf("Failed to detect semantically equivalent net.IPNets")
231
-	}
232
-
233
-	_, net3, _ = net.ParseCIDR("192.168.31.33/24")
234
-	if CompareIPNet(net2, net3) == true {
235
-		t.Fatalf("Failed to detect different net.IPNets")
236
-	}
237
-}
238
-
239
-func TestIPCopyFunctions(t *testing.T) {
240
-	ip := net.ParseIP("172.28.30.134")
241
-	cp := GetIPCopy(ip)
242
-
243
-	if !ip.Equal(cp) {
244
-		t.Fatalf("Failed to return a copy of net.IP")
245
-	}
246
-
247
-	if &ip == &cp {
248
-		t.Fatalf("Failed to return a true copy of net.IP")
249
-	}
250
-}
251
-
252
-func TestNetIPCopyFunctions(t *testing.T) {
253
-	_, net, _ := net.ParseCIDR("192.168.30.23/24")
254
-	cp := GetIPNetCopy(net)
255
-
256
-	if CompareIPNet(net, cp) == false {
257
-		t.Fatalf("Failed to return a copy of net.IPNet")
258
-	}
259
-
260
-	if net == cp {
261
-		t.Fatalf("Failed to return a true copy of net.IPNet")
262
-	}
263
-}
264
-
265
-func TestPortBindingEqual(t *testing.T) {
266
-	pb1 := &PortBinding{
267
-		Proto:    TCP,
268
-		IP:       net.ParseIP("172.17.0.1"),
269
-		Port:     80,
270
-		HostIP:   net.ParseIP("192.168.100.1"),
271
-		HostPort: 8080,
272
-	}
273
-
274
-	pb2 := &PortBinding{
275
-		Proto:    UDP,
276
-		IP:       net.ParseIP("172.17.0.1"),
277
-		Port:     22,
278
-		HostIP:   net.ParseIP("192.168.100.1"),
279
-		HostPort: 2222,
280
-	}
281
-	if !pb1.Equal(pb1) {
282
-		t.Fatalf("PortBinding.Equal() returned false negative")
283
-	}
284
-
285
-	if pb1.Equal(nil) {
286
-		t.Fatalf("PortBinding.Equal() returned false negative")
287
-	}
288
-
289
-	if pb1.Equal(pb2) {
290
-		t.Fatalf("PortBinding.Equal() returned false positive")
291
-	}
292
-
293
-	if pb1.Equal(pb2) != pb2.Equal(pb1) {
294
-		t.Fatalf("PortBinding.Equal() failed commutative check")
295
-	}
296
-}
297
-
298
-func TestPortBindingGetCopy(t *testing.T) {
299
-	pb := &PortBinding{
300
-		Proto:    TCP,
301
-		IP:       net.ParseIP("172.17.0.1"),
302
-		Port:     80,
303
-		HostIP:   net.ParseIP("192.168.100.1"),
304
-		HostPort: 8080,
305
-	}
306
-	cp := pb.GetCopy()
307
-
308
-	if !pb.Equal(&cp) {
309
-		t.Fatalf("Failed to return a copy of PortBinding")
310
-	}
311
-
312
-	if pb == &cp {
313
-		t.Fatalf("Failed to return a true copy of PortBinding")
314
-	}
315
-}
316
-
317
-func TestPortBindingContainerAddr(t *testing.T) {
318
-	pb := PortBinding{
319
-		Proto:    TCP,
320
-		IP:       net.ParseIP("172.17.0.1"),
321
-		Port:     80,
322
-		HostIP:   net.ParseIP("192.168.100.1"),
323
-		HostPort: 8080,
324
-	}
325
-
326
-	container, err := pb.ContainerAddr()
327
-
328
-	if err != nil {
329
-		t.Fatal(err)
330
-	}
331
-
332
-	switch netAddr := container.(type) {
333
-	case *net.TCPAddr:
334
-		if !pb.IP.Equal(netAddr.IP) {
335
-			t.Fatalf("PortBinding.ContainerAddr() Failed to return a ContainerAddr")
336
-		}
337
-		if int(pb.Port) != netAddr.Port {
338
-			t.Fatalf("PortBinding.ContainerAddr() Failed to return a ContainerAddr")
339
-		}
340
-	case *net.UDPAddr:
341
-		t.Fatalf("PortBinding.ContainerAddr() Failed to check correct proto")
342
-	}
343
-}
... ...
@@ -133,7 +133,7 @@ func (n *network) Delete() error {
133 133
 
134 134
 func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) {
135 135
 	if name == "" {
136
-		return nil, ErrInvalidName
136
+		return nil, ErrInvalidName(name)
137 137
 	}
138 138
 	ep := &endpoint{name: name, iFaces: []*endpointInterface{}, generic: make(map[string]interface{})}
139 139
 	ep.id = types.UUID(stringid.GenerateRandomID())
... ...
@@ -173,7 +173,7 @@ func (n *network) WalkEndpoints(walker EndpointWalker) {
173 173
 
174 174
 func (n *network) EndpointByName(name string) (Endpoint, error) {
175 175
 	if name == "" {
176
-		return nil, ErrInvalidName
176
+		return nil, ErrInvalidName(name)
177 177
 	}
178 178
 	var e Endpoint
179 179
 
... ...
@@ -188,7 +188,7 @@ func (n *network) EndpointByName(name string) (Endpoint, error) {
188 188
 	n.WalkEndpoints(s)
189 189
 
190 190
 	if e == nil {
191
-		return nil, ErrNoSuchEndpoint
191
+		return nil, ErrNoSuchEndpoint(name)
192 192
 	}
193 193
 
194 194
 	return e, nil
... ...
@@ -196,12 +196,12 @@ func (n *network) EndpointByName(name string) (Endpoint, error) {
196 196
 
197 197
 func (n *network) EndpointByID(id string) (Endpoint, error) {
198 198
 	if id == "" {
199
-		return nil, ErrInvalidID
199
+		return nil, ErrInvalidID(id)
200 200
 	}
201 201
 	n.Lock()
202 202
 	defer n.Unlock()
203 203
 	if e, ok := n.endpoints[types.UUID(id)]; ok {
204 204
 		return e, nil
205 205
 	}
206
-	return nil, ErrNoSuchEndpoint
206
+	return nil, ErrNoSuchEndpoint(id)
207 207
 }
... ...
@@ -84,6 +84,8 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
84 84
 
85 85
 		if useProxy {
86 86
 			m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port)
87
+		} else {
88
+			m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
87 89
 		}
88 90
 	case *net.UDPAddr:
89 91
 		proto = "udp"
... ...
@@ -99,6 +101,8 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
99 99
 
100 100
 		if useProxy {
101 101
 			m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port)
102
+		} else {
103
+			m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
102 104
 		}
103 105
 	default:
104 106
 		return nil, ErrUnknownBackendAddressType
... ...
@@ -123,9 +127,7 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
123 123
 
124 124
 	cleanup := func() error {
125 125
 		// need to undo the iptables rules before we return
126
-		if m.userlandProxy != nil {
127
-			m.userlandProxy.Stop()
128
-		}
126
+		m.userlandProxy.Stop()
129 127
 		pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
130 128
 		if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
131 129
 			return err
... ...
@@ -134,13 +136,11 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
134 134
 		return nil
135 135
 	}
136 136
 
137
-	if m.userlandProxy != nil {
138
-		if err := m.userlandProxy.Start(); err != nil {
139
-			if err := cleanup(); err != nil {
140
-				return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
141
-			}
142
-			return nil, err
137
+	if err := m.userlandProxy.Start(); err != nil {
138
+		if err := cleanup(); err != nil {
139
+			return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
143 140
 		}
141
+		return nil, err
144 142
 	}
145 143
 
146 144
 	pm.currentMappings[key] = m
... ...
@@ -2,20 +2,16 @@ package portmapper
2 2
 
3 3
 import (
4 4
 	"net"
5
-	"os"
5
+	"strings"
6 6
 	"testing"
7
-	"time"
8 7
 
9
-	"github.com/docker/docker/pkg/reexec"
10 8
 	"github.com/docker/libnetwork/iptables"
11
-	"github.com/docker/libnetwork/netutils"
9
+	_ "github.com/docker/libnetwork/netutils"
12 10
 )
13 11
 
14
-func TestMain(m *testing.M) {
15
-	if reexec.Init() {
16
-		return
17
-	}
18
-	os.Exit(m.Run())
12
+func init() {
13
+	// override this func to mock out the proxy server
14
+	newProxy = newMockProxyCommand
19 15
 }
20 16
 
21 17
 func TestSetIptablesChain(t *testing.T) {
... ...
@@ -37,7 +33,6 @@ func TestSetIptablesChain(t *testing.T) {
37 37
 }
38 38
 
39 39
 func TestMapTCPPorts(t *testing.T) {
40
-	defer netutils.SetupTestNetNS(t)()
41 40
 	pm := New()
42 41
 	dstIP1 := net.ParseIP("192.168.0.1")
43 42
 	dstIP2 := net.ParseIP("192.168.0.2")
... ...
@@ -117,7 +112,6 @@ func TestGetUDPIPAndPort(t *testing.T) {
117 117
 }
118 118
 
119 119
 func TestMapUDPPorts(t *testing.T) {
120
-	defer netutils.SetupTestNetNS(t)()
121 120
 	pm := New()
122 121
 	dstIP1 := net.ParseIP("192.168.0.1")
123 122
 	dstIP2 := net.ParseIP("192.168.0.2")
... ...
@@ -164,11 +158,6 @@ func TestMapUDPPorts(t *testing.T) {
164 164
 }
165 165
 
166 166
 func TestMapAllPortsSingleInterface(t *testing.T) {
167
-	newProxy = newMockProxyCommand
168
-	defer func() {
169
-		newProxy = newProxyCommand
170
-	}()
171
-	defer netutils.SetupTestNetNS(t)()
172 167
 	pm := New()
173 168
 	dstIP1 := net.ParseIP("0.0.0.0")
174 169
 	srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
... ...
@@ -177,6 +166,12 @@ func TestMapAllPortsSingleInterface(t *testing.T) {
177 177
 	var host net.Addr
178 178
 	var err error
179 179
 
180
+	defer func() {
181
+		for _, val := range hosts {
182
+			pm.Unmap(val)
183
+		}
184
+	}()
185
+
180 186
 	for i := 0; i < 10; i++ {
181 187
 		start, end := pm.Allocator.Begin, pm.Allocator.End
182 188
 		for i := start; i < end; i++ {
... ...
@@ -201,27 +196,76 @@ func TestMapAllPortsSingleInterface(t *testing.T) {
201 201
 	}
202 202
 }
203 203
 
204
-func TestExecProxy(t *testing.T) {
205
-	defer netutils.SetupTestNetNS(t)()
206
-	args := []string{
207
-		userlandProxyCommandName,
208
-		"-proto", "tcp",
209
-		"-host-ip", "0.0.0.0",
210
-		"-host-port", "9999",
211
-		"-container-ip", "172.168.1.1",
212
-		"-container-port", "8888",
213
-	}
214
-	os.Args = args
215
-	doneChan := make(chan bool)
216
-	go func() {
217
-		execProxy()
218
-		doneChan <- true
219
-	}()
204
+func TestMapTCPDummyListen(t *testing.T) {
205
+	pm := New()
206
+	dstIP := net.ParseIP("0.0.0.0")
207
+	dstAddr := &net.TCPAddr{IP: dstIP, Port: 80}
208
+
209
+	// no-op for dummy
210
+	srcAddr := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
211
+
212
+	addrEqual := func(addr1, addr2 net.Addr) bool {
213
+		return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String())
214
+	}
220 215
 
221
-	select {
222
-	case <-doneChan:
223
-		t.Fatal("execProxy is not supposed to exit")
224
-	case <-time.After(3 * time.Second):
225
-		return
216
+	if host, err := pm.Map(srcAddr, dstIP, 80, false); err != nil {
217
+		t.Fatalf("Failed to allocate port: %s", err)
218
+	} else if !addrEqual(dstAddr, host) {
219
+		t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s",
220
+			dstAddr.String(), dstAddr.Network(), host.String(), host.Network())
221
+	}
222
+	if _, err := net.Listen("tcp", "0.0.0.0:80"); err == nil {
223
+		t.Fatal("Listen on mapped port without proxy should fail")
224
+	} else {
225
+		if !strings.Contains(err.Error(), "address already in use") {
226
+			t.Fatalf("Error should be about address already in use, got %v", err)
227
+		}
228
+	}
229
+	if _, err := net.Listen("tcp", "0.0.0.0:81"); err != nil {
230
+		t.Fatal(err)
231
+	}
232
+	if host, err := pm.Map(srcAddr, dstIP, 81, false); err == nil {
233
+		t.Fatalf("Bound port shouldn't be allocated, but it was on: %v", host)
234
+	} else {
235
+		if !strings.Contains(err.Error(), "address already in use") {
236
+			t.Fatalf("Error should be about address already in use, got %v", err)
237
+		}
238
+	}
239
+}
240
+
241
+func TestMapUDPDummyListen(t *testing.T) {
242
+	pm := New()
243
+	dstIP := net.ParseIP("0.0.0.0")
244
+	dstAddr := &net.UDPAddr{IP: dstIP, Port: 80}
245
+
246
+	// no-op for dummy
247
+	srcAddr := &net.UDPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
248
+
249
+	addrEqual := func(addr1, addr2 net.Addr) bool {
250
+		return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String())
251
+	}
252
+
253
+	if host, err := pm.Map(srcAddr, dstIP, 80, false); err != nil {
254
+		t.Fatalf("Failed to allocate port: %s", err)
255
+	} else if !addrEqual(dstAddr, host) {
256
+		t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s",
257
+			dstAddr.String(), dstAddr.Network(), host.String(), host.Network())
258
+	}
259
+	if _, err := net.ListenUDP("udp", &net.UDPAddr{IP: dstIP, Port: 80}); err == nil {
260
+		t.Fatal("Listen on mapped port without proxy should fail")
261
+	} else {
262
+		if !strings.Contains(err.Error(), "address already in use") {
263
+			t.Fatalf("Error should be about address already in use, got %v", err)
264
+		}
265
+	}
266
+	if _, err := net.ListenUDP("udp", &net.UDPAddr{IP: dstIP, Port: 81}); err != nil {
267
+		t.Fatal(err)
268
+	}
269
+	if host, err := pm.Map(srcAddr, dstIP, 81, false); err == nil {
270
+		t.Fatalf("Bound port shouldn't be allocated, but it was on: %v", host)
271
+	} else {
272
+		if !strings.Contains(err.Error(), "address already in use") {
273
+			t.Fatalf("Error should be about address already in use, got %v", err)
274
+		}
226 275
 	}
227 276
 }
... ...
@@ -3,6 +3,7 @@ package portmapper
3 3
 import (
4 4
 	"flag"
5 5
 	"fmt"
6
+	"io"
6 7
 	"io/ioutil"
7 8
 	"log"
8 9
 	"net"
... ...
@@ -159,3 +160,50 @@ func (p *proxyCommand) Stop() error {
159 159
 	}
160 160
 	return nil
161 161
 }
162
+
163
+// dummyProxy just listen on some port, it is needed to prevent accidental
164
+// port allocations on bound port, because without userland proxy we using
165
+// iptables rules and not net.Listen
166
+type dummyProxy struct {
167
+	listener io.Closer
168
+	addr     net.Addr
169
+}
170
+
171
+func newDummyProxy(proto string, hostIP net.IP, hostPort int) userlandProxy {
172
+	switch proto {
173
+	case "tcp":
174
+		addr := &net.TCPAddr{IP: hostIP, Port: hostPort}
175
+		return &dummyProxy{addr: addr}
176
+	case "udp":
177
+		addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
178
+		return &dummyProxy{addr: addr}
179
+	}
180
+	return nil
181
+}
182
+
183
+func (p *dummyProxy) Start() error {
184
+	switch addr := p.addr.(type) {
185
+	case *net.TCPAddr:
186
+		l, err := net.ListenTCP("tcp", addr)
187
+		if err != nil {
188
+			return err
189
+		}
190
+		p.listener = l
191
+	case *net.UDPAddr:
192
+		l, err := net.ListenUDP("udp", addr)
193
+		if err != nil {
194
+			return err
195
+		}
196
+		p.listener = l
197
+	default:
198
+		return fmt.Errorf("Unknown addr type: %T", p.addr)
199
+	}
200
+	return nil
201
+}
202
+
203
+func (p *dummyProxy) Stop() error {
204
+	if p.listener != nil {
205
+		return p.listener.Close()
206
+	}
207
+	return nil
208
+}
... ...
@@ -20,8 +20,10 @@ var once sync.Once
20 20
 // interface. It represents a linux network namespace, and moves an interface
21 21
 // into it when called on method AddInterface or sets the gateway etc.
22 22
 type networkNamespace struct {
23
-	path  string
24
-	sinfo *Info
23
+	path        string
24
+	sinfo       *Info
25
+	nextIfIndex int
26
+	sync.Mutex
25 27
 }
26 28
 
27 29
 func createBasePath() {
... ...
@@ -167,6 +169,11 @@ func (n *networkNamespace) RemoveInterface(i *Interface) error {
167 167
 }
168 168
 
169 169
 func (n *networkNamespace) AddInterface(i *Interface) error {
170
+	n.Lock()
171
+	i.DstName = fmt.Sprintf("%s%d", i.DstName, n.nextIfIndex)
172
+	n.nextIfIndex++
173
+	n.Unlock()
174
+
170 175
 	runtime.LockOSThread()
171 176
 	defer runtime.UnlockOSThread()
172 177
 
... ...
@@ -214,7 +221,10 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
214 214
 		return err
215 215
 	}
216 216
 
217
+	n.Lock()
217 218
 	n.sinfo.Interfaces = append(n.sinfo.Interfaces, i)
219
+	n.Unlock()
220
+
218 221
 	return nil
219 222
 }
220 223
 
... ...
@@ -3,7 +3,7 @@ package sandbox
3 3
 import (
4 4
 	"net"
5 5
 
6
-	"github.com/docker/libnetwork/netutils"
6
+	"github.com/docker/libnetwork/types"
7 7
 )
8 8
 
9 9
 // Sandbox represents a network sandbox, identified by a specific key.  It
... ...
@@ -20,7 +20,9 @@ type Sandbox interface {
20 20
 
21 21
 	// Add an existing Interface to this sandbox. The operation will rename
22 22
 	// from the Interface SrcName to DstName as it moves, and reconfigure the
23
-	// interface according to the specified settings.
23
+	// interface according to the specified settings. The caller is expected
24
+	// to only provide a prefix for DstName. The AddInterface api will auto-generate
25
+	// an appropriate suffix for the DstName to disambiguate.
24 26
 	AddInterface(*Interface) error
25 27
 
26 28
 	// Remove an interface from the sandbox by renamin to original name
... ...
@@ -62,7 +64,9 @@ type Interface struct {
62 62
 	SrcName string
63 63
 
64 64
 	// The name that will be assigned to the interface once moves inside a
65
-	// network namespace.
65
+	// network namespace. When the caller passes in a DstName, it is only
66
+	// expected to pass a prefix. The name will modified with an appropriately
67
+	// auto-generated suffix.
66 68
 	DstName string
67 69
 
68 70
 	// IPv4 address for the interface.
... ...
@@ -77,8 +81,8 @@ func (i *Interface) GetCopy() *Interface {
77 77
 	return &Interface{
78 78
 		SrcName:     i.SrcName,
79 79
 		DstName:     i.DstName,
80
-		Address:     netutils.GetIPNetCopy(i.Address),
81
-		AddressIPv6: netutils.GetIPNetCopy(i.AddressIPv6),
80
+		Address:     types.GetIPNetCopy(i.Address),
81
+		AddressIPv6: types.GetIPNetCopy(i.AddressIPv6),
82 82
 	}
83 83
 }
84 84
 
... ...
@@ -96,11 +100,11 @@ func (i *Interface) Equal(o *Interface) bool {
96 96
 		return false
97 97
 	}
98 98
 
99
-	if !netutils.CompareIPNet(i.Address, o.Address) {
99
+	if !types.CompareIPNet(i.Address, o.Address) {
100 100
 		return false
101 101
 	}
102 102
 
103
-	if !netutils.CompareIPNet(i.AddressIPv6, o.AddressIPv6) {
103
+	if !types.CompareIPNet(i.AddressIPv6, o.AddressIPv6) {
104 104
 		return false
105 105
 	}
106 106
 
... ...
@@ -113,8 +117,8 @@ func (s *Info) GetCopy() *Info {
113 113
 	for i, iface := range s.Interfaces {
114 114
 		list[i] = iface.GetCopy()
115 115
 	}
116
-	gw := netutils.GetIPCopy(s.Gateway)
117
-	gw6 := netutils.GetIPCopy(s.GatewayIPv6)
116
+	gw := types.GetIPCopy(s.Gateway)
117
+	gw6 := types.GetIPCopy(s.GatewayIPv6)
118 118
 
119 119
 	return &Info{Interfaces: list, Gateway: gw, GatewayIPv6: gw6}
120 120
 }
... ...
@@ -15,6 +15,8 @@ import (
15 15
 const (
16 16
 	vethName1     = "wierdlongname1"
17 17
 	vethName2     = "wierdlongname2"
18
+	vethName3     = "wierdlongname3"
19
+	vethName4     = "wierdlongname4"
18 20
 	sboxIfaceName = "containername"
19 21
 )
20 22
 
... ...
@@ -36,33 +38,59 @@ func newInfo(t *testing.T) (*Info, error) {
36 36
 	veth := &netlink.Veth{
37 37
 		LinkAttrs: netlink.LinkAttrs{Name: vethName1, TxQLen: 0},
38 38
 		PeerName:  vethName2}
39
-	err := netlink.LinkAdd(veth)
40
-	if err != nil {
39
+	if err := netlink.LinkAdd(veth); err != nil {
41 40
 		return nil, err
42 41
 	}
43 42
 
44 43
 	// Store the sandbox side pipe interface
45 44
 	// This is needed for cleanup on DeleteEndpoint()
46
-	intf := &Interface{}
47
-	intf.SrcName = vethName2
48
-	intf.DstName = sboxIfaceName
45
+	intf1 := &Interface{}
46
+	intf1.SrcName = vethName2
47
+	intf1.DstName = sboxIfaceName
49 48
 
50 49
 	ip4, addr, err := net.ParseCIDR("192.168.1.100/24")
51 50
 	if err != nil {
52 51
 		return nil, err
53 52
 	}
54
-	intf.Address = addr
55
-	intf.Address.IP = ip4
53
+	intf1.Address = addr
54
+	intf1.Address.IP = ip4
56 55
 
57 56
 	// ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
58 57
 	ip6, addrv6, err := net.ParseCIDR("fe80::2/64")
59 58
 	if err != nil {
60 59
 		return nil, err
61 60
 	}
62
-	intf.AddressIPv6 = addrv6
63
-	intf.AddressIPv6.IP = ip6
61
+	intf1.AddressIPv6 = addrv6
62
+	intf1.AddressIPv6.IP = ip6
63
+
64
+	veth = &netlink.Veth{
65
+		LinkAttrs: netlink.LinkAttrs{Name: vethName3, TxQLen: 0},
66
+		PeerName:  vethName4}
67
+
68
+	if err := netlink.LinkAdd(veth); err != nil {
69
+		return nil, err
70
+	}
71
+
72
+	intf2 := &Interface{}
73
+	intf2.SrcName = vethName4
74
+	intf2.DstName = sboxIfaceName
75
+
76
+	ip4, addr, err = net.ParseCIDR("192.168.2.100/24")
77
+	if err != nil {
78
+		return nil, err
79
+	}
80
+	intf2.Address = addr
81
+	intf2.Address.IP = ip4
64 82
 
65
-	sinfo := &Info{Interfaces: []*Interface{intf}}
83
+	// ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
84
+	ip6, addrv6, err = net.ParseCIDR("fe80::3/64")
85
+	if err != nil {
86
+		return nil, err
87
+	}
88
+	intf2.AddressIPv6 = addrv6
89
+	intf2.AddressIPv6.IP = ip6
90
+
91
+	sinfo := &Info{Interfaces: []*Interface{intf1, intf2}}
66 92
 	sinfo.Gateway = net.ParseIP("192.168.1.1")
67 93
 	// sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1")
68 94
 	sinfo.GatewayIPv6 = net.ParseIP("fe80::1")
... ...
@@ -97,7 +125,13 @@ func verifySandbox(t *testing.T, s Sandbox) {
97 97
 	}
98 98
 	defer netns.Set(origns)
99 99
 
100
-	_, err = netlink.LinkByName(sboxIfaceName)
100
+	_, err = netlink.LinkByName(sboxIfaceName + "0")
101
+	if err != nil {
102
+		t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName,
103
+			err)
104
+	}
105
+
106
+	_, err = netlink.LinkByName(sboxIfaceName + "1")
101 107
 	if err != nil {
102 108
 		t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName,
103 109
 			err)
... ...
@@ -1,5 +1,345 @@
1 1
 // Package types contains types that are common across libnetwork project
2 2
 package types
3 3
 
4
+import (
5
+	"bytes"
6
+	"fmt"
7
+	"net"
8
+	"strings"
9
+)
10
+
4 11
 // UUID represents a globally unique ID of various resources like network and endpoint
5 12
 type UUID string
13
+
14
+// TransportPort represent a local Layer 4 endpoint
15
+type TransportPort struct {
16
+	Proto Protocol
17
+	Port  uint16
18
+}
19
+
20
+// GetCopy returns a copy of this TransportPort structure instance
21
+func (t *TransportPort) GetCopy() TransportPort {
22
+	return TransportPort{Proto: t.Proto, Port: t.Port}
23
+}
24
+
25
+// PortBinding represent a port binding between the container an the host
26
+type PortBinding struct {
27
+	Proto    Protocol
28
+	IP       net.IP
29
+	Port     uint16
30
+	HostIP   net.IP
31
+	HostPort uint16
32
+}
33
+
34
+// HostAddr returns the host side transport address
35
+func (p PortBinding) HostAddr() (net.Addr, error) {
36
+	switch p.Proto {
37
+	case UDP:
38
+		return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
39
+	case TCP:
40
+		return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
41
+	default:
42
+		return nil, ErrInvalidProtocolBinding(p.Proto.String())
43
+	}
44
+}
45
+
46
+// ContainerAddr returns the container side transport address
47
+func (p PortBinding) ContainerAddr() (net.Addr, error) {
48
+	switch p.Proto {
49
+	case UDP:
50
+		return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil
51
+	case TCP:
52
+		return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
53
+	default:
54
+		return nil, ErrInvalidProtocolBinding(p.Proto.String())
55
+	}
56
+}
57
+
58
+// GetCopy returns a copy of this PortBinding structure instance
59
+func (p *PortBinding) GetCopy() PortBinding {
60
+	return PortBinding{
61
+		Proto:    p.Proto,
62
+		IP:       GetIPCopy(p.IP),
63
+		Port:     p.Port,
64
+		HostIP:   GetIPCopy(p.HostIP),
65
+		HostPort: p.HostPort,
66
+	}
67
+}
68
+
69
+// Equal checks if this instance of PortBinding is equal to the passed one
70
+func (p *PortBinding) Equal(o *PortBinding) bool {
71
+	if p == o {
72
+		return true
73
+	}
74
+
75
+	if o == nil {
76
+		return false
77
+	}
78
+
79
+	if p.Proto != o.Proto || p.Port != o.Port || p.HostPort != o.HostPort {
80
+		return false
81
+	}
82
+
83
+	if p.IP != nil {
84
+		if !p.IP.Equal(o.IP) {
85
+			return false
86
+		}
87
+	} else {
88
+		if o.IP != nil {
89
+			return false
90
+		}
91
+	}
92
+
93
+	if p.HostIP != nil {
94
+		if !p.HostIP.Equal(o.HostIP) {
95
+			return false
96
+		}
97
+	} else {
98
+		if o.HostIP != nil {
99
+			return false
100
+		}
101
+	}
102
+
103
+	return true
104
+}
105
+
106
+// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid.
107
+type ErrInvalidProtocolBinding string
108
+
109
+func (ipb ErrInvalidProtocolBinding) Error() string {
110
+	return fmt.Sprintf("invalid transport protocol: %s", string(ipb))
111
+}
112
+
113
+const (
114
+	// ICMP is for the ICMP ip protocol
115
+	ICMP = 1
116
+	// TCP is for the TCP ip protocol
117
+	TCP = 6
118
+	// UDP is for the UDP ip protocol
119
+	UDP = 17
120
+)
121
+
122
+// Protocol represents a IP protocol number
123
+type Protocol uint8
124
+
125
+func (p Protocol) String() string {
126
+	switch p {
127
+	case ICMP:
128
+		return "icmp"
129
+	case TCP:
130
+		return "tcp"
131
+	case UDP:
132
+		return "udp"
133
+	default:
134
+		return fmt.Sprintf("%d", p)
135
+	}
136
+}
137
+
138
+// ParseProtocol returns the respective Protocol type for the passed string
139
+func ParseProtocol(s string) Protocol {
140
+	switch strings.ToLower(s) {
141
+	case "icmp":
142
+		return ICMP
143
+	case "udp":
144
+		return UDP
145
+	case "tcp":
146
+		return TCP
147
+	default:
148
+		return 0
149
+	}
150
+}
151
+
152
+// GetMacCopy returns a copy of the passed MAC address
153
+func GetMacCopy(from net.HardwareAddr) net.HardwareAddr {
154
+	to := make(net.HardwareAddr, len(from))
155
+	copy(to, from)
156
+	return to
157
+}
158
+
159
+// GetIPCopy returns a copy of the passed IP address
160
+func GetIPCopy(from net.IP) net.IP {
161
+	to := make(net.IP, len(from))
162
+	copy(to, from)
163
+	return to
164
+}
165
+
166
+// GetIPNetCopy returns a copy of the passed IP Network
167
+func GetIPNetCopy(from *net.IPNet) *net.IPNet {
168
+	if from == nil {
169
+		return nil
170
+	}
171
+	bm := make(net.IPMask, len(from.Mask))
172
+	copy(bm, from.Mask)
173
+	return &net.IPNet{IP: GetIPCopy(from.IP), Mask: bm}
174
+}
175
+
176
+// CompareIPNet returns equal if the two IP Networks are equal
177
+func CompareIPNet(a, b *net.IPNet) bool {
178
+	if a == b {
179
+		return true
180
+	}
181
+	if a == nil || b == nil {
182
+		return false
183
+	}
184
+	return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
185
+}
186
+
187
+/******************************
188
+ * Well-known Error Interfaces
189
+ ******************************/
190
+
191
+// MaskableError is an interface for errors which can be ignored by caller
192
+type MaskableError interface {
193
+	// Maskable makes implementer into MaskableError type
194
+	Maskable()
195
+}
196
+
197
+// BadRequestError is an interface for errors originated by a bad request
198
+type BadRequestError interface {
199
+	// BadRequest makes implementer into BadRequestError type
200
+	BadRequest()
201
+}
202
+
203
+// NotFoundError is an interface for errors raised because a needed resource is not available
204
+type NotFoundError interface {
205
+	// NotFound makes implementer into NotFoundError type
206
+	NotFound()
207
+}
208
+
209
+// ForbiddenError is an interface for errors which denote an valid request that cannot be honored
210
+type ForbiddenError interface {
211
+	// Forbidden makes implementer into ForbiddenError type
212
+	Forbidden()
213
+}
214
+
215
+// NoServiceError  is an interface for errors returned when the required service is not available
216
+type NoServiceError interface {
217
+	// NoService makes implementer into NoServiceError type
218
+	NoService()
219
+}
220
+
221
+// TimeoutError  is an interface for errors raised because of timeout
222
+type TimeoutError interface {
223
+	// Timeout makes implementer into TimeoutError type
224
+	Timeout()
225
+}
226
+
227
+// NotImplementedError  is an interface for errors raised because of requested functionality is not yet implemented
228
+type NotImplementedError interface {
229
+	// NotImplemented makes implementer into NotImplementedError type
230
+	NotImplemented()
231
+}
232
+
233
+// InternalError is an interface for errors raised because of an internal error
234
+type InternalError interface {
235
+	// Internal makes implementer into InternalError type
236
+	Internal()
237
+}
238
+
239
+/******************************
240
+ * Weel-known Error Formatters
241
+ ******************************/
242
+
243
+// BadRequestErrorf creates an instance of BadRequestError
244
+func BadRequestErrorf(format string, params ...interface{}) error {
245
+	return badRequest(fmt.Sprintf(format, params...))
246
+}
247
+
248
+// NotFoundErrorf creates an instance of NotFoundError
249
+func NotFoundErrorf(format string, params ...interface{}) error {
250
+	return notFound(fmt.Sprintf(format, params...))
251
+}
252
+
253
+// ForbiddenErrorf creates an instance of ForbiddenError
254
+func ForbiddenErrorf(format string, params ...interface{}) error {
255
+	return forbidden(fmt.Sprintf(format, params...))
256
+}
257
+
258
+// NoServiceErrorf creates an instance of NoServiceError
259
+func NoServiceErrorf(format string, params ...interface{}) error {
260
+	return noService(fmt.Sprintf(format, params...))
261
+}
262
+
263
+// NotImplementedErrorf creates an instance of NotImplementedError
264
+func NotImplementedErrorf(format string, params ...interface{}) error {
265
+	return notImpl(fmt.Sprintf(format, params...))
266
+}
267
+
268
+// TimeoutErrorf creates an instance of TimeoutError
269
+func TimeoutErrorf(format string, params ...interface{}) error {
270
+	return timeout(fmt.Sprintf(format, params...))
271
+}
272
+
273
+// InternalErrorf creates an instance of InternalError
274
+func InternalErrorf(format string, params ...interface{}) error {
275
+	return internal(fmt.Sprintf(format, params...))
276
+}
277
+
278
+// InternalMaskableErrorf creates an instance of InternalError and MaskableError
279
+func InternalMaskableErrorf(format string, params ...interface{}) error {
280
+	return maskInternal(fmt.Sprintf(format, params...))
281
+}
282
+
283
+/***********************
284
+ * Internal Error Types
285
+ ***********************/
286
+type badRequest string
287
+
288
+func (br badRequest) Error() string {
289
+	return string(br)
290
+}
291
+func (br badRequest) BadRequest() {}
292
+
293
+type maskBadRequest string
294
+
295
+type notFound string
296
+
297
+func (nf notFound) Error() string {
298
+	return string(nf)
299
+}
300
+func (nf notFound) NotFound() {}
301
+
302
+type forbidden string
303
+
304
+func (frb forbidden) Error() string {
305
+	return string(frb)
306
+}
307
+func (frb forbidden) Forbidden() {}
308
+
309
+type noService string
310
+
311
+func (ns noService) Error() string {
312
+	return string(ns)
313
+}
314
+func (ns noService) NoService() {}
315
+
316
+type maskNoService string
317
+
318
+type timeout string
319
+
320
+func (to timeout) Error() string {
321
+	return string(to)
322
+}
323
+func (to timeout) Timeout() {}
324
+
325
+type notImpl string
326
+
327
+func (ni notImpl) Error() string {
328
+	return string(ni)
329
+}
330
+func (ni notImpl) NotImplemented() {}
331
+
332
+type internal string
333
+
334
+func (nt internal) Error() string {
335
+	return string(nt)
336
+}
337
+func (nt internal) Internal() {}
338
+
339
+type maskInternal string
340
+
341
+func (mnt maskInternal) Error() string {
342
+	return string(mnt)
343
+}
344
+func (mnt maskInternal) Internal() {}
345
+func (mnt maskInternal) Maskable() {}
6 346
new file mode 100644
... ...
@@ -0,0 +1,99 @@
0
+package types
1
+
2
+import (
3
+	"testing"
4
+
5
+	_ "github.com/docker/libnetwork/netutils"
6
+)
7
+
8
+func TestErrorConstructors(t *testing.T) {
9
+	var err error
10
+
11
+	err = BadRequestErrorf("Io ho %d uccello", 1)
12
+	if err.Error() != "Io ho 1 uccello" {
13
+		t.Fatal(err)
14
+	}
15
+	if _, ok := err.(BadRequestError); !ok {
16
+		t.Fatal(err)
17
+	}
18
+	if _, ok := err.(MaskableError); ok {
19
+		t.Fatal(err)
20
+	}
21
+
22
+	err = NotFoundErrorf("Can't find the %s", "keys")
23
+	if err.Error() != "Can't find the keys" {
24
+		t.Fatal(err)
25
+	}
26
+	if _, ok := err.(NotFoundError); !ok {
27
+		t.Fatal(err)
28
+	}
29
+	if _, ok := err.(MaskableError); ok {
30
+		t.Fatal(err)
31
+	}
32
+
33
+	err = ForbiddenErrorf("Can't open door %d", 2)
34
+	if err.Error() != "Can't open door 2" {
35
+		t.Fatal(err)
36
+	}
37
+	if _, ok := err.(ForbiddenError); !ok {
38
+		t.Fatal(err)
39
+	}
40
+	if _, ok := err.(MaskableError); ok {
41
+		t.Fatal(err)
42
+	}
43
+
44
+	err = NotImplementedErrorf("Functionality %s is not implemented", "x")
45
+	if err.Error() != "Functionality x is not implemented" {
46
+		t.Fatal(err)
47
+	}
48
+	if _, ok := err.(NotImplementedError); !ok {
49
+		t.Fatal(err)
50
+	}
51
+	if _, ok := err.(MaskableError); ok {
52
+		t.Fatal(err)
53
+	}
54
+
55
+	err = TimeoutErrorf("Process %s timed out", "abc")
56
+	if err.Error() != "Process abc timed out" {
57
+		t.Fatal(err)
58
+	}
59
+	if _, ok := err.(TimeoutError); !ok {
60
+		t.Fatal(err)
61
+	}
62
+	if _, ok := err.(MaskableError); ok {
63
+		t.Fatal(err)
64
+	}
65
+
66
+	err = NoServiceErrorf("Driver %s is not available", "mh")
67
+	if err.Error() != "Driver mh is not available" {
68
+		t.Fatal(err)
69
+	}
70
+	if _, ok := err.(NoServiceError); !ok {
71
+		t.Fatal(err)
72
+	}
73
+	if _, ok := err.(MaskableError); ok {
74
+		t.Fatal(err)
75
+	}
76
+
77
+	err = InternalErrorf("Not sure what happened")
78
+	if err.Error() != "Not sure what happened" {
79
+		t.Fatal(err)
80
+	}
81
+	if _, ok := err.(InternalError); !ok {
82
+		t.Fatal(err)
83
+	}
84
+	if _, ok := err.(MaskableError); ok {
85
+		t.Fatal(err)
86
+	}
87
+
88
+	err = InternalMaskableErrorf("Minor issue, it can be ignored")
89
+	if err.Error() != "Minor issue, it can be ignored" {
90
+		t.Fatal(err)
91
+	}
92
+	if _, ok := err.(InternalError); !ok {
93
+		t.Fatal(err)
94
+	}
95
+	if _, ok := err.(MaskableError); !ok {
96
+		t.Fatal(err)
97
+	}
98
+}