mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #194 from aboch/rest
REST API: Support query by partial id
This commit is contained in:
commit
ef6ddb33f5
2 changed files with 129 additions and 7 deletions
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/libnetwork"
|
"github.com/docker/libnetwork"
|
||||||
"github.com/docker/libnetwork/types"
|
"github.com/docker/libnetwork/types"
|
||||||
|
@ -15,6 +16,7 @@ var (
|
||||||
successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK}
|
successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK}
|
||||||
createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated}
|
createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated}
|
||||||
mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest}
|
mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest}
|
||||||
|
badQueryresponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -23,14 +25,19 @@ const (
|
||||||
// Router URL variable definition
|
// Router URL variable definition
|
||||||
nwName = "{" + urlNwName + ":" + regex + "}"
|
nwName = "{" + urlNwName + ":" + regex + "}"
|
||||||
nwID = "{" + urlNwID + ":" + regex + "}"
|
nwID = "{" + urlNwID + ":" + regex + "}"
|
||||||
|
nwPID = "{" + urlNwPID + ":" + regex + "}"
|
||||||
epName = "{" + urlEpName + ":" + regex + "}"
|
epName = "{" + urlEpName + ":" + regex + "}"
|
||||||
epID = "{" + urlEpID + ":" + regex + "}"
|
epID = "{" + urlEpID + ":" + regex + "}"
|
||||||
|
epPID = "{" + urlEpPID + ":" + regex + "}"
|
||||||
cnID = "{" + urlCnID + ":" + regex + "}"
|
cnID = "{" + urlCnID + ":" + regex + "}"
|
||||||
|
|
||||||
// Internal URL variable name, they can be anything
|
// Internal URL variable name, they can be anything
|
||||||
urlNwName = "network-name"
|
urlNwName = "network-name"
|
||||||
urlNwID = "network-id"
|
urlNwID = "network-id"
|
||||||
|
urlNwPID = "network-partial-id"
|
||||||
urlEpName = "endpoint-name"
|
urlEpName = "endpoint-name"
|
||||||
urlEpID = "endpoint-id"
|
urlEpID = "endpoint-id"
|
||||||
|
urlEpPID = "endpoint-partial-id"
|
||||||
urlCnID = "container-id"
|
urlCnID = "container-id"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -77,9 +84,12 @@ func (h *httpHandler) initRouter() {
|
||||||
"GET": {
|
"GET": {
|
||||||
// Order matters
|
// Order matters
|
||||||
{"/networks", []string{"name", nwName}, procGetNetworks},
|
{"/networks", []string{"name", nwName}, procGetNetworks},
|
||||||
|
{"/networks", []string{"partial-id", nwPID}, procGetNetworks},
|
||||||
{"/networks", nil, procGetNetworks},
|
{"/networks", nil, procGetNetworks},
|
||||||
{"/networks/" + nwID, nil, procGetNetwork},
|
{"/networks/" + nwID, nil, procGetNetwork},
|
||||||
{"/networks/" + nwID + "/endpoints", []string{"name", epName}, procGetEndpoints},
|
{"/networks/" + nwID + "/endpoints", []string{"name", epName}, procGetEndpoints},
|
||||||
|
{"/networks/" + nwID + "/endpoints", []string{"partial-id", epPID}, procGetEndpoints},
|
||||||
|
|
||||||
{"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint},
|
{"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint},
|
||||||
},
|
},
|
||||||
"POST": {
|
"POST": {
|
||||||
|
@ -234,12 +244,26 @@ func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body
|
||||||
func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||||
var list []*networkResource
|
var list []*networkResource
|
||||||
|
|
||||||
// If query parameter is specified, return a filtered collection
|
// Look for query filters and validate
|
||||||
if name, queryByName := vars[urlNwName]; queryByName {
|
name, queryByName := vars[urlNwName]
|
||||||
nw, errRsp := findNetwork(c, name, byName)
|
shortID, queryByPid := vars[urlNwPID]
|
||||||
if errRsp.isOK() {
|
if queryByName && queryByPid {
|
||||||
|
return nil, &badQueryresponse
|
||||||
|
}
|
||||||
|
|
||||||
|
if queryByName {
|
||||||
|
if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() {
|
||||||
list = append(list, buildNetworkResource(nw))
|
list = append(list, buildNetworkResource(nw))
|
||||||
}
|
}
|
||||||
|
} else if queryByPid {
|
||||||
|
// Return all the prefix-matching networks
|
||||||
|
l := func(nw libnetwork.Network) bool {
|
||||||
|
if strings.HasPrefix(nw.ID(), shortID) {
|
||||||
|
list = append(list, buildNetworkResource(nw))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
c.WalkNetworks(l)
|
||||||
} else {
|
} else {
|
||||||
for _, nw := range c.Networks() {
|
for _, nw := range c.Networks() {
|
||||||
list = append(list, buildNetworkResource(nw))
|
list = append(list, buildNetworkResource(nw))
|
||||||
|
@ -295,6 +319,13 @@ func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, bod
|
||||||
}
|
}
|
||||||
|
|
||||||
func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
|
||||||
|
// Look for query filters and validate
|
||||||
|
name, queryByName := vars[urlEpName]
|
||||||
|
shortID, queryByPid := vars[urlEpPID]
|
||||||
|
if queryByName && queryByPid {
|
||||||
|
return nil, &badQueryresponse
|
||||||
|
}
|
||||||
|
|
||||||
nwT, nwBy := detectNetworkTarget(vars)
|
nwT, nwBy := detectNetworkTarget(vars)
|
||||||
nw, errRsp := findNetwork(c, nwT, nwBy)
|
nw, errRsp := findNetwork(c, nwT, nwBy)
|
||||||
if !errRsp.isOK() {
|
if !errRsp.isOK() {
|
||||||
|
@ -304,11 +335,19 @@ func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, bo
|
||||||
var list []*endpointResource
|
var list []*endpointResource
|
||||||
|
|
||||||
// If query parameter is specified, return a filtered collection
|
// If query parameter is specified, return a filtered collection
|
||||||
if epT, queryByName := vars[urlEpName]; queryByName {
|
if queryByName {
|
||||||
ep, errRsp := findEndpoint(c, nwT, epT, nwBy, byName)
|
if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() {
|
||||||
if errRsp.isOK() {
|
|
||||||
list = append(list, buildEndpointResource(ep))
|
list = append(list, buildEndpointResource(ep))
|
||||||
}
|
}
|
||||||
|
} else if queryByPid {
|
||||||
|
// Return all the prefix-matching networks
|
||||||
|
l := func(ep libnetwork.Endpoint) bool {
|
||||||
|
if strings.HasPrefix(ep.ID(), shortID) {
|
||||||
|
list = append(list, buildEndpointResource(ep))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
nw.WalkEndpoints(l)
|
||||||
} else {
|
} else {
|
||||||
for _, ep := range nw.Endpoints() {
|
for _, ep := range nw.Endpoints() {
|
||||||
epr := buildEndpointResource(ep)
|
epr := buildEndpointResource(ep)
|
||||||
|
|
|
@ -482,6 +482,43 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDetectGetNetworksInvalidQueryComposition(t *testing.T) {
|
||||||
|
c, err := libnetwork.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := map[string]string{urlNwName: "x", urlNwPID: "y"}
|
||||||
|
_, errRsp := procGetNetworks(c, vars, nil)
|
||||||
|
if errRsp.StatusCode != http.StatusBadRequest {
|
||||||
|
t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDetectGetEndpointsInvalidQueryComposition(t *testing.T) {
|
||||||
|
defer netutils.SetupTestNetNS(t)()
|
||||||
|
|
||||||
|
c, err := libnetwork.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = c.ConfigureNetworkDriver(bridgeNetType, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.NewNetwork(bridgeNetType, "network", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := map[string]string{urlNwName: "network", urlEpName: "x", urlEpPID: "y"}
|
||||||
|
_, errRsp := procGetEndpoints(c, vars, nil)
|
||||||
|
if errRsp.StatusCode != http.StatusBadRequest {
|
||||||
|
t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFindNetworkUtil(t *testing.T) {
|
func TestFindNetworkUtil(t *testing.T) {
|
||||||
defer netutils.SetupTestNetNS(t)()
|
defer netutils.SetupTestNetNS(t)()
|
||||||
|
|
||||||
|
@ -1281,6 +1318,29 @@ func TestEndToEnd(t *testing.T) {
|
||||||
t.Fatalf("Incongruent resource found: %v", list[0])
|
t.Fatalf("Incongruent resource found: %v", list[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Query network by partial id
|
||||||
|
chars := []byte(nid)
|
||||||
|
partial := string(chars[0 : len(chars)/2])
|
||||||
|
req, err = http.NewRequest("GET", "/networks?partial-id="+partial, 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
|
// Get network by id
|
||||||
req, err = http.NewRequest("GET", "/networks/"+nid, nil)
|
req, err = http.NewRequest("GET", "/networks/"+nid, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1373,6 +1433,29 @@ func TestEndToEnd(t *testing.T) {
|
||||||
t.Fatalf("Incongruent resource found: %v", epList[0])
|
t.Fatalf("Incongruent resource found: %v", epList[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Query endpoint by partial id
|
||||||
|
chars = []byte(eid)
|
||||||
|
partial = string(chars[0 : len(chars)/2])
|
||||||
|
req, err = http.NewRequest("GET", "/networks/"+nid+"/endpoints?partial-id="+partial, 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
|
// Get endpoint by id
|
||||||
req, err = http.NewRequest("GET", "/networks/"+nid+"/endpoints/"+eid, nil)
|
req, err = http.NewRequest("GET", "/networks/"+nid+"/endpoints/"+eid, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue