mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
03504cab65
1. replaced --net option for service UI with SERVICE.[NETWORK] format 2. Making using of the default network/driver backend support 3. NetworkName and NetworkType from the UI/API can be empty string and it will be replaced with DefaultNetwork and DefaultDriver As per the design goals, we wanted to keep libnetwork core free of handling defaults. Rather, the clients (docker & dnet) must handle the defaultness of these entities. Also, since there is no API to get these Default values from the backend, UI will not handle the default values either. Hence, this falls under the responsibility of the API layer to handle this specific case. Signed-off-by: Madhu Venugopal <madhu@docker.com>
231 lines
6.3 KiB
Go
231 lines
6.3 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"text/tabwriter"
|
|
|
|
flag "github.com/docker/docker/pkg/mflag"
|
|
"github.com/docker/docker/pkg/stringid"
|
|
)
|
|
|
|
type command struct {
|
|
name string
|
|
description string
|
|
}
|
|
|
|
var (
|
|
networkCommands = []command{
|
|
{"create", "Create a network"},
|
|
{"rm", "Remove a network"},
|
|
{"ls", "List all networks"},
|
|
{"info", "Display information of a network"},
|
|
}
|
|
)
|
|
|
|
// CmdNetwork handles the root Network UI
|
|
func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error {
|
|
cmd := cli.Subcmd(chain, "network", "COMMAND [OPTIONS] [arg...]", networkUsage(chain), false)
|
|
cmd.Require(flag.Min, 1)
|
|
err := cmd.ParseFlags(args, true)
|
|
if err == nil {
|
|
cmd.Usage()
|
|
return fmt.Errorf("invalid command : %v", args)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// CmdNetworkCreate handles Network Create UI
|
|
func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
|
|
cmd := cli.Subcmd(chain, "create", "NETWORK-NAME", "Creates a new network with a name specified by the user", false)
|
|
flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network")
|
|
cmd.Require(flag.Exact, 1)
|
|
err := cmd.ParseFlags(args, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Construct network create request body
|
|
ops := make(map[string]interface{})
|
|
nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, Options: ops}
|
|
obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var replyID string
|
|
err = json.Unmarshal(obj, &replyID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintf(cli.out, "%s\n", replyID)
|
|
return nil
|
|
}
|
|
|
|
// CmdNetworkRm handles Network Delete UI
|
|
func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error {
|
|
cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false)
|
|
cmd.Require(flag.Exact, 1)
|
|
err := cmd.ParseFlags(args, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
id, err := lookupNetworkID(cli, cmd.Arg(0))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, _, err = readBody(cli.call("DELETE", "/networks/"+id, nil, nil))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CmdNetworkLs handles Network List UI
|
|
func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error {
|
|
cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false)
|
|
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
|
|
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
|
|
nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
|
|
last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
|
|
err := cmd.ParseFlags(args, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if *last == -1 && *nLatest {
|
|
*last = 1
|
|
}
|
|
|
|
var networkResources []networkResource
|
|
err = json.Unmarshal(obj, &networkResources)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
|
|
|
// unless quiet (-q) is specified, print field titles
|
|
if !*quiet {
|
|
fmt.Fprintln(wr, "NETWORK ID\tNAME\tTYPE")
|
|
}
|
|
|
|
for _, networkResource := range networkResources {
|
|
ID := networkResource.ID
|
|
netName := networkResource.Name
|
|
if !*noTrunc {
|
|
ID = stringid.TruncateID(ID)
|
|
}
|
|
if *quiet {
|
|
fmt.Fprintln(wr, ID)
|
|
continue
|
|
}
|
|
netType := networkResource.Type
|
|
fmt.Fprintf(wr, "%s\t%s\t%s\t",
|
|
ID,
|
|
netName,
|
|
netType)
|
|
fmt.Fprint(wr, "\n")
|
|
}
|
|
wr.Flush()
|
|
return nil
|
|
}
|
|
|
|
// CmdNetworkInfo handles Network Info UI
|
|
func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error {
|
|
cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false)
|
|
cmd.Require(flag.Exact, 1)
|
|
err := cmd.ParseFlags(args, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
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 {
|
|
return err
|
|
}
|
|
networkResource := &networkResource{}
|
|
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintf(cli.out, "Network Id: %s\n", networkResource.ID)
|
|
fmt.Fprintf(cli.out, "Name: %s\n", networkResource.Name)
|
|
fmt.Fprintf(cli.out, "Type: %s\n", networkResource.Type)
|
|
if networkResource.Services != nil {
|
|
for _, serviceResource := range networkResource.Services {
|
|
fmt.Fprintf(cli.out, " Service Id: %s\n", serviceResource.ID)
|
|
fmt.Fprintf(cli.out, "\tName: %s\n", serviceResource.Name)
|
|
}
|
|
}
|
|
|
|
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"
|
|
|
|
for _, cmd := range networkCommands {
|
|
help += fmt.Sprintf(" %-25.25s%s\n", cmd.name, cmd.description)
|
|
}
|
|
|
|
help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain)
|
|
return help
|
|
}
|