mirror of
https://github.com/tailscale/tailscale.git
synced 2025-03-28 03:52:35 +00:00
tailcfg: add Hostinfo.HowUnequal method
Change-Id: I80ee49c2ab581feccc4aa6ab47bc3c8392d9989d Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
0aa4c6f147
commit
bc537adb1a
@ -577,6 +577,68 @@ func (h *Hostinfo) Equal(h2 *Hostinfo) bool {
|
|||||||
return reflect.DeepEqual(h, h2)
|
return reflect.DeepEqual(h, h2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HowUnequal returns a list of paths through Hostinfo where h and h2 differ.
|
||||||
|
// If they differ in nil-ness, the path is "nil", otherwise the path is like
|
||||||
|
// "ShieldsUp" or "NetInfo.nil" or "NetInfo.PCP".
|
||||||
|
func (h *Hostinfo) HowUnequal(h2 *Hostinfo) (path []string) {
|
||||||
|
return appendStructPtrDiff(nil, "", reflect.ValueOf(h), reflect.ValueOf(h2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendStructPtrDiff(base []string, pfx string, p1, p2 reflect.Value) (ret []string) {
|
||||||
|
ret = base
|
||||||
|
if p1.IsNil() && p2.IsNil() {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
mkPath := func(b string) string {
|
||||||
|
if pfx == "" {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return pfx + "." + b
|
||||||
|
}
|
||||||
|
if p1.IsNil() || p2.IsNil() {
|
||||||
|
return append(base, mkPath("nil"))
|
||||||
|
}
|
||||||
|
v1, v2 := p1.Elem(), p2.Elem()
|
||||||
|
t := v1.Type()
|
||||||
|
for i, n := 0, t.NumField(); i < n; i++ {
|
||||||
|
sf := t.Field(i)
|
||||||
|
switch sf.Type.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
if v1.Field(i).String() != v2.Field(i).String() {
|
||||||
|
ret = append(ret, mkPath(sf.Name))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case reflect.Bool:
|
||||||
|
if v1.Field(i).Bool() != v2.Field(i).Bool() {
|
||||||
|
ret = append(ret, mkPath(sf.Name))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
if v1.Field(i).Int() != v2.Field(i).Int() {
|
||||||
|
ret = append(ret, mkPath(sf.Name))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
if v1.Field(i).Uint() != v2.Field(i).Uint() {
|
||||||
|
ret = append(ret, mkPath(sf.Name))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case reflect.Slice, reflect.Map:
|
||||||
|
if !reflect.DeepEqual(v1.Field(i).Interface(), v2.Field(i).Interface()) {
|
||||||
|
ret = append(ret, mkPath(sf.Name))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case reflect.Ptr:
|
||||||
|
if sf.Type.Elem().Kind() == reflect.Struct {
|
||||||
|
ret = appendStructPtrDiff(ret, sf.Name, v1.Field(i), v2.Field(i))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("unsupported type at %s: %s", mkPath(sf.Name), sf.Type.String()))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
// SignatureType specifies a scheme for signing RegisterRequest messages. It
|
// SignatureType specifies a scheme for signing RegisterRequest messages. It
|
||||||
// specifies the crypto algorithms to use, the contents of what is signed, and
|
// specifies the crypto algorithms to use, the contents of what is signed, and
|
||||||
// any other relevant details. Historically, requests were unsigned so the zero
|
// any other relevant details. Historically, requests were unsigned so the zero
|
||||||
|
@ -190,6 +190,82 @@ func TestHostinfoEqual(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHostinfoHowEqual(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
a, b *Hostinfo
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
a: nil,
|
||||||
|
b: nil,
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: new(Hostinfo),
|
||||||
|
b: nil,
|
||||||
|
want: []string{"nil"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: nil,
|
||||||
|
b: new(Hostinfo),
|
||||||
|
want: []string{"nil"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: new(Hostinfo),
|
||||||
|
b: new(Hostinfo),
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: &Hostinfo{
|
||||||
|
IPNVersion: "1",
|
||||||
|
ShieldsUp: false,
|
||||||
|
RoutableIPs: []netaddr.IPPrefix{netaddr.MustParseIPPrefix("1.2.3.0/24")},
|
||||||
|
},
|
||||||
|
b: &Hostinfo{
|
||||||
|
IPNVersion: "2",
|
||||||
|
ShieldsUp: true,
|
||||||
|
RoutableIPs: []netaddr.IPPrefix{netaddr.MustParseIPPrefix("1.2.3.0/25")},
|
||||||
|
},
|
||||||
|
want: []string{"IPNVersion", "ShieldsUp", "RoutableIPs"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: &Hostinfo{
|
||||||
|
IPNVersion: "1",
|
||||||
|
},
|
||||||
|
b: &Hostinfo{
|
||||||
|
IPNVersion: "2",
|
||||||
|
NetInfo: new(NetInfo),
|
||||||
|
},
|
||||||
|
want: []string{"IPNVersion", "NetInfo.nil"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: &Hostinfo{
|
||||||
|
IPNVersion: "1",
|
||||||
|
NetInfo: &NetInfo{
|
||||||
|
WorkingIPv6: "true",
|
||||||
|
HavePortMap: true,
|
||||||
|
LinkType: "foo",
|
||||||
|
PreferredDERP: 123,
|
||||||
|
DERPLatency: map[string]float64{
|
||||||
|
"foo": 1.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
b: &Hostinfo{
|
||||||
|
IPNVersion: "2",
|
||||||
|
NetInfo: &NetInfo{},
|
||||||
|
},
|
||||||
|
want: []string{"IPNVersion", "NetInfo.WorkingIPv6", "NetInfo.HavePortMap", "NetInfo.PreferredDERP", "NetInfo.LinkType", "NetInfo.DERPLatency"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tt := range tests {
|
||||||
|
got := tt.a.HowUnequal(tt.b)
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("%d. got %q; want %q", i, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNodeEqual(t *testing.T) {
|
func TestNodeEqual(t *testing.T) {
|
||||||
nodeHandles := []string{
|
nodeHandles := []string{
|
||||||
"ID", "StableID", "Name", "User", "Sharer",
|
"ID", "StableID", "Name", "User", "Sharer",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user