Bläddra i källkod

Changes in rest api

- Fix POST to collection
- Only resource ID in URI, search by name as query parameter
- Fix URLs, consistency and restrict regex

Signed-off-by: Alessandro Boch <aboch@docker.com>
Alessandro Boch 10 år sedan
förälder
incheckning
7d80a5a84a

+ 61 - 58
libnetwork/api/api.go

@@ -17,8 +17,17 @@ var (
 )
 
 const (
-	urlNwName = "name"
-	urlNwID   = "id"
+	// Resource name regex
+	regex = "[a-zA-Z_0-9-]+"
+	// Router URL variable definition
+	nwName = "{" + urlNwName + ":" + regex + "}"
+	nwID   = "{" + urlNwID + ":" + regex + "}"
+	epName = "{" + urlEpName + ":" + regex + "}"
+	epID   = "{" + urlEpID + ":" + regex + "}"
+	cnID   = "{" + urlCnID + ":" + regex + "}"
+	// Internal URL variable name, they can be anything
+	urlNwName = "network-name"
+	urlNwID   = "network-id"
 	urlEpName = "endpoint-name"
 	urlEpID   = "endpoint-id"
 	urlCnID   = "container-id"
@@ -59,42 +68,38 @@ func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) {
 }
 
 func (h *httpHandler) initRouter() {
-	m := map[string]map[string]processor{
+	m := map[string][]struct {
+		url string
+		qrs []string
+		fct processor
+	}{
 		"GET": {
-			"/networks":                                                                   procGetNetworks,
-			"/networks/name/{" + urlNwName + ":.*}":                                       procGetNetwork,
-			"/networks/id/{" + urlNwID + ":.*}":                                           procGetNetwork,
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/":                            procGetEndpoints,
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/":                                procGetEndpoints,
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procGetEndpoint,
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}":     procGetEndpoint,
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}":     procGetEndpoint,
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}":         procGetEndpoint,
+			// Order matters
+			{"/networks", []string{"name", nwName}, procGetNetworks},
+			{"/networks", nil, procGetNetworks},
+			{"/networks/" + nwID, nil, procGetNetwork},
+			{"/networks/" + nwID + "/endpoints", []string{"name", epName}, procGetEndpoints},
+			{"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint},
 		},
 		"POST": {
-			"/networks/name/{" + urlNwName + ":.*}":                                                                     procCreateNetwork,
-			"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}":                                procCreateEndpoint,
-			"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procJoinEndpoint,
+			{"/networks", nil, procCreateNetwork},
+			{"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint},
+			{"/networks/" + nwID + "/endpoints/" + epID + "/containers", nil, procJoinEndpoint},
 		},
 		"DELETE": {
-			"/networks/name/{" + urlNwName + ":.*}":                                                                     procDeleteNetwork,
-			"/networks/id/{" + urlNwID + ":.*}":                                                                         procDeleteNetwork,
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}":                               procDeleteEndpoint,
-			"/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}":                                   procDeleteEndpoint,
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}":                                   procDeleteEndpoint,
-			"/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}":                                       procDeleteEndpoint,
-			"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
-			"/networks/name/{" + urlNwName + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}":     procLeaveEndpoint,
-			"/networks/id/{" + urlNwID + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}":     procLeaveEndpoint,
-			"/networks/id/{" + urlNwID + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}":         procLeaveEndpoint,
+			{"/networks/" + nwID, nil, procDeleteNetwork},
+			{"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint},
+			{"/networks/id/" + nwID + "/endpoints/" + epID + "/containers/" + cnID, nil, procLeaveEndpoint},
 		},
 	}
 
 	h.r = mux.NewRouter()
 	for method, routes := range m {
-		for route, fct := range routes {
-			f := makeHandler(h.c, fct)
-			h.r.Path(route).Methods(method).HandlerFunc(f)
+		for _, route := range routes {
+			r := h.r.Path(route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct))
+			if route.qrs != nil {
+				r.Queries(route.qrs...)
+			}
 		}
 	}
 }
@@ -208,12 +213,7 @@ func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, b
 		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
 	}
 
