Selaa lähdekoodia

Modified Client to make use of the corrected REST API

Also supporting name, id & partial-id lookups for all the network
commands

Signed-off-by: Madhu Venugopal <madhu@docker.com>
Madhu Venugopal 10 vuotta sitten
vanhempi
commit
5ee5e2452f
3 muutettua tiedostoa jossa 138 lisäystä ja 51 poistoa
  1. 70 40
      libnetwork/client/client_test.go
  2. 66 9
      libnetwork/client/network.go
  3. 2 2
      libnetwork/cmd/dnet/dnet.go

+ 70 - 40
libnetwork/client/client_test.go

@@ -2,7 +2,10 @@ package client
 
 import (
 	"bytes"
+	"encoding/json"
 	"io"
+	"os"
+	"strings"
 	"testing"
 
 	_ "github.com/docker/libnetwork/netutils"
@@ -15,12 +18,46 @@ type nopCloser struct {
 
 func (nopCloser) Close() error { return nil }
 
+func TestMain(m *testing.M) {
+	setupMockHTTPCallback()
+	os.Exit(m.Run())
+}
+
+var callbackFunc func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error)
+var mockNwJSON, mockNwListJSON []byte
+var mockNwName = "test"
+var mockNwID = "23456789"
+
+func setupMockHTTPCallback() {
+	var list []networkResource
+	nw := networkResource{Name: mockNwName, ID: mockNwID}
+	mockNwJSON, _ = json.Marshal(nw)
+	list = append(list, nw)
+	mockNwListJSON, _ = json.Marshal(list)
+	callbackFunc = func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
+		var rsp string
+		switch method {
+		case "GET":
+			if strings.Contains(path, "networks?name=") {
+				rsp = string(mockNwListJSON)
+			} else if strings.HasSuffix(path, "networks") {
+				rsp = string(mockNwListJSON)
+			} else if strings.HasSuffix(path, "networks/"+mockNwID) {
+				rsp = string(mockNwJSON)
+			}
+		case "POST":
+			rsp = mockNwID
+		case "PUT":
+		case "DELETE":
+			rsp = ""
+		}
+		return nopCloser{bytes.NewBufferString(rsp)}, 200, nil
+	}
+}
+
 func TestClientDummyCommand(t *testing.T) {
 	var out, errOut bytes.Buffer
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
-	}
-	cli := NewNetworkCli(&out, &errOut, cFunc)
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
 
 	err := cli.Cmd("docker", "dummy")
 	if err == nil {
@@ -30,10 +67,7 @@ func TestClientDummyCommand(t *testing.T) {
 
 func TestClientNetworkInvalidCommand(t *testing.T) {
 	var out, errOut bytes.Buffer
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
-	}
-	cli := NewNetworkCli(&out, &errOut, cFunc)
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
 
 	err := cli.Cmd("docker", "network", "invalid")
 	if err == nil {
@@ -43,12 +77,9 @@ func TestClientNetworkInvalidCommand(t *testing.T) {
 
 func TestClientNetworkCreate(t *testing.T) {
 	var out, errOut bytes.Buffer
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
-	}
-	cli := NewNetworkCli(&out, &errOut, cFunc)
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
 
-	err := cli.Cmd("docker", "network", "create", "test")
+	err := cli.Cmd("docker", "network", "create", mockNwName)
 	if err != nil {
 		t.Fatal(err.Error())
 	}
@@ -56,17 +87,14 @@ func TestClientNetworkCreate(t *testing.T) {
 
 func TestClientNetworkCreateWithDriver(t *testing.T) {
 	var out, errOut bytes.Buffer
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
-	}
-	cli := NewNetworkCli(&out, &errOut, cFunc)
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
 
-	err := cli.Cmd("docker", "network", "create", "-f=dummy", "test")
+	err := cli.Cmd("docker", "network", "create", "-f=dummy", mockNwName)
 	if err == nil {
 		t.Fatalf("Passing incorrect flags to the create command must fail")
 	}
 
-	err = cli.Cmd("docker", "network", "create", "-d=dummy", "test")
+	err = cli.Cmd("docker", "network", "create", "-d=dummy", mockNwName)
 	if err != nil {
 		t.Fatalf(err.Error())
 	}
@@ -74,12 +102,9 @@ func TestClientNetworkCreateWithDriver(t *testing.T) {
 
 func TestClientNetworkRm(t *testing.T) {
 	var out, errOut bytes.Buffer
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
-		return nopCloser{bytes.NewBufferString("")}, 200, nil
-	}
-	cli := NewNetworkCli(&out, &errOut, cFunc)
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
 
-	err := cli.Cmd("docker", "network", "rm", "test")
+	err := cli.Cmd("docker", "network", "rm", mockNwName)
 	if err != nil {
 		t.Fatal(err.Error())
 	}
@@ -87,35 +112,40 @@ func TestClientNetworkRm(t *testing.T) {
 
 func TestClientNetworkLs(t *testing.T) {
 	var out, errOut bytes.Buffer
-	networks := "db,web,test"
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
-		return nopCloser{bytes.NewBufferString(networks)}, 200, nil
-	}
-	cli := NewNetworkCli(&out, &errOut, cFunc)
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
 
 	err := cli.Cmd("docker", "network", "ls")
 	if err != nil {
 		t.Fatal(err.Error())
 	}
-	if out.String() != networks {
-		t.Fatal("Network List command fail to return the intended list")
+	if out.String() != string(mockNwListJSON) {
+		t.Fatal("Network List command fail to return the expected list")
 	}
 }
 
 func TestClientNetworkInfo(t *testing.T) {
 	var out, errOut bytes.Buffer
-	info := "dummy info"
-	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
-		return nopCloser{bytes.NewBufferString(info)}, 200, nil
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
+
+	err := cli.Cmd("docker", "network", "info", mockNwName)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+	if out.String() != string(mockNwJSON) {
+		t.Fatal("Network info command fail to return the expected object")
 	}
-	cli := NewNetworkCli(&out, &errOut, cFunc)
+}
+
+func TestClientNetworkInfoById(t *testing.T) {
+	var out, errOut bytes.Buffer
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
 
-	err := cli.Cmd("docker", "network", "info", "test")
+	err := cli.Cmd("docker", "network", "info", mockNwID)
 	if err != nil {
 		t.Fatal(err.Error())
 	}
-	if out.String() != info {
-		t.Fatal("Network List command fail to return the intended list")
+	if out.String() != string(mockNwJSON) {
+		t.Fatal("Network info command fail to return the expected object")
 	}
 }
 
@@ -127,7 +157,7 @@ func TestClientNetworkCreateHelp(t *testing.T) {
 	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
 		return nil, 0, nil
 	}
-	cli := NewNetworkCli(&out, &errOut, cFunc)
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
 
 	err := cli.Cmd("docker", "network", "create", "--help")
 	if err != nil {
@@ -144,7 +174,7 @@ func TestClientNetworkCreateMissingArgument(t *testing.T) {
 	cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
 		return nil, 0, nil
 	}
-	cli := NewNetworkCli(&out, &errOut, cFunc)
+	cli := NewNetworkCli(&out, &errOut, callbackFunc)
 
 	err := cli.Cmd("docker", "network", "create")
 	if err != nil {

+ 66 - 9
libnetwork/client/network.go

@@ -2,8 +2,10 @@ package client
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"io"
+	"net/http"
 
 	flag "github.com/docker/docker/pkg/mflag"
 )
@@ -33,7 +35,7 @@ func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error {
 	err := cmd.ParseFlags(args, true)
 	if err == nil {
 		cmd.Usage()
-		return fmt.Errorf("Invalid command : %v", args)
+		return fmt.Errorf("invalid command : %v", args)
 	}
 	return err
 }
@@ -55,7 +57,6 @@ func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
 
 	obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
 	if err != nil {
-		fmt.Fprintf(cli.err, "%s", err.Error())
 		return err
 	}
 	if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
@@ -66,15 +67,18 @@ func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
 
 // CmdNetworkRm handles Network Delete UI
 func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error {
-	cmd := cli.Subcmd(chain, "rm", "NETWORK-NAME", "Deletes a network", false)
+	cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false)
 	cmd.Require(flag.Min, 1)
 	err := cmd.ParseFlags(args, true)
 	if err != nil {
 		return err
 	}
-	obj, _, err := readBody(cli.call("DELETE", "/networks/name/"+cmd.Arg(0), nil, nil))
+	id, err := lookupNetworkID(cli, cmd.Arg(0))
+	if err != nil {
+		return err
+	}
+	obj, _, err := readBody(cli.call("DELETE", "/networks/"+id, nil, nil))
 	if err != nil {
-		fmt.Fprintf(cli.err, "%s", err.Error())
 		return err
 	}
 	if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
@@ -92,7 +96,6 @@ func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error {
 	}
 	obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
 	if err != nil {
-		fmt.Fprintf(cli.err, "%s", err.Error())
 		return err
 	}
 	if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
@@ -103,15 +106,20 @@ func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error {
 
 // CmdNetworkInfo handles Network Info UI
 func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error {
-	cmd := cli.Subcmd(chain, "info", "NETWORK-NAME", "Displays detailed information on a network", false)
+	cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false)
 	cmd.Require(flag.Min, 1)
 	err := cmd.ParseFlags(args, true)
 	if err != nil {
 		return err
 	}
-	obj, _, err := readBody(cli.call("GET", "/networks/name/"+cmd.Arg(0), nil, nil))
+
+	id, err := lookupNetworkID(cli, cmd.Arg(0))
+	if err != nil {
+		return err
+	}
+
+	obj, _, err := readBody(cli.call("GET", "/networks/"+id, nil, nil))
 	if err != nil {
-		fmt.Fprintf(cli.err, "%s", err.Error())
 		return err
 	}
 	if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
@@ -120,6 +128,55 @@ func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error {
 	return nil
 }
 
+// Helper function to predict if a string is a name or id or partial-id
+// This provides a best-effort mechanism to identify a id with the help of GET Filter APIs
+// Being a UI, its most likely that name will be used by the user, which is used to lookup
+// the corresponding ID. If ID is not found, this function will assume that the passed string
+// is an ID by itself.
+
+func lookupNetworkID(cli *NetworkCli, nameID string) (string, error) {
+	obj, statusCode, err := readBody(cli.call("GET", "/networks?name="+nameID, nil, nil))
+	if err != nil {
+		return "", err
+	}
+
+	if statusCode != http.StatusOK {
+		return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
+	}
+
+	var list []*networkResource
+	err = json.Unmarshal(obj, &list)
+	if err != nil {
+		return "", err
+	}
+	if len(list) > 0 {
+		// name query filter will always return a single-element collection
+		return list[0].ID, nil
+	}
+
+	// Check for Partial-id
+	obj, statusCode, err = readBody(cli.call("GET", "/networks?partial-id="+nameID, nil, nil))
+	if err != nil {
+		return "", err
+	}
+
+	if statusCode != http.StatusOK {
+		return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
+	}
+
+	err = json.Unmarshal(obj, &list)
+	if err != nil {
+		return "", err
+	}
+	if len(list) == 0 {
+		return "", fmt.Errorf("resource not found %s", nameID)
+	}
+	if len(list) > 1 {
+		return "", fmt.Errorf("multiple Networks matching the partial identifier (%s). Please use full identifier", nameID)
+	}
+	return list[0].ID, nil
+}
+
 func networkUsage(chain string) string {
 	help := "Commands:\n"
 

+ 2 - 2
libnetwork/cmd/dnet/dnet.go

@@ -158,7 +158,7 @@ func (d *dnetConnection) httpCall(method, path string, data interface{}, headers
 		statusCode = resp.StatusCode
 	}
 	if err != nil {
-		return nil, statusCode, fmt.Errorf("An error occurred trying to connect: %v", err)
+		return nil, statusCode, fmt.Errorf("error when trying to connect: %v", err)
 	}
 
 	if statusCode < 200 || statusCode >= 400 {
@@ -166,7 +166,7 @@ func (d *dnetConnection) httpCall(method, path string, data interface{}, headers
 		if err != nil {
 			return nil, statusCode, err
 		}
-		return nil, statusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
+		return nil, statusCode, fmt.Errorf("error : %s", bytes.TrimSpace(body))
 	}
 
 	return resp.Body, statusCode, nil