Prechádzať zdrojové kódy

Add filter for `network ls` to hide predefined net

Add filter support for `network ls` to hide predefined network,
then user can use "docker network rm `docker network ls -f type=custom`"
to delete a bundle of userdefined networks.

Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
Zhang Wei 9 rokov pred
rodič
commit
26dd026bd7

+ 1 - 1
api/client/client.go

@@ -65,7 +65,7 @@ type apiClient interface {
 	NetworkCreate(options types.NetworkCreate) (types.NetworkCreateResponse, error)
 	NetworkDisconnect(networkID, containerID string) error
 	NetworkInspect(networkID string) (types.NetworkResource, error)
-	NetworkList() ([]types.NetworkResource, error)
+	NetworkList(options types.NetworkListOptions) ([]types.NetworkResource, error)
 	NetworkRemove(networkID string) error
 	RegistryLogin(auth types.AuthConfig) (types.AuthResponse, error)
 	ServerVersion() (types.Version, error)

+ 13 - 2
api/client/lib/network.go

@@ -3,8 +3,10 @@ package lib
 import (
 	"encoding/json"
 	"net/http"
+	"net/url"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
 )
 
 // NetworkCreate creates a new network in the docker host.
@@ -44,9 +46,18 @@ func (cli *Client) NetworkDisconnect(networkID, containerID string) error {
 }
 
 // NetworkList returns the list of networks configured in the docker host.
-func (cli *Client) NetworkList() ([]types.NetworkResource, error) {
+func (cli *Client) NetworkList(options types.NetworkListOptions) ([]types.NetworkResource, error) {
+	query := url.Values{}
+	if options.Filters.Len() > 0 {
+		filterJSON, err := filters.ToParam(options.Filters)
+		if err != nil {
+			return nil, err
+		}
+
+		query.Set("filters", filterJSON)
+	}
 	var networkResources []types.NetworkResource
-	resp, err := cli.get("/networks", nil, nil)
+	resp, err := cli.get("/networks", query, nil)
 	if err != nil {
 		return networkResources, err
 	}

+ 20 - 2
api/client/network.go

@@ -7,6 +7,7 @@ import (
 	"text/tabwriter"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/network"
 	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
@@ -138,12 +139,29 @@ func (cli *DockerCli) CmdNetworkLs(args ...string) error {
 	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)
-	if err := cmd.ParseFlags(args, true); err != nil {
+	err := cmd.ParseFlags(args, true)
+	if err != nil {
 		return err
 	}
 
-	networkResources, err := cli.client.NetworkList()
+	// 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(options)
 	if err != nil {
 		return err
 	}

+ 1 - 0
api/server/router/network/backend.go

@@ -12,6 +12,7 @@ type Backend interface {
 	FindNetwork(idName string) (libnetwork.Network, error)
 	GetNetwork(idName string, by int) (libnetwork.Network, error)
 	GetNetworksByID(partialID string) []libnetwork.Network
+	GetAllNetworks() []libnetwork.Network
 	CreateNetwork(name, driver string, ipam network.IPAM,
 		options map[string]string) (libnetwork.Network, error)
 	ConnectContainerToNetwork(containerName, networkName string) error

+ 110 - 0
api/server/router/network/filter.go

@@ -0,0 +1,110 @@
+package network
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+
+	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/runconfig"
+	"github.com/docker/libnetwork"
+)
+
+type filterHandler func([]libnetwork.Network, string) ([]libnetwork.Network, error)
+
+var (
+	// supportedFilters predefined some supported filter handler function
+	supportedFilters = map[string]filterHandler{
+		"type": filterNetworkByType,
+		"name": filterNetworkByName,
+		"id":   filterNetworkByID,
+	}
+
+	// acceptFilters is an acceptable filter flag list
+	// generated for validation. e.g.
+	// acceptedFilters = map[string]bool{
+	//     "type": true,
+	//     "name": true,
+	//     "id":   true,
+	// }
+	acceptedFilters = func() map[string]bool {
+		ret := make(map[string]bool)
+		for k := range supportedFilters {
+			ret[k] = true
+		}
+		return ret
+	}()
+)
+
+func filterNetworkByType(nws []libnetwork.Network, netType string) (retNws []libnetwork.Network, err error) {
+	switch netType {
+	case "builtin":
+		for _, nw := range nws {
+			if runconfig.IsPreDefinedNetwork(nw.Name()) {
+				retNws = append(retNws, nw)
+			}
+		}
+	case "custom":
+		for _, nw := range nws {
+			if !runconfig.IsPreDefinedNetwork(nw.Name()) {
+				retNws = append(retNws, nw)
+			}
+		}
+	default:
+		return nil, fmt.Errorf("Invalid filter: 'type'='%s'", netType)
+	}
+	return retNws, nil
+}
+
+func filterNetworkByName(nws []libnetwork.Network, name string) (retNws []libnetwork.Network, err error) {
+	for _, nw := range nws {
+		// exact match (fast path)
+		if nw.Name() == name {
+			retNws = append(retNws, nw)
+			continue
+		}
+
+		// regexp match (slow path)
+		match, err := regexp.MatchString(name, nw.Name())
+		if err != nil || !match {
+			continue
+		} else {
+			retNws = append(retNws, nw)
+		}
+	}
+	return retNws, nil
+}
+
+func filterNetworkByID(nws []libnetwork.Network, id string) (retNws []libnetwork.Network, err error) {
+	for _, nw := range nws {
+		if strings.HasPrefix(nw.ID(), id) {
+			retNws = append(retNws, nw)
+		}
+	}
+	return retNws, nil
+}
+
+// filterAllNetworks filter network list according to user specified filter
+// and return user chosen networks
+func filterNetworks(nws []libnetwork.Network, filter filters.Args) ([]libnetwork.Network, error) {
+	// if filter is empty, return original network list
+	if filter.Len() == 0 {
+		return nws, nil
+	}
+
+	var displayNet []libnetwork.Network
+	for fkey, fhandler := range supportedFilters {
+		errFilter := filter.WalkValues(fkey, func(fval string) error {
+			passList, err := fhandler(nws, fval)
+			if err != nil {
+				return err
+			}
+			displayNet = append(displayNet, passList...)
+			return nil
+		})
+		if errFilter != nil {
+			return nil, errFilter
+		}
+	}
+	return displayNet, nil
+}

+ 14 - 20
api/server/router/network/network_routes.go

@@ -7,7 +7,6 @@ import (
 
 	"golang.org/x/net/context"
 
-	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
@@ -28,29 +27,24 @@ func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWrit
 		return err
 	}
 
-	list := []*types.NetworkResource{}
-	netFilters.WalkValues("name", func(name string) error {
-		if nw, err := n.backend.GetNetwork(name, daemon.NetworkByName); err == nil {
-			list = append(list, buildNetworkResource(nw))
-		} else {
-			logrus.Errorf("failed to get network for filter=%s : %v", name, err)
+	if netFilters.Len() != 0 {
+		if err := netFilters.Validate(acceptedFilters); err != nil {
+			return err
 		}
-		return nil
-	})
+	}
 
-	netFilters.WalkValues("id", func(id string) error {
-		for _, nw := range n.backend.GetNetworksByID(id) {
-			list = append(list, buildNetworkResource(nw))
-		}
-		return nil
-	})
+	list := []*types.NetworkResource{}
 
-	if !netFilters.Include("name") && !netFilters.Include("id") {
-		nwList := n.backend.GetNetworksByID("")
-		for _, nw := range nwList {
-			list = append(list, buildNetworkResource(nw))
-		}
+	nwList := n.backend.GetAllNetworks()
+	displayable, err := filterNetworks(nwList, netFilters)
+	if err != nil {
+		return err
 	}
+
+	for _, nw := range displayable {
+		list = append(list, buildNetworkResource(nw))
+	}
+
 	return httputils.WriteJSON(w, http.StatusOK, list)
 }
 

+ 5 - 0
api/types/client.go

@@ -86,6 +86,11 @@ type EventsOptions struct {
 	Filters filters.Args
 }
 
+// NetworkListOptions holds parameters to filter the list of networks with.
+type NetworkListOptions struct {
+	Filters filters.Args
+}
+
 // HijackedResponse holds connection information for a hijacked request.
 type HijackedResponse struct {
 	Conn   net.Conn

+ 13 - 0
daemon/network.go

@@ -85,6 +85,19 @@ func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network {
 	return list
 }
 
+// GetAllNetworks returns a list containing all networks
+func (daemon *Daemon) GetAllNetworks() []libnetwork.Network {
+	c := daemon.netController
+	list := []libnetwork.Network{}
+	l := func(nw libnetwork.Network) bool {
+		list = append(list, nw)
+		return false
+	}
+	c.WalkNetworks(l)
+
+	return list
+}
+
 // CreateNetwork creates a network with the given name, driver and other optional parameters
 func (daemon *Daemon) CreateNetwork(name, driver string, ipam network.IPAM, options map[string]string) (libnetwork.Network, error) {
 	c := daemon.netController

+ 1 - 0
docs/reference/api/docker_remote_api.md

@@ -109,6 +109,7 @@ This section lists each version from latest to oldest.  Each listing includes a
   the push or pull completes.
 * `POST /containers/create` now allows you to set a read/write rate limit for a 
   device (in bytes per second or IO per second).
+* `GET /networks` now supports filtering by `name`, `id` and `type`.
 
 ### v1.21 API changes
 

+ 5 - 4
docs/reference/api/docker_remote_api_v1.22.md

@@ -2728,7 +2728,7 @@ Status Codes
 
 **Example request**:
 
-    GET /networks HTTP/1.1
+    GET /networks?filters={"type":{"custom":true}} HTTP/1.1
 
 **Example response**:
 
@@ -2794,11 +2794,12 @@ Content-Type: application/json
 ]
 ```
 
-
-
 Query Parameters:
 
-- **filters** - JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: `name=[network-names]` , `id=[network-ids]`
+- **filters** - JSON encoded network list filter. The filter value is one of: 
+  -   `name=<network-name>` Matches all or part of a network name.
+  -   `id=<network-id>` Matches all or part of a network id.
+  -   `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks.
 
 Status Codes:
 

+ 84 - 0
docs/reference/commandline/network_ls.md

@@ -13,6 +13,7 @@ parent = "smn_cli"
     Usage:  docker network ls [OPTIONS]
 
     Lists all the networks created by the user
+      -f, --filter=[]       Filter output based on conditions provided
       --help=false          Print usage
       --no-trunc=false      Do not truncate the output
       -q, --quiet=false     Only display numeric IDs
@@ -38,8 +39,91 @@ NETWORK ID                                                         NAME
 c288470c46f6c8949c5f7e5099b5b7947b07eabe8d9a27d79a9cbf111adcbf47   host                host                
 7b369448dccbf865d397c8d2be0cda7cf7edc6b0945f77d2529912ae917a0185   bridge              bridge              
 95e74588f40db048e86320c6526440c504650a1ff3e9f7d60a497c4d2163e5bd   foo                 bridge    
+63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161   dev                 bridge
 ```
 
+## Filtering
+
+The filtering flag (`-f` or `--filter`) format is a `key=value` pair. If there
+is more than one filter, then pass multiple flags (e.g. `--filter "foo=bar" --filter "bif=baz"`).
+Multiple filter flags are combined as an `OR` filter. For example, 
+`-f type=custom -f type=builtin` returns both `custom` and `builtin` networks.
+
+The currently supported filters are:
+
+* id (network's id)
+* name (network's name)
+* type (custom|builtin)
+
+#### Type
+
+The `type` filter supports two values; `builtin` displays predefined networks
+(`bridge`, `none`, `host`), whereas `custom` displays user defined networks.
+
+The following filter matches all user defined networks:
+
+```bash
+$ docker network ls --filter type=custom
+NETWORK ID          NAME                DRIVER
+95e74588f40d        foo                 bridge
+63d1ff1f77b0        dev                 bridge
+```
+
+By having this flag it allows for batch cleanup. For example, use this filter
+to delete all user defined networks:
+
+```bash
+$ docker network rm `docker network ls --filter type=custom -q`
+```
+
+A warning will be issued when trying to remove a network that has containers
+attached.
+
+#### Name
+
+The `name` filter matches on all or part of a network's name.
+
+The following filter matches all networks with a name containing the `foobar` string.
+
+```bash
+$ docker network ls --filter name=foobar
+NETWORK ID          NAME                DRIVER
+06e7eef0a170        foobar              bridge
+```
+
+You can also filter for a substring in a name as this shows:
+
+```bash
+$ docker ps --filter name=foo
+NETWORK ID          NAME                DRIVER
+95e74588f40d        foo                 bridge
+06e7eef0a170        foobar              bridge
+```
+
+#### ID
+
+The `id` filter matches on all or part of a network's ID.
+
+The following filter matches all networks with a name containing the
+`06e7eef01700` string.
+
+```bash
+$ docker network ls --filter id=63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161
+NETWORK ID          NAME                DRIVER
+63d1ff1f77b0        dev                 bridge
+```
+
+You can also filter for a substring in a ID as this shows:
+
+```bash
+$ docker ps --filter id=95e74588f40d
+NETWORK ID          NAME                DRIVER
+95e74588f40d        foo                 bridge
+
+$ docker ps --filter id=95e
+NETWORK ID          NAME                DRIVER
+95e74588f40d        foo                 bridge
+```
 
 ## Related information
 

+ 46 - 0
integration-cli/docker_cli_network_unix_test.go

@@ -10,6 +10,7 @@ import (
 	"net/http"
 	"net/http/httptest"
 	"os"
+	"sort"
 	"strings"
 
 	"github.com/docker/docker/api/types"
@@ -242,6 +243,25 @@ func isNwPresent(c *check.C, name string) bool {
 	return false
 }
 
+// assertNwList checks network list retrived with ls command
+// equals to expected network list
+// note: out should be `network ls [option]` result
+func assertNwList(c *check.C, out string, expectNws []string) {
+	lines := strings.Split(out, "\n")
+	var nwList []string
+	for _, line := range lines[1 : len(lines)-1] {
+		netFields := strings.Fields(line)
+		// wrap all network name in nwList
+		nwList = append(nwList, netFields[1])
+	}
+	// first need to sort out and expected
+	sort.StringSlice(nwList).Sort()
+	sort.StringSlice(expectNws).Sort()
+
+	// network ls should contains all expected networks
+	c.Assert(nwList, checker.DeepEquals, expectNws)
+}
+
 func getNwResource(c *check.C, name string) *types.NetworkResource {
 	out, _ := dockerCmd(c, "network", "inspect", name)
 	nr := []types.NetworkResource{}
@@ -257,6 +277,32 @@ func (s *DockerNetworkSuite) TestDockerNetworkLsDefault(c *check.C) {
 	}
 }
 
+func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *check.C) {
+	out, _ := dockerCmd(c, "network", "create", "dev")
+	defer func() {
+		dockerCmd(c, "network", "rm", "dev")
+	}()
+	containerID := strings.TrimSpace(out)
+
+	// filter with partial ID and partial name
+	// only show 'bridge' and 'dev' network
+	out, _ = dockerCmd(c, "network", "ls", "-f", "id="+containerID[0:5], "-f", "name=dge")
+	assertNwList(c, out, []string{"dev", "bridge"})
+
+	// only show built-in network (bridge, none, host)
+	out, _ = dockerCmd(c, "network", "ls", "-f", "type=builtin")
+	assertNwList(c, out, []string{"bridge", "none", "host"})
+
+	// only show custom networks (dev)
+	out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom")
+	assertNwList(c, out, []string{"dev"})
+
+	// show all networks with filter
+	// it should be equivalent of ls without option
+	out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom", "-f", "type=builtin")
+	assertNwList(c, out, []string{"dev", "bridge", "host", "none"})
+}
+
 func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *check.C) {
 	dockerCmd(c, "network", "create", "test")
 	assertNwIsAvailable(c, "test")

+ 90 - 2
man/docker-network-ls.1.md

@@ -6,6 +6,7 @@ docker-network-ls - list networks
 
 # SYNOPSIS
 **docker network ls**
+[**-f**|**--filter**[=*[]*]]
 [**--no-trunc**[=*true*|*false*]]
 [**-q**|**--quiet**[=*true*|*false*]]
 [**--help**]
@@ -16,7 +17,7 @@ Lists all the networks the Engine `daemon` knows about. This includes the
 networks that span across multiple hosts in a cluster, for example:
 
 ```bash
-    $ sudo docker network ls
+    $ docker network ls
     NETWORK ID          NAME                DRIVER
     7fca4eb8c647        bridge              bridge
     9f904ee27bf5        none                null
@@ -27,16 +28,103 @@ networks that span across multiple hosts in a cluster, for example:
 Use the `--no-trunc` option to display the full network id:
 
 ```bash
-docker network ls --no-trunc
+$ docker network ls --no-trunc
 NETWORK ID                                                         NAME                DRIVER
 18a2866682b85619a026c81b98a5e375bd33e1b0936a26cc497c283d27bae9b3   none                null                
 c288470c46f6c8949c5f7e5099b5b7947b07eabe8d9a27d79a9cbf111adcbf47   host                host                
 7b369448dccbf865d397c8d2be0cda7cf7edc6b0945f77d2529912ae917a0185   bridge              bridge              
 95e74588f40db048e86320c6526440c504650a1ff3e9f7d60a497c4d2163e5bd   foo                 bridge    
+63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161   dev                 bridge
+```
+
+## Filtering
+
+The filtering flag (`-f` or `--filter`) format is a `key=value` pair. If there
+is more than one filter, then pass multiple flags (e.g. `--filter "foo=bar" --filter "bif=baz"`).
+Multiple filter flags are combined as an `OR` filter. For example, 
+`-f type=custom -f type=builtin` returns both `custom` and `builtin` networks.
+
+The currently supported filters are:
+
+* id (network's id)
+* name (network's name)
+* type (custom|builtin)
+
+#### Type
+
+The `type` filter supports two values; `builtin` displays predefined networks
+(`bridge`, `none`, `host`), whereas `custom` displays user defined networks.
+
+The following filter matches all user defined networks:
+
+```bash
+$ docker network ls --filter type=custom
+NETWORK ID          NAME                DRIVER
+95e74588f40d        foo                 bridge
+63d1ff1f77b0        dev                 bridge
+```
+
+By having this flag it allows for batch cleanup. For example, use this filter
+to delete all user defined networks:
+
+```bash
+$ docker network rm `docker network ls --filter type=custom -q`
+```
+
+A warning will be issued when trying to remove a network that has containers
+attached.
+
+#### Name
+
+The `name` filter matches on all or part of a network's name.
+
+The following filter matches all networks with a name containing the `foobar` string.
+
+```bash
+$ docker network ls --filter name=foobar
+NETWORK ID          NAME                DRIVER
+06e7eef0a170        foobar              bridge
+```
+
+You can also filter for a substring in a name as this shows:
+
+```bash
+$ docker ps --filter name=foo
+NETWORK ID          NAME                DRIVER
+95e74588f40d        foo                 bridge
+06e7eef0a170        foobar              bridge
+```
+
+#### ID
+
+The `id` filter matches on all or part of a network's ID.
+
+The following filter matches all networks with a name containing the
+`06e7eef01700` string.
+
+```bash
+$ docker network ls --filter id=63d1ff1f77b07ca51070a8c227e962238358bd310bde1529cf62e6c307ade161
+NETWORK ID          NAME                DRIVER
+63d1ff1f77b0        dev                 bridge
+```
+
+You can also filter for a substring in a ID as this shows:
+
+```bash
+$ docker ps --filter id=95e74588f40d
+NETWORK ID          NAME                DRIVER
+95e74588f40d        foo                 bridge
+
+$ docker ps --filter id=95e
+NETWORK ID          NAME                DRIVER
+95e74588f40d        foo                 bridge
 ```
 
 # OPTIONS
 
+**-f**, **--filter**=*[]*
+  filter output based on conditions provided. 
+
 **--no-trunc**=*true*|*false*
   Do not truncate the output