Procházet zdrojové kódy

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 před 6 roky
rodič
revize
131cbaf5b7

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