Skip to content

Commit 344039b

Browse files
committed
Populate DNS records for IPv6-only endpoints
Also, return IPv6 records from Network.getSvcRecords() so that /etc/hosts entries are deleted when an IPv6-only endpoint is removed. Signed-off-by: Rob Murray <[email protected]>
1 parent 265f0a7 commit 344039b

3 files changed

Lines changed: 135 additions & 34 deletions

File tree

libnetwork/etchosts/etchosts.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ func mergeRecords(path string, recs []Record) ([]byte, error) {
143143
}
144144

145145
// Delete deletes an arbitrary number of Records already existing in /etc/hosts file
146+
//
147+
// FIXME(robmry) - this only matches on hostname, not address. So, if a container
148+
// is connected to two networks then disconnected from one of them, the hosts
149+
// entries for both networks are deleted.
146150
func Delete(path string, recs []Record) error {
147151
defer pathLock(path)()
148152

libnetwork/libnetwork_internal_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/docker/docker/internal/testutils/netnsutils"
1414
"github.com/docker/docker/libnetwork/config"
1515
"github.com/docker/docker/libnetwork/driverapi"
16+
"github.com/docker/docker/libnetwork/etchosts"
1617
"github.com/docker/docker/libnetwork/ipams/defaultipam"
1718
"github.com/docker/docker/libnetwork/ipamutils"
1819
"github.com/docker/docker/libnetwork/netlabel"
@@ -358,6 +359,91 @@ func TestAuxAddresses(t *testing.T) {
358359
}
359360
}
360361

362+
func TestUpdateSvcRecord(t *testing.T) {
363+
skip.If(t, runtime.GOOS == "windows", "bridge driver and IPv6, only works on linux")
364+
365+
tests := []struct {
366+
name string
367+
epName string
368+
addr4 string
369+
addr6 string
370+
expSvcRecs []etchosts.Record
371+
}{
372+
{
373+
name: "v4only",
374+
epName: "ep4",
375+
addr4: "172.16.0.2/24",
376+
expSvcRecs: []etchosts.Record{
377+
{Hosts: "id-ep4", IP: "172.16.0.2"},
378+
},
379+
},
380+
/* TODO(robmry) - add this test when the bridge driver understands v6-only
381+
{
382+
name: "v6only",
383+
epName: "ep6",
384+
addr6: "fde6:045d:b2aa::2/64",
385+
expSvcRecs: []etchosts.Record{
386+
{Hosts: "id-ep6", IP: "fde6:45d:b2aa::2"},
387+
},
388+
},
389+
*/
390+
{
391+
name: "dual-stack",
392+
epName: "ep46",
393+
addr4: "172.16.1.2/24",
394+
addr6: "fd60:8677:5a4c::2/64",
395+
expSvcRecs: []etchosts.Record{
396+
{Hosts: "id-ep46", IP: "172.16.1.2"},
397+
{Hosts: "id-ep46", IP: "fd60:8677:5a4c::2"},
398+
},
399+
},
400+
}
401+
402+
for _, tc := range tests {
403+
tc := tc
404+
t.Run(tc.name, func(t *testing.T) {
405+
defer netnsutils.SetupTestOSContext(t)()
406+
ctrlr, err := New(OptionBoltdbWithRandomDBFile(t))
407+
assert.NilError(t, err)
408+
defer ctrlr.Stop()
409+
410+
var ipam4, ipam6 []*IpamConf
411+
var ip4, ip6 net.IP
412+
if tc.addr4 != "" {
413+
var net4 *net.IPNet
414+
ip4, net4, err = net.ParseCIDR(tc.addr4)
415+
assert.NilError(t, err)
416+
ipam4 = []*IpamConf{{PreferredPool: net4.String()}}
417+
}
418+
if tc.addr6 != "" {
419+
var net6 *net.IPNet
420+
ip6, net6, err = net.ParseCIDR(tc.addr6)
421+
assert.NilError(t, err)
422+
ipam6 = []*IpamConf{{PreferredPool: net6.String()}}
423+
}
424+
n, err := ctrlr.NewNetwork("bridge", "net1", "", nil,
425+
NetworkOptionEnableIPv4(tc.addr4 != ""),
426+
NetworkOptionEnableIPv6(tc.addr6 != ""),
427+
NetworkOptionIpam(defaultipam.DriverName, "", ipam4, ipam6, nil),
428+
)
429+
assert.NilError(t, err)
430+
ep, err := n.CreateEndpoint(context.Background(), tc.epName,
431+
CreateOptionDNSNames([]string{tc.epName, "id-" + tc.epName}),
432+
CreateOptionIpam(ip4, ip6, nil, nil),
433+
)
434+
assert.NilError(t, err)
435+
436+
n.updateSvcRecord(context.Background(), ep, true)
437+
recs := n.getSvcRecords(ep)
438+
assert.Check(t, is.DeepEqual(recs, tc.expSvcRecs))
439+
440+
n.updateSvcRecord(context.Background(), ep, false)
441+
recs = n.getSvcRecords(ep)
442+
assert.Check(t, is.Nil(recs))
443+
})
444+
}
445+
}
446+
361447
func TestSRVServiceQuery(t *testing.T) {
362448
skip.If(t, runtime.GOOS == "windows", "test only works on linux")
363449

libnetwork/network.go

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,11 +1329,14 @@ func (n *Network) updateSvcRecord(ctx context.Context, ep *Endpoint, isAdd bool)
13291329
defer span.End()
13301330

13311331
iface := ep.Iface()
1332-
if iface == nil || iface.Address() == nil {
1332+
if iface == nil {
13331333
return
13341334
}
13351335

1336-
var ipv6 net.IP
1336+
var ipv4, ipv6 net.IP
1337+
if iface.Address() != nil {
1338+
ipv4 = iface.Address().IP
1339+
}
13371340
if iface.AddressIPv6() != nil {
13381341
ipv6 = iface.AddressIPv6().IP
13391342
}
@@ -1347,12 +1350,12 @@ func (n *Network) updateSvcRecord(ctx context.Context, ep *Endpoint, isAdd bool)
13471350
if isAdd {
13481351
for i, dnsName := range dnsNames {
13491352
ipMapUpdate := i == 0 // ipMapUpdate indicates whether PTR records should be updated.
1350-
n.addSvcRecords(ep.ID(), dnsName, serviceID, iface.Address().IP, ipv6, ipMapUpdate, "updateSvcRecord")
1353+
n.addSvcRecords(ep.ID(), dnsName, serviceID, ipv4, ipv6, ipMapUpdate, "updateSvcRecord")
13511354
}
13521355
} else {
13531356
for i, dnsName := range dnsNames {
13541357
ipMapUpdate := i == 0 // ipMapUpdate indicates whether PTR records should be updated.
1355-
n.deleteSvcRecords(ep.ID(), dnsName, serviceID, iface.Address().IP, ipv6, ipMapUpdate, "updateSvcRecord")
1358+
n.deleteSvcRecords(ep.ID(), dnsName, serviceID, ipv4, ipv6, ipMapUpdate, "updateSvcRecord")
13561359
}
13571360
}
13581361
}
@@ -1392,14 +1395,14 @@ func delNameToIP(svcMap *setmatrix.SetMatrix[svcMapEntry], name, serviceID strin
13921395
}
13931396

13941397
// TODO(aker): remove ipMapUpdate param and add a proper method dedicated to update PTR records.
1395-
func (n *Network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP, ipMapUpdate bool, method string) {
1398+
func (n *Network) addSvcRecords(eID, name, serviceID string, epIPv4, epIPv6 net.IP, ipMapUpdate bool, method string) {
13961399
// Do not add service names for ingress network as this is a
13971400
// routing only network
13981401
if n.ingress {
13991402
return
14001403
}
14011404
networkID := n.ID()
1402-
log.G(context.TODO()).Debugf("%s (%.7s).addSvcRecords(%s, %s, %s, %t) %s sid:%s", eID, networkID, name, epIP, epIPv6, ipMapUpdate, method, serviceID)
1405+
log.G(context.TODO()).Debugf("%s (%.7s).addSvcRecords(%s, %s, %s, %t) %s sid:%s", eID, networkID, name, epIPv4, epIPv6, ipMapUpdate, method, serviceID)
14031406

14041407
c := n.getController()
14051408
c.mu.Lock()
@@ -1412,26 +1415,30 @@ func (n *Network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP
14121415
}
14131416

14141417
if ipMapUpdate {
1415-
addIPToName(&sr.ipMap, name, serviceID, epIP)
1418+
if epIPv4 != nil {
1419+
addIPToName(&sr.ipMap, name, serviceID, epIPv4)
1420+
}
14161421
if epIPv6 != nil {
14171422
addIPToName(&sr.ipMap, name, serviceID, epIPv6)
14181423
}
14191424
}
14201425

1421-
addNameToIP(&sr.svcMap, name, serviceID, epIP)
1426+
if epIPv4 != nil {
1427+
addNameToIP(&sr.svcMap, name, serviceID, epIPv4)
1428+
}
14221429
if epIPv6 != nil {
14231430
addNameToIP(&sr.svcIPv6Map, name, serviceID, epIPv6)
14241431
}
14251432
}
14261433

1427-
func (n *Network) deleteSvcRecords(eID, name, serviceID string, epIP net.IP, epIPv6 net.IP, ipMapUpdate bool, method string) {
1434+
func (n *Network) deleteSvcRecords(eID, name, serviceID string, epIPv4, epIPv6 net.IP, ipMapUpdate bool, method string) {
14281435
// Do not delete service names from ingress network as this is a
14291436
// routing only network
14301437
if n.ingress {
14311438
return
14321439
}
14331440
networkID := n.ID()
1434-
log.G(context.TODO()).Debugf("%s (%.7s).deleteSvcRecords(%s, %s, %s, %t) %s sid:%s ", eID, networkID, name, epIP, epIPv6, ipMapUpdate, method, serviceID)
1441+
log.G(context.TODO()).Debugf("%s (%.7s).deleteSvcRecords(%s, %s, %s, %t) %s sid:%s ", eID, networkID, name, epIPv4, epIPv6, ipMapUpdate, method, serviceID)
14351442

14361443
c := n.getController()
14371444
c.mu.Lock()
@@ -1443,15 +1450,17 @@ func (n *Network) deleteSvcRecords(eID, name, serviceID string, epIP net.IP, epI
14431450
}
14441451

14451452
if ipMapUpdate {
1446-
delIPToName(&sr.ipMap, name, serviceID, epIP)
1447-
1453+
if epIPv4 != nil {
1454+
delIPToName(&sr.ipMap, name, serviceID, epIPv4)
1455+
}
14481456
if epIPv6 != nil {
14491457
delIPToName(&sr.ipMap, name, serviceID, epIPv6)
14501458
}
14511459
}
14521460

1453-
delNameToIP(&sr.svcMap, name, serviceID, epIP)
1454-
1461+
if epIPv4 != nil {
1462+
delNameToIP(&sr.svcMap, name, serviceID, epIPv4)
1463+
}
14551464
if epIPv6 != nil {
14561465
delNameToIP(&sr.svcIPv6Map, name, serviceID, epIPv6)
14571466
}
@@ -1476,27 +1485,29 @@ func (n *Network) getSvcRecords(ep *Endpoint) []etchosts.Record {
14761485
return nil
14771486
}
14781487

1479-
svcMapKeys := sr.svcMap.Keys()
1480-
// Loop on service names on this network
1481-
for _, k := range svcMapKeys {
1482-
if strings.Split(k, ".")[0] == epName {
1483-
continue
1484-
}
1485-
// Get all the IPs associated to this service
1486-
mapEntryList, ok := sr.svcMap.Get(k)
1487-
if !ok {
1488-
// The key got deleted
1489-
continue
1490-
}
1491-
if len(mapEntryList) == 0 {
1492-
log.G(context.TODO()).Warnf("Found empty list of IP addresses for service %s on network %s (%s)", k, n.name, n.id)
1493-
continue
1494-
}
1488+
for _, svcMap := range []*setmatrix.SetMatrix[svcMapEntry]{&sr.svcMap, &sr.svcIPv6Map} {
1489+
svcMapKeys := svcMap.Keys()
1490+
// Loop on service names on this network
1491+
for _, k := range svcMapKeys {
1492+
if strings.Split(k, ".")[0] == epName {
1493+
continue
1494+
}
1495+
// Get all the IPs associated to this service
1496+
mapEntryList, ok := svcMap.Get(k)
1497+
if !ok {
1498+
// The key got deleted
1499+
continue
1500+
}
1501+
if len(mapEntryList) == 0 {
1502+
log.G(context.TODO()).Warnf("Found empty list of IP addresses for service %s on network %s (%s)", k, n.name, n.id)
1503+
continue
1504+
}
14951505

1496-
recs = append(recs, etchosts.Record{
1497-
Hosts: k,
1498-
IP: mapEntryList[0].ip,
1499-
})
1506+
recs = append(recs, etchosts.Record{
1507+
Hosts: k,
1508+
IP: mapEntryList[0].ip,
1509+
})
1510+
}
15001511
}
15011512

15021513
return recs

0 commit comments

Comments
 (0)