浏览代码

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 <Karthik.188@gmail.com>
karthik nayak 6 年之前
父节点
当前提交
131cbaf5b7
共有 3 个文件被更改,包括 90 次插入8 次删除
  1. 7 6
      api/types/network/network.go
  2. 38 0
      daemon/network/filter.go
  3. 45 2
      daemon/network/filter_test.go

+ 7 - 6
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.

+ 38 - 0
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 {

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