health: compare warnable codes to avoid errors on release branch (#17637)

This compares the warnings we actually care about and skips the unstable
warnings and the changes with no warnings.

Fixes #17635

Signed-off-by: Claus Lensbøl <claus@tailscale.com>
This commit is contained in:
Claus Lensbøl 2025-10-24 12:08:35 -04:00 committed by GitHub
parent d47c697748
commit 7418583e47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 77 additions and 27 deletions

View File

@ -116,7 +116,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
tailscale.com/syncs from tailscale.com/cmd/derper+ tailscale.com/syncs from tailscale.com/cmd/derper+
tailscale.com/tailcfg from tailscale.com/client/local+ tailscale.com/tailcfg from tailscale.com/client/local+
tailscale.com/tka from tailscale.com/client/local+ tailscale.com/tka from tailscale.com/client/local+
LW tailscale.com/tsconst from tailscale.com/net/netmon+ tailscale.com/tsconst from tailscale.com/net/netmon+
tailscale.com/tstime from tailscale.com/derp+ tailscale.com/tstime from tailscale.com/derp+
tailscale.com/tstime/mono from tailscale.com/tstime/rate tailscale.com/tstime/mono from tailscale.com/tstime/rate
tailscale.com/tstime/rate from tailscale.com/derp/derpserver tailscale.com/tstime/rate from tailscale.com/derp/derpserver

View File

@ -116,7 +116,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/tailcfg from tailscale.com/client/tailscale/apitype+ tailscale.com/tailcfg from tailscale.com/client/tailscale/apitype+
tailscale.com/tempfork/heap from tailscale.com/wgengine/magicsock tailscale.com/tempfork/heap from tailscale.com/wgengine/magicsock
tailscale.com/tka from tailscale.com/control/controlclient+ tailscale.com/tka from tailscale.com/control/controlclient+
tailscale.com/tsconst from tailscale.com/net/netns tailscale.com/tsconst from tailscale.com/net/netns+
tailscale.com/tsd from tailscale.com/cmd/tailscaled+ tailscale.com/tsd from tailscale.com/cmd/tailscaled+
tailscale.com/tstime from tailscale.com/control/controlclient+ tailscale.com/tstime from tailscale.com/control/controlclient+
tailscale.com/tstime/mono from tailscale.com/net/tstun+ tailscale.com/tstime/mono from tailscale.com/net/tstun+

View File

@ -19,6 +19,7 @@ import (
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
"tailscale.com/metrics" "tailscale.com/metrics"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/tsconst"
"tailscale.com/tstest" "tailscale.com/tstest"
"tailscale.com/tstime" "tailscale.com/tstime"
"tailscale.com/types/opt" "tailscale.com/types/opt"
@ -739,21 +740,27 @@ func TestControlHealthNotifies(t *testing.T) {
ht.SetIPNState("NeedsLogin", true) ht.SetIPNState("NeedsLogin", true)
ht.GotStreamedMapResponse() ht.GotStreamedMapResponse()
// Expect events at starup, before doing anything else // Expect events at starup, before doing anything else, skip unstable
// event and no warning event as they show up at different times.
synctest.Wait() synctest.Wait()
if err := eventbustest.ExpectExactly(tw, if err := eventbustest.Expect(tw,
eventbustest.Type[Change](), // warming-up CompareWarnableCode(t, tsconst.HealthWarnableWarmingUp),
eventbustest.Type[Change](), // is-using-unstable-version CompareWarnableCode(t, tsconst.HealthWarnableNotInMapPoll),
eventbustest.Type[Change](), // not-in-map-poll CompareWarnableCode(t, tsconst.HealthWarnableWarmingUp),
); err != nil { ); err != nil {
t.Errorf("startup error: %v", err) t.Errorf("startup error: %v", err)
} }
// Only set initial state if we need to // Only set initial state if we need to
if len(test.initialState) != 0 { if len(test.initialState) != 0 {
t.Log("Setting initial state")
ht.SetControlHealth(test.initialState) ht.SetControlHealth(test.initialState)
synctest.Wait() synctest.Wait()
if err := eventbustest.ExpectExactly(tw, eventbustest.Type[Change]()); err != nil { if err := eventbustest.Expect(tw,
CompareWarnableCode(t, tsconst.HealthWarnableMagicsockReceiveFuncError),
// Skip event with no warnable
CompareWarnableCode(t, tsconst.HealthWarnableNoDERPHome),
); err != nil {
t.Errorf("initial state error: %v", err) t.Errorf("initial state error: %v", err)
} }
} }
@ -771,6 +778,22 @@ func TestControlHealthNotifies(t *testing.T) {
} }
} }
func CompareWarnableCode(t *testing.T, code string) func(Change) bool {
t.Helper()
return func(c Change) bool {
t.Helper()
if c.Warnable != nil {
t.Logf("Warnable code: %s", c.Warnable.Code)
if string(c.Warnable.Code) == code {
return true
}
} else {
t.Log("No Warnable")
}
return false
}
}
func TestControlHealthIgnoredOutsideMapPoll(t *testing.T) { func TestControlHealthIgnoredOutsideMapPoll(t *testing.T) {
synctest.Test(t, func(t *testing.T) { synctest.Test(t, func(t *testing.T) {
bus := eventbustest.NewBus(t) bus := eventbustest.NewBus(t)

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"tailscale.com/feature/buildfeatures" "tailscale.com/feature/buildfeatures"
"tailscale.com/tsconst"
"tailscale.com/version" "tailscale.com/version"
) )
@ -26,7 +27,7 @@ This file contains definitions for the Warnables maintained within this `health`
// updateAvailableWarnable is a Warnable that warns the user that an update is available. // updateAvailableWarnable is a Warnable that warns the user that an update is available.
var updateAvailableWarnable = condRegister(func() *Warnable { var updateAvailableWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "update-available", Code: tsconst.HealthWarnableUpdateAvailable,
Title: "Update available", Title: "Update available",
Severity: SeverityLow, Severity: SeverityLow,
Text: func(args Args) string { Text: func(args Args) string {
@ -42,7 +43,7 @@ var updateAvailableWarnable = condRegister(func() *Warnable {
// securityUpdateAvailableWarnable is a Warnable that warns the user that an important security update is available. // securityUpdateAvailableWarnable is a Warnable that warns the user that an important security update is available.
var securityUpdateAvailableWarnable = condRegister(func() *Warnable { var securityUpdateAvailableWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "security-update-available", Code: tsconst.HealthWarnableSecurityUpdateAvailable,
Title: "Security update available", Title: "Security update available",
Severity: SeverityMedium, Severity: SeverityMedium,
Text: func(args Args) string { Text: func(args Args) string {
@ -59,7 +60,7 @@ var securityUpdateAvailableWarnable = condRegister(func() *Warnable {
// so they won't be surprised by all the issues that may arise. // so they won't be surprised by all the issues that may arise.
var unstableWarnable = condRegister(func() *Warnable { var unstableWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "is-using-unstable-version", Code: tsconst.HealthWarnableIsUsingUnstableVersion,
Title: "Using an unstable version", Title: "Using an unstable version",
Severity: SeverityLow, Severity: SeverityLow,
Text: StaticMessage("This is an unstable version of Tailscale meant for testing and development purposes. Please report any issues to Tailscale."), Text: StaticMessage("This is an unstable version of Tailscale meant for testing and development purposes. Please report any issues to Tailscale."),
@ -69,7 +70,7 @@ var unstableWarnable = condRegister(func() *Warnable {
// NetworkStatusWarnable is a Warnable that warns the user that the network is down. // NetworkStatusWarnable is a Warnable that warns the user that the network is down.
var NetworkStatusWarnable = condRegister(func() *Warnable { var NetworkStatusWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "network-status", Code: tsconst.HealthWarnableNetworkStatus,
Title: "Network down", Title: "Network down",
Severity: SeverityMedium, Severity: SeverityMedium,
Text: StaticMessage("Tailscale cannot connect because the network is down. Check your Internet connection."), Text: StaticMessage("Tailscale cannot connect because the network is down. Check your Internet connection."),
@ -81,7 +82,7 @@ var NetworkStatusWarnable = condRegister(func() *Warnable {
// IPNStateWarnable is a Warnable that warns the user that Tailscale is stopped. // IPNStateWarnable is a Warnable that warns the user that Tailscale is stopped.
var IPNStateWarnable = condRegister(func() *Warnable { var IPNStateWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "wantrunning-false", Code: tsconst.HealthWarnableWantRunningFalse,
Title: "Tailscale off", Title: "Tailscale off",
Severity: SeverityLow, Severity: SeverityLow,
Text: StaticMessage("Tailscale is stopped."), Text: StaticMessage("Tailscale is stopped."),
@ -91,7 +92,7 @@ var IPNStateWarnable = condRegister(func() *Warnable {
// localLogWarnable is a Warnable that warns the user that the local log is misconfigured. // localLogWarnable is a Warnable that warns the user that the local log is misconfigured.
var localLogWarnable = condRegister(func() *Warnable { var localLogWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "local-log-config-error", Code: tsconst.HealthWarnableLocalLogConfigError,
Title: "Local log misconfiguration", Title: "Local log misconfiguration",
Severity: SeverityLow, Severity: SeverityLow,
Text: func(args Args) string { Text: func(args Args) string {
@ -104,7 +105,7 @@ var localLogWarnable = condRegister(func() *Warnable {
// and provides the last login error if available. // and provides the last login error if available.
var LoginStateWarnable = condRegister(func() *Warnable { var LoginStateWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "login-state", Code: tsconst.HealthWarnableLoginState,
Title: "Logged out", Title: "Logged out",
Severity: SeverityMedium, Severity: SeverityMedium,
Text: func(args Args) string { Text: func(args Args) string {
@ -121,7 +122,7 @@ var LoginStateWarnable = condRegister(func() *Warnable {
// notInMapPollWarnable is a Warnable that warns the user that we are using a stale network map. // notInMapPollWarnable is a Warnable that warns the user that we are using a stale network map.
var notInMapPollWarnable = condRegister(func() *Warnable { var notInMapPollWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "not-in-map-poll", Code: tsconst.HealthWarnableNotInMapPoll,
Title: "Out of sync", Title: "Out of sync",
Severity: SeverityMedium, Severity: SeverityMedium,
DependsOn: []*Warnable{NetworkStatusWarnable, IPNStateWarnable}, DependsOn: []*Warnable{NetworkStatusWarnable, IPNStateWarnable},
@ -134,7 +135,7 @@ var notInMapPollWarnable = condRegister(func() *Warnable {
// noDERPHomeWarnable is a Warnable that warns the user that Tailscale doesn't have a home DERP. // noDERPHomeWarnable is a Warnable that warns the user that Tailscale doesn't have a home DERP.
var noDERPHomeWarnable = condRegister(func() *Warnable { var noDERPHomeWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "no-derp-home", Code: tsconst.HealthWarnableNoDERPHome,
Title: "No home relay server", Title: "No home relay server",
Severity: SeverityMedium, Severity: SeverityMedium,
DependsOn: []*Warnable{NetworkStatusWarnable}, DependsOn: []*Warnable{NetworkStatusWarnable},
@ -147,7 +148,7 @@ var noDERPHomeWarnable = condRegister(func() *Warnable {
// noDERPConnectionWarnable is a Warnable that warns the user that Tailscale couldn't connect to a specific DERP server. // noDERPConnectionWarnable is a Warnable that warns the user that Tailscale couldn't connect to a specific DERP server.
var noDERPConnectionWarnable = condRegister(func() *Warnable { var noDERPConnectionWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "no-derp-connection", Code: tsconst.HealthWarnableNoDERPConnection,
Title: "Relay server unavailable", Title: "Relay server unavailable",
Severity: SeverityMedium, Severity: SeverityMedium,
DependsOn: []*Warnable{ DependsOn: []*Warnable{
@ -177,7 +178,7 @@ var noDERPConnectionWarnable = condRegister(func() *Warnable {
// heard from the home DERP region for a while. // heard from the home DERP region for a while.
var derpTimeoutWarnable = condRegister(func() *Warnable { var derpTimeoutWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "derp-timed-out", Code: tsconst.HealthWarnableDERPTimedOut,
Title: "Relay server timed out", Title: "Relay server timed out",
Severity: SeverityMedium, Severity: SeverityMedium,
DependsOn: []*Warnable{ DependsOn: []*Warnable{
@ -198,7 +199,7 @@ var derpTimeoutWarnable = condRegister(func() *Warnable {
// derpRegionErrorWarnable is a Warnable that warns the user that a DERP region is reporting an issue. // derpRegionErrorWarnable is a Warnable that warns the user that a DERP region is reporting an issue.
var derpRegionErrorWarnable = condRegister(func() *Warnable { var derpRegionErrorWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "derp-region-error", Code: tsconst.HealthWarnableDERPRegionError,
Title: "Relay server error", Title: "Relay server error",
Severity: SeverityLow, Severity: SeverityLow,
DependsOn: []*Warnable{NetworkStatusWarnable}, DependsOn: []*Warnable{NetworkStatusWarnable},
@ -211,7 +212,7 @@ var derpRegionErrorWarnable = condRegister(func() *Warnable {
// noUDP4BindWarnable is a Warnable that warns the user that Tailscale couldn't listen for incoming UDP connections. // noUDP4BindWarnable is a Warnable that warns the user that Tailscale couldn't listen for incoming UDP connections.
var noUDP4BindWarnable = condRegister(func() *Warnable { var noUDP4BindWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "no-udp4-bind", Code: tsconst.HealthWarnableNoUDP4Bind,
Title: "NAT traversal setup failure", Title: "NAT traversal setup failure",
Severity: SeverityMedium, Severity: SeverityMedium,
DependsOn: []*Warnable{NetworkStatusWarnable, IPNStateWarnable}, DependsOn: []*Warnable{NetworkStatusWarnable, IPNStateWarnable},
@ -223,7 +224,7 @@ var noUDP4BindWarnable = condRegister(func() *Warnable {
// mapResponseTimeoutWarnable is a Warnable that warns the user that Tailscale hasn't received a network map from the coordination server in a while. // mapResponseTimeoutWarnable is a Warnable that warns the user that Tailscale hasn't received a network map from the coordination server in a while.
var mapResponseTimeoutWarnable = condRegister(func() *Warnable { var mapResponseTimeoutWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "mapresponse-timeout", Code: tsconst.HealthWarnableMapResponseTimeout,
Title: "Network map response timeout", Title: "Network map response timeout",
Severity: SeverityMedium, Severity: SeverityMedium,
DependsOn: []*Warnable{NetworkStatusWarnable, IPNStateWarnable}, DependsOn: []*Warnable{NetworkStatusWarnable, IPNStateWarnable},
@ -236,7 +237,7 @@ var mapResponseTimeoutWarnable = condRegister(func() *Warnable {
// tlsConnectionFailedWarnable is a Warnable that warns the user that Tailscale could not establish an encrypted connection with a server. // tlsConnectionFailedWarnable is a Warnable that warns the user that Tailscale could not establish an encrypted connection with a server.
var tlsConnectionFailedWarnable = condRegister(func() *Warnable { var tlsConnectionFailedWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "tls-connection-failed", Code: tsconst.HealthWarnableTLSConnectionFailed,
Title: "Encrypted connection failed", Title: "Encrypted connection failed",
Severity: SeverityMedium, Severity: SeverityMedium,
DependsOn: []*Warnable{NetworkStatusWarnable}, DependsOn: []*Warnable{NetworkStatusWarnable},
@ -249,7 +250,7 @@ var tlsConnectionFailedWarnable = condRegister(func() *Warnable {
// magicsockReceiveFuncWarnable is a Warnable that warns the user that one of the Magicsock functions is not running. // magicsockReceiveFuncWarnable is a Warnable that warns the user that one of the Magicsock functions is not running.
var magicsockReceiveFuncWarnable = condRegister(func() *Warnable { var magicsockReceiveFuncWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "magicsock-receive-func-error", Code: tsconst.HealthWarnableMagicsockReceiveFuncError,
Title: "MagicSock function not running", Title: "MagicSock function not running",
Severity: SeverityMedium, Severity: SeverityMedium,
Text: func(args Args) string { Text: func(args Args) string {
@ -261,7 +262,7 @@ var magicsockReceiveFuncWarnable = condRegister(func() *Warnable {
// testWarnable is a Warnable that is used within this package for testing purposes only. // testWarnable is a Warnable that is used within this package for testing purposes only.
var testWarnable = condRegister(func() *Warnable { var testWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "test-warnable", Code: tsconst.HealthWarnableTestWarnable,
Title: "Test warnable", Title: "Test warnable",
Severity: SeverityLow, Severity: SeverityLow,
Text: func(args Args) string { Text: func(args Args) string {
@ -273,7 +274,7 @@ var testWarnable = condRegister(func() *Warnable {
// applyDiskConfigWarnable is a Warnable that warns the user that there was an error applying the envknob config stored on disk. // applyDiskConfigWarnable is a Warnable that warns the user that there was an error applying the envknob config stored on disk.
var applyDiskConfigWarnable = condRegister(func() *Warnable { var applyDiskConfigWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "apply-disk-config", Code: tsconst.HealthWarnableApplyDiskConfig,
Title: "Could not apply configuration", Title: "Could not apply configuration",
Severity: SeverityMedium, Severity: SeverityMedium,
Text: func(args Args) string { Text: func(args Args) string {
@ -291,7 +292,7 @@ const warmingUpWarnableDuration = 5 * time.Second
// the backend is fully started. // the backend is fully started.
var warmingUpWarnable = condRegister(func() *Warnable { var warmingUpWarnable = condRegister(func() *Warnable {
return &Warnable{ return &Warnable{
Code: "warming-up", Code: tsconst.HealthWarnableWarmingUp,
Title: "Tailscale is starting", Title: "Tailscale is starting",
Severity: SeverityLow, Severity: SeverityLow,
Text: StaticMessage("Tailscale is starting. Please wait."), Text: StaticMessage("Tailscale is starting. Please wait."),

26
tsconst/health.go Normal file
View File

@ -0,0 +1,26 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package tsconst
const (
HealthWarnableUpdateAvailable = "update-available"
HealthWarnableSecurityUpdateAvailable = "security-update-available"
HealthWarnableIsUsingUnstableVersion = "is-using-unstable-version"
HealthWarnableNetworkStatus = "network-status"
HealthWarnableWantRunningFalse = "wantrunning-false"
HealthWarnableLocalLogConfigError = "local-log-config-error"
HealthWarnableLoginState = "login-state"
HealthWarnableNotInMapPoll = "not-in-map-poll"
HealthWarnableNoDERPHome = "no-derp-home"
HealthWarnableNoDERPConnection = "no-derp-connection"
HealthWarnableDERPTimedOut = "derp-timed-out"
HealthWarnableDERPRegionError = "derp-region-error"
HealthWarnableNoUDP4Bind = "no-udp4-bind"
HealthWarnableMapResponseTimeout = "mapresponse-timeout"
HealthWarnableTLSConnectionFailed = "tls-connection-failed"
HealthWarnableMagicsockReceiveFuncError = "magicsock-receive-func-error"
HealthWarnableTestWarnable = "test-warnable"
HealthWarnableApplyDiskConfig = "apply-disk-config"
HealthWarnableWarmingUp = "warming-up"
)