package libnetwork import ( "context" "fmt" "net" "net/netip" "testing" "github.com/Microsoft/hcsshim" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestAddEpToResolver(t *testing.T) { const ( ep1v4 = "192.0.2.11" ep2v4 = "192.0.2.12" epFiveDNS = "192.0.2.13" epNoIntDNS = "192.0.2.14" ep1v6 = "2001:db8:aaaa::2" gw1v4 = "192.0.2.1" gw2v4 = "192.0.2.2" gw1v6 = "2001:db8:aaaa::1" dns1v4 = "198.51.100.1" dns2v4 = "198.51.100.2" dns3v4 = "198.51.100.3" ) hnsEndpoints := map[string]hcsshim.HNSEndpoint{ ep1v4: { IPAddress: net.ParseIP(ep1v4), GatewayAddress: gw1v4, DNSServerList: gw1v4 + "," + dns1v4, EnableInternalDNS: true, }, ep2v4: { IPAddress: net.ParseIP(ep2v4), GatewayAddress: gw1v4, DNSServerList: gw1v4 + "," + dns2v4, EnableInternalDNS: true, }, epFiveDNS: { IPAddress: net.ParseIP(epFiveDNS), GatewayAddress: gw1v4, DNSServerList: gw1v4 + "," + dns1v4 + "," + dns2v4 + "," + dns3v4 + ",198.51.100.4", EnableInternalDNS: true, }, epNoIntDNS: { IPAddress: net.ParseIP(epNoIntDNS), GatewayAddress: gw1v4, DNSServerList: gw1v4 + "," + dns1v4, //EnableInternalDNS: false, }, ep1v6: { IPv6Address: net.ParseIP(ep1v6), GatewayAddressV6: gw1v6, DNSServerList: gw1v6 + "," + dns1v4, EnableInternalDNS: true, }, } makeIPNet := func(addr, netmask string) *net.IPNet { t.Helper() ip, ipnet, err := net.ParseCIDR(addr + "/" + netmask) assert.NilError(t, err) return &net.IPNet{IP: ip, Mask: ipnet.Mask} } testcases := []struct { name string epToAdd *EndpointInterface hnsEndpoints []hcsshim.HNSEndpoint resolverLAs []string expIPToExtDNS map[netip.Addr][maxExtDNS]extDNSEntry expResolverIdx int }{ { name: "ipv4", epToAdd: &EndpointInterface{ addr: makeIPNet(ep1v4, "32"), }, hnsEndpoints: []hcsshim.HNSEndpoint{ hnsEndpoints[ep1v4], }, resolverLAs: []string{gw1v4}, expIPToExtDNS: map[netip.Addr][maxExtDNS]extDNSEntry{ netip.MustParseAddr(ep1v4): {{IPStr: dns1v4}}, }, }, { name: "limit of three dns servers", epToAdd: &EndpointInterface{ addr: makeIPNet(epFiveDNS, "32"), }, hnsEndpoints: []hcsshim.HNSEndpoint{ hnsEndpoints[epFiveDNS], }, resolverLAs: []string{gw1v4}, // Expect the internal resolver to keep the first three ext-servers. expIPToExtDNS: map[netip.Addr][maxExtDNS]extDNSEntry{ netip.MustParseAddr(epFiveDNS): { {IPStr: dns1v4}, {IPStr: dns2v4}, {IPStr: dns3v4}, }, }, }, { name: "disabled internal resolver", epToAdd: &EndpointInterface{ addr: makeIPNet(epNoIntDNS, "32"), }, hnsEndpoints: []hcsshim.HNSEndpoint{ hnsEndpoints[epNoIntDNS], hnsEndpoints[ep2v4], }, resolverLAs: []string{gw1v4}, }, { name: "missing internal resolver", epToAdd: &EndpointInterface{ addr: makeIPNet(ep1v4, "32"), }, hnsEndpoints: []hcsshim.HNSEndpoint{ hnsEndpoints[ep1v4], }, // The only resolver is for the gateway on a different network. resolverLAs: []string{gw2v4}, }, { name: "multiple resolvers and endpoints", epToAdd: &EndpointInterface{ addr: makeIPNet(ep2v4, "32"), }, hnsEndpoints: []hcsshim.HNSEndpoint{ hnsEndpoints[ep1v4], hnsEndpoints[ep2v4], }, // Put the internal resolver for this network second in the list. expResolverIdx: 1, resolverLAs: []string{gw2v4, gw1v4}, expIPToExtDNS: map[netip.Addr][maxExtDNS]extDNSEntry{ netip.MustParseAddr(ep2v4): {{IPStr: dns2v4}}, }, }, { name: "ipv6", epToAdd: &EndpointInterface{ addrv6: makeIPNet(ep1v6, "80"), }, hnsEndpoints: []hcsshim.HNSEndpoint{ hnsEndpoints[ep1v6], }, resolverLAs: []string{gw1v6}, expIPToExtDNS: map[netip.Addr][maxExtDNS]extDNSEntry{ netip.MustParseAddr(ep1v6): {{IPStr: dns1v4}}, }, }, } eMapCmpOpts := []cmp.Option{ cmpopts.EquateEmpty(), cmpopts.EquateComparable(netip.Addr{}), cmpopts.IgnoreUnexported(extDNSEntry{}), } emptyEMap := map[netip.Addr][maxExtDNS]extDNSEntry{} for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { // Set up resolvers with the required listen-addresses. var resolvers []*Resolver for _, la := range tc.resolverLAs { resolvers = append(resolvers, NewResolver(la, true, nil)) } // Add the endpoint and check expected results. err := addEpToResolverImpl(context.TODO(), "netname", "epname", tc.epToAdd, resolvers, tc.hnsEndpoints) assert.Check(t, err) for i, resolver := range resolvers { if i == tc.expResolverIdx { assert.Check(t, is.DeepEqual(resolver.ipToExtDNS.eMap, tc.expIPToExtDNS, eMapCmpOpts...), fmt.Sprintf("resolveridx=%d", i)) } else { assert.Check(t, is.DeepEqual(resolver.ipToExtDNS.eMap, emptyEMap, eMapCmpOpts...), fmt.Sprintf("resolveridx=%d", i)) } } // Delete the endpoint, check nothing got left behind. err = deleteEpFromResolverImpl("epname", tc.epToAdd, resolvers, tc.hnsEndpoints) assert.Check(t, err) for i, resolver := range resolvers { assert.Check(t, is.DeepEqual(resolver.ipToExtDNS.eMap, emptyEMap, eMapCmpOpts...), fmt.Sprintf("resolveridx=%d", i)) } }) } }