mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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>
This commit is contained in:
parent
ef6ddb33f5
commit
5ee5e2452f
3 changed files with 139 additions and 52 deletions
|
@ -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, cFunc)
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "info", "test")
|
||||
err := cli.Cmd("docker", "network", "info", mockNwName)
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientNetworkInfoById(t *testing.T) {
|
||||
var out, errOut bytes.Buffer
|
||||
cli := NewNetworkCli(&out, &errOut, callbackFunc)
|
||||
|
||||
err := cli.Cmd("docker", "network", "info", mockNwID)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
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 {
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue