mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-13 20:29:55 +00:00
The status snapshot tore down on every management retry because state.Status() blanks the status when an error is wrapped, and the SubscribeStatus stream propagated that as FailedPrecondition. The UI treated any stream error as "daemon not running" and flickered the tray to Not running between retries. Disconnect was also unresponsive: Down set Idle before the retry goroutine exited, which then overwrote it with Set(Connecting) on the next attempt; the backoff sleep (up to 15s) wasn't context-aware, so the goroutine kept running long after actCancel. - buildStatusResponse falls back to the underlying status (via new state.CurrentStatus) instead of breaking the stream on wrapped errors. - UI only flips to DaemonUnavailable on codes.Unavailable / non-status errors, so a live daemon returning FailedPrecondition is not reported as down. - connect retry uses backoff.WithContext so actCancel interrupts the inter-attempt sleep, and skips Wrap(err) when the dial fails due to ctx cancellation. - Down sets Idle after waiting for giveUpChan, so the retry goroutine can no longer race the disconnect. - Tray hides Connect during Connecting and keeps Disconnect enabled so the user can abort an in-flight connection attempt.
82 lines
1.7 KiB
Go
82 lines
1.7 KiB
Go
package internal
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
)
|
|
|
|
type StatusType string
|
|
|
|
const (
|
|
StatusIdle StatusType = "Idle"
|
|
|
|
StatusConnecting StatusType = "Connecting"
|
|
StatusConnected StatusType = "Connected"
|
|
StatusNeedsLogin StatusType = "NeedsLogin"
|
|
StatusLoginFailed StatusType = "LoginFailed"
|
|
StatusSessionExpired StatusType = "SessionExpired"
|
|
)
|
|
|
|
// CtxInitState setup context state into the context tree.
|
|
//
|
|
// This function should be used to initialize context before
|
|
// CtxGetState will be executed.
|
|
func CtxInitState(ctx context.Context) context.Context {
|
|
return context.WithValue(ctx, stateCtx, &contextState{
|
|
status: StatusIdle,
|
|
})
|
|
}
|
|
|
|
// CtxGetState object to get/update state/errors of process.
|
|
func CtxGetState(ctx context.Context) *contextState {
|
|
return ctx.Value(stateCtx).(*contextState)
|
|
}
|
|
|
|
type contextState struct {
|
|
err error
|
|
status StatusType
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
func (c *contextState) Set(update StatusType) {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
c.status = update
|
|
c.err = nil
|
|
}
|
|
|
|
func (c *contextState) Status() (StatusType, error) {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
if c.err != nil {
|
|
return "", c.err
|
|
}
|
|
|
|
return c.status, nil
|
|
}
|
|
|
|
// CurrentStatus returns the last status set via Set, ignoring any wrapped
|
|
// error. Use when the status is needed for reporting purposes (e.g. the
|
|
// status snapshot stream) and a transient wrapped error from a retry loop
|
|
// shouldn't blank out the underlying status.
|
|
func (c *contextState) CurrentStatus() StatusType {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
return c.status
|
|
}
|
|
|
|
func (c *contextState) Wrap(err error) error {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
c.err = err
|
|
return err
|
|
}
|
|
|
|
type stateKey int
|
|
|
|
var stateCtx stateKey
|