ipn,ipn/local: broadcast ClientVersion if AutoUpdate.Check (#19107)
If AutoUpdate.Check is false, the client has opted out of checking for updates, so we shouldn't broadcast ClientVersion. If the client has opted in, it should be included in the initial Notify. Updates tailscale/corp#32629 Signed-off-by: kari-ts <kari@tailscale.com>
This commit is contained in:
@@ -85,6 +85,8 @@ const (
|
||||
NotifyHealthActions NotifyWatchOpt = 1 << 9 // if set, include PrimaryActions in health.State. Otherwise append the action URL to the text
|
||||
|
||||
NotifyInitialSuggestedExitNode NotifyWatchOpt = 1 << 10 // if set, the first Notify message (sent immediately) will contain the current SuggestedExitNode if available
|
||||
|
||||
NotifyInitialClientVersion NotifyWatchOpt = 1 << 11 // if set, the first Notify message (sent immediately) will contain the current ClientVersion if available and if update checks are enabled
|
||||
)
|
||||
|
||||
// Notify is a communication from a backend (e.g. tailscaled) to a frontend
|
||||
|
||||
+17
-3
@@ -3143,7 +3143,7 @@ func (b *LocalBackend) WatchNotificationsAs(ctx context.Context, actor ipnauth.A
|
||||
|
||||
b.mu.Lock()
|
||||
|
||||
const initialBits = ipn.NotifyInitialState | ipn.NotifyInitialPrefs | ipn.NotifyInitialNetMap | ipn.NotifyInitialDriveShares | ipn.NotifyInitialSuggestedExitNode
|
||||
const initialBits = ipn.NotifyInitialState | ipn.NotifyInitialPrefs | ipn.NotifyInitialNetMap | ipn.NotifyInitialDriveShares | ipn.NotifyInitialSuggestedExitNode | ipn.NotifyInitialClientVersion
|
||||
if mask&initialBits != 0 {
|
||||
cn := b.currentNode()
|
||||
ini = &ipn.Notify{Version: version.Long()}
|
||||
@@ -3171,6 +3171,11 @@ func (b *LocalBackend) WatchNotificationsAs(ctx context.Context, actor ipnauth.A
|
||||
ini.SuggestedExitNode = &en.ID
|
||||
}
|
||||
}
|
||||
if mask&ipn.NotifyInitialClientVersion != 0 {
|
||||
if prefs := b.pm.CurrentPrefs(); prefs.Valid() && prefs.AutoUpdate().Check {
|
||||
ini.ClientVersion = b.lastClientVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
@@ -3551,10 +3556,13 @@ func (b *LocalBackend) tellRecipientToBrowseToURLLocked(url string, recipient no
|
||||
// a non-nil ClientVersion message.
|
||||
func (b *LocalBackend) onClientVersion(v *tailcfg.ClientVersion) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
b.lastClientVersion = v
|
||||
b.health.SetLatestVersion(v)
|
||||
b.mu.Unlock()
|
||||
b.send(ipn.Notify{ClientVersion: v})
|
||||
prefs := b.pm.CurrentPrefs()
|
||||
if prefs.Valid() && prefs.AutoUpdate().Check {
|
||||
b.sendLocked(ipn.Notify{ClientVersion: v})
|
||||
}
|
||||
}
|
||||
|
||||
func (b *LocalBackend) onTailnetDefaultAutoUpdate(au bool) {
|
||||
@@ -4757,6 +4765,12 @@ func (b *LocalBackend) setPrefsLocked(newp *ipn.Prefs) ipn.PrefsView {
|
||||
b.authReconfigLocked()
|
||||
}
|
||||
|
||||
if newp.AutoUpdate.Check && !oldp.AutoUpdate().Check {
|
||||
if cv := b.lastClientVersion; cv != nil {
|
||||
b.sendLocked(ipn.Notify{ClientVersion: cv})
|
||||
}
|
||||
}
|
||||
|
||||
b.sendLocked(ipn.Notify{Prefs: &prefs})
|
||||
return prefs
|
||||
}
|
||||
|
||||
@@ -7400,6 +7400,98 @@ func TestDeps(t *testing.T) {
|
||||
}.Check(t)
|
||||
}
|
||||
|
||||
func TestOnClientVersionRespectsAutoUpdateCheck(t *testing.T) {
|
||||
lb := newTestLocalBackend(t)
|
||||
|
||||
cv := &tailcfg.ClientVersion{
|
||||
RunningLatest: false,
|
||||
LatestVersion: "1.96.0",
|
||||
}
|
||||
|
||||
// With Check disabled, onClientVersion should cache but not broadcast.
|
||||
lb.SetPrefsForTest(&ipn.Prefs{
|
||||
AutoUpdate: ipn.AutoUpdatePrefs{Check: false},
|
||||
})
|
||||
|
||||
nw := newNotificationWatcher(t, lb, ipnauth.Self)
|
||||
nw.watch(0, nil, unexpectedClientVersion)
|
||||
lb.onClientVersion(cv)
|
||||
nw.check()
|
||||
|
||||
// Verify it was cached despite not being broadcast.
|
||||
lb.mu.Lock()
|
||||
cached := lb.lastClientVersion
|
||||
lb.mu.Unlock()
|
||||
if cached == nil || cached.LatestVersion != "1.96.0" {
|
||||
t.Fatalf("lastClientVersion not cached: got %v", cached)
|
||||
}
|
||||
|
||||
// With Check enabled, onClientVersion should broadcast.
|
||||
lb.SetPrefsForTest(&ipn.Prefs{
|
||||
AutoUpdate: ipn.AutoUpdatePrefs{Check: true},
|
||||
})
|
||||
|
||||
nw.watch(0, []wantedNotification{
|
||||
wantClientVersionNotify("1.96.0"),
|
||||
})
|
||||
lb.onClientVersion(cv)
|
||||
nw.check()
|
||||
}
|
||||
|
||||
func TestWatchNotificationsInitialClientVersion(t *testing.T) {
|
||||
lb := newTestLocalBackend(t)
|
||||
|
||||
cv := &tailcfg.ClientVersion{
|
||||
RunningLatest: false,
|
||||
LatestVersion: "1.96.0",
|
||||
}
|
||||
|
||||
// Set Check=true and cache a ClientVersion.
|
||||
lb.SetPrefsForTest(&ipn.Prefs{
|
||||
AutoUpdate: ipn.AutoUpdatePrefs{Check: true},
|
||||
})
|
||||
lb.mu.Lock()
|
||||
lb.lastClientVersion = cv
|
||||
lb.mu.Unlock()
|
||||
|
||||
// Watch with NotifyInitialClientVersion should include ClientVersion.
|
||||
nw := newNotificationWatcher(t, lb, ipnauth.Self)
|
||||
nw.watch(ipn.NotifyInitialClientVersion, []wantedNotification{
|
||||
wantClientVersionNotify("1.96.0"),
|
||||
})
|
||||
nw.check()
|
||||
|
||||
// Watch without the flag, should not include it.
|
||||
nw2 := newNotificationWatcher(t, lb, ipnauth.Self)
|
||||
nw2.watch(0, nil, unexpectedClientVersion)
|
||||
nw2.check()
|
||||
|
||||
// Watch with the flag but Check=false, should not include it.
|
||||
lb.SetPrefsForTest(&ipn.Prefs{
|
||||
AutoUpdate: ipn.AutoUpdatePrefs{Check: false},
|
||||
})
|
||||
nw3 := newNotificationWatcher(t, lb, ipnauth.Self)
|
||||
nw3.watch(ipn.NotifyInitialClientVersion, nil, unexpectedClientVersion)
|
||||
nw3.check()
|
||||
}
|
||||
|
||||
func wantClientVersionNotify(wantLatest string) wantedNotification {
|
||||
return wantedNotification{
|
||||
name: fmt.Sprintf("ClientVersion-%s", wantLatest),
|
||||
cond: func(_ testing.TB, _ ipnauth.Actor, n *ipn.Notify) bool {
|
||||
return n.ClientVersion != nil && n.ClientVersion.LatestVersion == wantLatest
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func unexpectedClientVersion(t testing.TB, _ ipnauth.Actor, n *ipn.Notify) bool {
|
||||
if n.ClientVersion != nil {
|
||||
t.Errorf("unexpected ClientVersion: %v", n.ClientVersion)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkError(tb testing.TB, got, want error, fatal bool) {
|
||||
tb.Helper()
|
||||
f := tb.Errorf
|
||||
|
||||
Reference in New Issue
Block a user