summaryrefslogtreecommitdiffhomepage
path: root/tailcfg
diff options
context:
space:
mode:
Diffstat (limited to 'tailcfg')
-rw-r--r--tailcfg/tailcfg.go122
-rw-r--r--tailcfg/tailcfg_equal.go244
-rw-r--r--tailcfg/tailcfg_test.go2
-rw-r--r--tailcfg/tailcfg_view.go1
4 files changed, 250 insertions, 119 deletions
diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go
index b77193f2b..6efd17f80 100644
--- a/tailcfg/tailcfg.go
+++ b/tailcfg/tailcfg.go
@@ -5,8 +5,9 @@ package tailcfg
//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan --clonefunc
+//go:generage go run tailscale.com/cmd/equaler --type Node,Hostinfo,NetInfo,Service
+
import (
- "bytes"
"encoding/hex"
"errors"
"fmt"
@@ -497,7 +498,7 @@ const (
// Service represents a service running on a node.
type Service struct {
- _ structs.Incomparable
+ _ structs.Incomparable `codegen:"noequal"`
// Proto is the type of service. It's usually the constant TCP
// or UDP ("tcp" or "udp"), but it can also be one of the
@@ -582,9 +583,6 @@ type Hostinfo struct {
Cloud string `json:",omitempty"`
Userspace opt.Bool `json:",omitempty"` // if the client is running in userspace (netstack) mode
UserspaceRouter opt.Bool `json:",omitempty"` // if the client's subnet router is running in userspace (netstack) mode
-
- // NOTE: any new fields containing pointers in this type
- // require changes to Hostinfo.Equal.
}
// TailscaleSSHEnabled reports whether or not this node is acting as a
@@ -664,9 +662,7 @@ type NetInfo struct {
// This should only be updated rarely, or when there's a
// material change, as any change here also gets uploaded to
// the control plane.
- DERPLatency map[string]float64 `json:",omitempty"`
-
- // Update BasicallyEqual when adding fields.
+ DERPLatency map[string]float64 `json:",omitempty" codegen:"noequal"`
}
func (ni *NetInfo) String() string {
@@ -704,40 +700,6 @@ func conciseOptBool(b opt.Bool, trueVal string) string {
return ""
}
-// BasicallyEqual reports whether ni and ni2 are basically equal, ignoring
-// changes in DERP ServerLatency & RegionLatency.
-func (ni *NetInfo) BasicallyEqual(ni2 *NetInfo) bool {
- if (ni == nil) != (ni2 == nil) {
- return false
- }
- if ni == nil {
- return true
- }
- return ni.MappingVariesByDestIP == ni2.MappingVariesByDestIP &&
- ni.HairPinning == ni2.HairPinning &&
- ni.WorkingIPv6 == ni2.WorkingIPv6 &&
- ni.OSHasIPv6 == ni2.OSHasIPv6 &&
- ni.WorkingUDP == ni2.WorkingUDP &&
- ni.WorkingICMPv4 == ni2.WorkingICMPv4 &&
- ni.HavePortMap == ni2.HavePortMap &&
- ni.UPnP == ni2.UPnP &&
- ni.PMP == ni2.PMP &&
- ni.PCP == ni2.PCP &&
- ni.PreferredDERP == ni2.PreferredDERP &&
- ni.LinkType == ni2.LinkType
-}
-
-// Equal reports whether h and h2 are equal.
-func (h *Hostinfo) Equal(h2 *Hostinfo) bool {
- if h == nil && h2 == nil {
- return true
- }
- if (h == nil) != (h2 == nil) {
- return false
- }
- 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".
@@ -1689,82 +1651,6 @@ func (id UserID) String() string { return fmt.Sprintf("userid:%x", int64(id)) }
func (id LoginID) String() string { return fmt.Sprintf("loginid:%x", int64(id)) }
func (id NodeID) String() string { return fmt.Sprintf("nodeid:%x", int64(id)) }
-// Equal reports whether n and n2 are equal.
-func (n *Node) Equal(n2 *Node) bool {
- if n == nil && n2 == nil {
- return true
- }
- return n != nil && n2 != nil &&
- n.ID == n2.ID &&
- n.StableID == n2.StableID &&
- n.Name == n2.Name &&
- n.User == n2.User &&
- n.Sharer == n2.Sharer &&
- n.UnsignedPeerAPIOnly == n2.UnsignedPeerAPIOnly &&
- n.Key == n2.Key &&
- n.KeyExpiry.Equal(n2.KeyExpiry) &&
- bytes.Equal(n.KeySignature, n2.KeySignature) &&
- n.Machine == n2.Machine &&
- n.DiscoKey == n2.DiscoKey &&
- eqPtr(n.Online, n2.Online) &&
- eqCIDRs(n.Addresses, n2.Addresses) &&
- eqCIDRs(n.AllowedIPs, n2.AllowedIPs) &&
- eqCIDRs(n.PrimaryRoutes, n2.PrimaryRoutes) &&
- eqStrings(n.Endpoints, n2.Endpoints) &&
- n.DERP == n2.DERP &&
- n.Cap == n2.Cap &&
- n.Hostinfo.Equal(n2.Hostinfo) &&
- n.Created.Equal(n2.Created) &&
- eqTimePtr(n.LastSeen, n2.LastSeen) &&
- n.MachineAuthorized == n2.MachineAuthorized &&
- eqStrings(n.Capabilities, n2.Capabilities) &&
- n.ComputedName == n2.ComputedName &&
- n.computedHostIfDifferent == n2.computedHostIfDifferent &&
- n.ComputedNameWithHost == n2.ComputedNameWithHost &&
- eqStrings(n.Tags, n2.Tags) &&
- n.Expired == n2.Expired &&
- eqPtr(n.SelfNodeV4MasqAddrForThisPeer, n2.SelfNodeV4MasqAddrForThisPeer) &&
- n.IsWireGuardOnly == n2.IsWireGuardOnly
-}
-
-func eqPtr[T comparable](a, b *T) bool {
- if a == b { // covers nil
- return true
- }
- if a == nil || b == nil {
- return false
- }
- return *a == *b
-}
-
-func eqStrings(a, b []string) bool {
- if len(a) != len(b) || ((a == nil) != (b == nil)) {
- return false
- }
- for i, v := range a {
- if v != b[i] {
- return false
- }
- }
- return true
-}
-
-func eqCIDRs(a, b []netip.Prefix) bool {
- if len(a) != len(b) || ((a == nil) != (b == nil)) {
- return false
- }
- for i, v := range a {
- if v != b[i] {
- return false
- }
- }
- return true
-}
-
-func eqTimePtr(a, b *time.Time) bool {
- return ((a == nil) == (b == nil)) && (a == nil || a.Equal(*b))
-}
-
// Oauth2Token is a copy of golang.org/x/oauth2.Token, to avoid the
// go.mod dependency on App Engine and grpc, which was causing problems.
// All we actually needed was this struct on the client side.
diff --git a/tailcfg/tailcfg_equal.go b/tailcfg/tailcfg_equal.go
new file mode 100644
index 000000000..c01454890
--- /dev/null
+++ b/tailcfg/tailcfg_equal.go
@@ -0,0 +1,244 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+// Code generated by tailscale.com/cmd/equaler; DO NOT EDIT.
+
+package tailcfg
+
+import (
+ "net/netip"
+ "time"
+
+ "golang.org/x/exp/slices"
+ "tailscale.com/types/key"
+ "tailscale.com/types/opt"
+ "tailscale.com/types/structs"
+ "tailscale.com/types/tkatype"
+)
+
+// Equal reports whether a and b are equal.
+func (a *Node) Equal(b *Node) bool {
+ if a == b {
+ return true
+ }
+ return a != nil && b != nil &&
+ a.ID == b.ID &&
+ a.StableID == b.StableID &&
+ a.Name == b.Name &&
+ a.User == b.User &&
+ a.Sharer == b.Sharer &&
+ a.Key == b.Key &&
+ a.KeyExpiry.Equal(b.KeyExpiry) &&
+ ((a.KeySignature == nil) == (b.KeySignature == nil)) &&
+ slices.Equal(a.KeySignature, b.KeySignature) &&
+ a.Machine == b.Machine &&
+ a.DiscoKey == b.DiscoKey &&
+ ((a.Addresses == nil) == (b.Addresses == nil)) &&
+ slices.Equal(a.Addresses, b.Addresses) &&
+ ((a.AllowedIPs == nil) == (b.AllowedIPs == nil)) &&
+ slices.Equal(a.AllowedIPs, b.AllowedIPs) &&
+ ((a.Endpoints == nil) == (b.Endpoints == nil)) &&
+ slices.Equal(a.Endpoints, b.Endpoints) &&
+ a.DERP == b.DERP &&
+ a.Hostinfo.Equal(b.Hostinfo) &&
+ a.Created.Equal(b.Created) &&
+ a.Cap == b.Cap &&
+ ((a.Tags == nil) == (b.Tags == nil)) &&
+ slices.Equal(a.Tags, b.Tags) &&
+ ((a.PrimaryRoutes == nil) == (b.PrimaryRoutes == nil)) &&
+ slices.Equal(a.PrimaryRoutes, b.PrimaryRoutes) &&
+ ((a.LastSeen == nil) == (b.LastSeen == nil)) && (a.LastSeen == nil || a.LastSeen.Equal(*b.LastSeen)) &&
+ ((a.Online == nil) == (b.Online == nil)) && (a.Online == nil || *a.Online == *b.Online) &&
+ a.KeepAlive == b.KeepAlive &&
+ a.MachineAuthorized == b.MachineAuthorized &&
+ ((a.Capabilities == nil) == (b.Capabilities == nil)) &&
+ slices.Equal(a.Capabilities, b.Capabilities) &&
+ a.UnsignedPeerAPIOnly == b.UnsignedPeerAPIOnly &&
+ a.ComputedName == b.ComputedName &&
+ a.computedHostIfDifferent == b.computedHostIfDifferent &&
+ a.ComputedNameWithHost == b.ComputedNameWithHost &&
+ a.DataPlaneAuditLogID == b.DataPlaneAuditLogID &&
+ a.Expired == b.Expired &&
+ ((a.SelfNodeV4MasqAddrForThisPeer == nil) == (b.SelfNodeV4MasqAddrForThisPeer == nil)) && (a.SelfNodeV4MasqAddrForThisPeer == nil || *a.SelfNodeV4MasqAddrForThisPeer == *b.SelfNodeV4MasqAddrForThisPeer) &&
+ a.IsWireGuardOnly == b.IsWireGuardOnly &&
+ true
+}
+
+// A compilation failure here means this code must be regenerated, with the command at the top of this file.
+var _NodeEqualNeedsRegeneration = Node(struct {
+ ID NodeID
+ StableID StableNodeID
+ Name string
+ User UserID
+ Sharer UserID
+ Key key.NodePublic
+ KeyExpiry time.Time
+ KeySignature tkatype.MarshaledSignature
+ Machine key.MachinePublic
+ DiscoKey key.DiscoPublic
+ Addresses []netip.Prefix
+ AllowedIPs []netip.Prefix
+ Endpoints []string
+ DERP string
+ Hostinfo HostinfoView
+ Created time.Time
+ Cap CapabilityVersion
+ Tags []string
+ PrimaryRoutes []netip.Prefix
+ LastSeen *time.Time
+ Online *bool
+ KeepAlive bool
+ MachineAuthorized bool
+ Capabilities []string
+ UnsignedPeerAPIOnly bool
+ ComputedName string
+ computedHostIfDifferent string
+ ComputedNameWithHost string
+ DataPlaneAuditLogID string
+ Expired bool
+ SelfNodeV4MasqAddrForThisPeer *netip.Addr
+ IsWireGuardOnly bool
+}{})
+
+// Equal reports whether a and b are equal.
+func (a *Hostinfo) Equal(b *Hostinfo) bool {
+ if a == b {
+ return true
+ }
+ return a != nil && b != nil &&
+ a.IPNVersion == b.IPNVersion &&
+ a.FrontendLogID == b.FrontendLogID &&
+ a.BackendLogID == b.BackendLogID &&
+ a.OS == b.OS &&
+ a.OSVersion == b.OSVersion &&
+ a.Container == b.Container &&
+ a.Env == b.Env &&
+ a.Distro == b.Distro &&
+ a.DistroVersion == b.DistroVersion &&
+ a.DistroCodeName == b.DistroCodeName &&
+ a.App == b.App &&
+ a.Desktop == b.Desktop &&
+ a.Package == b.Package &&
+ a.DeviceModel == b.DeviceModel &&
+ a.PushDeviceToken == b.PushDeviceToken &&
+ a.Hostname == b.Hostname &&
+ a.ShieldsUp == b.ShieldsUp &&
+ a.ShareeNode == b.ShareeNode &&
+ a.NoLogsNoSupport == b.NoLogsNoSupport &&
+ a.WireIngress == b.WireIngress &&
+ a.AllowsUpdate == b.AllowsUpdate &&
+ a.Machine == b.Machine &&
+ a.GoArch == b.GoArch &&
+ a.GoArchVar == b.GoArchVar &&
+ a.GoVersion == b.GoVersion &&
+ ((a.RoutableIPs == nil) == (b.RoutableIPs == nil)) &&
+ slices.Equal(a.RoutableIPs, b.RoutableIPs) &&
+ ((a.RequestTags == nil) == (b.RequestTags == nil)) &&
+ slices.Equal(a.RequestTags, b.RequestTags) &&
+ ((a.Services == nil) == (b.Services == nil)) &&
+ slices.EqualFunc(a.Services, b.Services, func(aa Service, bb Service) bool { return aa.Equal(&bb) }) &&
+ ((a.NetInfo == nil) == (b.NetInfo == nil)) && (a.NetInfo == nil || a.NetInfo.Equal(b.NetInfo)) &&
+ ((a.SSH_HostKeys == nil) == (b.SSH_HostKeys == nil)) &&
+ slices.Equal(a.SSH_HostKeys, b.SSH_HostKeys) &&
+ a.Cloud == b.Cloud &&
+ a.Userspace == b.Userspace &&
+ a.UserspaceRouter == b.UserspaceRouter &&
+ true
+}
+
+// A compilation failure here means this code must be regenerated, with the command at the top of this file.
+var _HostinfoEqualNeedsRegeneration = Hostinfo(struct {
+ IPNVersion string
+ FrontendLogID string
+ BackendLogID string
+ OS string
+ OSVersion string
+ Container opt.Bool
+ Env string
+ Distro string
+ DistroVersion string
+ DistroCodeName string
+ App string
+ Desktop opt.Bool
+ Package string
+ DeviceModel string
+ PushDeviceToken string
+ Hostname string
+ ShieldsUp bool
+ ShareeNode bool
+ NoLogsNoSupport bool
+ WireIngress bool
+ AllowsUpdate bool
+ Machine string
+ GoArch string
+ GoArchVar string
+ GoVersion string
+ RoutableIPs []netip.Prefix
+ RequestTags []string
+ Services []Service
+ NetInfo *NetInfo
+ SSH_HostKeys []string
+ Cloud string
+ Userspace opt.Bool
+ UserspaceRouter opt.Bool
+}{})
+
+// Equal reports whether a and b are equal.
+func (a *NetInfo) Equal(b *NetInfo) bool {
+ if a == b {
+ return true
+ }
+ return a != nil && b != nil &&
+ a.MappingVariesByDestIP == b.MappingVariesByDestIP &&
+ a.HairPinning == b.HairPinning &&
+ a.WorkingIPv6 == b.WorkingIPv6 &&
+ a.OSHasIPv6 == b.OSHasIPv6 &&
+ a.WorkingUDP == b.WorkingUDP &&
+ a.WorkingICMPv4 == b.WorkingICMPv4 &&
+ a.HavePortMap == b.HavePortMap &&
+ a.UPnP == b.UPnP &&
+ a.PMP == b.PMP &&
+ a.PCP == b.PCP &&
+ a.PreferredDERP == b.PreferredDERP &&
+ a.LinkType == b.LinkType &&
+ // Skipping DERPLatency because of codegen:noequal
+ true
+}
+
+// A compilation failure here means this code must be regenerated, with the command at the top of this file.
+var _NetInfoEqualNeedsRegeneration = NetInfo(struct {
+ MappingVariesByDestIP opt.Bool
+ HairPinning opt.Bool
+ WorkingIPv6 opt.Bool
+ OSHasIPv6 opt.Bool
+ WorkingUDP opt.Bool
+ WorkingICMPv4 opt.Bool
+ HavePortMap bool
+ UPnP opt.Bool
+ PMP opt.Bool
+ PCP opt.Bool
+ PreferredDERP int
+ LinkType string
+ DERPLatency map[string]float64
+}{})
+
+// Equal reports whether a and b are equal.
+func (a *Service) Equal(b *Service) bool {
+ if a == b {
+ return true
+ }
+ return a != nil && b != nil &&
+ // Skipping _ because of codegen:noequal
+ a.Proto == b.Proto &&
+ a.Port == b.Port &&
+ a.Description == b.Description &&
+ true
+}
+
+// A compilation failure here means this code must be regenerated, with the command at the top of this file.
+var _ServiceEqualNeedsRegeneration = Service(struct {
+ _ structs.Incomparable
+ Proto ServiceProto
+ Port uint16
+ Description string
+}{})
diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go
index b0e3f982e..bc94e9843 100644
--- a/tailcfg/tailcfg_test.go
+++ b/tailcfg/tailcfg_test.go
@@ -572,7 +572,7 @@ func TestNetInfoFields(t *testing.T) {
"DERPLatency",
}
if have := fieldsOf(reflect.TypeOf(NetInfo{})); !reflect.DeepEqual(have, handled) {
- t.Errorf("NetInfo.Clone/BasicallyEqually check might be out of sync\nfields: %q\nhandled: %q\n",
+ t.Errorf("NetInfo.Clone/Equal check might be out of sync\nfields: %q\nhandled: %q\n",
have, handled)
}
}
diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go
index b4ff3b738..957605255 100644
--- a/tailcfg/tailcfg_view.go
+++ b/tailcfg/tailcfg_view.go
@@ -402,6 +402,7 @@ func (v NetInfoView) LinkType() string { return v.ж.LinkType }
func (v NetInfoView) DERPLatency() views.Map[string, float64] { return views.MapOf(v.ж.DERPLatency) }
func (v NetInfoView) String() string { return v.ж.String() }
+func (v NetInfoView) Equal(v2 NetInfoView) bool { return v.ж.Equal(v2.ж) }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _NetInfoViewNeedsRegeneration = NetInfo(struct {