diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 53ba9b7a2..d5e0a440c 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -164,6 +164,15 @@ type Node struct { Hostinfo Hostinfo Created time.Time + // Tags are the list of ACL tags applied to this node. + // Tags take the form of `tag:` where value starts + // with a letter and only contains alphanumerics and dashes `-`. + // Some valid tag examples: + // `tag:prod` + // `tag:database` + // `tag:lab-1` + Tags []string `json:",omitempty"` + // PrimaryRoutes are the routes from AllowedIPs that this node // is currently the primary subnet router for, as determined // by the control plane. It does not include the self address @@ -1172,7 +1181,8 @@ func (n *Node) Equal(n2 *Node) bool { eqStrings(n.Capabilities, n2.Capabilities) && n.ComputedName == n2.ComputedName && n.computedHostIfDifferent == n2.computedHostIfDifferent && - n.ComputedNameWithHost == n2.ComputedNameWithHost + n.ComputedNameWithHost == n2.ComputedNameWithHost && + eqStrings(n.Tags, n2.Tags) } func eqBoolPtr(a, b *bool) bool { diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index ad5758afb..a9d179766 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -51,6 +51,7 @@ func (src *Node) Clone() *Node { dst.AllowedIPs = append(src.AllowedIPs[:0:0], src.AllowedIPs...) dst.Endpoints = append(src.Endpoints[:0:0], src.Endpoints...) dst.Hostinfo = *src.Hostinfo.Clone() + dst.Tags = append(src.Tags[:0:0], src.Tags...) dst.PrimaryRoutes = append(src.PrimaryRoutes[:0:0], src.PrimaryRoutes...) if dst.LastSeen != nil { dst.LastSeen = new(time.Time) @@ -81,6 +82,7 @@ func (src *Node) Clone() *Node { DERP string Hostinfo Hostinfo Created time.Time + Tags []string PrimaryRoutes []netaddr.IPPrefix LastSeen *time.Time Online *bool diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index 4cc987265..b094b71e1 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -195,7 +195,7 @@ func TestNodeEqual(t *testing.T) { "ID", "StableID", "Name", "User", "Sharer", "Key", "KeyExpiry", "Machine", "DiscoKey", "Addresses", "AllowedIPs", "Endpoints", "DERP", "Hostinfo", - "Created", "PrimaryRoutes", + "Created", "Tags", "PrimaryRoutes", "LastSeen", "Online", "KeepAlive", "MachineAuthorized", "Capabilities", "ComputedName", "computedHostIfDifferent", "ComputedNameWithHost", @@ -366,6 +366,26 @@ func TestNodeEqual(t *testing.T) { &Node{DERP: "bar"}, false, }, + { + &Node{Tags: []string{"tag:foo"}}, + &Node{Tags: []string{"tag:foo"}}, + true, + }, + { + &Node{Tags: []string{"tag:foo", "tag:bar"}}, + &Node{Tags: []string{"tag:bar"}}, + false, + }, + { + &Node{Tags: []string{"tag:foo"}}, + &Node{Tags: []string{"tag:bar"}}, + false, + }, + { + &Node{Tags: []string{"tag:foo"}}, + &Node{}, + false, + }, } for i, tt := range tests { got := tt.a.Equal(tt.b)