mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-07 16:17:41 +00:00
health: add warming-up warnable (#12553)
This commit is contained in:
parent
30f8d8199a
commit
6e55d8f6a1
@ -92,7 +92,8 @@ type Tracker struct {
|
|||||||
lastMapRequestHeard time.Time // time we got a 200 from control for a MapRequest
|
lastMapRequestHeard time.Time // time we got a 200 from control for a MapRequest
|
||||||
ipnState string
|
ipnState string
|
||||||
ipnWantRunning bool
|
ipnWantRunning bool
|
||||||
anyInterfaceUp opt.Bool // empty means unknown (assume true)
|
ipnWantRunningLastTrue time.Time // when ipnWantRunning last changed false -> true
|
||||||
|
anyInterfaceUp opt.Bool // empty means unknown (assume true)
|
||||||
udp4Unbound bool
|
udp4Unbound bool
|
||||||
controlHealth []string
|
controlHealth []string
|
||||||
lastLoginErr error
|
lastLoginErr error
|
||||||
@ -705,7 +706,29 @@ func (t *Tracker) SetIPNState(state string, wantRunning bool) {
|
|||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
t.ipnState = state
|
t.ipnState = state
|
||||||
|
prevWantRunning := t.ipnWantRunning
|
||||||
t.ipnWantRunning = wantRunning
|
t.ipnWantRunning = wantRunning
|
||||||
|
|
||||||
|
if state == "Running" {
|
||||||
|
// Any time we are told the backend is Running (control+DERP are connected), the Warnable
|
||||||
|
// should be set to healthy, no matter if 5 seconds have passed or not.
|
||||||
|
t.setHealthyLocked(warmingUpWarnable)
|
||||||
|
} else if wantRunning && !prevWantRunning && t.ipnWantRunningLastTrue.IsZero() {
|
||||||
|
// The first time we see wantRunning=true and it used to be false, it means the user requested
|
||||||
|
// the backend to start. We store this timestamp and use it to silence some warnings that are
|
||||||
|
// expected during startup.
|
||||||
|
t.ipnWantRunningLastTrue = time.Now()
|
||||||
|
t.setUnhealthyLocked(warmingUpWarnable, nil)
|
||||||
|
time.AfterFunc(warmingUpWarnableDuration, func() {
|
||||||
|
t.mu.Lock()
|
||||||
|
t.updateWarmingUpWarnableLocked()
|
||||||
|
t.mu.Unlock()
|
||||||
|
})
|
||||||
|
} else if !wantRunning {
|
||||||
|
// Reset the timer when the user decides to stop the backend.
|
||||||
|
t.ipnWantRunningLastTrue = time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
t.selfCheckLocked()
|
t.selfCheckLocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -858,6 +881,8 @@ func (t *Tracker) multiErrLocked() error {
|
|||||||
// updateBuiltinWarnablesLocked performs a number of checks on the state of the backend,
|
// updateBuiltinWarnablesLocked performs a number of checks on the state of the backend,
|
||||||
// and adds/removes Warnings from the Tracker as needed.
|
// and adds/removes Warnings from the Tracker as needed.
|
||||||
func (t *Tracker) updateBuiltinWarnablesLocked() {
|
func (t *Tracker) updateBuiltinWarnablesLocked() {
|
||||||
|
t.updateWarmingUpWarnableLocked()
|
||||||
|
|
||||||
if t.checkForUpdates {
|
if t.checkForUpdates {
|
||||||
if cv := t.latestVersion; cv != nil && !cv.RunningLatest && cv.LatestVersion != "" {
|
if cv := t.latestVersion; cv != nil && !cv.RunningLatest && cv.LatestVersion != "" {
|
||||||
if cv.UrgentSecurityUpdate {
|
if cv.UrgentSecurityUpdate {
|
||||||
@ -1037,6 +1062,14 @@ func (t *Tracker) updateBuiltinWarnablesLocked() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateWarmingUpWarnableLocked ensures the warmingUpWarnable is healthy if wantRunning has been set to true
|
||||||
|
// for more than warmingUpWarnableDuration.
|
||||||
|
func (t *Tracker) updateWarmingUpWarnableLocked() {
|
||||||
|
if !t.ipnWantRunningLastTrue.IsZero() && time.Now().After(t.ipnWantRunningLastTrue.Add(warmingUpWarnableDuration)) {
|
||||||
|
t.setHealthyLocked(warmingUpWarnable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ReceiveFuncStats tracks the calls made to a wireguard-go receive func.
|
// ReceiveFuncStats tracks the calls made to a wireguard-go receive func.
|
||||||
type ReceiveFuncStats struct {
|
type ReceiveFuncStats struct {
|
||||||
// name is the name of the receive func.
|
// name is the name of the receive func.
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -199,15 +200,17 @@ func TestCheckDependsOnAppearsInUnhealthyState(t *testing.T) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("Expected an UnhealthyState for w1, got nothing")
|
t.Fatalf("Expected an UnhealthyState for w1, got nothing")
|
||||||
}
|
}
|
||||||
if len(us1.DependsOn) != 0 {
|
wantDependsOn := []WarnableCode{warmingUpWarnable.Code}
|
||||||
t.Fatalf("Expected no DependsOn in the unhealthy state, got: %v", us1.DependsOn)
|
if !reflect.DeepEqual(us1.DependsOn, wantDependsOn) {
|
||||||
|
t.Fatalf("Expected DependsOn = %v in the unhealthy state, got: %v", wantDependsOn, us1.DependsOn)
|
||||||
}
|
}
|
||||||
ht.SetUnhealthy(w2, Args{ArgError: "w2 is also unhealthy now"})
|
ht.SetUnhealthy(w2, Args{ArgError: "w2 is also unhealthy now"})
|
||||||
us2, ok := ht.CurrentState().Warnings[w2.Code]
|
us2, ok := ht.CurrentState().Warnings[w2.Code]
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("Expected an UnhealthyState for w2, got nothing")
|
t.Fatalf("Expected an UnhealthyState for w2, got nothing")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(us2.DependsOn, []WarnableCode{w1.Code}) {
|
wantDependsOn = slices.Concat([]WarnableCode{w1.Code}, wantDependsOn)
|
||||||
t.Fatalf("Expected DependsOn = [w1.Code] in the unhealthy state, got: %v", us2.DependsOn)
|
if !reflect.DeepEqual(us2.DependsOn, wantDependsOn) {
|
||||||
|
t.Fatalf("Expected DependsOn = %v in the unhealthy state, got: %v", wantDependsOn, us2.DependsOn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,11 +41,18 @@ func (w *Warnable) unhealthyState(ws *warningState) *UnhealthyState {
|
|||||||
text = w.Text(Args{})
|
text = w.Text(Args{})
|
||||||
}
|
}
|
||||||
|
|
||||||
dependsOnWarnableCodes := make([]WarnableCode, len(w.DependsOn))
|
dependsOnWarnableCodes := make([]WarnableCode, len(w.DependsOn), len(w.DependsOn)+1)
|
||||||
for i, d := range w.DependsOn {
|
for i, d := range w.DependsOn {
|
||||||
dependsOnWarnableCodes[i] = d.Code
|
dependsOnWarnableCodes[i] = d.Code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if w != warmingUpWarnable {
|
||||||
|
// Here we tell the frontend that all Warnables depend on warmingUpWarnable. GUIs will silence all warnings until all
|
||||||
|
// their dependencies are healthy. This is a special case to prevent the GUI from showing a bunch of warnings when
|
||||||
|
// the backend is still warming up.
|
||||||
|
dependsOnWarnableCodes = append(dependsOnWarnableCodes, warmingUpWarnable.Code)
|
||||||
|
}
|
||||||
|
|
||||||
return &UnhealthyState{
|
return &UnhealthyState{
|
||||||
WarnableCode: w.Code,
|
WarnableCode: w.Code,
|
||||||
Severity: w.Severity,
|
Severity: w.Severity,
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -212,3 +213,17 @@
|
|||||||
return fmt.Sprintf("The coordination server is reporting an health issue: %v", args[ArgError])
|
return fmt.Sprintf("The coordination server is reporting an health issue: %v", args[ArgError])
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// warmingUpWarnableDuration is the duration for which the warmingUpWarnable is reported by the backend after the user
|
||||||
|
// has changed ipnWantRunning to true from false.
|
||||||
|
const warmingUpWarnableDuration = 5 * time.Second
|
||||||
|
|
||||||
|
// warmingUpWarnable is a Warnable that is reported by the backend when it is starting up, for a maximum time of
|
||||||
|
// warmingUpWarnableDuration. The GUIs use the presence of this Warnable to prevent showing any other warnings until
|
||||||
|
// the backend is fully started.
|
||||||
|
var warmingUpWarnable = Register(&Warnable{
|
||||||
|
Code: "warming-up",
|
||||||
|
Title: "Tailscale is starting",
|
||||||
|
Severity: SeverityLow,
|
||||||
|
Text: StaticMessage("Tailscale is starting. Please wait."),
|
||||||
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user