Console: survive a launcher-dropped connection during product installs

The launcher drops RPC sessions idle >30s (easily hit while the operator sits
in the install dialogs), but a dropped socket still reports Connected until an
I/O fails. InstallProductWorker then skipped its reopen, streamed the archive
on the fresh out-of-band connection, and died on the first progress poll —
and the install-completed handler registered launch entries without checking
e.Error, so AddApp's disconnected-guard exception crashed the whole console.

- PodInfo.EnsureConnectionAlive: probe an "open" connection with a Ping and
  reconnect when it is dead; used by the install and uninstall workers.
- SiteManagement.PodInfo_InstallProductCompleted: register launch entries only
  on success, and surface registration failures as the row's Install Failed
  state instead of an unhandled exception.
- Regression test pins the premise + recovery against vPOD's launcher server
  (stale socket reports open, Ping exposes it, reconnect restores service).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-09 21:40:14 -05:00
co-authored by Claude Fable 5
parent 1daaf4af78
commit e0a3c72370
3 changed files with 74 additions and 11 deletions
+18 -3
View File
@@ -1138,12 +1138,27 @@ public class SiteManagement : Form
{
Tuple<PodData, Guid> tuple = e.UserState as Tuple<PodData, Guid>;
PodData a = tuple.A;
foreach (LaunchData appData in BuildLaunchData(tuple.B, a))
Exception error = e.Error;
// Register the launch entries only when the transfer succeeded, and never
// let a pod that dropped mid-install take the console down (AddApp throws
// when the connection is gone).
if (error == null)
{
a.Conn.AddApp(appData);
try
{
foreach (LaunchData appData in BuildLaunchData(tuple.B, a))
{
a.Conn.AddApp(appData);
}
}
catch (Exception ex)
{
Program.LogException(ex, "Registering launch entries failed.");
error = ex;
}
}
a.OperationInProgress = false;
a.Error = e.Error;
a.Error = error;
dgvPods.InvalidateCell(colOperationProgress.Index, mPods.IndexOf(a));
}