-	name := vars[urlNwName]
-	if name != create.Name {
-		return "", &mismatchResponse
-	}
-
-	nw, err := c.NewNetwork(create.NetworkType, name, nil)
+	nw, err := c.NewNetwork(create.NetworkType, create.Name, nil)
 	if err != nil {
 		return "", convertNetworkError(err)
 	}
@@ -232,10 +232,19 @@ func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body
 
 func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
 	var list []*networkResource
-	for _, nw := range c.Networks() {
-		nwr := buildNetworkResource(nw)
-		list = append(list, nwr)
+
+	// If query parameter is specified, return a filtered collection
+	if name, queryByName := vars[urlNwName]; queryByName {
+		nw, errRsp := findNetwork(c, name, byName)
+		if errRsp.isOK() {
+			list = append(list, buildNetworkResource(nw))
+		}
+	} else {
+		for _, nw := range c.Networks() {
+			list = append(list, buildNetworkResource(nw))
+		}
 	}
+
 	return list, &successResponse
 }
 
@@ -250,21 +259,12 @@ func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string,
 		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
 	}
 
-	epn := vars[urlEpName]
-	if ec.Name != epn {
-		return "", &mismatchResponse
-	}
-
 	nwT, nwBy := detectNetworkTarget(vars)
 	n, errRsp := findNetwork(c, nwT, nwBy)
 	if !errRsp.isOK() {
 		return "", errRsp
 	}
 
-	if ec.NetworkID != n.ID() {
-		return "", &mismatchResponse
-	}
-
 	var setFctList []libnetwork.EndpointOption
 	if ec.ExposedPorts != nil {
 		setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts))
@@ -273,7 +273,7 @@ func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string,
 		setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping))
 	}
 
-	ep, err := n.CreateEndpoint(epn, setFctList...)
+	ep, err := n.CreateEndpoint(ec.Name, setFctList...)
 	if err != nil {
 		return "", convertNetworkError(err)
 	}
@@ -294,17 +294,25 @@ func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, bod
 }
 
 func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
-	target, by := detectNetworkTarget(vars)
-
-	nw, errRsp := findNetwork(c, target, by)
+	nwT, nwBy := detectNetworkTarget(vars)
+	nw, errRsp := findNetwork(c, nwT, nwBy)
 	if !errRsp.isOK() {
 		return nil, errRsp
 	}
 
 	var list []*endpointResource
-	for _, ep := range nw.Endpoints() {
-		epr := buildEndpointResource(ep)
-		list = append(list, epr)
+
+	// If query parameter is specified, return a filtered collection
+	if epT, queryByName := vars[urlEpName]; queryByName {
+		ep, errRsp := findEndpoint(c, nwT, epT, nwBy, byName)
+		if errRsp.isOK() {
+			list = append(list, buildEndpointResource(ep))
+		}
+	} else {
+		for _, ep := range nw.Endpoints() {
+			epr := buildEndpointResource(ep)
+			list = append(list, epr)
+		}
 	}
 
 	return list, &successResponse
@@ -336,11 +344,6 @@ func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, bo
 		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
 	}
 
-	cid := vars[urlCnID]
-	if ej.ContainerID != cid {
-		return "", &mismatchResponse
-	}
-
 	nwT, nwBy := detectNetworkTarget(vars)
 	epT, epBy := detectEndpointTarget(vars)
 

+ 220 - 136
libnetwork/api/api_test.go

