fix(deployment): instance delete fully removes the record

Deleting an instance only undeployed it from the site and set the state
to NotDeployed, leaving an orphan record that could never be removed —
the state-transition matrix rejected delete from NotDeployed.

Delete now removes the instance record entirely (deployment history,
snapshot, attribute/alarm overrides, and connection bindings go with
it), and is permitted from any state.
This commit is contained in:
Joseph Doherty
2026-05-15 12:05:13 -04:00
parent 17e24ddd20
commit 1d5465f31c
6 changed files with 45 additions and 17 deletions

View File

@@ -282,7 +282,9 @@ public class DeploymentService
}
/// <summary>
/// WP-6: Delete an instance. Stops actor, removes config. S&amp;F NOT cleared.
/// WP-6: Delete an instance. Stops the site actor, removes site config, and
/// removes the central instance record (deployment history, snapshot,
/// overrides, and connection bindings go with it). S&amp;F NOT cleared.
/// Delete fails if site unreachable (30s timeout via CommunicationOptions).
/// </summary>
public async Task<Result<InstanceLifecycleResponse>> DeleteInstanceAsync(
@@ -309,12 +311,10 @@ public class DeploymentService
if (response.Success)
{
// Remove deployed snapshot
await _repository.DeleteDeployedSnapshotAsync(instanceId, cancellationToken);
// Set state to NotDeployed (or the instance record could be deleted entirely by higher layers)
instance.State = InstanceState.NotDeployed;
await _repository.UpdateInstanceAsync(instance, cancellationToken);
// Delete means delete: remove the instance record entirely.
// Deployment records, snapshot, overrides, and connection bindings
// are removed with it (see repository implementation).
await _repository.DeleteInstanceAsync(instanceId, cancellationToken);
await _repository.SaveChangesAsync(cancellationToken);
}

View File

@@ -7,11 +7,12 @@ namespace ScadaLink.DeploymentManager;
///
/// State | Deploy | Disable | Enable | Delete
/// ----------|--------|---------|--------|-------
/// NotDeploy | OK | NO | NO | NO
/// NotDeploy | OK | NO | NO | OK
/// Enabled | OK | OK | NO | OK
/// Disabled | OK* | NO | OK | OK
///
/// * Deploy on a Disabled instance also enables it.
/// Delete removes the instance record entirely; it is valid from any state.
/// </summary>
public static class StateTransitionValidator
{
@@ -25,7 +26,7 @@ public static class StateTransitionValidator
currentState == InstanceState.Disabled;
public static bool CanDelete(InstanceState currentState) =>
currentState is InstanceState.Enabled or InstanceState.Disabled;
currentState is InstanceState.NotDeployed or InstanceState.Enabled or InstanceState.Disabled;
/// <summary>
/// Returns a human-readable error message if the transition is invalid, or null if valid.