daemon/network: match any type filter value

matchesType tests a network against only the first value of the type
filter. The values come from a map. Their order is random and changes
from one network to the next. A GET /networks request that filters on
both type=builtin and type=custom lists a random subset of the
networks. Before networks were filtered one at a time, the daemon
filtered the list once for each type value and listed every network
that matched any of them.

Match a network when its type is any of the filter's values.

integration-cli's TestDockerNetworkLsFilter checks this request but is
skipped in every run. Its OnlyDefaultNetworks requirement is met only
when GET /networks lists no networks at all. The daemon always lists
the predefined bridge, host and none networks. Port the test to
integration/network as TestNetworkListFilters, which uses the API
client. The new test checks the same id, name, type, label and driver
filter terms against two networks that it creates and the predefined
networks. It derives the expected results for type=custom and
driver=bridge from the unfiltered list, as the daemon can have other
networks. Remove the integration-cli test, its assertNwList helper and
the OnlyDefaultNetworks requirement. Nothing else uses them.

Add the combined type values to the filter unit test as well. Both
tests fail on the combined values without the fix.

Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
Cory Snider committed 2026-11-04 11:40:35 -05:00
1 parent 1f98bfc847
commit 1fc4e13cc5
5 files changed
+124 -95

No files matched your search

+4 -8
View File
@@ -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")
}
+5
View File
@@ -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")),
@@ -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")
-14
View File
@@ -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"
}
+115
View File
@@ -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)