diff --git a/daemon/network/filter.go b/daemon/network/filter.go index b60ac3d090..e990e89fcc 100644 --- a/daemon/network/filter.go +++ b/daemon/network/filter.go @@ -1,6 +1,7 @@ package network import ( + "slices" "time" "github.com/moby/moby/v2/daemon/internal/filters" @@ -176,13 +177,8 @@ func matchesUse(danglingOnly bool, nw FilterNetwork) bool { } func matchesType(netTypes []string, nw FilterNetwork) bool { - for _, netType := range netTypes { - switch netType { - case "builtin": - return IsPredefined(nw.Name()) - case "custom": - return !IsPredefined(nw.Name()) - } + if IsPredefined(nw.Name()) { + return slices.Contains(netTypes, "builtin") } - return false + return slices.Contains(netTypes, "custom") } diff --git a/daemon/network/filter_test.go b/daemon/network/filter_test.go index d972657419..fdbf1ec596 100644 --- a/daemon/network/filter_test.go +++ b/daemon/network/filter_test.go @@ -173,6 +173,11 @@ func TestFilter(t *testing.T) { filter: filters.NewArgs(filters.Arg("type", "builtin")), results: []string{network.NetworkHost, network.NetworkBridge, network.NetworkNone}, }, + { + subtest: "type=builtin,type=custom", + filter: filters.NewArgs(filters.Arg("type", "builtin"), filters.Arg("type", "custom")), + results: []string{network.NetworkHost, network.NetworkBridge, network.NetworkNone, "myoverlay", "mydrivernet", "mykvnet", "networkwithcontainer", "networkwithservice", "idoverlap"}, + }, { subtest: "type=invalid", filter: filters.NewArgs(filters.Arg("type", "invalid")), diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go index 9c8ce7d390..9ce76857e5 100644 --- a/integration-cli/docker_cli_network_unix_test.go +++ b/integration-cli/docker_cli_network_unix_test.go @@ -273,22 +273,6 @@ func isNwPresent(t *testing.T, name string) bool { return false } -// assertNwList checks network list retrieved with ls command -// equals to expected network list -// note: out should be `network ls [option]` result -func assertNwList(t *testing.T, 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]) - } - - // network ls should contains all expected networks - assert.DeepEqual(t, nwList, expectNws) -} - func getNwResource(t *testing.T, name string) *network.Inspect { out := cli.DockerCmd(t, "network", "inspect", name).Stdout() var nr []network.Inspect @@ -306,63 +290,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkCreateHostBind(c *testing.T) { assert.Assert(c, is.Contains(out, "192.168.10.1:5000->5000/tcp")) } -func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *testing.T) { - testRequires(c, func() bool { return OnlyDefaultNetworks(testutil.GetContext(c)) }) - - testNet := "testnet1" - testLabel := "foo" - testValue := "bar" - out := cli.DockerCmd(c, "network", "create", "dev").Stdout() - defer func() { - cli.DockerCmd(c, "network", "rm", "dev") - cli.DockerCmd(c, "network", "rm", testNet) - }() - networkID := strings.TrimSpace(out) - - // filter with partial ID - // only show 'dev' network - out = cli.DockerCmd(c, "network", "ls", "-f", "id="+networkID[0:5]).Stdout() - assertNwList(c, out, []string{"dev"}) - - out = cli.DockerCmd(c, "network", "ls", "-f", "name=dge").Stdout() - assertNwList(c, out, []string{"bridge"}) - - // only show built-in network (bridge, none, host) - out = cli.DockerCmd(c, "network", "ls", "-f", "type=builtin").Stdout() - assertNwList(c, out, []string{"bridge", "host", "none"}) - - // only show custom networks (dev) - out = cli.DockerCmd(c, "network", "ls", "-f", "type=custom").Stdout() - assertNwList(c, out, []string{"dev"}) - - // show all networks with filter - // it should be equivalent of ls without option - out = cli.DockerCmd(c, "network", "ls", "-f", "type=custom", "-f", "type=builtin").Stdout() - assertNwList(c, out, []string{"bridge", "dev", "host", "none"}) - - cli.DockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet) - assertNwIsAvailable(c, testNet) - - out = cli.DockerCmd(c, "network", "ls", "-f", "label="+testLabel).Stdout() - assertNwList(c, out, []string{testNet}) - - out = cli.DockerCmd(c, "network", "ls", "-f", "label="+testLabel+"="+testValue).Stdout() - assertNwList(c, out, []string{testNet}) - - out = cli.DockerCmd(c, "network", "ls", "-f", "label=nonexistent").Stdout() - outArr := strings.Split(strings.TrimSpace(out), "\n") - assert.Equal(c, len(outArr), 1, out+"\n") - - out = cli.DockerCmd(c, "network", "ls", "-f", "driver=null").Stdout() - assertNwList(c, out, []string{"none"}) - - out = cli.DockerCmd(c, "network", "ls", "-f", "driver=host").Stdout() - assertNwList(c, out, []string{"host"}) - - out = cli.DockerCmd(c, "network", "ls", "-f", "driver=bridge").Stdout() - assertNwList(c, out, []string{"bridge", "dev", testNet}) -} - func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *testing.T) { cli.DockerCmd(c, "network", "create", "test") assertNwIsAvailable(c, "test") diff --git a/integration-cli/requirements_test.go b/integration-cli/requirements_test.go index 5e7dd97cee..1cc02e2c9b 100644 --- a/integration-cli/requirements_test.go +++ b/integration-cli/requirements_test.go @@ -1,7 +1,6 @@ package main import ( - "context" "errors" "net" "net/http" @@ -16,7 +15,6 @@ import ( "github.com/containerd/containerd/v2/plugins" "github.com/moby/moby/api/types/swarm" - "github.com/moby/moby/client" "github.com/moby/moby/v2/internal/testutil/registry" ) @@ -28,18 +26,6 @@ func DaemonIsLinux() bool { return testEnv.DaemonInfo.OSType == "linux" } -func OnlyDefaultNetworks(ctx context.Context) bool { - apiClient, err := client.New(client.FromEnv) - if err != nil { - return false - } - res, err := apiClient.NetworkList(ctx, client.NetworkListOptions{}) - if err != nil || len(res.Items) > 0 { - return false - } - return true -} - func IsAmd64() bool { return testEnv.DaemonInfo.Architecture == "amd64" } diff --git a/integration/network/network_linux_test.go b/integration/network/network_linux_test.go index d4d611b0e9..4d87abd0ea 100644 --- a/integration/network/network_linux_test.go +++ b/integration/network/network_linux_test.go @@ -232,6 +232,121 @@ func TestForbidDuplicateNetworkNames(t *testing.T) { assert.Error(t, err, "Error response from daemon: network with name testnet already exists", "2nd NetworkCreate call should have failed") } +// TestNetworkListFilters checks the id, name, type, label and driver filter +// terms of GET /networks. Expectations that depend on which other networks +// the daemon has are derived from the unfiltered list. +func TestNetworkListFilters(t *testing.T) { + ctx := setupTest(t) + apiClient := testEnv.APIClient() + + const ( + devName = "test-list-filters-dev" + labelledName = "test-list-filters-labelled" + labelKey = "com.example.test-list-filters" + labelValue = "bar" + ) + devID := network.CreateNoError(ctx, t, apiClient, devName) + defer network.RemoveNoError(ctx, t, apiClient, devName) + network.CreateNoError(ctx, t, apiClient, labelledName, func(o *client.NetworkCreateOptions) { + o.Labels = map[string]string{labelKey: labelValue} + }) + defer network.RemoveNoError(ctx, t, apiClient, labelledName) + + list := func(t *testing.T, filters client.Filters) []networktypes.Summary { + t.Helper() + res, err := apiClient.NetworkList(ctx, client.NetworkListOptions{Filters: filters}) + assert.NilError(t, err) + return res.Items + } + names := func(nws []networktypes.Summary, match func(networktypes.Summary) bool) []string { + out := []string{} + for _, nw := range nws { + if match(nw) { + out = append(out, nw.Name) + } + } + slices.Sort(out) + return out + } + anyNetwork := func(networktypes.Summary) bool { return true } + + all := list(t, nil) + assert.Assert(t, is.Contains(names(all, anyNetwork), devName)) + assert.Assert(t, is.Contains(names(all, anyNetwork), labelledName)) + predefined := []string{networktypes.NetworkBridge, networktypes.NetworkHost, networktypes.NetworkNone} + + tests := []struct { + name string + filters client.Filters + expected []string + }{ + { + name: "id prefix", + filters: make(client.Filters).Add("id", devID[:12]), + expected: []string{devName}, + }, + { + name: "name substring", + filters: make(client.Filters).Add("name", "list-filters-dev"), + expected: []string{devName}, + }, + { + name: "type=builtin", + filters: make(client.Filters).Add("type", "builtin"), + expected: predefined, + }, + { + name: "type=custom", + filters: make(client.Filters).Add("type", "custom"), + expected: names(all, func(nw networktypes.Summary) bool { + return !slices.Contains(predefined, nw.Name) + }), + }, + { + name: "type=builtin and type=custom", + filters: make(client.Filters).Add("type", "builtin", "custom"), + expected: names(all, anyNetwork), + }, + { + name: "label key", + filters: make(client.Filters).Add("label", labelKey), + expected: []string{labelledName}, + }, + { + name: "label key=value", + filters: make(client.Filters).Add("label", labelKey+"="+labelValue), + expected: []string{labelledName}, + }, + { + name: "label key not set", + filters: make(client.Filters).Add("label", labelKey+".unset"), + expected: []string{}, + }, + { + name: "driver=null", + filters: make(client.Filters).Add("driver", "null"), + expected: []string{networktypes.NetworkNone}, + }, + { + name: "driver=host", + filters: make(client.Filters).Add("driver", "host"), + expected: []string{networktypes.NetworkHost}, + }, + { + name: "driver=bridge", + filters: make(client.Filters).Add("driver", "bridge"), + expected: names(all, func(nw networktypes.Summary) bool { + return nw.Driver == "bridge" + }), + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert.Check(t, is.DeepEqual(names(list(t, tc.filters), anyNetwork), tc.expected)) + }) + } +} + // TestHostGatewayFromDocker0 checks that, when docker0 has IPv6, host-gateway maps to both IPv4 and IPv6. func TestHostGatewayFromDocker0(t *testing.T) { ctx := testutil.StartSpan(baseContext, t)