mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-08-19 17:36:17 +00:00
fix: retry re-auth during cluster join and always reconnect to node A
- Extract re-auth retry into ReauthenticateWithRetry helper (fixes cognitive complexity warning) - Retry auth up to 10x with 3s delay — node B's auth services need time to restart after joining the cluster - Wrap join in try/finally so the test always reconnects to node A, even if the join fails Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Management.Automation;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Threading;
|
||||
using PSProxmoxVE.Core.Authentication;
|
||||
using PSProxmoxVE.Core.Exceptions;
|
||||
using PSProxmoxVE.Core.Models.Vms;
|
||||
@@ -57,6 +58,9 @@ namespace PSProxmoxVE.Cmdlets.Cluster
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the join task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
private const int ReauthMaxAttempts = 10;
|
||||
private const int ReauthDelayMs = 3000;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"this node to cluster via '{Hostname}'", "Join cluster"))
|
||||
@@ -81,22 +85,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
|
||||
|
||||
if (Wait.IsPresent && !string.IsNullOrEmpty(upid))
|
||||
{
|
||||
var nodeName = upid.Split(':').Length > 1 ? upid.Split(':')[1] : session.Hostname;
|
||||
var taskService = new TaskService();
|
||||
try
|
||||
{
|
||||
task = taskService.WaitForTask(session, nodeName, upid);
|
||||
}
|
||||
catch (PveApiException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
// Cluster join restarts auth services on this node, invalidating
|
||||
// the ticket mid-poll. Re-authenticate and retry.
|
||||
WriteVerbose("Session expired during join — re-authenticating to poll task status...");
|
||||
var newSession = PveAuthenticator.AuthenticateWithCredentials(
|
||||
session.Hostname, session.Port, session.SkipCertificateCheck,
|
||||
"root@pam", plainPassword);
|
||||
task = taskService.WaitForTask(newSession, nodeName, upid);
|
||||
}
|
||||
task = WaitForJoinTask(session, upid, plainPassword);
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
@@ -107,5 +96,55 @@ namespace PSProxmoxVE.Cmdlets.Cluster
|
||||
Marshal.ZeroFreeGlobalAllocUnicode(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the join task to complete. If the ticket is invalidated mid-poll
|
||||
/// (cluster join restarts auth services), re-authenticates with retry and
|
||||
/// continues waiting.
|
||||
/// </summary>
|
||||
private PveTask WaitForJoinTask(PveSession session, string upid, string plainPassword)
|
||||
{
|
||||
var nodeName = upid.Split(':').Length > 1 ? upid.Split(':')[1] : session.Hostname;
|
||||
var taskService = new TaskService();
|
||||
|
||||
try
|
||||
{
|
||||
return taskService.WaitForTask(session, nodeName, upid);
|
||||
}
|
||||
catch (PveApiException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
WriteVerbose("Session expired during join — waiting for auth services to restart...");
|
||||
var newSession = ReauthenticateWithRetry(session, plainPassword);
|
||||
return taskService.WaitForTask(newSession, nodeName, upid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retries authentication against this node until auth services come back online
|
||||
/// after the cluster join restarts them.
|
||||
/// </summary>
|
||||
private PveSession ReauthenticateWithRetry(PveSession session, string plainPassword)
|
||||
{
|
||||
for (int attempt = 0; attempt < ReauthMaxAttempts; attempt++)
|
||||
{
|
||||
Thread.Sleep(ReauthDelayMs);
|
||||
try
|
||||
{
|
||||
return PveAuthenticator.AuthenticateWithCredentials(
|
||||
session.Hostname, session.Port, session.SkipCertificateCheck,
|
||||
"root@pam", plainPassword);
|
||||
}
|
||||
catch (PveApiException retryEx) when (
|
||||
retryEx.StatusCode == HttpStatusCode.Unauthorized ||
|
||||
retryEx.StatusCode == HttpStatusCode.ServiceUnavailable)
|
||||
{
|
||||
WriteVerbose($"Re-auth attempt {attempt + 1}/{ReauthMaxAttempts} failed, retrying...");
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
"Unable to re-authenticate after cluster join. " +
|
||||
"Auth services on this node may still be restarting.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user