From 0e3048d8e01921758655dea212ecf010346d9300 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 14 Oct 2020 18:35:55 -0700 Subject: [PATCH] control/controlclient: support delta userprofiles from control I was going to make support for this advertised from the client, but turns out only "tailscale status" even uses the UserProfiles field and fails gracefully (omits that field) if a user profile for a user is missing, so I think we can just reuse the DeltaPeers field from the client to ask the control server to also delta encode the user profiles. For the few users running 1.1.x (unstable) versions between DeltaPeers support (1.1.82) and this (~1.1.541), they'll just sometimes have missing names in "tailscale status --json" or "tailscale status --web" (the only places the UserProfile is used). --- control/controlclient/direct.go | 15 +++++++++++++-- tailcfg/tailcfg.go | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index 356db72fd..977825400 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -616,6 +616,7 @@ func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*NetworkM }() var lastDERPMap *tailcfg.DERPMap + var lastUserProfile = map[tailcfg.UserID]tailcfg.UserProfile{} // If allowStream, then the server will use an HTTP long poll to // return incremental results. There is always one response right @@ -665,6 +666,9 @@ func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*NetworkM undeltaPeers(&resp, previousPeers) previousPeers = cloneNodes(resp.Peers) // defensive/lazy clone, since this escapes to who knows where + for _, up := range resp.UserProfiles { + lastUserProfile[up.ID] = up + } if resp.DERPMap != nil { vlogf("netmap: new map contains DERP map") @@ -708,8 +712,15 @@ func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*NetworkM DERPMap: lastDERPMap, Debug: resp.Debug, } - for _, profile := range resp.UserProfiles { - nm.UserProfiles[profile.ID] = profile + for _, peer := range resp.Peers { + userID := peer.User + if _, ok := nm.UserProfiles[userID]; ok { + // Already populated it from a previous peer. + continue + } + if up, ok := lastUserProfile[userID]; ok { + nm.UserProfiles[userID] = up + } } if resp.Node.MachineAuthorized { nm.MachineStatus = tailcfg.MachineAuthorized diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 01e6b5ff3..3acfbcc90 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -573,8 +573,8 @@ type MapResponse struct { // ACLs Domain string PacketFilter []FilterRule - UserProfiles []UserProfile - Roles []Role // deprecated; clients should not rely on Roles + UserProfiles []UserProfile // as of 1.1.541: may be new or updated user profiles only + Roles []Role // deprecated; clients should not rely on Roles // TODO: Groups []Group // TODO: Capabilities []Capability