package daemon import ( "encoding/json" "errors" "net/netip" "testing" containertypes "github.com/moby/moby/api/types/container" networktypes "github.com/moby/moby/api/types/network" "github.com/moby/moby/v2/daemon/container" "github.com/moby/moby/v2/daemon/libnetwork" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestDNSNamesOrder(t *testing.T) { d := &Daemon{} ctr := &container.Container{ ID: "35de8003b19e27f636fc6ecbf4d7072558b872a8544f287fd69ad8182ad59023", Name: "foobar", Config: &containertypes.Config{ Hostname: "baz", }, HostConfig: &containertypes.HostConfig{}, } nw := buildNetwork(t, map[string]any{ "id": "1234567890", "name": "testnet", "networkType": "bridge", "enableIPv6": false, }) epSettings := &networktypes.EndpointSettings{ Aliases: []string{"myctr"}, } if err := d.updateNetworkConfig(ctr, nw, epSettings); err != nil { t.Fatal(err) } assert.Check(t, is.DeepEqual(epSettings.DNSNames, []string{"foobar", "myctr", "35de8003b19e", "baz"})) } func buildNetwork(t *testing.T, config map[string]any) *libnetwork.Network { t.Helper() b, err := json.Marshal(config) if err != nil { t.Fatal(err) } nw := &libnetwork.Network{} if err := nw.UnmarshalJSON(b); err != nil { t.Fatal(err) } return nw } func TestEndpointIPAMConfigWithOutOfRangeAddrs(t *testing.T) { tests := []struct { name string ipamConfig *networktypes.EndpointIPAMConfig v4Subnets []*libnetwork.IpamConf v6Subnets []*libnetwork.IpamConf expectedErrors []string }{ { name: "valid config", ipamConfig: &networktypes.EndpointIPAMConfig{ IPv4Address: netip.MustParseAddr("192.168.100.10"), IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"), LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.169.254"), netip.MustParseAddr("fe80::42:a8ff:fe33:6230")}, }, v4Subnets: []*libnetwork.IpamConf{ {PreferredPool: "192.168.100.0/24"}, }, v6Subnets: []*libnetwork.IpamConf{ {PreferredPool: "2a01:d2:af:420b:25c1:1816:bb33::/112"}, }, }, { name: "static addresses out of range", ipamConfig: &networktypes.EndpointIPAMConfig{ IPv4Address: netip.MustParseAddr("192.168.100.10"), IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"), }, v4Subnets: []*libnetwork.IpamConf{ {PreferredPool: "192.168.255.0/24"}, }, v6Subnets: []*libnetwork.IpamConf{ {PreferredPool: "2001:db8::/112"}, }, expectedErrors: []string{ "no configured subnet or ip-range contain the IP address 192.168.100.10", "no configured subnet or ip-range contain the IP address 2a01:d2:af:420b:25c1:1816:bb33:855c", }, }, { name: "static addresses with dynamic network subnets", ipamConfig: &networktypes.EndpointIPAMConfig{ IPv4Address: netip.MustParseAddr("192.168.100.10"), IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"), }, v4Subnets: []*libnetwork.IpamConf{ {}, }, v6Subnets: []*libnetwork.IpamConf{ {}, }, expectedErrors: []string{ "user specified IP address is supported only when connecting to networks with user configured subnets", "user specified IP address is supported only when connecting to networks with user configured subnets", }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { errs := validateIPAMConfigIsInRange(nil, tc.ipamConfig, tc.v4Subnets, tc.v6Subnets) if tc.expectedErrors == nil { assert.NilError(t, errors.Join(errs...)) return } assert.Check(t, len(errs) == len(tc.expectedErrors), "errs: %+v", errs) err := errors.Join(errs...) for _, expected := range tc.expectedErrors { assert.Check(t, is.ErrorContains(err, expected)) } }) } } func TestEndpointIPAMConfigWithInvalidConfig(t *testing.T) { tests := []struct { name string ipamConfig *networktypes.EndpointIPAMConfig expectedErrors []string }{ { name: "valid config", ipamConfig: &networktypes.EndpointIPAMConfig{ IPv4Address: netip.MustParseAddr("192.168.100.10"), IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"), LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.169.254"), netip.MustParseAddr("fe80::42:a8ff:fe33:6230")}, }, }, { name: "invalid IP addresses", ipamConfig: &networktypes.EndpointIPAMConfig{ IPv4Address: netip.MustParseAddr("2001::1"), IPv6Address: netip.MustParseAddr("1.2.3.4"), }, expectedErrors: []string{ "invalid IPv4 address: 2001::1", "invalid IPv6 address: 1.2.3.4", }, }, { name: "ipv6 address with a zone", ipamConfig: &networktypes.EndpointIPAMConfig{IPv6Address: netip.MustParseAddr("fe80::1cc0:3e8c:119f:c2e1%ens18")}, expectedErrors: []string{ "invalid IPv6 address: fe80::1cc0:3e8c:119f:c2e1%ens18", }, }, { name: "ipv6-mapped ipv4 address", ipamConfig: &networktypes.EndpointIPAMConfig{IPv6Address: netip.MustParseAddr("::ffff:192.168.100.10")}, expectedErrors: []string{ "invalid IPv6 address: ::ffff:192.168.100.10", }, }, { name: "unspecified address is invalid", ipamConfig: &networktypes.EndpointIPAMConfig{ IPv4Address: netip.IPv4Unspecified(), IPv6Address: netip.IPv6Unspecified(), LinkLocalIPs: []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified()}, }, expectedErrors: []string{ "invalid IPv4 address: 0.0.0.0", "invalid IPv6 address: ::", "invalid link-local IP address: 0.0.0.0", "invalid link-local IP address: ::", }, }, { name: "empty link-local", ipamConfig: &networktypes.EndpointIPAMConfig{ LinkLocalIPs: make([]netip.Addr, 1), }, expectedErrors: []string{"invalid link-local IP address:"}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { errs := normalizeEndpointIPAMConfig(nil, tc.ipamConfig) if tc.expectedErrors == nil { assert.NilError(t, errors.Join(errs...)) return } assert.Check(t, len(errs) == len(tc.expectedErrors), "errs: %+v", errs) err := errors.Join(errs...) for _, expected := range tc.expectedErrors { assert.Check(t, is.ErrorContains(err, expected)) } }) } }