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