Utku Ozdemir 5ffc3f14bd
feat: show siderolink status on dashboard
Add a new resource, `SiderolinkStatus`, which combines the following info:
- The Siderolink API endpoint without the query parameters or fragments (potentially sensitive info due to the join token)
- The status of the Siderolink connection

This resource is not set as sensitive, so it can be retrieved by the users with `os:operator` role (e.g., using `talosctl dashboard` through Omni).

Make use of this resource in the dashboard to display the status of the Siderolink connection.

Additionally, rework the status columns in the dashboard to:
- Display a Linux terminal compatible "tick" or a "cross" prefix for statuses in addition to the red/green color coding.
- Move and combine some statuses to save rows and make them more even.

Closes siderolabs/talos#8643.

Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
2024-06-18 12:31:54 +02:00

165 lines
4.1 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package dashboard
import (
"github.com/rivo/tview"
"github.com/siderolabs/talos/internal/pkg/dashboard/apidata"
"github.com/siderolabs/talos/internal/pkg/dashboard/components"
"github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata"
)
// SummaryGrid represents the summary grid with the basic node information and the logs.
type SummaryGrid struct {
tview.Grid
app *tview.Application
apiDataListeners []APIDataListener
resourceListeners []ResourceDataListener
nodeSelectListeners []NodeSelectListener
active bool
node string
logViewers map[string]*components.LogViewer
diagnostics *components.Diagnostics
diagnosticsVisible bool
}
const summaryTopFixedRows = 7
// NewSummaryGrid initializes SummaryGrid.
func NewSummaryGrid(app *tview.Application) *SummaryGrid {
widget := &SummaryGrid{
app: app,
Grid: *tview.NewGrid(),
logViewers: make(map[string]*components.LogViewer),
}
widget.SetRows(summaryTopFixedRows, 0).SetColumns(-3, -2, -3)
talosInfo := components.NewTalosInfo()
widget.AddItem(talosInfo, 0, 0, 1, 1, 0, 0, false)
kubernetesInfo := components.NewKubernetesInfo()
widget.AddItem(kubernetesInfo, 0, 1, 1, 1, 0, 0, false)
networkInfo := components.NewNetworkInfo()
widget.AddItem(networkInfo, 0, 2, 1, 1, 0, 0, false)
widget.diagnostics = components.NewDiagnostics()
widget.apiDataListeners = []APIDataListener{
kubernetesInfo,
}
widget.resourceListeners = []ResourceDataListener{
talosInfo,
kubernetesInfo,
networkInfo,
widget.diagnostics,
}
widget.nodeSelectListeners = []NodeSelectListener{
talosInfo,
kubernetesInfo,
networkInfo,
widget.diagnostics,
}
return widget
}
// OnNodeSelect implements the NodeSelectListener interface.
func (widget *SummaryGrid) OnNodeSelect(node string) {
widget.node = node
widget.updateLogViewer()
for _, nodeSelectListener := range widget.nodeSelectListeners {
nodeSelectListener.OnNodeSelect(node)
}
widget.updateDiagnostics()
}
// OnAPIDataChange implements the APIDataListener interface.
func (widget *SummaryGrid) OnAPIDataChange(node string, data *apidata.Data) {
for _, dataWidget := range widget.apiDataListeners {
dataWidget.OnAPIDataChange(node, data)
}
}
// OnResourceDataChange implements the ResourceDataListener interface.
func (widget *SummaryGrid) OnResourceDataChange(nodeResource resourcedata.Data) {
for _, resourceListener := range widget.resourceListeners {
resourceListener.OnResourceDataChange(nodeResource)
}
widget.updateDiagnostics()
}
// OnLogDataChange implements the LogDataListener interface.
func (widget *SummaryGrid) OnLogDataChange(node, logLine, logError string) {
widget.logViewer(node).WriteLog(logLine, logError)
}
func (widget *SummaryGrid) updateDiagnostics() {
height := widget.diagnostics.GetCurrentHeight()
switch {
case height == 0 && widget.diagnosticsVisible:
widget.RemoveItem(widget.diagnostics)
widget.SetRows(summaryTopFixedRows, 0)
widget.diagnosticsVisible = false
case height > 0 && !widget.diagnosticsVisible:
widget.SetRows(summaryTopFixedRows, 0, height)
widget.AddItem(widget.diagnostics, 2, 0, 1, 3, 0, 0, false)
widget.diagnosticsVisible = true
case height > 0:
widget.SetRows(summaryTopFixedRows, 0, height)
}
}
func (widget *SummaryGrid) updateLogViewer() {
if !widget.active {
return
}
widget.logViewer(widget.node)
for currNode, logViewer := range widget.logViewers {
if currNode == widget.node {
widget.AddItem(logViewer, 1, 0, 1, 3, 0, 0, false)
widget.app.SetFocus(logViewer)
return
}
widget.RemoveItem(logViewer)
}
}
func (widget *SummaryGrid) logViewer(node string) *components.LogViewer {
logViewer, ok := widget.logViewers[node]
if !ok {
logViewer = components.NewLogViewer()
widget.logViewers[node] = logViewer
}
return logViewer
}
// OnScreenSelect implements the screenSelectListener interface.
func (widget *SummaryGrid) onScreenSelect(active bool) {
widget.active = active
widget.updateLogViewer()
}