Fix incorrect temperature data during cluster initialization

During cluster startup, nodes were temporarily using the primary cluster
endpoint for temperature collection before cluster metadata validation
completed. This caused all nodes to show the same (incorrect) temperature
values for ~4 minutes until validation finished and per-node endpoints
were established.

Example: minipc would show delly's temperature (90°C) instead of its own
(50°C) from startup until cluster validation completed.

Root cause:
- Temperature collection started immediately at startup
- Cluster endpoint validation happened asynchronously
- Code fell back to primary endpoint when ClusterEndpoints was empty
- All nodes used same endpoint, got same temperature data

Fix: Skip temperature collection for cluster nodes until:
1. ClusterEndpoints array is populated (validation complete)
2. Node's specific endpoint is found in the cluster metadata

This ensures correct temperature data from the very first collection,
maintaining data integrity during startup. When persisted config exists,
endpoints are available immediately so no delay occurs. For new clusters,
temperature collection begins once validation completes (~30s).

Preserves Pulse's correctness guarantee: users can trust metrics
immediately after restart without waiting for "warm-up" period.
This commit is contained in:
rcourtman 2025-11-14 23:38:44 +00:00
parent 8204b54051
commit c9b4f7e88b

View file

@ -5796,17 +5796,42 @@ func (m *Monitor) pollPVEInstance(ctx context.Context, instanceName string, clie
// Determine SSH hostname to use (most robust approach):
// Prefer the resolved host for this node, with cluster overrides when available.
sshHost := modelNode.Host
foundNodeEndpoint := false
if modelNode.IsClusterMember && instanceCfg.IsCluster {
// For cluster members, wait until we have validated endpoints
// This prevents collecting wrong temperature data during initialization
if len(instanceCfg.ClusterEndpoints) == 0 {
tempCancel()
log.Debug().
Str("node", node.Node).
Str("instance", instanceCfg.Name).
Msg("Skipping temperature collection - cluster endpoints not yet validated")
continue
}
hasFingerprint := instanceCfg.Fingerprint != ""
for _, ep := range instanceCfg.ClusterEndpoints {
if strings.EqualFold(ep.NodeName, node.Node) {
if effective := clusterEndpointEffectiveURL(ep, instanceCfg.VerifySSL, hasFingerprint); effective != "" {
sshHost = effective
foundNodeEndpoint = true
}
break
}
}
// If this node is a cluster member but we didn't find its specific endpoint,
// skip temperature collection to avoid using wrong endpoint
if !foundNodeEndpoint {
tempCancel()
log.Debug().
Str("node", node.Node).
Str("instance", instanceCfg.Name).
Int("endpointCount", len(instanceCfg.ClusterEndpoints)).
Msg("Skipping temperature collection - node endpoint not found in cluster metadata")
continue
}
}
if strings.TrimSpace(sshHost) == "" {