Migrate network command to cobra

- Migrates network command and subcommands (connect, create, disconnect,
  inspect, list and remove) to spf13/cobra
- Create a RequiredExactArgs helper function for command that require an
  exact number of arguments.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
Vincent Demeester 2016-06-06 10:28:52 +02:00
parent 28dde09cdf
commit 4bd202b00f
No known key found for this signature in database
GPG Key ID: 083CC6FD6EB699A3
14 changed files with 581 additions and 438 deletions

View File

@ -3,42 +3,35 @@ package client
// Command returns a cli command handler if one exists
func (cli *DockerCli) Command(name string) func(...string) error {
return map[string]func(...string) error{
"attach": cli.CmdAttach,
"build": cli.CmdBuild,
"commit": cli.CmdCommit,
"cp": cli.CmdCp,
"events": cli.CmdEvents,
"exec": cli.CmdExec,
"history": cli.CmdHistory,
"images": cli.CmdImages,
"import": cli.CmdImport,
"info": cli.CmdInfo,
"inspect": cli.CmdInspect,
"kill": cli.CmdKill,
"load": cli.CmdLoad,
"login": cli.CmdLogin,
"logout": cli.CmdLogout,
"network": cli.CmdNetwork,
"network create": cli.CmdNetworkCreate,
"network connect": cli.CmdNetworkConnect,
"network disconnect": cli.CmdNetworkDisconnect,
"network inspect": cli.CmdNetworkInspect,
"network ls": cli.CmdNetworkLs,
"network rm": cli.CmdNetworkRm,
"pause": cli.CmdPause,
"port": cli.CmdPort,
"ps": cli.CmdPs,
"pull": cli.CmdPull,
"push": cli.CmdPush,
"rename": cli.CmdRename,
"restart": cli.CmdRestart,
"rm": cli.CmdRm,
"save": cli.CmdSave,
"stats": cli.CmdStats,
"tag": cli.CmdTag,
"top": cli.CmdTop,
"update": cli.CmdUpdate,
"version": cli.CmdVersion,
"wait": cli.CmdWait,
"attach": cli.CmdAttach,
"build": cli.CmdBuild,
"commit": cli.CmdCommit,
"cp": cli.CmdCp,
"events": cli.CmdEvents,
"exec": cli.CmdExec,
"history": cli.CmdHistory,
"images": cli.CmdImages,
"import": cli.CmdImport,
"info": cli.CmdInfo,
"inspect": cli.CmdInspect,
"kill": cli.CmdKill,
"load": cli.CmdLoad,
"login": cli.CmdLogin,
"logout": cli.CmdLogout,
"pause": cli.CmdPause,
"port": cli.CmdPort,
"ps": cli.CmdPs,
"pull": cli.CmdPull,
"push": cli.CmdPush,
"rename": cli.CmdRename,
"restart": cli.CmdRestart,
"rm": cli.CmdRm,
"save": cli.CmdSave,
"stats": cli.CmdStats,
"tag": cli.CmdTag,
"top": cli.CmdTop,
"update": cli.CmdUpdate,
"version": cli.CmdVersion,
"wait": cli.CmdWait,
}[name]
}

View File

@ -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
}

31
api/client/network/cmd.go Normal file
View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -4,6 +4,7 @@ import (
"github.com/docker/docker/api/client"
"github.com/docker/docker/api/client/container"
"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/cli"
cliflags "github.com/docker/docker/cli/flags"
@ -43,6 +44,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
container.NewUnpauseCommand(dockerCli),
image.NewRemoveCommand(dockerCli),
image.NewSearchCommand(dockerCli),
network.NewNetworkCommand(dockerCli),
volume.NewVolumeCommand(dockerCli),
)

View File

@ -23,7 +23,6 @@ var DockerCommandUsage = []Command{
{"load", "Load an image from a tar archive or STDIN"},
{"login", "Log in to a Docker registry"},
{"logout", "Log out from a Docker registry"},
{"network", "Manage Docker networks"},
{"pause", "Pause all processes within a container"},
{"port", "List port mappings or a specific mapping for the CONTAINER"},
{"ps", "List containers"},

View File

@ -119,6 +119,12 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
cmdsToTest = append(cmdsToTest, "volume inspect")
cmdsToTest = append(cmdsToTest, "volume ls")
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
// to save runtime of test

View File

@ -362,7 +362,7 @@ func (s *DockerSuite) TestInspectContainerNetworkDefault(c *check.C) {
contName := "test1"
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")
c.Assert(out, checker.Contains, "bridge")
out = inspectField(c, contName, "NetworkSettings.Networks.bridge.NetworkID")

View File

@ -379,7 +379,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkCreateLabel(c *check.C) {
dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, 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(strings.TrimSpace(out), check.Equals, testValue)
@ -423,7 +423,7 @@ func (s *DockerSuite) TestDockerNetworkInspect(c *check.C) {
c.Assert(err, check.IsNil)
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")
}