mirror of https://github.com/docker/docs.git
Merge pull request #23210 from vdemeester/migrate-network-command-to-cobra
Use spf13/cobra for docker network and subcommands
This commit is contained in:
commit
07a7c0632f
|
@ -3,42 +3,35 @@ package client
|
||||||
// Command returns a cli command handler if one exists
|
// Command returns a cli command handler if one exists
|
||||||
func (cli *DockerCli) Command(name string) func(...string) error {
|
func (cli *DockerCli) Command(name string) func(...string) error {
|
||||||
return map[string]func(...string) error{
|
return map[string]func(...string) error{
|
||||||
"attach": cli.CmdAttach,
|
"attach": cli.CmdAttach,
|
||||||
"build": cli.CmdBuild,
|
"build": cli.CmdBuild,
|
||||||
"commit": cli.CmdCommit,
|
"commit": cli.CmdCommit,
|
||||||
"cp": cli.CmdCp,
|
"cp": cli.CmdCp,
|
||||||
"events": cli.CmdEvents,
|
"events": cli.CmdEvents,
|
||||||
"exec": cli.CmdExec,
|
"exec": cli.CmdExec,
|
||||||
"history": cli.CmdHistory,
|
"history": cli.CmdHistory,
|
||||||
"images": cli.CmdImages,
|
"images": cli.CmdImages,
|
||||||
"import": cli.CmdImport,
|
"import": cli.CmdImport,
|
||||||
"info": cli.CmdInfo,
|
"info": cli.CmdInfo,
|
||||||
"inspect": cli.CmdInspect,
|
"inspect": cli.CmdInspect,
|
||||||
"kill": cli.CmdKill,
|
"kill": cli.CmdKill,
|
||||||
"load": cli.CmdLoad,
|
"load": cli.CmdLoad,
|
||||||
"login": cli.CmdLogin,
|
"login": cli.CmdLogin,
|
||||||
"logout": cli.CmdLogout,
|
"logout": cli.CmdLogout,
|
||||||
"network": cli.CmdNetwork,
|
"pause": cli.CmdPause,
|
||||||
"network create": cli.CmdNetworkCreate,
|
"port": cli.CmdPort,
|
||||||
"network connect": cli.CmdNetworkConnect,
|
"ps": cli.CmdPs,
|
||||||
"network disconnect": cli.CmdNetworkDisconnect,
|
"pull": cli.CmdPull,
|
||||||
"network inspect": cli.CmdNetworkInspect,
|
"push": cli.CmdPush,
|
||||||
"network ls": cli.CmdNetworkLs,
|
"rename": cli.CmdRename,
|
||||||
"network rm": cli.CmdNetworkRm,
|
"restart": cli.CmdRestart,
|
||||||
"pause": cli.CmdPause,
|
"rm": cli.CmdRm,
|
||||||
"port": cli.CmdPort,
|
"save": cli.CmdSave,
|
||||||
"ps": cli.CmdPs,
|
"stats": cli.CmdStats,
|
||||||
"pull": cli.CmdPull,
|
"tag": cli.CmdTag,
|
||||||
"push": cli.CmdPush,
|
"top": cli.CmdTop,
|
||||||
"rename": cli.CmdRename,
|
"update": cli.CmdUpdate,
|
||||||
"restart": cli.CmdRestart,
|
"version": cli.CmdVersion,
|
||||||
"rm": cli.CmdRm,
|
"wait": cli.CmdWait,
|
||||||
"save": cli.CmdSave,
|
|
||||||
"stats": cli.CmdStats,
|
|
||||||
"tag": cli.CmdTag,
|
|
||||||
"top": cli.CmdTop,
|
|
||||||
"update": cli.CmdUpdate,
|
|
||||||
"version": cli.CmdVersion,
|
|
||||||
"wait": cli.CmdWait,
|
|
||||||
}[name]
|
}[name]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,397 +0,0 @@
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/client/inspect"
|
|
||||||
Cli "github.com/docker/docker/cli"
|
|
||||||
"github.com/docker/docker/opts"
|
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
|
||||||
"github.com/docker/docker/pkg/stringid"
|
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/filters"
|
|
||||||
"github.com/docker/engine-api/types/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdNetwork is the parent subcommand for all network commands
|
|
||||||
//
|
|
||||||
// Usage: docker network <COMMAND> [OPTIONS]
|
|
||||||
func (cli *DockerCli) CmdNetwork(args ...string) error {
|
|
||||||
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"}, "bridge", "Driver to manage the Network")
|
|
||||||
flOpts := opts.NewMapOpts(nil, nil)
|
|
||||||
|
|
||||||
flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver")
|
|
||||||
flIpamSubnet := opts.NewListOpts(nil)
|
|
||||||
flIpamIPRange := opts.NewListOpts(nil)
|
|
||||||
flIpamGateway := opts.NewListOpts(nil)
|
|
||||||
flIpamAux := opts.NewMapOpts(nil, nil)
|
|
||||||
flIpamOpt := opts.NewMapOpts(nil, nil)
|
|
||||||
flLabels := opts.NewListOpts(nil)
|
|
||||||
|
|
||||||
cmd.Var(&flIpamSubnet, []string{"-subnet"}, "subnet in CIDR format that represents a network segment")
|
|
||||||
cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
|
|
||||||
cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet")
|
|
||||||
cmd.Var(flIpamAux, []string{"-aux-address"}, "auxiliary ipv4 or ipv6 addresses used by Network driver")
|
|
||||||
cmd.Var(flOpts, []string{"o", "-opt"}, "set driver specific options")
|
|
||||||
cmd.Var(flIpamOpt, []string{"-ipam-opt"}, "set IPAM driver specific options")
|
|
||||||
cmd.Var(&flLabels, []string{"-label"}, "set metadata on a network")
|
|
||||||
|
|
||||||
flInternal := cmd.Bool([]string{"-internal"}, false, "restricts external access to the network")
|
|
||||||
flIPv6 := cmd.Bool([]string{"-ipv6"}, false, "enable IPv6 networking")
|
|
||||||
|
|
||||||
cmd.Require(flag.Exact, 1)
|
|
||||||
err := cmd.ParseFlags(args, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the default driver to "" if the user didn't set the value.
|
|
||||||
// That way we can know whether it was user input or not.
|
|
||||||
driver := *flDriver
|
|
||||||
if !cmd.IsSet("-driver") && !cmd.IsSet("d") {
|
|
||||||
driver = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct network create request body
|
|
||||||
nc := types.NetworkCreate{
|
|
||||||
Driver: driver,
|
|
||||||
IPAM: network.IPAM{Driver: *flIpamDriver, Config: ipamCfg, Options: flIpamOpt.GetAll()},
|
|
||||||
Options: flOpts.GetAll(),
|
|
||||||
CheckDuplicate: true,
|
|
||||||
Internal: *flInternal,
|
|
||||||
EnableIPv6: *flIPv6,
|
|
||||||
Labels: runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.client.NetworkCreate(context.Background(), cmd.Arg(0), nc)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(cli.out, "%s\n", resp.ID)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdNetworkRm deletes one or more networks
|
|
||||||
//
|
|
||||||
// Usage: docker network rm NETWORK-NAME|NETWORK-ID [NETWORK-NAME|NETWORK-ID...]
|
|
||||||
func (cli *DockerCli) CmdNetworkRm(args ...string) error {
|
|
||||||
cmd := Cli.Subcmd("network rm", []string{"NETWORK [NETWORK...]"}, "Deletes one or more networks", false)
|
|
||||||
cmd.Require(flag.Min, 1)
|
|
||||||
if err := cmd.ParseFlags(args, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
status := 0
|
|
||||||
for _, net := range cmd.Args() {
|
|
||||||
if err := cli.client.NetworkRemove(ctx, net); err != nil {
|
|
||||||
fmt.Fprintf(cli.err, "%s\n", err)
|
|
||||||
status = 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintf(cli.out, "%s\n", net)
|
|
||||||
}
|
|
||||||
if status != 0 {
|
|
||||||
return Cli.StatusError{StatusCode: status}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdNetworkConnect connects a container to a network
|
|
||||||
//
|
|
||||||
// Usage: docker network connect [OPTIONS] <NETWORK> <CONTAINER>
|
|
||||||
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
|
|
||||||
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
|
|
||||||
flIPAddress := cmd.String([]string{"-ip"}, "", "IP Address")
|
|
||||||
flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address")
|
|
||||||
flLinks := opts.NewListOpts(runconfigopts.ValidateLink)
|
|
||||||
cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
|
|
||||||
flAliases := opts.NewListOpts(nil)
|
|
||||||
cmd.Var(&flAliases, []string{"-alias"}, "Add network-scoped alias for the container")
|
|
||||||
cmd.Require(flag.Min, 2)
|
|
||||||
if err := cmd.ParseFlags(args, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
epConfig := &network.EndpointSettings{
|
|
||||||
IPAMConfig: &network.EndpointIPAMConfig{
|
|
||||||
IPv4Address: *flIPAddress,
|
|
||||||
IPv6Address: *flIPv6Address,
|
|
||||||
},
|
|
||||||
Links: flLinks.GetAll(),
|
|
||||||
Aliases: flAliases.GetAll(),
|
|
||||||
}
|
|
||||||
return cli.client.NetworkConnect(context.Background(), cmd.Arg(0), cmd.Arg(1), epConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
force := cmd.Bool([]string{"f", "-force"}, false, "Force the container to disconnect from a network")
|
|
||||||
cmd.Require(flag.Exact, 2)
|
|
||||||
if err := cmd.ParseFlags(args, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cli.client.NetworkDisconnect(context.Background(), cmd.Arg(0), cmd.Arg(1), *force)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdNetworkLs lists all the networks managed by docker daemon
|
|
||||||
//
|
|
||||||
// Usage: docker network ls [OPTIONS]
|
|
||||||
func (cli *DockerCli) CmdNetworkLs(args ...string) error {
|
|
||||||
cmd := Cli.Subcmd("network ls", nil, "Lists networks", true)
|
|
||||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
|
|
||||||
noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")
|
|
||||||
|
|
||||||
flFilter := opts.NewListOpts(nil)
|
|
||||||
cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
|
|
||||||
|
|
||||||
cmd.Require(flag.Exact, 0)
|
|
||||||
err := cmd.ParseFlags(args, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consolidate all filter flags, and sanity check them early.
|
|
||||||
// They'll get process after get response from server.
|
|
||||||
netFilterArgs := filters.NewArgs()
|
|
||||||
for _, f := range flFilter.GetAll() {
|
|
||||||
if netFilterArgs, err = filters.ParseFlag(f, netFilterArgs); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
options := types.NetworkListOptions{
|
|
||||||
Filters: netFilterArgs,
|
|
||||||
}
|
|
||||||
|
|
||||||
networkResources, err := cli.client.NetworkList(context.Background(), options)
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
sort.Sort(byNetworkName(networkResources))
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
type byNetworkName []types.NetworkResource
|
|
||||||
|
|
||||||
func (r byNetworkName) Len() int { return len(r) }
|
|
||||||
func (r byNetworkName) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
|
||||||
func (r byNetworkName) Less(i, j int) bool { return r[i].Name < r[j].Name }
|
|
||||||
|
|
||||||
// CmdNetworkInspect inspects the network object for more details
|
|
||||||
//
|
|
||||||
// Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...]
|
|
||||||
func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
|
|
||||||
cmd := Cli.Subcmd("network inspect", []string{"NETWORK [NETWORK...]"}, "Displays detailed information on one or more networks", false)
|
|
||||||
tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template")
|
|
||||||
cmd.Require(flag.Min, 1)
|
|
||||||
|
|
||||||
if err := cmd.ParseFlags(args, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
inspectSearcher := func(name string) (interface{}, []byte, error) {
|
|
||||||
i, err := cli.client.NetworkInspect(ctx, name)
|
|
||||||
return i, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return inspect.Inspect(cli.out, cmd.Args(), *tmplStr, inspectSearcher)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consolidates the ipam configuration as a group from different related configurations
|
|
||||||
// user can configure network with multiple non-overlapping subnets and hence it is
|
|
||||||
// possible to correlate the various related parameters and consolidate them.
|
|
||||||
// consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
|
|
||||||
// structured ipam data.
|
|
||||||
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
|
|
||||||
if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
|
|
||||||
return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
|
|
||||||
}
|
|
||||||
iData := map[string]*network.IPAMConfig{}
|
|
||||||
|
|
||||||
// Populate non-overlapping subnets into consolidation map
|
|
||||||
for _, s := range subnets {
|
|
||||||
for k := range iData {
|
|
||||||
ok1, err := subnetMatches(s, k)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ok2, err := subnetMatches(k, s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ok1 || ok2 {
|
|
||||||
return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate and add valid ip ranges
|
|
||||||
for _, r := range ranges {
|
|
||||||
match := false
|
|
||||||
for _, s := range subnets {
|
|
||||||
ok, err := subnetMatches(s, r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if iData[s].IPRange != "" {
|
|
||||||
return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
|
|
||||||
}
|
|
||||||
d := iData[s]
|
|
||||||
d.IPRange = r
|
|
||||||
match = true
|
|
||||||
}
|
|
||||||
if !match {
|
|
||||||
return nil, fmt.Errorf("no matching subnet for range %s", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate and add valid gateways
|
|
||||||
for _, g := range gateways {
|
|
||||||
match := false
|
|
||||||
for _, s := range subnets {
|
|
||||||
ok, err := subnetMatches(s, g)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if iData[s].Gateway != "" {
|
|
||||||
return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
|
|
||||||
}
|
|
||||||
d := iData[s]
|
|
||||||
d.Gateway = g
|
|
||||||
match = true
|
|
||||||
}
|
|
||||||
if !match {
|
|
||||||
return nil, fmt.Errorf("no matching subnet for gateway %s", g)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate and add aux-addresses
|
|
||||||
for key, aa := range auxaddrs {
|
|
||||||
match := false
|
|
||||||
for _, s := range subnets {
|
|
||||||
ok, err := subnetMatches(s, aa)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
iData[s].AuxAddress[key] = aa
|
|
||||||
match = true
|
|
||||||
}
|
|
||||||
if !match {
|
|
||||||
return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
idl := []network.IPAMConfig{}
|
|
||||||
for _, v := range iData {
|
|
||||||
idl = append(idl, *v)
|
|
||||||
}
|
|
||||||
return idl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func subnetMatches(subnet, data string) (bool, error) {
|
|
||||||
var (
|
|
||||||
ip net.IP
|
|
||||||
)
|
|
||||||
|
|
||||||
_, s, err := net.ParseCIDR(subnet)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Contains(data, "/") {
|
|
||||||
ip, _, err = net.ParseCIDR(data)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ip = net.ParseIP(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.Contains(ip), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func networkUsage() string {
|
|
||||||
networkCommands := [][]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 := range networkCommands {
|
|
||||||
help += fmt.Sprintf(" %-25.25s%s\n", cmd[0], cmd[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
|
|
||||||
return help
|
|
||||||
}
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewNetworkCommand returns a cobra command for `network` subcommands
|
||||||
|
func NewNetworkCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "network",
|
||||||
|
Short: "Manage Docker networks",
|
||||||
|
Args: cli.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.AddCommand(
|
||||||
|
newConnectCommand(dockerCli),
|
||||||
|
newCreateCommand(dockerCli),
|
||||||
|
newDisconnectCommand(dockerCli),
|
||||||
|
newInspectCommand(dockerCli),
|
||||||
|
newListCommand(dockerCli),
|
||||||
|
newRemoveCommand(dockerCli),
|
||||||
|
)
|
||||||
|
return cmd
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/docker/docker/opts"
|
||||||
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||||
|
"github.com/docker/engine-api/types/network"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type connectOptions struct {
|
||||||
|
network string
|
||||||
|
container string
|
||||||
|
ipaddress string
|
||||||
|
ipv6address string
|
||||||
|
links opts.ListOpts
|
||||||
|
aliases []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnectCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
opts := connectOptions{
|
||||||
|
links: opts.NewListOpts(runconfigopts.ValidateLink),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "connect [OPTIONS] NETWORK CONTAINER",
|
||||||
|
Short: "Connects a container to a network",
|
||||||
|
Args: cli.ExactArgs(2),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
opts.network = args[0]
|
||||||
|
opts.container = args[1]
|
||||||
|
return runConnect(dockerCli, opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.StringVar(&opts.ipaddress, "ip", "", "IP Address")
|
||||||
|
flags.StringVar(&opts.ipv6address, "ip6", "", "IPv6 Address")
|
||||||
|
flags.Var(&opts.links, "link", "Add link to another container")
|
||||||
|
flags.StringSliceVar(&opts.aliases, "alias", []string{}, "Add network-scoped alias for the container")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runConnect(dockerCli *client.DockerCli, opts connectOptions) error {
|
||||||
|
client := dockerCli.Client()
|
||||||
|
|
||||||
|
epConfig := &network.EndpointSettings{
|
||||||
|
IPAMConfig: &network.EndpointIPAMConfig{
|
||||||
|
IPv4Address: opts.ipaddress,
|
||||||
|
IPv6Address: opts.ipv6address,
|
||||||
|
},
|
||||||
|
Links: opts.links.GetAll(),
|
||||||
|
Aliases: opts.aliases,
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.NetworkConnect(context.Background(), opts.network, opts.container, epConfig)
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/docker/docker/opts"
|
||||||
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/docker/engine-api/types/network"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type createOptions struct {
|
||||||
|
name string
|
||||||
|
driver string
|
||||||
|
driverOpts opts.MapOpts
|
||||||
|
labels []string
|
||||||
|
internal bool
|
||||||
|
ipv6 bool
|
||||||
|
|
||||||
|
ipamDriver string
|
||||||
|
ipamSubnet []string
|
||||||
|
ipamIPRange []string
|
||||||
|
ipamGateway []string
|
||||||
|
ipamAux opts.MapOpts
|
||||||
|
ipamOpt opts.MapOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
opts := createOptions{
|
||||||
|
driverOpts: *opts.NewMapOpts(nil, nil),
|
||||||
|
ipamAux: *opts.NewMapOpts(nil, nil),
|
||||||
|
ipamOpt: *opts.NewMapOpts(nil, nil),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "create",
|
||||||
|
Short: "Create a network",
|
||||||
|
Args: cli.ExactArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
opts.name = args[0]
|
||||||
|
return runCreate(dockerCli, opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.StringVarP(&opts.driver, "driver", "d", "bridge", "Driver to manage the Network")
|
||||||
|
flags.VarP(&opts.driverOpts, "opt", "o", "Set driver specific options")
|
||||||
|
flags.StringSliceVar(&opts.labels, "label", []string{}, "Set metadata on a network")
|
||||||
|
flags.BoolVar(&opts.internal, "internal", false, "restricts external access to the network")
|
||||||
|
flags.BoolVar(&opts.ipv6, "ipv6", false, "enable IPv6 networking")
|
||||||
|
|
||||||
|
flags.StringVar(&opts.ipamDriver, "ipam-driver", "default", "IP Address Management Driver")
|
||||||
|
flags.StringSliceVar(&opts.ipamSubnet, "subnet", []string{}, "subnet in CIDR format that represents a network segment")
|
||||||
|
flags.StringSliceVar(&opts.ipamIPRange, "ip-range", []string{}, "allocate container ip from a sub-range")
|
||||||
|
flags.StringSliceVar(&opts.ipamGateway, "gateway", []string{}, "ipv4 or ipv6 Gateway for the master subnet")
|
||||||
|
|
||||||
|
flags.Var(&opts.ipamAux, "aux-address", "auxiliary ipv4 or ipv6 addresses used by Network driver")
|
||||||
|
flags.Var(&opts.ipamOpt, "ipam-opt", "set IPAM driver specific options")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCreate(dockerCli *client.DockerCli, opts createOptions) error {
|
||||||
|
client := dockerCli.Client()
|
||||||
|
|
||||||
|
ipamCfg, err := consolidateIpam(opts.ipamSubnet, opts.ipamIPRange, opts.ipamGateway, opts.ipamAux.GetAll())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct network create request body
|
||||||
|
nc := types.NetworkCreate{
|
||||||
|
Driver: opts.driver,
|
||||||
|
Options: opts.driverOpts.GetAll(),
|
||||||
|
IPAM: network.IPAM{
|
||||||
|
Driver: opts.ipamDriver,
|
||||||
|
Config: ipamCfg,
|
||||||
|
Options: opts.ipamOpt.GetAll(),
|
||||||
|
},
|
||||||
|
CheckDuplicate: true,
|
||||||
|
Internal: opts.internal,
|
||||||
|
EnableIPv6: opts.ipv6,
|
||||||
|
Labels: runconfigopts.ConvertKVStringsToMap(opts.labels),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.NetworkCreate(context.Background(), opts.name, nc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(dockerCli.Out(), "%s\n", resp.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consolidates the ipam configuration as a group from different related configurations
|
||||||
|
// user can configure network with multiple non-overlapping subnets and hence it is
|
||||||
|
// possible to correlate the various related parameters and consolidate them.
|
||||||
|
// consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
|
||||||
|
// structured ipam data.
|
||||||
|
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
|
||||||
|
if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
|
||||||
|
return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
|
||||||
|
}
|
||||||
|
iData := map[string]*network.IPAMConfig{}
|
||||||
|
|
||||||
|
// Populate non-overlapping subnets into consolidation map
|
||||||
|
for _, s := range subnets {
|
||||||
|
for k := range iData {
|
||||||
|
ok1, err := subnetMatches(s, k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ok2, err := subnetMatches(k, s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ok1 || ok2 {
|
||||||
|
return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and add valid ip ranges
|
||||||
|
for _, r := range ranges {
|
||||||
|
match := false
|
||||||
|
for _, s := range subnets {
|
||||||
|
ok, err := subnetMatches(s, r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if iData[s].IPRange != "" {
|
||||||
|
return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
|
||||||
|
}
|
||||||
|
d := iData[s]
|
||||||
|
d.IPRange = r
|
||||||
|
match = true
|
||||||
|
}
|
||||||
|
if !match {
|
||||||
|
return nil, fmt.Errorf("no matching subnet for range %s", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and add valid gateways
|
||||||
|
for _, g := range gateways {
|
||||||
|
match := false
|
||||||
|
for _, s := range subnets {
|
||||||
|
ok, err := subnetMatches(s, g)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if iData[s].Gateway != "" {
|
||||||
|
return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
|
||||||
|
}
|
||||||
|
d := iData[s]
|
||||||
|
d.Gateway = g
|
||||||
|
match = true
|
||||||
|
}
|
||||||
|
if !match {
|
||||||
|
return nil, fmt.Errorf("no matching subnet for gateway %s", g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and add aux-addresses
|
||||||
|
for key, aa := range auxaddrs {
|
||||||
|
match := false
|
||||||
|
for _, s := range subnets {
|
||||||
|
ok, err := subnetMatches(s, aa)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
iData[s].AuxAddress[key] = aa
|
||||||
|
match = true
|
||||||
|
}
|
||||||
|
if !match {
|
||||||
|
return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idl := []network.IPAMConfig{}
|
||||||
|
for _, v := range iData {
|
||||||
|
idl = append(idl, *v)
|
||||||
|
}
|
||||||
|
return idl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func subnetMatches(subnet, data string) (bool, error) {
|
||||||
|
var (
|
||||||
|
ip net.IP
|
||||||
|
)
|
||||||
|
|
||||||
|
_, s, err := net.ParseCIDR(subnet)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(data, "/") {
|
||||||
|
ip, _, err = net.ParseCIDR(data)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ip = net.ParseIP(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Contains(ip), nil
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type disconnectOptions struct {
|
||||||
|
network string
|
||||||
|
container string
|
||||||
|
force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDisconnectCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
opts := disconnectOptions{}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "disconnect [OPTIONS] NETWORK CONTAINER",
|
||||||
|
Short: "Disconnects container from a network",
|
||||||
|
Args: cli.ExactArgs(2),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
opts.network = args[0]
|
||||||
|
opts.container = args[1]
|
||||||
|
return runDisconnect(dockerCli, opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.BoolVarP(&opts.force, "force", "f", false, "Force the container to disconnect from a network")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDisconnect(dockerCli *client.DockerCli, opts disconnectOptions) error {
|
||||||
|
client := dockerCli.Client()
|
||||||
|
|
||||||
|
return client.NetworkDisconnect(context.Background(), opts.network, opts.container, opts.force)
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/api/client/inspect"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type inspectOptions struct {
|
||||||
|
format string
|
||||||
|
names []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
var opts inspectOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "inspect [OPTIONS] NETWORK [NETWORK...]",
|
||||||
|
Short: "Displays detailed information on one or more networks",
|
||||||
|
Args: cli.RequiresMinArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
opts.names = args
|
||||||
|
return runInspect(dockerCli, opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringVarP(&opts.format, "format", "f", "", "Format the output using the given go template")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
|
||||||
|
client := dockerCli.Client()
|
||||||
|
|
||||||
|
getNetFunc := func(name string) (interface{}, []byte, error) {
|
||||||
|
i, err := client.NetworkInspect(context.Background(), name)
|
||||||
|
return i, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return inspect.Inspect(dockerCli.Out(), opts.names, opts.format, getNetFunc)
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/docker/engine-api/types/filters"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type byNetworkName []types.NetworkResource
|
||||||
|
|
||||||
|
func (r byNetworkName) Len() int { return len(r) }
|
||||||
|
func (r byNetworkName) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||||
|
func (r byNetworkName) Less(i, j int) bool { return r[i].Name < r[j].Name }
|
||||||
|
|
||||||
|
type listOptions struct {
|
||||||
|
quiet bool
|
||||||
|
noTrunc bool
|
||||||
|
filter []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newListCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
var opts listOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "ls",
|
||||||
|
Aliases: []string{"list"},
|
||||||
|
Short: "List networks",
|
||||||
|
Args: cli.NoArgs,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return runList(dockerCli, opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display volume names")
|
||||||
|
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate the output")
|
||||||
|
flags.StringSliceVarP(&opts.filter, "filter", "f", []string{}, "Provide filter values (i.e. 'dangling=true')")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runList(dockerCli *client.DockerCli, opts listOptions) error {
|
||||||
|
client := dockerCli.Client()
|
||||||
|
|
||||||
|
netFilterArgs := filters.NewArgs()
|
||||||
|
for _, f := range opts.filter {
|
||||||
|
var err error
|
||||||
|
netFilterArgs, err = filters.ParseFlag(f, netFilterArgs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options := types.NetworkListOptions{
|
||||||
|
Filters: netFilterArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
networkResources, err := client.NetworkList(context.Background(), options)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0)
|
||||||
|
if !opts.quiet {
|
||||||
|
fmt.Fprintf(w, "NETWORK ID\tNAME\tDRIVER")
|
||||||
|
fmt.Fprintf(w, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(byNetworkName(networkResources))
|
||||||
|
for _, networkResource := range networkResources {
|
||||||
|
ID := networkResource.ID
|
||||||
|
netName := networkResource.Name
|
||||||
|
if !opts.noTrunc {
|
||||||
|
ID = stringid.TruncateID(ID)
|
||||||
|
}
|
||||||
|
if opts.quiet {
|
||||||
|
fmt.Fprintln(w, ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
driver := networkResource.Driver
|
||||||
|
fmt.Fprintf(w, "%s\t%s\t%s\t",
|
||||||
|
ID,
|
||||||
|
netName,
|
||||||
|
driver)
|
||||||
|
fmt.Fprint(w, "\n")
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "rm NETWORK [NETWORK]...",
|
||||||
|
Aliases: []string{"remove"},
|
||||||
|
Short: "Remove a network",
|
||||||
|
Args: cli.RequiresMinArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return runRemove(dockerCli, args)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRemove(dockerCli *client.DockerCli, networks []string) error {
|
||||||
|
client := dockerCli.Client()
|
||||||
|
ctx := context.Background()
|
||||||
|
status := 0
|
||||||
|
|
||||||
|
for _, name := range networks {
|
||||||
|
if err := client.NetworkRemove(ctx, name); err != nil {
|
||||||
|
fmt.Fprintf(dockerCli.Err(), "%s\n", err)
|
||||||
|
status = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(dockerCli.Err(), "%s\n", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if status != 0 {
|
||||||
|
return cli.StatusError{StatusCode: status}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/docker/docker/api/client"
|
"github.com/docker/docker/api/client"
|
||||||
"github.com/docker/docker/api/client/container"
|
"github.com/docker/docker/api/client/container"
|
||||||
"github.com/docker/docker/api/client/image"
|
"github.com/docker/docker/api/client/image"
|
||||||
|
"github.com/docker/docker/api/client/network"
|
||||||
"github.com/docker/docker/api/client/volume"
|
"github.com/docker/docker/api/client/volume"
|
||||||
"github.com/docker/docker/cli"
|
"github.com/docker/docker/cli"
|
||||||
cliflags "github.com/docker/docker/cli/flags"
|
cliflags "github.com/docker/docker/cli/flags"
|
||||||
|
@ -43,6 +44,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
||||||
container.NewUnpauseCommand(dockerCli),
|
container.NewUnpauseCommand(dockerCli),
|
||||||
image.NewRemoveCommand(dockerCli),
|
image.NewRemoveCommand(dockerCli),
|
||||||
image.NewSearchCommand(dockerCli),
|
image.NewSearchCommand(dockerCli),
|
||||||
|
network.NewNetworkCommand(dockerCli),
|
||||||
volume.NewVolumeCommand(dockerCli),
|
volume.NewVolumeCommand(dockerCli),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ var DockerCommandUsage = []Command{
|
||||||
{"load", "Load an image from a tar archive or STDIN"},
|
{"load", "Load an image from a tar archive or STDIN"},
|
||||||
{"login", "Log in to a Docker registry"},
|
{"login", "Log in to a Docker registry"},
|
||||||
{"logout", "Log out from a Docker registry"},
|
{"logout", "Log out from a Docker registry"},
|
||||||
{"network", "Manage Docker networks"},
|
|
||||||
{"pause", "Pause all processes within a container"},
|
{"pause", "Pause all processes within a container"},
|
||||||
{"port", "List port mappings or a specific mapping for the CONTAINER"},
|
{"port", "List port mappings or a specific mapping for the CONTAINER"},
|
||||||
{"ps", "List containers"},
|
{"ps", "List containers"},
|
||||||
|
|
|
@ -119,6 +119,12 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
|
||||||
cmdsToTest = append(cmdsToTest, "volume inspect")
|
cmdsToTest = append(cmdsToTest, "volume inspect")
|
||||||
cmdsToTest = append(cmdsToTest, "volume ls")
|
cmdsToTest = append(cmdsToTest, "volume ls")
|
||||||
cmdsToTest = append(cmdsToTest, "volume rm")
|
cmdsToTest = append(cmdsToTest, "volume rm")
|
||||||
|
cmdsToTest = append(cmdsToTest, "network connect")
|
||||||
|
cmdsToTest = append(cmdsToTest, "network create")
|
||||||
|
cmdsToTest = append(cmdsToTest, "network disconnect")
|
||||||
|
cmdsToTest = append(cmdsToTest, "network inspect")
|
||||||
|
cmdsToTest = append(cmdsToTest, "network ls")
|
||||||
|
cmdsToTest = append(cmdsToTest, "network rm")
|
||||||
|
|
||||||
// Divide the list of commands into go routines and run the func testcommand on the commands in parallel
|
// Divide the list of commands into go routines and run the func testcommand on the commands in parallel
|
||||||
// to save runtime of test
|
// to save runtime of test
|
||||||
|
|
|
@ -362,7 +362,7 @@ func (s *DockerSuite) TestInspectContainerNetworkDefault(c *check.C) {
|
||||||
|
|
||||||
contName := "test1"
|
contName := "test1"
|
||||||
dockerCmd(c, "run", "--name", contName, "-d", "busybox", "top")
|
dockerCmd(c, "run", "--name", contName, "-d", "busybox", "top")
|
||||||
netOut, _ := dockerCmd(c, "network", "inspect", "--format='{{.ID}}'", "bridge")
|
netOut, _ := dockerCmd(c, "network", "inspect", "--format={{.ID}}", "bridge")
|
||||||
out := inspectField(c, contName, "NetworkSettings.Networks")
|
out := inspectField(c, contName, "NetworkSettings.Networks")
|
||||||
c.Assert(out, checker.Contains, "bridge")
|
c.Assert(out, checker.Contains, "bridge")
|
||||||
out = inspectField(c, contName, "NetworkSettings.Networks.bridge.NetworkID")
|
out = inspectField(c, contName, "NetworkSettings.Networks.bridge.NetworkID")
|
||||||
|
|
|
@ -379,7 +379,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkCreateLabel(c *check.C) {
|
||||||
dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet)
|
dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet)
|
||||||
assertNwIsAvailable(c, testNet)
|
assertNwIsAvailable(c, testNet)
|
||||||
|
|
||||||
out, _, err := dockerCmdWithError("network", "inspect", "--format='{{ .Labels."+testLabel+" }}'", testNet)
|
out, _, err := dockerCmdWithError("network", "inspect", "--format={{ .Labels."+testLabel+" }}", testNet)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(strings.TrimSpace(out), check.Equals, testValue)
|
c.Assert(strings.TrimSpace(out), check.Equals, testValue)
|
||||||
|
|
||||||
|
@ -423,7 +423,7 @@ func (s *DockerSuite) TestDockerNetworkInspect(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(networkResources, checker.HasLen, 1)
|
c.Assert(networkResources, checker.HasLen, 1)
|
||||||
|
|
||||||
out, _ = dockerCmd(c, "network", "inspect", "--format='{{ .Name }}'", "host")
|
out, _ = dockerCmd(c, "network", "inspect", "--format={{ .Name }}", "host")
|
||||||
c.Assert(strings.TrimSpace(out), check.Equals, "host")
|
c.Assert(strings.TrimSpace(out), check.Equals, "host")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue