fix: fix graph diffs in dashboard when node aliases are used

When `talosctl dashboard` is used with node "aliases" (e.g., node names or machine IDs in Omni) passed via `-n` flag, the graphs in the monitor tab were not rendered correctly: The matching of the old and current data were done incorrectly.

Fix this by pushing node alias->IP resolution down to the (api & log) data sources of the dashboard, by passing a resolver to them.

Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
This commit is contained in:
Utku Ozdemir 2024-08-02 16:49:14 +02:00
parent 9a126d70e0
commit 9d34158500
No known key found for this signature in database
GPG Key ID: DBD13117B0A14E93
4 changed files with 63 additions and 32 deletions

View File

@ -12,6 +12,8 @@ import (
"golang.org/x/sync/errgroup"
"google.golang.org/protobuf/types/known/emptypb"
"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers"
"github.com/siderolabs/talos/internal/pkg/dashboard/resolver"
"github.com/siderolabs/talos/pkg/machinery/client"
)
@ -19,6 +21,8 @@ import (
type Source struct {
*client.Client
Resolver resolver.Resolver
Interval time.Duration
ctx context.Context //nolint:containedctx
@ -101,7 +105,7 @@ func (source *Source) gather() *Data {
defer resultLock.Unlock()
for _, msg := range resp.GetMessages() {
node := msg.GetMetadata().GetHostname()
node := source.node(msg)
if _, ok := result.Nodes[node]; !ok {
result.Nodes[node] = &Node{}
@ -122,7 +126,7 @@ func (source *Source) gather() *Data {
defer resultLock.Unlock()
for _, msg := range resp.GetMessages() {
node := msg.GetMetadata().GetHostname()
node := source.node(msg)
if _, ok := result.Nodes[node]; !ok {
result.Nodes[node] = &Node{}
@ -143,7 +147,7 @@ func (source *Source) gather() *Data {
defer resultLock.Unlock()
for _, msg := range resp.GetMessages() {
node := msg.GetMetadata().GetHostname()
node := source.node(msg)
if _, ok := result.Nodes[node]; !ok {
result.Nodes[node] = &Node{}
@ -164,7 +168,7 @@ func (source *Source) gather() *Data {
defer resultLock.Unlock()
for _, msg := range resp.GetMessages() {
node := msg.GetMetadata().GetHostname()
node := source.node(msg)
if _, ok := result.Nodes[node]; !ok {
result.Nodes[node] = &Node{}
@ -185,7 +189,7 @@ func (source *Source) gather() *Data {
defer resultLock.Unlock()
for _, msg := range resp.GetMessages() {
node := msg.GetMetadata().GetHostname()
node := source.node(msg)
if _, ok := result.Nodes[node]; !ok {
result.Nodes[node] = &Node{}
@ -206,7 +210,7 @@ func (source *Source) gather() *Data {
defer resultLock.Unlock()
for _, msg := range resp.GetMessages() {
node := msg.GetMetadata().GetHostname()
node := source.node(msg)
if _, ok := result.Nodes[node]; !ok {
result.Nodes[node] = &Node{}
@ -227,7 +231,7 @@ func (source *Source) gather() *Data {
defer resultLock.Unlock()
for _, msg := range resp.GetMessages() {
node := msg.GetMetadata().GetHostname()
node := source.node(msg)
if _, ok := result.Nodes[node]; !ok {
result.Nodes[node] = &Node{}
@ -248,7 +252,7 @@ func (source *Source) gather() *Data {
defer resultLock.Unlock()
for _, msg := range resp.GetMessages() {
node := msg.GetMetadata().GetHostname()
node := source.node(msg)
if _, ok := result.Nodes[node]; !ok {
result.Nodes[node] = &Node{}
@ -269,7 +273,7 @@ func (source *Source) gather() *Data {
defer resultLock.Unlock()
for _, msg := range resp.GetMessages() {
node := msg.GetMetadata().GetHostname()
node := source.node(msg)
if _, ok := result.Nodes[node]; !ok {
result.Nodes[node] = &Node{}
@ -295,3 +299,9 @@ func (source *Source) gather() *Data {
return result
}
func (source *Source) node(msg helpers.Message) string {
hostname := msg.GetMetadata().GetHostname()
return source.Resolver.Resolve(hostname)
}

View File

@ -27,6 +27,7 @@ import (
"github.com/siderolabs/talos/internal/pkg/dashboard/apidata"
"github.com/siderolabs/talos/internal/pkg/dashboard/components"
"github.com/siderolabs/talos/internal/pkg/dashboard/logdata"
"github.com/siderolabs/talos/internal/pkg/dashboard/resolver"
"github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata"
"github.com/siderolabs/talos/pkg/machinery/client"
)
@ -247,16 +248,19 @@ func buildDashboard(ctx context.Context, cli *client.Client, opts ...Option) (*D
}
}
nodeResolver := resolver.New(ipsToNodeAliases)
dashboard.apiDataSource = &apidata.Source{
Client: cli,
Interval: defOptions.interval,
Resolver: nodeResolver,
}
dashboard.resourceDataSource = &resourcedata.Source{
COSI: cli.COSI,
}
dashboard.logDataSource = logdata.NewSource(cli)
dashboard.logDataSource = logdata.NewSource(cli, nodeResolver)
return dashboard, nil
}
@ -386,24 +390,18 @@ func (d *Dashboard) startDataHandler(ctx context.Context) func() error {
case <-ctx.Done():
return ctx.Err()
case nodeLog := <-d.logDataSource.LogCh:
nodeAlias := d.attemptResolveIPToAlias(nodeLog.Node)
if time.Since(lastLogTime) < 50*time.Millisecond {
d.app.QueueUpdate(func() {
d.processLog(nodeAlias, nodeLog.Log, nodeLog.Error)
d.processLog(nodeLog.Node, nodeLog.Log, nodeLog.Error)
})
} else {
d.app.QueueUpdateDraw(func() {
d.processLog(nodeAlias, nodeLog.Log, nodeLog.Error)
d.processLog(nodeLog.Node, nodeLog.Log, nodeLog.Error)
})
}
lastLogTime = time.Now()
case d.data = <-dataCh:
d.data.Nodes = maps.Map(d.data.Nodes, func(key string, v *apidata.Node) (string, *apidata.Node) {
return d.attemptResolveIPToAlias(key), v
})
d.app.QueueUpdateDraw(func() {
d.processAPIData()
})
@ -483,16 +481,6 @@ func (d *Dashboard) selectScreen(screen Screen) {
d.footer.SelectScreen(string(screen))
}
// attemptResolveIPToAlias attempts to resolve the given node IP to its alias as it appears in "nodes" in the context.
// If the IP is not found in the context, the IP is returned as-is.
func (d *Dashboard) attemptResolveIPToAlias(node string) string {
if resolved, ok := d.ipsToNodeAliases[node]; ok {
return resolved
}
return node
}
// collectNodeIPsToNodeAliases probes all nodes in the context for their IP addresses by calling their .Version endpoint and maps them to the node aliases in the context.
//
// Sample output:

View File

@ -18,6 +18,7 @@ import (
"google.golang.org/grpc/status"
"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers"
"github.com/siderolabs/talos/internal/pkg/dashboard/resolver"
"github.com/siderolabs/talos/internal/pkg/dashboard/util"
"github.com/siderolabs/talos/pkg/machinery/api/common"
"github.com/siderolabs/talos/pkg/machinery/client"
@ -34,6 +35,8 @@ type Data struct {
type Source struct {
client *client.Client
resolver resolver.Resolver
logCtxCancel context.CancelFunc
eg errgroup.Group
@ -43,10 +46,11 @@ type Source struct {
}
// NewSource initializes and returns Source data source.
func NewSource(client *client.Client) *Source {
func NewSource(client *client.Client, resolver resolver.Resolver) *Source {
return &Source{
client: client,
LogCh: make(chan Data),
client: client,
resolver: resolver,
LogCh: make(chan Data),
}
}
@ -82,7 +86,9 @@ func (source *Source) tailNodeWithRetries(ctx context.Context, node string) erro
}
if readErr != nil {
source.LogCh <- Data{Node: node, Error: readErr.Error()}
resolved := source.resolver.Resolve(node)
source.LogCh <- Data{Node: resolved, Error: readErr.Error()}
}
// back off a bit before retrying

View File

@ -0,0 +1,27 @@
// 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 resolver resolves the node names.
package resolver
// Resolver resolves the node names.
type Resolver struct {
db map[string]string
}
// New creates a new Resolver.
func New(db map[string]string) Resolver {
return Resolver{
db: db,
}
}
// Resolve attempts to resolve the node name.
func (n *Resolver) Resolve(node string) string {
if resolved, ok := n.db[node]; ok {
return resolved
}
return node
}