diff --git a/control/controlclient/map.go b/control/controlclient/map.go index 769c8f1e3..79b3999c3 100644 --- a/control/controlclient/map.go +++ b/control/controlclient/map.go @@ -86,6 +86,7 @@ type mapSession struct { lastDomain string lastDomainAuditLogID string lastHealth []string + lastHealthV2 []tailcfg.Health lastPopBrowserURL string lastTKAInfo *tailcfg.TKAInfo lastNetmapSummary string // from NetworkMap.VeryConcise @@ -383,6 +384,9 @@ func (ms *mapSession) updateStateFromResponse(resp *tailcfg.MapResponse) { if resp.Health != nil { ms.lastHealth = resp.Health } + if resp.HealthV2 != nil { + ms.lastHealthV2 = resp.HealthV2 + } if resp.TKAInfo != nil { ms.lastTKAInfo = resp.TKAInfo } @@ -802,6 +806,14 @@ func (ms *mapSession) sortedPeers() []tailcfg.NodeView { func (ms *mapSession) netmap() *netmap.NetworkMap { peerViews := ms.sortedPeers() + var healths []tailcfg.Health + for _, h := range ms.lastHealth { + healths = append(healths, tailcfg.Health{ID: "legacy", Title: "(debugging) legacy warning", Text: h}) + } + for _, h := range ms.lastHealthV2 { + healths = append(healths, h) + } + nm := &netmap.NetworkMap{ NodeKey: ms.publicNodeKey, PrivateKey: ms.privateNodeKey, @@ -816,7 +828,7 @@ func (ms *mapSession) netmap() *netmap.NetworkMap { SSHPolicy: ms.lastSSHPolicy, CollectServices: ms.collectServices, DERPMap: ms.lastDERPMap, - ControlHealth: ms.lastHealth, + ControlHealth: healths, TKAEnabled: ms.lastTKAInfo != nil && !ms.lastTKAInfo.Disabled, } diff --git a/health/health.go b/health/health.go index b0733f353..ac67c5ccd 100644 --- a/health/health.go +++ b/health/health.go @@ -111,7 +111,7 @@ type Tracker struct { ipnWantRunning bool ipnWantRunningLastTrue time.Time // when ipnWantRunning last changed false -> true anyInterfaceUp opt.Bool // empty means unknown (assume true) - controlHealth []string + controlHealth []tailcfg.Health lastLoginErr error localLogConfigErr error tlsConnectionErrors map[string]error // map[ServerName]error @@ -637,7 +637,7 @@ func (t *Tracker) updateLegacyErrorWarnableLocked(key Subsystem, err error) { } } -func (t *Tracker) SetControlHealth(problems []string) { +func (t *Tracker) SetControlHealth(problems []tailcfg.Health) { if t.nil() { return } @@ -1151,9 +1151,15 @@ func (t *Tracker) updateBuiltinWarnablesLocked() { if len(t.controlHealth) > 0 { for _, s := range t.controlHealth { - t.setUnhealthyLocked(controlHealthWarnable, Args{ - ArgError: s, - }) + t.setUnhealthyLocked(&Warnable{ + Code: WarnableCode(s.ID), + Title: s.Title, + Text: func(args Args) string { + return s.Text + }, + Severity: SeverityHigh, + ImpactsConnectivity: s.ImpactsConnectivity, + }, nil) } } else { t.setHealthyLocked(controlHealthWarnable) diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 83fab9c97..42887a0fe 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -1987,7 +1987,8 @@ type MapResponse struct { // Note that this package's type, due its use of a slice and omitempty, is // unable to marshal a zero-length non-nil slice. The control server needs // to marshal this type using a separate type. See MapResponse docs. - Health []string `json:",omitempty"` + Health []string `json:",omitempty"` + HealthV2 []Health `json:",omitempty"` // SSHPolicy, if non-nil, updates the SSH policy for how incoming // SSH connections should be handled. @@ -2032,6 +2033,14 @@ type MapResponse struct { DefaultAutoUpdate opt.Bool `json:",omitempty"` } +type Health struct { + ID string + Title string + Text string + Link string + ImpactsConnectivity bool +} + // ClientVersion is information about the latest client version that's available // for the client (and whether they're already running it). // diff --git a/types/netmap/netmap.go b/types/netmap/netmap.go index b1eecaa8f..8a6c168cf 100644 --- a/types/netmap/netmap.go +++ b/types/netmap/netmap.go @@ -59,7 +59,7 @@ type NetworkMap struct { // If empty, there are no known problems from the control plane's // point of view, but the node might know about its own health // check problems. - ControlHealth []string + ControlHealth []tailcfg.Health // TKAEnabled indicates whether the tailnet key authority should be // enabled, from the perspective of the control plane. diff --git a/types/netmap/nodemut.go b/types/netmap/nodemut.go index e31c731be..91c2f880c 100644 --- a/types/netmap/nodemut.go +++ b/types/netmap/nodemut.go @@ -163,6 +163,7 @@ func mapResponseContainsNonPatchFields(res *tailcfg.MapResponse) bool { res.PacketFilters != nil || res.UserProfiles != nil || res.Health != nil || + res.HealthV2 != nil || res.SSHPolicy != nil || res.TKAInfo != nil || res.DomainDataPlaneAuditLogID != "" ||