ipn: fix netmap change tracking and dns map generation (#609)
Signed-off-by: Dmytro Shynkevych <dmytro@tailscale.com>main
parent
3e3c24b8f6
commit
c7582dc234
@ -0,0 +1,20 @@ |
|||||||
|
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Code generated by tailscale.com/cmd/cloner -type Persist; DO NOT EDIT.
|
||||||
|
|
||||||
|
package controlclient |
||||||
|
|
||||||
|
import () |
||||||
|
|
||||||
|
// Clone makes a deep copy of Persist.
|
||||||
|
// The result aliases no memory with the original.
|
||||||
|
func (src *Persist) Clone() *Persist { |
||||||
|
if src == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
dst := new(Persist) |
||||||
|
*dst = *src |
||||||
|
return dst |
||||||
|
} |
||||||
@ -0,0 +1,118 @@ |
|||||||
|
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package tsdns |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"sort" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"inet.af/netaddr" |
||||||
|
) |
||||||
|
|
||||||
|
// Map is all the data Resolver needs to resolve DNS queries within the Tailscale network.
|
||||||
|
type Map struct { |
||||||
|
// nameToIP is a mapping of Tailscale domain names to their IP addresses.
|
||||||
|
// For example, monitoring.tailscale.us -> 100.64.0.1.
|
||||||
|
nameToIP map[string]netaddr.IP |
||||||
|
// names are the keys of nameToIP in sorted order.
|
||||||
|
names []string |
||||||
|
} |
||||||
|
|
||||||
|
// NewMap returns a new Map with name to address mapping given by nameToIP.
|
||||||
|
func NewMap(nameToIP map[string]netaddr.IP) *Map { |
||||||
|
names := make([]string, 0, len(nameToIP)) |
||||||
|
for name := range nameToIP { |
||||||
|
names = append(names, name) |
||||||
|
} |
||||||
|
sort.Strings(names) |
||||||
|
|
||||||
|
return &Map{ |
||||||
|
nameToIP: nameToIP, |
||||||
|
names: names, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func printSingleNameIP(buf *strings.Builder, name string, ip netaddr.IP) { |
||||||
|
// Output width is exactly 80 columns.
|
||||||
|
fmt.Fprintf(buf, "%s\t%s\n", name, ip) |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Map) Pretty() string { |
||||||
|
buf := new(strings.Builder) |
||||||
|
for _, name := range m.names { |
||||||
|
printSingleNameIP(buf, name, m.nameToIP[name]) |
||||||
|
} |
||||||
|
return buf.String() |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Map) PrettyDiffFrom(old *Map) string { |
||||||
|
var ( |
||||||
|
oldNameToIP map[string]netaddr.IP |
||||||
|
newNameToIP map[string]netaddr.IP |
||||||
|
oldNames []string |
||||||
|
newNames []string |
||||||
|
) |
||||||
|
if old != nil { |
||||||
|
oldNameToIP = old.nameToIP |
||||||
|
oldNames = old.names |
||||||
|
} |
||||||
|
if m != nil { |
||||||
|
newNameToIP = m.nameToIP |
||||||
|
newNames = m.names |
||||||
|
} |
||||||
|
|
||||||
|
buf := new(strings.Builder) |
||||||
|
|
||||||
|
for len(oldNames) > 0 && len(newNames) > 0 { |
||||||
|
var name string |
||||||
|
|
||||||
|
newName, oldName := newNames[0], oldNames[0] |
||||||
|
switch { |
||||||
|
case oldName < newName: |
||||||
|
name = oldName |
||||||
|
oldNames = oldNames[1:] |
||||||
|
case oldName > newName: |
||||||
|
name = newName |
||||||
|
newNames = newNames[1:] |
||||||
|
case oldNames[0] == newNames[0]: |
||||||
|
name = oldNames[0] |
||||||
|
oldNames = oldNames[1:] |
||||||
|
newNames = newNames[1:] |
||||||
|
} |
||||||
|
|
||||||
|
ipOld, inOld := oldNameToIP[name] |
||||||
|
ipNew, inNew := newNameToIP[name] |
||||||
|
switch { |
||||||
|
case !inOld: |
||||||
|
buf.WriteByte('+') |
||||||
|
printSingleNameIP(buf, name, ipNew) |
||||||
|
case !inNew: |
||||||
|
buf.WriteByte('-') |
||||||
|
printSingleNameIP(buf, name, ipOld) |
||||||
|
case ipOld != ipNew: |
||||||
|
buf.WriteByte('-') |
||||||
|
printSingleNameIP(buf, name, ipOld) |
||||||
|
buf.WriteByte('+') |
||||||
|
printSingleNameIP(buf, name, ipNew) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for _, name := range oldNames { |
||||||
|
if _, ok := newNameToIP[name]; !ok { |
||||||
|
buf.WriteByte('-') |
||||||
|
printSingleNameIP(buf, name, oldNameToIP[name]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for _, name := range newNames { |
||||||
|
if _, ok := oldNameToIP[name]; !ok { |
||||||
|
buf.WriteByte('+') |
||||||
|
printSingleNameIP(buf, name, newNameToIP[name]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return buf.String() |
||||||
|
} |
||||||
@ -0,0 +1,138 @@ |
|||||||
|
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package tsdns |
||||||
|
|
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
|
||||||
|
"inet.af/netaddr" |
||||||
|
) |
||||||
|
|
||||||
|
func TestPretty(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
name string |
||||||
|
dmap *Map |
||||||
|
want string |
||||||
|
}{ |
||||||
|
{"empty", NewMap(nil), ""}, |
||||||
|
{ |
||||||
|
"single", |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"hello.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
}), |
||||||
|
"hello.ipn.dev\t100.101.102.103\n", |
||||||
|
}, |
||||||
|
{ |
||||||
|
"multiple", |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test1.domain": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
"test2.sub.domain": netaddr.IPv4(100, 99, 9, 1), |
||||||
|
}), |
||||||
|
"test1.domain\t100.101.102.103\ntest2.sub.domain\t100.99.9.1\n", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
for _, tt := range tests { |
||||||
|
t.Run(tt.name, func(t *testing.T) { |
||||||
|
got := tt.dmap.Pretty() |
||||||
|
if tt.want != got { |
||||||
|
t.Errorf("want %v; got %v", tt.want, got) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestPrettyDiffFrom(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
name string |
||||||
|
map1 *Map |
||||||
|
map2 *Map |
||||||
|
want string |
||||||
|
}{ |
||||||
|
{ |
||||||
|
"from_empty", |
||||||
|
nil, |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 103, 102, 101), |
||||||
|
}), |
||||||
|
"+test1.ipn.dev\t100.101.102.103\n+test2.ipn.dev\t100.103.102.101\n", |
||||||
|
}, |
||||||
|
{ |
||||||
|
"equal", |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 103, 102, 101), |
||||||
|
}), |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 103, 102, 101), |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
}), |
||||||
|
"", |
||||||
|
}, |
||||||
|
{ |
||||||
|
"changed_ip", |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 103, 102, 101), |
||||||
|
}), |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 104, 102, 101), |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
}), |
||||||
|
"-test2.ipn.dev\t100.103.102.101\n+test2.ipn.dev\t100.104.102.101\n", |
||||||
|
}, |
||||||
|
{ |
||||||
|
"new_domain", |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 103, 102, 101), |
||||||
|
}), |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test3.ipn.dev": netaddr.IPv4(100, 105, 106, 107), |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 103, 102, 101), |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
}), |
||||||
|
"+test3.ipn.dev\t100.105.106.107\n", |
||||||
|
}, |
||||||
|
{ |
||||||
|
"gone_domain", |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 103, 102, 101), |
||||||
|
}), |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
}), |
||||||
|
"-test2.ipn.dev\t100.103.102.101\n", |
||||||
|
}, |
||||||
|
{ |
||||||
|
"mixed", |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 101, 102, 103), |
||||||
|
"test4.ipn.dev": netaddr.IPv4(100, 107, 106, 105), |
||||||
|
"test5.ipn.dev": netaddr.IPv4(100, 64, 1, 1), |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 103, 102, 101), |
||||||
|
}), |
||||||
|
NewMap(map[string]netaddr.IP{ |
||||||
|
"test2.ipn.dev": netaddr.IPv4(100, 104, 102, 101), |
||||||
|
"test1.ipn.dev": netaddr.IPv4(100, 100, 101, 102), |
||||||
|
"test3.ipn.dev": netaddr.IPv4(100, 64, 1, 1), |
||||||
|
}), |
||||||
|
"-test1.ipn.dev\t100.101.102.103\n+test1.ipn.dev\t100.100.101.102\n" + |
||||||
|
"-test2.ipn.dev\t100.103.102.101\n+test2.ipn.dev\t100.104.102.101\n" + |
||||||
|
"+test3.ipn.dev\t100.64.1.1\n-test4.ipn.dev\t100.107.106.105\n-test5.ipn.dev\t100.64.1.1\n", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
for _, tt := range tests { |
||||||
|
t.Run(tt.name, func(t *testing.T) { |
||||||
|
got := tt.map2.PrettyDiffFrom(tt.map1) |
||||||
|
if tt.want != got { |
||||||
|
t.Errorf("want %v; got %v", tt.want, got) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue