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