From 131cbaf5b7b15ffc5e3fc11ed30ce0f67a76c6f8 Mon Sep 17 00:00:00 2001 From: karthik nayak Date: Sun, 6 Jan 2019 10:37:09 -0500 Subject: [PATCH] Network: add support for 'dangling' filter Like its counterpart in images and volumes, introduce the dangling filter while listing networks. When the filter value is set to true, only networks which aren't attached to containers and aren't builtin networks are shown. When set to false, all builtin networks and networks which are attached to containers are shown. Signed-off-by: Karthik Nayak --- api/types/network/network.go | 13 +++++----- daemon/network/filter.go | 38 ++++++++++++++++++++++++++++ daemon/network/filter_test.go | 47 +++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/api/types/network/network.go b/api/types/network/network.go index ccb448f23a..014a8d5c96 100644 --- a/api/types/network/network.go +++ b/api/types/network/network.go @@ -112,12 +112,13 @@ type ConfigReference struct { } var acceptedFilters = map[string]bool{ - "driver": true, - "type": true, - "name": true, - "id": true, - "label": true, - "scope": true, + "driver": true, + "type": true, + "name": true, + "id": true, + "label": true, + "scope": true, + "dangling": true, } // ValidateFilters validates the list of filter args with the available filters. diff --git a/daemon/network/filter.go b/daemon/network/filter.go index 01a7d2dd81..258a0f4446 100644 --- a/daemon/network/filter.go +++ b/daemon/network/filter.go @@ -3,6 +3,7 @@ package network // import "github.com/docker/docker/daemon/network" import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" "github.com/docker/docker/runconfig" "github.com/pkg/errors" ) @@ -51,6 +52,24 @@ func FilterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.N displayNet = append(displayNet, nw) } + if values := filter.Get("dangling"); len(values) > 0 { + if len(values) > 1 { + return nil, errdefs.InvalidParameter(errors.New(`got more than one value for filter key "dangling"`)) + } + + var danglingOnly bool + switch values[0] { + case "0", "false": + // dangling is false already + case "1", "true": + danglingOnly = true + default: + return nil, errdefs.InvalidParameter(errors.New(`invalid value for filter 'dangling', must be "true" (or "1"), or "false" (or "0")`)) + } + + displayNet = filterNetworkByUse(displayNet, danglingOnly) + } + if filter.Contains("type") { typeNet := []types.NetworkResource{} errFilter := filter.WalkValues("type", func(fval string) error { @@ -70,6 +89,25 @@ func FilterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.N return displayNet, nil } +func filterNetworkByUse(nws []types.NetworkResource, danglingOnly bool) []types.NetworkResource { + retNws := []types.NetworkResource{} + + filterFunc := func(nw types.NetworkResource) bool { + if danglingOnly { + return !runconfig.IsPreDefinedNetwork(nw.Name) && len(nw.Containers) == 0 && len(nw.Services) == 0 + } + return runconfig.IsPreDefinedNetwork(nw.Name) || len(nw.Containers) > 0 || len(nw.Services) > 0 + } + + for _, nw := range nws { + if filterFunc(nw) { + retNws = append(retNws, nw) + } + } + + return retNws +} + func filterNetworkByType(nws []types.NetworkResource, netType string) ([]types.NetworkResource, error) { retNws := []types.NetworkResource{} switch netType { diff --git a/daemon/network/filter_test.go b/daemon/network/filter_test.go index d3180a05a9..042f5d1788 100644 --- a/daemon/network/filter_test.go +++ b/daemon/network/filter_test.go @@ -42,6 +42,16 @@ func TestFilterNetworks(t *testing.T) { Driver: "mykvdriver", Scope: "global", }, + { + Name: "networkwithcontainer", + Driver: "nwc", + Scope: "local", + Containers: map[string]types.EndpointResource{ + "customcontainer": { + Name: "customendpoint", + }, + }, + }, } bridgeDriverFilters := filters.NewArgs() @@ -71,11 +81,18 @@ func TestFilterNetworks(t *testing.T) { globalScopeFilters := filters.NewArgs() globalScopeFilters.Add("scope", "global") + trueDanglingFilters := filters.NewArgs() + trueDanglingFilters.Add("dangling", "true") + + falseDanglingFilters := filters.NewArgs() + falseDanglingFilters.Add("dangling", "false") + testCases := []struct { filter filters.Args resultCount int err string name string + results []string }{ { filter: bridgeDriverFilters, @@ -97,7 +114,7 @@ func TestFilterNetworks(t *testing.T) { }, { filter: customDriverFilters, - resultCount: 3, + resultCount: 4, err: "", name: "custom driver filters", }, @@ -115,7 +132,7 @@ func TestFilterNetworks(t *testing.T) { }, { filter: localScopeFilters, - resultCount: 4, + resultCount: 5, err: "", name: "local scope filters", }, @@ -131,6 +148,20 @@ func TestFilterNetworks(t *testing.T) { err: "", name: "global scope filters", }, + { + filter: trueDanglingFilters, + resultCount: 3, + err: "", + name: "dangling filter is 'True'", + results: []string{"myoverlay", "mydrivernet", "mykvnet"}, + }, + { + filter: falseDanglingFilters, + resultCount: 4, + err: "", + name: "dangling filter is 'False'", + results: []string{"host", "bridge", "none", "networkwithcontainer"}, + }, } for _, testCase := range testCases { @@ -157,6 +188,18 @@ func TestFilterNetworks(t *testing.T) { if len(result) != testCase.resultCount { t.Fatalf("expect '%d' networks, got '%d' networks", testCase.resultCount, len(result)) } + + if len(testCase.results) > 0 { + resultMap := make(map[string]bool) + for _, r := range result { + resultMap[r.Name] = true + } + for _, r := range testCase.results { + if _, ok := resultMap[r]; !ok { + t.Fatalf("expected result: '%s' not found", r) + } + } + } } }) }