mirror of
https://github.com/siderolabs/talos.git
synced 2025-10-27 14:31:11 +01:00
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:
parent
9a126d70e0
commit
9d34158500
@ -12,6 +12,8 @@ import (
|
|||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"google.golang.org/protobuf/types/known/emptypb"
|
"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"
|
"github.com/siderolabs/talos/pkg/machinery/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,6 +21,8 @@ import (
|
|||||||
type Source struct {
|
type Source struct {
|
||||||
*client.Client
|
*client.Client
|
||||||
|
|
||||||
|
Resolver resolver.Resolver
|
||||||
|
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
|
|
||||||
ctx context.Context //nolint:containedctx
|
ctx context.Context //nolint:containedctx
|
||||||
@ -101,7 +105,7 @@ func (source *Source) gather() *Data {
|
|||||||
defer resultLock.Unlock()
|
defer resultLock.Unlock()
|
||||||
|
|
||||||
for _, msg := range resp.GetMessages() {
|
for _, msg := range resp.GetMessages() {
|
||||||
node := msg.GetMetadata().GetHostname()
|
node := source.node(msg)
|
||||||
|
|
||||||
if _, ok := result.Nodes[node]; !ok {
|
if _, ok := result.Nodes[node]; !ok {
|
||||||
result.Nodes[node] = &Node{}
|
result.Nodes[node] = &Node{}
|
||||||
@ -122,7 +126,7 @@ func (source *Source) gather() *Data {
|
|||||||
defer resultLock.Unlock()
|
defer resultLock.Unlock()
|
||||||
|
|
||||||
for _, msg := range resp.GetMessages() {
|
for _, msg := range resp.GetMessages() {
|
||||||
node := msg.GetMetadata().GetHostname()
|
node := source.node(msg)
|
||||||
|
|
||||||
if _, ok := result.Nodes[node]; !ok {
|
if _, ok := result.Nodes[node]; !ok {
|
||||||
result.Nodes[node] = &Node{}
|
result.Nodes[node] = &Node{}
|
||||||
@ -143,7 +147,7 @@ func (source *Source) gather() *Data {
|
|||||||
defer resultLock.Unlock()
|
defer resultLock.Unlock()
|
||||||
|
|
||||||
for _, msg := range resp.GetMessages() {
|
for _, msg := range resp.GetMessages() {
|
||||||
node := msg.GetMetadata().GetHostname()
|
node := source.node(msg)
|
||||||
|
|
||||||
if _, ok := result.Nodes[node]; !ok {
|
if _, ok := result.Nodes[node]; !ok {
|
||||||
result.Nodes[node] = &Node{}
|
result.Nodes[node] = &Node{}
|
||||||
@ -164,7 +168,7 @@ func (source *Source) gather() *Data {
|
|||||||
defer resultLock.Unlock()
|
defer resultLock.Unlock()
|
||||||
|
|
||||||
for _, msg := range resp.GetMessages() {
|
for _, msg := range resp.GetMessages() {
|
||||||
node := msg.GetMetadata().GetHostname()
|
node := source.node(msg)
|
||||||
|
|
||||||
if _, ok := result.Nodes[node]; !ok {
|
if _, ok := result.Nodes[node]; !ok {
|
||||||
result.Nodes[node] = &Node{}
|
result.Nodes[node] = &Node{}
|
||||||
@ -185,7 +189,7 @@ func (source *Source) gather() *Data {
|
|||||||
defer resultLock.Unlock()
|
defer resultLock.Unlock()
|
||||||
|
|
||||||
for _, msg := range resp.GetMessages() {
|
for _, msg := range resp.GetMessages() {
|
||||||
node := msg.GetMetadata().GetHostname()
|
node := source.node(msg)
|
||||||
|
|
||||||
if _, ok := result.Nodes[node]; !ok {
|
if _, ok := result.Nodes[node]; !ok {
|
||||||
result.Nodes[node] = &Node{}
|
result.Nodes[node] = &Node{}
|
||||||
@ -206,7 +210,7 @@ func (source *Source) gather() *Data {
|
|||||||
defer resultLock.Unlock()
|
defer resultLock.Unlock()
|
||||||
|
|
||||||
for _, msg := range resp.GetMessages() {
|
for _, msg := range resp.GetMessages() {
|
||||||
node := msg.GetMetadata().GetHostname()
|
node := source.node(msg)
|
||||||
|
|
||||||
if _, ok := result.Nodes[node]; !ok {
|
if _, ok := result.Nodes[node]; !ok {
|
||||||
result.Nodes[node] = &Node{}
|
result.Nodes[node] = &Node{}
|
||||||
@ -227,7 +231,7 @@ func (source *Source) gather() *Data {
|
|||||||
defer resultLock.Unlock()
|
defer resultLock.Unlock()
|
||||||
|
|
||||||
for _, msg := range resp.GetMessages() {
|
for _, msg := range resp.GetMessages() {
|
||||||
node := msg.GetMetadata().GetHostname()
|
node := source.node(msg)
|
||||||
|
|
||||||
if _, ok := result.Nodes[node]; !ok {
|
if _, ok := result.Nodes[node]; !ok {
|
||||||
result.Nodes[node] = &Node{}
|
result.Nodes[node] = &Node{}
|
||||||
@ -248,7 +252,7 @@ func (source *Source) gather() *Data {
|
|||||||
defer resultLock.Unlock()
|
defer resultLock.Unlock()
|
||||||
|
|
||||||
for _, msg := range resp.GetMessages() {
|
for _, msg := range resp.GetMessages() {
|
||||||
node := msg.GetMetadata().GetHostname()
|
node := source.node(msg)
|
||||||
|
|
||||||
if _, ok := result.Nodes[node]; !ok {
|
if _, ok := result.Nodes[node]; !ok {
|
||||||
result.Nodes[node] = &Node{}
|
result.Nodes[node] = &Node{}
|
||||||
@ -269,7 +273,7 @@ func (source *Source) gather() *Data {
|
|||||||
defer resultLock.Unlock()
|
defer resultLock.Unlock()
|
||||||
|
|
||||||
for _, msg := range resp.GetMessages() {
|
for _, msg := range resp.GetMessages() {
|
||||||
node := msg.GetMetadata().GetHostname()
|
node := source.node(msg)
|
||||||
|
|
||||||
if _, ok := result.Nodes[node]; !ok {
|
if _, ok := result.Nodes[node]; !ok {
|
||||||
result.Nodes[node] = &Node{}
|
result.Nodes[node] = &Node{}
|
||||||
@ -295,3 +299,9 @@ func (source *Source) gather() *Data {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (source *Source) node(msg helpers.Message) string {
|
||||||
|
hostname := msg.GetMetadata().GetHostname()
|
||||||
|
|
||||||
|
return source.Resolver.Resolve(hostname)
|
||||||
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/siderolabs/talos/internal/pkg/dashboard/apidata"
|
"github.com/siderolabs/talos/internal/pkg/dashboard/apidata"
|
||||||
"github.com/siderolabs/talos/internal/pkg/dashboard/components"
|
"github.com/siderolabs/talos/internal/pkg/dashboard/components"
|
||||||
"github.com/siderolabs/talos/internal/pkg/dashboard/logdata"
|
"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/internal/pkg/dashboard/resourcedata"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/client"
|
"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{
|
dashboard.apiDataSource = &apidata.Source{
|
||||||
Client: cli,
|
Client: cli,
|
||||||
Interval: defOptions.interval,
|
Interval: defOptions.interval,
|
||||||
|
Resolver: nodeResolver,
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard.resourceDataSource = &resourcedata.Source{
|
dashboard.resourceDataSource = &resourcedata.Source{
|
||||||
COSI: cli.COSI,
|
COSI: cli.COSI,
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard.logDataSource = logdata.NewSource(cli)
|
dashboard.logDataSource = logdata.NewSource(cli, nodeResolver)
|
||||||
|
|
||||||
return dashboard, nil
|
return dashboard, nil
|
||||||
}
|
}
|
||||||
@ -386,24 +390,18 @@ func (d *Dashboard) startDataHandler(ctx context.Context) func() error {
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
case nodeLog := <-d.logDataSource.LogCh:
|
case nodeLog := <-d.logDataSource.LogCh:
|
||||||
nodeAlias := d.attemptResolveIPToAlias(nodeLog.Node)
|
|
||||||
|
|
||||||
if time.Since(lastLogTime) < 50*time.Millisecond {
|
if time.Since(lastLogTime) < 50*time.Millisecond {
|
||||||
d.app.QueueUpdate(func() {
|
d.app.QueueUpdate(func() {
|
||||||
d.processLog(nodeAlias, nodeLog.Log, nodeLog.Error)
|
d.processLog(nodeLog.Node, nodeLog.Log, nodeLog.Error)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
d.app.QueueUpdateDraw(func() {
|
d.app.QueueUpdateDraw(func() {
|
||||||
d.processLog(nodeAlias, nodeLog.Log, nodeLog.Error)
|
d.processLog(nodeLog.Node, nodeLog.Log, nodeLog.Error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
lastLogTime = time.Now()
|
lastLogTime = time.Now()
|
||||||
case d.data = <-dataCh:
|
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.app.QueueUpdateDraw(func() {
|
||||||
d.processAPIData()
|
d.processAPIData()
|
||||||
})
|
})
|
||||||
@ -483,16 +481,6 @@ func (d *Dashboard) selectScreen(screen Screen) {
|
|||||||
d.footer.SelectScreen(string(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.
|
// 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:
|
// Sample output:
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import (
|
|||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers"
|
"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/internal/pkg/dashboard/util"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/api/common"
|
"github.com/siderolabs/talos/pkg/machinery/api/common"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/client"
|
"github.com/siderolabs/talos/pkg/machinery/client"
|
||||||
@ -34,6 +35,8 @@ type Data struct {
|
|||||||
type Source struct {
|
type Source struct {
|
||||||
client *client.Client
|
client *client.Client
|
||||||
|
|
||||||
|
resolver resolver.Resolver
|
||||||
|
|
||||||
logCtxCancel context.CancelFunc
|
logCtxCancel context.CancelFunc
|
||||||
|
|
||||||
eg errgroup.Group
|
eg errgroup.Group
|
||||||
@ -43,9 +46,10 @@ type Source struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSource initializes and returns Source data source.
|
// NewSource initializes and returns Source data source.
|
||||||
func NewSource(client *client.Client) *Source {
|
func NewSource(client *client.Client, resolver resolver.Resolver) *Source {
|
||||||
return &Source{
|
return &Source{
|
||||||
client: client,
|
client: client,
|
||||||
|
resolver: resolver,
|
||||||
LogCh: make(chan Data),
|
LogCh: make(chan Data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +86,9 @@ func (source *Source) tailNodeWithRetries(ctx context.Context, node string) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
if readErr != nil {
|
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
|
// back off a bit before retrying
|
||||||
|
|||||||
27
internal/pkg/dashboard/resolver/resolver.go
Normal file
27
internal/pkg/dashboard/resolver/resolver.go
Normal 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
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user