New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Docker Network UX & remote API changes #16645
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,207 @@ | ||
// +build experimental | ||
|
||
package client | ||
|
||
import ( | ||
nwclient "github.com/docker/libnetwork/client" | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"text/tabwriter" | ||
|
||
"github.com/docker/docker/api/types" | ||
Cli "github.com/docker/docker/cli" | ||
flag "github.com/docker/docker/pkg/mflag" | ||
"github.com/docker/docker/pkg/stringid" | ||
) | ||
|
||
// CmdNetwork is used to create, display and configure network endpoints. | ||
// CmdNetwork is the parent subcommand for all network commands | ||
// | ||
// Usage: docker network <COMMAND> [OPTIONS] | ||
func (cli *DockerCli) CmdNetwork(args ...string) error { | ||
nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper)) | ||
args = append([]string{"network"}, args...) | ||
return nCli.Cmd("docker", args...) | ||
cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false) | ||
cmd.Require(flag.Min, 1) | ||
err := cmd.ParseFlags(args, true) | ||
cmd.Usage() | ||
return err | ||
} | ||
|
||
// CmdNetworkCreate creates a new network with a given name | ||
// | ||
// Usage: docker network create [OPTIONS] <NETWORK-NAME> | ||
func (cli *DockerCli) CmdNetworkCreate(args ...string) error { | ||
cmd := Cli.Subcmd("network create", []string{"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 | ||
nc := types.NetworkCreate{Name: cmd.Arg(0), Driver: *flDriver, CheckDuplicate: true} | ||
obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil)) | ||
if err != nil { | ||
return err | ||
} | ||
var resp types.NetworkCreateResponse | ||
err = json.Unmarshal(obj, &resp) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Fprintf(cli.out, "%s\n", resp.ID) | ||
return nil | ||
} | ||
|
||
// CmdNetworkRm deletes a network | ||
// | ||
// Usage: docker network rm <NETWORK-NAME | NETWORK-ID> | ||
func (cli *DockerCli) CmdNetworkRm(args ...string) error { | ||
cmd := Cli.Subcmd("network rm", []string{"NETWORK"}, "Deletes a network", false) | ||
cmd.Require(flag.Exact, 1) | ||
err := cmd.ParseFlags(args, true) | ||
if err != nil { | ||
return err | ||
} | ||
_, _, err = readBody(cli.call("DELETE", "/networks/"+cmd.Arg(0), nil, nil)) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// CmdNetworkConnect connects a container to a network | ||
// | ||
// Usage: docker network connect <NETWORK> <CONTAINER> | ||
func (cli *DockerCli) CmdNetworkConnect(args ...string) error { | ||
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false) | ||
cmd.Require(flag.Exact, 2) | ||
err := cmd.ParseFlags(args, true) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
nc := types.NetworkConnect{Container: cmd.Arg(1)} | ||
_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/connect", nc, nil)) | ||
return err | ||
} | ||
|
||
// CmdNetworkDisconnect disconnects a container from a network | ||
// | ||
// Usage: docker network disconnect <NETWORK> <CONTAINER> | ||
func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error { | ||
cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false) | ||
cmd.Require(flag.Exact, 2) | ||
err := cmd.ParseFlags(args, true) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
nc := types.NetworkConnect{Container: cmd.Arg(1)} | ||
_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/disconnect", nc, nil)) | ||
return err | ||
} | ||
|
||
// CmdNetworkLs lists all the netorks managed by docker daemon | ||
// | ||
// Usage: docker network ls [OPTIONS] | ||
func (cli *DockerCli) CmdNetworkLs(args ...string) error { | ||
cmd := Cli.Subcmd("network ls", []string{""}, "Lists all the networks created by the user", false) | ||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") | ||
noTrunc := cmd.Bool([]string{"", "-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 []types.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\tDRIVER") | ||
} | ||
|
||
for _, networkResource := range networkResources { | ||
ID := networkResource.ID | ||
netName := networkResource.Name | ||
if !*noTrunc { | ||
ID = stringid.TruncateID(ID) | ||
} | ||
if *quiet { | ||
fmt.Fprintln(wr, ID) | ||
continue | ||
} | ||
driver := networkResource.Driver | ||
fmt.Fprintf(wr, "%s\t%s\t%s\t", | ||
ID, | ||
netName, | ||
driver) | ||
fmt.Fprint(wr, "\n") | ||
} | ||
wr.Flush() | ||
return nil | ||
} | ||
|
||
// CmdNetworkInspect inspects the network object for more details | ||
// | ||
// Usage: docker network inspect <NETWORK> | ||
// CmdNetworkInspect handles Network inspect UI | ||
func (cli *DockerCli) CmdNetworkInspect(args ...string) error { | ||
cmd := Cli.Subcmd("network inspect", []string{"NETWORK"}, "Displays detailed information on a network", false) | ||
cmd.Require(flag.Exact, 1) | ||
err := cmd.ParseFlags(args, true) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
obj, _, err := readBody(cli.call("GET", "/networks/"+cmd.Arg(0), nil, nil)) | ||
if err != nil { | ||
return err | ||
} | ||
networkResource := &types.NetworkResource{} | ||
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil { | ||
return err | ||
} | ||
|
||
indented := new(bytes.Buffer) | ||
if err := json.Indent(indented, obj, "", " "); err != nil { | ||
return err | ||
} | ||
if _, err := io.Copy(cli.out, indented); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's surprising to have this one dump raw JSON instead of a structured table. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the network inspect output will certainly expand to accomodate more data in it (such as stats) & more network resources. Structured table doesn't suit well. |
||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func networkUsage() string { | ||
networkCommands := map[string]string{ | ||
"create": "Create a network", | ||
"connect": "Connect container to a network", | ||
"disconnect": "Disconnect container from a network", | ||
"inspect": "Display detailed network information", | ||
"ls": "List all networks", | ||
"rm": "Remove a network", | ||
} | ||
|
||
help := "Commands:\n" | ||
|
||
for cmd, description := range networkCommands { | ||
help += fmt.Sprintf(" %-25.25s%s\n", cmd, description) | ||
} | ||
|
||
help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.") | ||
return help | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,41 @@ | ||
package network | ||
|
||
import ( | ||
"github.com/docker/docker/api/server/httputils" | ||
"github.com/docker/docker/api/server/router" | ||
"github.com/docker/docker/api/server/router/local" | ||
"github.com/docker/docker/daemon" | ||
) | ||
|
||
// networkRouter is a router to talk with the network controller | ||
type networkRouter struct { | ||
daemon *daemon.Daemon | ||
routes []router.Route | ||
} | ||
|
||
// Routes returns the available routes to the network controller | ||
func (n networkRouter) Routes() []router.Route { | ||
return n.routes | ||
// NewRouter initializes a new network router | ||
func NewRouter(d *daemon.Daemon) router.Router { | ||
r := &networkRouter{ | ||
daemon: d, | ||
} | ||
r.initRoutes() | ||
return r | ||
} | ||
|
||
type networkRoute struct { | ||
path string | ||
handler httputils.APIFunc | ||
// Routes returns the available routes to the network controller | ||
func (r *networkRouter) Routes() []router.Route { | ||
return r.routes | ||
} | ||
|
||
// Handler returns the APIFunc to let the server wrap it in middlewares | ||
func (l networkRoute) Handler() httputils.APIFunc { | ||
return l.handler | ||
func (r *networkRouter) initRoutes() { | ||
r.routes = []router.Route{ | ||
// GET | ||
local.NewGetRoute("/networks", r.getNetworksList), | ||
local.NewGetRoute("/networks/{id:.*}", r.getNetwork), | ||
// POST | ||
local.NewPostRoute("/networks/create", r.postNetworkCreate), | ||
local.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. minor: since this post is going to connect a
same will be true for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its highly likely that we will expand the connect data to more than container-id (such as alias and other endpoint specific data). When it comes, we dont want to change the API again. So, I prefer to keep the current structure and it can easily be expanded by having backward compatible clients. |
||
local.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect), | ||
// DELETE | ||
local.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork), | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to have:
--format
ondocker inspect
is particularly useful, we should try to have the same here if it's not too much of an effort.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. the inspect output atm is simple. But with the introduction of ipam driver (moby/libnetwork#525), we are adding more stuffs to the network inspect and that might make the need for --format useful. I think we can decide on that in the subsequent PR for the ipam UX changes. WDYT ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't block this PR on that as long as it is in the plans. What is the timeline for the IPAM UX changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
immediately after this and Discovery PR are merged :)