@@ -112,7 +112,7 @@ func TestJoinOptionParser(t *testing.T) {
 }
 
 func TestJson(t *testing.T) {
-	nc := networkCreate{Name: "mynet", NetworkType: bridgeNetType}
+	nc := networkCreate{NetworkType: bridgeNetType}
 	b, err := json.Marshal(nc)
 	if err != nil {
 		t.Fatal(err)
@@ -124,26 +124,10 @@ func TestJson(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if nc.Name != ncp.Name || nc.NetworkType != ncp.NetworkType {
+	if nc.NetworkType != ncp.NetworkType {
 		t.Fatalf("Incorrect networkCreate after json encoding/deconding: %v", ncp)
 	}
 
-	ec := endpointCreate{Name: "mioEp", NetworkID: "0xabcde"}
-	b, err = json.Marshal(ec)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var ecp endpointCreate
-	err = json.Unmarshal(b, &ecp)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if ec.Name != ecp.Name || ec.NetworkID != ecp.NetworkID {
-		t.Fatalf("Incorrect endpointCreate after json encoding/deconding: %v", ecp)
-	}
-
 	jl := endpointJoin{ContainerID: "abcdef456789"}
 	b, err = json.Marshal(jl)
 	if err != nil {
@@ -157,7 +141,7 @@ func TestJson(t *testing.T) {
 	}
 
 	if jl.ContainerID != jld.ContainerID {
-		t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", ecp)
+		t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", jld)
 	}
 }
 
@@ -178,18 +162,18 @@ func TestCreateDeleteNetwork(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	goodVars := map[string]string{urlNwName: "myNet"}
-	_, errRsp := procCreateNetwork(c, goodVars, badBody)
+	vars := make(map[string]string)
+	_, errRsp := procCreateNetwork(c, nil, badBody)
 	if errRsp == &createdResponse {
 		t.Fatalf("Expected to fail but succeeded")
 	}
 
-	incompleteBody, err := json.Marshal(networkCreate{Name: "myNet"})
+	incompleteBody, err := json.Marshal(networkCreate{})
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	_, errRsp = procCreateNetwork(c, goodVars, incompleteBody)
+	_, errRsp = procCreateNetwork(c, vars, incompleteBody)
 	if errRsp == &createdResponse {
 		t.Fatalf("Expected to fail but succeeded")
 	}
@@ -199,47 +183,31 @@ func TestCreateDeleteNetwork(t *testing.T) {
 
 	ops := make(map[string]interface{})
 	ops[netlabel.GenericData] = options.Generic{}
-	nc := networkCreate{Name: "myNet", NetworkType: bridgeNetType, Options: ops}
+	nc := networkCreate{Name: "network_1", NetworkType: bridgeNetType, Options: ops}
 	goodBody, err := json.Marshal(nc)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	badVars := map[string]string{urlNwName: ""}
-	_, errRsp = procCreateNetwork(c, badVars, goodBody)
-	if errRsp == &createdResponse {
-		t.Fatalf("Expected to fail but succeeded")
-	}
-	if errRsp.StatusCode != http.StatusBadRequest {
-		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
-	}
-
-	badVars[urlNwName] = "badNetworkName"
-	_, errRsp = procCreateNetwork(c, badVars, goodBody)
-	if errRsp == &createdResponse {
-		t.Fatalf("Expected to fail but succeeded")
-	}
-	if errRsp.StatusCode != http.StatusBadRequest {
-		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
-	}
-
-	_, errRsp = procCreateNetwork(c, goodVars, goodBody)
+	_, errRsp = procCreateNetwork(c, vars, goodBody)
 	if errRsp != &createdResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
 	}
 
-	_, errRsp = procDeleteNetwork(c, badVars, nil)
+	vars[urlNwName] = ""
+	_, errRsp = procDeleteNetwork(c, vars, nil)
 	if errRsp == &successResponse {
 		t.Fatalf("Expected to fail but succeeded")
 	}
 
-	badVars[urlNwName] = ""
-	_, errRsp = procDeleteNetwork(c, badVars, nil)
+	vars[urlNwName] = "abc"
+	_, errRsp = procDeleteNetwork(c, vars, nil)
 	if errRsp == &successResponse {
 		t.Fatalf("Expected to fail but succeeded")
 	}
 
-	_, errRsp = procDeleteNetwork(c, goodVars, nil)
+	vars[urlNwName] = "network_1"
+	_, errRsp = procDeleteNetwork(c, vars, nil)
 	if errRsp != &successResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
 	}
@@ -263,7 +231,7 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	vars := map[string]string{urlNwName: "sh"}
+	vars := make(map[string]string)
 	inid, errRsp := procCreateNetwork(c, vars, body)
 	if errRsp != &createdResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
@@ -274,8 +242,7 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
 	}
 
 	ec1 := endpointCreate{
-		Name:      "ep1",
-		NetworkID: string(nid),
+		Name: "ep1",
 		ExposedPorts: []types.TransportPort{
 			types.TransportPort{Proto: types.TCP, Port: uint16(5000)},
 			types.TransportPort{Proto: types.UDP, Port: uint16(400)},
@@ -291,12 +258,13 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	ec2 := endpointCreate{Name: "ep2", NetworkID: nid}
+	ec2 := endpointCreate{Name: "ep2"}
 	b2, err := json.Marshal(ec2)
 	if err != nil {
 		t.Fatal(err)
 	}
 
+	vars[urlNwName] = "sh"
 	vars[urlEpName] = "ep1"
 	ieid1, errRsp := procCreateEndpoint(c, vars, b1)
 	if errRsp != &createdResponse {
@@ -472,6 +440,7 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
 	if errRsp != &successResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
 	}
+	delete(vars, urlEpName)
 	iepList, errRsp = procGetEndpoints(c, vars, nil)
 	if errRsp != &successResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
@@ -604,85 +573,46 @@ func TestCreateDeleteEndpoints(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	vars := map[string]string{urlNwName: "firstNet"}
+	vars := make(map[string]string)
 	i, errRsp := procCreateNetwork(c, vars, body)
 	if errRsp != &createdResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
 	}
 	nid := i2s(i)
 
-	vbad, err := json.Marshal("bad endppint create data")
+	vbad, err := json.Marshal("bad endppoint create data")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	vars[urlEpName] = "ep1"
+	vars[urlNwName] = "firstNet"
 	_, errRsp = procCreateEndpoint(c, vars, vbad)
 	if errRsp == &createdResponse {
 		t.Fatalf("Expected to fail but succeeded")
 	}
 
-	bad, err := json.Marshal(endpointCreate{Name: "ep1", NetworkID: "123456"})
-	if err != nil {
-		t.Fatal(err)
-	}
-	_, errRsp = procCreateEndpoint(c, vars, bad)
-	if errRsp == &createdResponse {
-		t.Fatalf("Expected to fail but succeeded")
-	}
-
-	soso, err := json.Marshal(endpointCreate{Name: "ep11", NetworkID: nid})
-	if err != nil {
-		t.Fatal(err)
-	}
-	_, errRsp = procCreateEndpoint(c, vars, soso)
-	if errRsp != &mismatchResponse {
-		t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp)
-	}
-
-	bla, err := json.Marshal(endpointCreate{Name: "", NetworkID: nid})
-	if err != nil {
-		t.Fatal(err)
-	}
-	vars[urlNwName] = "firstNet"
-	vars[urlEpName] = ""
-	_, errRsp = procCreateEndpoint(c, vars, bla)
-	if errRsp == &createdResponse {
-		t.Fatalf("Expected to fail but succeeded: %v", errRsp)
-	}
-
-	b, err := json.Marshal(endpointCreate{Name: "firstEp", NetworkID: nid})
+	b, err := json.Marshal(endpointCreate{Name: ""})
 	if err != nil {
 		t.Fatal(err)
 	}
 
 	vars[urlNwName] = "secondNet"
-	vars[urlEpName] = "firstEp"
 	_, errRsp = procCreateEndpoint(c, vars, b)
 	if errRsp == &createdResponse {
 		t.Fatalf("Expected to fail but succeeded")
 	}
 
 	vars[urlNwName] = "firstNet"
-	vars[urlEpName] = "ep1"
-	_, errRsp = procCreateEndpoint(c, vars, b)
-	if errRsp != &mismatchResponse {
-		t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp)
-	}
-
-	vars = make(map[string]string)
 	_, errRsp = procCreateEndpoint(c, vars, b)
 	if errRsp == &successResponse {
 		t.Fatalf("Expected failure but succeeded: %v", errRsp)
 	}
 
-	vars[urlNwName] = "firstNet"
-	_, errRsp = procCreateEndpoint(c, vars, b)
-	if errRsp == &successResponse {
-		t.Fatalf("Expected failure but succeeded: %v", errRsp)
+	b, err = json.Marshal(endpointCreate{Name: "firstEp"})
+	if err != nil {
+		t.Fatal(err)
 	}
 
-	vars[urlEpName] = "firstEp"
 	i, errRsp = procCreateEndpoint(c, vars, b)
 	if errRsp != &createdResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
@@ -714,8 +644,8 @@ func TestCreateDeleteEndpoints(t *testing.T) {
 		t.Fatalf("Unexepected failure: %v", errRsp)
 	}
 
-	if ep0 != ep1 || ep0 != ep2 || ep0 != ep3 {
-		t.Fatalf("Diffenrent queries returned different endpoints")
+	if ep0.ID() != ep1.ID() || ep0.ID() != ep2.ID() || ep0.ID() != ep3.ID() {
+		t.Fatalf("Diffenrent queries returned different endpoints: \nep0: %v\nep1: %v\nep2: %v\nep3: %v", ep0, ep1, ep2, ep3)
 	}
 
 	vars = make(map[string]string)
@@ -767,18 +697,17 @@ func TestJoinLeave(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	vars := map[string]string{urlNwName: "network"}
-	i, errRsp := procCreateNetwork(c, vars, nb)
+	vars := make(map[string]string)
+	_, errRsp := procCreateNetwork(c, vars, nb)
 	if errRsp != &createdResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
 	}
-	nid := i2s(i)
 
-	vars[urlEpName] = "epoint"
-	eb, err := json.Marshal(endpointCreate{Name: "epoint", NetworkID: nid})
+	eb, err := json.Marshal(endpointCreate{Name: "endpoint"})
 	if err != nil {
 		t.Fatal(err)
 	}
+	vars[urlNwName] = "network"
 	_, errRsp = procCreateEndpoint(c, vars, eb)
 	if errRsp != &createdResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
@@ -793,6 +722,7 @@ func TestJoinLeave(t *testing.T) {
 		t.Fatalf("Expected failure, got: %v", errRsp)
 	}
 
+	vars[urlEpName] = "endpoint"
 	bad, err := json.Marshal(endpointJoin{})
 	if err != nil {
 		t.Fatal(err)
@@ -812,44 +742,30 @@ func TestJoinLeave(t *testing.T) {
 	vars = make(map[string]string)
 	vars[urlNwName] = ""
 	vars[urlEpName] = ""
-	vars[urlCnID] = cid
-	_, errRsp = procJoinEndpoint(c, vars, jlb)
-	if errRsp == &successResponse {
-		t.Fatalf("Expected failure, got: %v", errRsp)
-	}
-
-	vars[urlNwName] = "network1"
-	vars[urlEpName] = ""
 	_, errRsp = procJoinEndpoint(c, vars, jlb)
 	if errRsp == &successResponse {
 		t.Fatalf("Expected failure, got: %v", errRsp)
 	}
 
 	vars[urlNwName] = "network"
-	vars[urlEpName] = "endpoint"
+	vars[urlEpName] = ""
 	_, errRsp = procJoinEndpoint(c, vars, jlb)
 	if errRsp == &successResponse {
 		t.Fatalf("Expected failure, got: %v", errRsp)
 	}
 
 	vars[urlEpName] = "epoint"
-	delete(vars, urlCnID)
 	_, errRsp = procJoinEndpoint(c, vars, jlb)
 	if errRsp == &successResponse {
 		t.Fatalf("Expected failure, got: %v", errRsp)
 	}
 
-	vars[urlCnID] = "who?"
-	_, errRsp = procJoinEndpoint(c, vars, jlb)
-	if errRsp == &successResponse {
-		t.Fatalf("Expected failure, got: %v", errRsp)
-	}
-
-	vars[urlCnID] = cid
+	vars[urlEpName] = "endpoint"
 	cdi, errRsp := procJoinEndpoint(c, vars, jlb)
 	if errRsp != &successResponse {
-		t.Fatalf("Unexpected failure, got: %v", errRsp)
+		t.Fatalf("Expected failure, got: %v", errRsp)
 	}
+
 	cd := i2c(cdi)
 	if cd.SandboxKey == "" {
 		t.Fatalf("Empty sandbox key")
@@ -898,6 +814,7 @@ func TestJoinLeave(t *testing.T) {
 	}
 
 	delete(vars, urlCnID)
+	vars[urlEpName] = "endpoint"
 	_, errRsp = procLeaveEndpoint(c, vars, jlb)
 	if errRsp == &successResponse {
 		t.Fatalf("Expected failure, got: %v", errRsp)
@@ -1194,15 +1111,24 @@ func TestHttpHandlerUninit(t *testing.T) {
 
 	handleRequest(rsp, req)
 	if rsp.statusCode != http.StatusOK {
-		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
+		t.Fatalf("Expected (%d). Got: (%d): %s", http.StatusOK, rsp.statusCode, rsp.body)
 	}
 
-	n, err := c.NewNetwork(bridgeNetType, "onenet", nil)
+	var list []*networkResource
+	err = json.Unmarshal(rsp.body, &list)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(list) != 0 {
+		t.Fatalf("Expected empty list. Got %v", list)
+	}
+
+	n, err := c.NewNetwork(bridgeNetType, "didietro", nil)
 	if err != nil {
 		t.Fatal(err)
 	}
 	nwr := buildNetworkResource(n)
-	expected, err := json.Marshal([]networkResource{*nwr})
+	expected, err := json.Marshal([]*networkResource{nwr})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1230,7 +1156,7 @@ func TestHttpHandlerBadBody(t *testing.T) {
 	}
 	handleRequest := NewHTTPHandler(c)
 
-	req, err := http.NewRequest("POST", "/networks/name/zero-network", &localReader{beBad: true})
+	req, err := http.NewRequest("POST", "/networks", &localReader{beBad: true})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1241,7 +1167,7 @@ func TestHttpHandlerBadBody(t *testing.T) {
 
 	body := []byte{}
 	lr := newLocalReader(body)
-	req, err = http.NewRequest("POST", "/networks/name/zero-network", lr)
+	req, err = http.NewRequest("POST", "/networks", lr)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1251,7 +1177,7 @@ func TestHttpHandlerBadBody(t *testing.T) {
 	}
 }
 
-func TestHttpHandlerGood(t *testing.T) {
+func TestEndToEnd(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 
 	rsp := newWriter()
@@ -1262,14 +1188,14 @@ func TestHttpHandlerGood(t *testing.T) {
 	}
 	handleRequest := NewHTTPHandler(c)
 
-	nc := networkCreate{Name: "zero-network", NetworkType: bridgeNetType}
+	// Create network
+	nc := networkCreate{Name: "network-fiftyfive", NetworkType: bridgeNetType}
 	body, err := json.Marshal(nc)
 	if err != nil {
 		t.Fatal(err)
 	}
-
 	lr := newLocalReader(body)
-	req, err := http.NewRequest("POST", "/networks/name/zero-network", lr)
+	req, err := http.NewRequest("POST", "/networks", lr)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1281,13 +1207,79 @@ func TestHttpHandlerGood(t *testing.T) {
 		t.Fatalf("Empty response body")
 	}
 
-	var id string
-	err = json.Unmarshal(rsp.body, &id)
+	var nid string
+	err = json.Unmarshal(rsp.body, &nid)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	req, err = http.NewRequest("GET", "/networks/id/"+id, nil)
+	// Query networks collection
+	req, err = http.NewRequest("GET", "/networks", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
+	}
+
+	b0 := make([]byte, len(rsp.body))
+	copy(b0, rsp.body)
+
+	req, err = http.NewRequest("GET", "/networks?name=network-fiftyfive", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
+	}
+
+	if !bytes.Equal(b0, rsp.body) {
+		t.Fatalf("Expected same body from GET /networks and GET /networks?name=<nw> when only network <nw> exist.")
+	}
+
+	// Query network by name
+	req, err = http.NewRequest("GET", "/networks?name=culo", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
+	}
+
+	var list []*networkResource
+	err = json.Unmarshal(rsp.body, &list)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(list) != 0 {
+		t.Fatalf("Expected empty list. Got %v", list)
+	}
+
+	req, err = http.NewRequest("GET", "/networks?name=network-fiftyfive", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
+	}
+
+	err = json.Unmarshal(rsp.body, &list)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(list) == 0 {
+		t.Fatalf("Expected non empty list")
+	}
+	if list[0].Name != "network-fiftyfive" || nid != list[0].ID {
+		t.Fatalf("Incongruent resource found: %v", list[0])
+	}
+
+	// Get network by id
+	req, err = http.NewRequest("GET", "/networks/"+nid, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1301,7 +1293,99 @@ func TestHttpHandlerGood(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	if nwr.Name != "zero-network" || id != nwr.ID {
-		t.Fatalf("Incongruent resource found")
+	if nwr.Name != "network-fiftyfive" || nid != nwr.ID {
+		t.Fatalf("Incongruent resource found: %v", nwr)
+	}
+
+	// Create endpoint
+	eb, err := json.Marshal(endpointCreate{Name: "ep-TwentyTwo"})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	lr = newLocalReader(eb)
+	req, err = http.NewRequest("POST", "/networks/"+nid+"/endpoints", lr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusCreated {
+		t.Fatalf("Unexpectded status code. Expected (%d). Got (%d): %s.", http.StatusCreated, rsp.statusCode, string(rsp.body))
+	}
+	if len(rsp.body) == 0 {
+		t.Fatalf("Empty response body")
+	}
+
+	var eid string
+	err = json.Unmarshal(rsp.body, &eid)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Query endpoint(s)
+	req, err = http.NewRequest("GET", "/networks/"+nid+"/endpoints", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusNotFound {
+		t.Fatalf("Expected StatusNotFound. Got (%d): %s", rsp.statusCode, rsp.body)
+	}
+
+	req, err = http.NewRequest("GET", "/networks/"+nid+"/endpoints?name=bla", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
+	}
+	var epList []*endpointResource
+	err = json.Unmarshal(rsp.body, &epList)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(epList) != 0 {
+		t.Fatalf("Expected empty list. Got %v", epList)
+	}
+
+	// Query endpoint by name
+	req, err = http.NewRequest("GET", "/networks/"+nid+"/endpoints?name=ep-TwentyTwo", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
+	}
+
+	err = json.Unmarshal(rsp.body, &epList)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(epList) == 0 {
+		t.Fatalf("Empty response body")
+	}
+	if epList[0].Name != "ep-TwentyTwo" || eid != epList[0].ID {
+		t.Fatalf("Incongruent resource found: %v", epList[0])
+	}
+
+	// Get endpoint by id
+	req, err = http.NewRequest("GET", "/networks/"+nid+"/endpoints/"+eid, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
+	}
+
+	var epr endpointResource
+	err = json.Unmarshal(rsp.body, &epr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if epr.Name != "ep-TwentyTwo" || epr.ID != eid {
+		t.Fatalf("Incongruent resource found: %v", epr)
 	}
 }

+ 0 - 1
libnetwork/api/types.go

@@ -35,7 +35,6 @@ type networkCreate struct {
 // endpointCreate represents the body of the "create endpoint" http request message
 type endpointCreate struct {
 	Name         string
-	NetworkID    string
 	ExposedPorts []types.TransportPort
 	PortMapping  []types.PortBinding
 }

+ 1 - 1
libnetwork/client/network.go

@@ -53,7 +53,7 @@ func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
 
 	nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver}
 
-	obj, _, err := readBody(cli.call("POST", "/networks/name/"+cmd.Arg(0), nc, nil))
+	obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
 	if err != nil {
 		fmt.Fprintf(cli.err, "%s", err.Error())
 		return err

+ 1 - 3
libnetwork/cmd/dnet/dnet.go

@@ -114,9 +114,7 @@ func (d *dnetConnection) dnetDaemon() error {
 	httpHandler := api.NewHTTPHandler(controller)
 	r := mux.NewRouter().StrictSlash(false)
 	post := r.PathPrefix("/networks").Subrouter()
-	post.Methods("GET").HandlerFunc(httpHandler)
-	post.Methods("PUT", "POST").HandlerFunc(httpHandler)
-	post.Methods("DELETE").HandlerFunc(httpHandler)
+	post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
 	return http.ListenAndServe(d.addr, r)
 }
 

+ 1 - 1
libnetwork/cmd/dnet/flags.go

@@ -16,7 +16,7 @@ type byName []command
 
 var (
 	flDaemon   = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
-	flHost     = flag.String([]string{"H", "-Host"}, "", "Daemon socket to connect to")
+	flHost     = flag.String([]string{"H", "-host"}, "", "Daemon socket to connect to")
 	flLogLevel = flag.String([]string{"l", "-log-level"}, "info", "Set the logging level")
 	flDebug    = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
 	flHelp     = flag.Bool([]string{"h", "-help"}, false, "Print usage")