mirror of https://github.com/tailscale/tailscale/
health, control/controlclient, wgengine: report when router unhealthy
Updates tailscale/corp#1338 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/1304/head
parent
2d96215d97
commit
fd8e070d01
@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package health is a registry for other packages to report & check
|
||||
// overall health status of the node.
|
||||
package health
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
m = map[string]error{} // error key => err (or nil for no error)
|
||||
watchers = map[*watchHandle]func(string, error){} // opt func to run if error state changes
|
||||
)
|
||||
|
||||
type watchHandle byte
|
||||
|
||||
// RegisterWatcher adds a function that will be called if an
|
||||
// error changes state either to unhealthy or from unhealthy. It is
|
||||
// not called on transition from unknown to healthy. It must be non-nil
|
||||
// and is run in its own goroutine. The returned func unregisters it.
|
||||
func RegisterWatcher(cb func(errKey string, err error)) (unregister func()) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
handle := new(watchHandle)
|
||||
watchers[handle] = cb
|
||||
return func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
delete(watchers, handle)
|
||||
}
|
||||
}
|
||||
|
||||
// SetRouter sets the state of the wgengine/router.Router.
|
||||
func SetRouterHealth(err error) { set("router", err) }
|
||||
|
||||
// RouterHealth returns the wgengine/router.Router error state.
|
||||
func RouterHealth() error { return get("router") }
|
||||
|
||||
func get(key string) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return m[key]
|
||||
}
|
||||
|
||||
func set(key string, err error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
old, ok := m[key]
|
||||
if !ok && err == nil {
|
||||
// Initial happy path.
|
||||
m[key] = nil
|
||||
return
|
||||
}
|
||||
if ok && (old == nil) == (err == nil) {
|
||||
// No change in overall error status (nil-vs-not), so
|
||||
// don't run callbacks, but exact error might've
|
||||
// changed, so note it.
|
||||
if err != nil {
|
||||
m[key] = err
|
||||
}
|
||||
return
|
||||
}
|
||||
m[key] = err
|
||||
for _, cb := range watchers {
|
||||
go cb(key, err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue