fix: verify apply duplicate and capture runtime failures
This commit is contained in:
@@ -218,6 +218,8 @@ public sealed class EntryPoint : IExtensionApplication
|
|||||||
}
|
}
|
||||||
catch (System.Exception exception)
|
catch (System.Exception exception)
|
||||||
{
|
{
|
||||||
|
if (string.Equals(commandName, "PXP-OA", StringComparison.OrdinalIgnoreCase) && string.Equals(Environment.GetEnvironmentVariable("P_REC_ANNOT_RUNTIME_MODE"), "Apply", StringComparison.OrdinalIgnoreCase))
|
||||||
|
ScriptDrivenFirstApplyAutomation.RecordApplyFailure(exception);
|
||||||
RuntimeDiagnostics.Write("CommandFailed", "ERROR", operationId, commandName, exception.Message, exception, document);
|
RuntimeDiagnostics.Write("CommandFailed", "ERROR", operationId, commandName, exception.Message, exception, document);
|
||||||
WriteUsage(operationId, commandName, exception.Message, document, null, stopwatch.ElapsedMilliseconds, exception);
|
WriteUsage(operationId, commandName, exception.Message, document, null, stopwatch.ElapsedMilliseconds, exception);
|
||||||
WriteEditor(document, $"\nP_Rec_Annot: безопасная диагностика завершилась ошибкой: {exception.Message}");
|
WriteEditor(document, $"\nP_Rec_Annot: безопасная диагностика завершилась ошибкой: {exception.Message}");
|
||||||
|
|||||||
@@ -132,8 +132,9 @@ internal sealed class FirstLimitedPipeAnnotationWriter
|
|||||||
var property = annotation.DynamicBlockReferencePropertyCollection
|
var property = annotation.DynamicBlockReferencePropertyCollection
|
||||||
.Cast<DynamicBlockReferenceProperty>()
|
.Cast<DynamicBlockReferenceProperty>()
|
||||||
.SingleOrDefault(value => string.Equals(value.PropertyName, "V", StringComparison.Ordinal));
|
.SingleOrDefault(value => string.Equals(value.PropertyName, "V", StringComparison.Ordinal));
|
||||||
if (property is null || !property.GetAllowedValues().OfType<string>().Contains(visibilityState, StringComparer.Ordinal))
|
var allowed = property?.GetAllowedValues().OfType<string>().ToArray() ?? [];
|
||||||
throw new InvalidOperationException("ANNOTATION_VISIBILITY_STATE_UNSUPPORTED");
|
if (property is null || !allowed.Contains(visibilityState, StringComparer.Ordinal))
|
||||||
|
throw new InvalidOperationException($"ANNOTATION_VISIBILITY_STATE_UNSUPPORTED:Allowed={string.Join('|', allowed)}");
|
||||||
property.Value = visibilityState;
|
property.Value = visibilityState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-4
@@ -34,6 +34,7 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
private static string? selectedTargetSnapshotId;
|
private static string? selectedTargetSnapshotId;
|
||||||
private static bool prepared;
|
private static bool prepared;
|
||||||
private static bool terminal;
|
private static bool terminal;
|
||||||
|
private static string? applyExceptionText;
|
||||||
|
|
||||||
internal static void Prepare()
|
internal static void Prepare()
|
||||||
{
|
{
|
||||||
@@ -97,7 +98,15 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
{
|
{
|
||||||
if (!prepared || terminal || runs.Count == 0) return;
|
if (!prepared || terminal || runs.Count == 0) return;
|
||||||
var last = runs[^1];
|
var last = runs[^1];
|
||||||
runs[^1] = last with { ApplyOutcome = outcome.ToString() };
|
runs[^1] = outcome == PipeAnnotationWriteOutcome.AlreadyExists
|
||||||
|
? last with { ApplyOutcome = outcome.ToString(), Create = 0, Skip = 1 }
|
||||||
|
: last with { ApplyOutcome = outcome.ToString() };
|
||||||
|
}
|
||||||
|
internal static void RecordApplyFailure(System.Exception exception)
|
||||||
|
{
|
||||||
|
if (!prepared || terminal) return;
|
||||||
|
applyExceptionText = exception.ToString();
|
||||||
|
Write(false, "AUTOMATION_APPLY_COMMAND_FAILED");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool TryGetPreparedContext(Document? commandDocument, out PlantRuntimeContext context)
|
internal static bool TryGetPreparedContext(Document? commandDocument, out PlantRuntimeContext context)
|
||||||
@@ -121,9 +130,9 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
if (runs.Any(x => !x.WillStart || !x.Ended || x.Failed || x.Cancelled)) throw new InvalidOperationException("AUTOMATION_PXP_OA_NOT_ENDED");
|
if (runs.Any(x => !x.WillStart || !x.Ended || x.Failed || x.Cancelled)) throw new InvalidOperationException("AUTOMATION_PXP_OA_NOT_ENDED");
|
||||||
if (runs.Any(x => string.IsNullOrWhiteSpace(x.PlanId))) throw new InvalidOperationException("PXP_OA_PLAN_RESULT_NOT_CAPTURED");
|
if (runs.Any(x => string.IsNullOrWhiteSpace(x.PlanId))) throw new InvalidOperationException("PXP_OA_PLAN_RESULT_NOT_CAPTURED");
|
||||||
if (!string.Equals(runs[0].PlanId, runs[1].PlanId, StringComparison.Ordinal)) throw new InvalidOperationException("AUTOMATION_PLAN_IDS_DIFFER");
|
if (!string.Equals(runs[0].PlanId, runs[1].PlanId, StringComparison.Ordinal)) throw new InvalidOperationException("AUTOMATION_PLAN_IDS_DIFFER");
|
||||||
if (runs.Any(x => x.Create != 1)) throw new InvalidOperationException("AUTOMATION_PREVIEW_CREATE_NOT_ONE");
|
|
||||||
if (runs[0].Candidates.Count == 0) throw new InvalidOperationException("AUTOMATION_NO_ELIGIBLE_CANDIDATE");
|
|
||||||
var apply = string.Equals(config!.Phase, "Apply", StringComparison.OrdinalIgnoreCase);
|
var apply = string.Equals(config!.Phase, "Apply", StringComparison.OrdinalIgnoreCase);
|
||||||
|
if ((!apply && runs.Any(x => x.Create != 1)) || (apply && (runs[0].Create != 1 || runs[1].Create != 0))) throw new InvalidOperationException("AUTOMATION_CREATE_COUNTS_INVALID");
|
||||||
|
if (runs[0].Candidates.Count == 0) throw new InvalidOperationException("AUTOMATION_NO_ELIGIBLE_CANDIDATE");
|
||||||
if (apply)
|
if (apply)
|
||||||
{
|
{
|
||||||
if (runs[0].PermitAccepted != true) throw new InvalidOperationException("AUTOMATION_APPLY_PERMIT_NOT_ACCEPTED");
|
if (runs[0].PermitAccepted != true) throw new InvalidOperationException("AUTOMATION_APPLY_PERMIT_NOT_ACCEPTED");
|
||||||
@@ -222,7 +231,7 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
var first = runs.ElementAtOrDefault(0); var second = runs.ElementAtOrDefault(1);
|
var first = runs.ElementAtOrDefault(0); var second = runs.ElementAtOrDefault(1);
|
||||||
var apply = string.Equals(config.Phase, "Apply", StringComparison.OrdinalIgnoreCase);
|
var apply = string.Equals(config.Phase, "Apply", StringComparison.OrdinalIgnoreCase);
|
||||||
var afterUndo = apply && terminal ? Counts(database!) : null;
|
var afterUndo = apply && terminal ? Counts(database!) : null;
|
||||||
var evidence = new FirstApplyAutomationEvidence { Phase = config.Phase, Completed = completed, Prepared = prepared, FunctionalEvidenceCompleted = completed, ErrorCode = error, DbmodBefore = dbmodBaseline, DbmodAfterCommand1 = first?.DbmodAfter, DbmodAfterCommand2 = second?.DbmodAfter, DbmodAfterUndo = apply && terminal ? Dbmod() : null, Baseline = baseline, AfterCommand1 = first?.After, AfterCommand2 = second?.After, AfterUndo = afterUndo, CompletedCommands = runs.Where(x => x.Ended).Select(x => "PXP-OA").Concat(apply && commandEvents.Any(x => string.Equals(x, "Ended:UNDO", StringComparison.OrdinalIgnoreCase)) ? ["UNDO"] : []).ToArray(), PlanIdRun1 = first?.PlanId, PlanIdRun2 = second?.PlanId, PlanIdsEqual = first is not null && second is not null ? string.Equals(first.PlanId, second.PlanId, StringComparison.Ordinal) : null, EligibleCandidateCount = first?.Candidates.Count, SelectedCandidateHash = first?.Candidates.OrderBy(x => x, StringComparer.Ordinal).FirstOrDefault(), ViewHash = first?.View, CreateRun1 = first?.Create, CreateRun2 = second?.Create, SkipRun2 = second?.Skip, ApplyOutcomeRun1 = first?.ApplyOutcome, ApplyOutcomeRun2 = second?.ApplyOutcome, PermitAccepted = first?.PermitAccepted, CommandsCompleted = runs.Count == 2 && runs.All(x => x.Ended) && (!apply || commandEvents.Any(x => string.Equals(x, "Ended:UNDO", StringComparison.OrdinalIgnoreCase))), InspectorsCompleted = baseline is not null && first?.After is not null && second?.After is not null && (!apply || afterUndo is not null), EvidenceFlushed = true, CurrentState = terminal ? (completed ? "Completed" : "Failed") : "Prepared", StateTransitions = transitions, PxpOaDiscovery = discovery, Sentinels = sentinels, CommandEvents = commandEvents, CommandCorrelations = correlations };
|
var evidence = new FirstApplyAutomationEvidence { Phase = config.Phase, Completed = completed, Prepared = prepared, FunctionalEvidenceCompleted = completed, ErrorCode = error, DbmodBefore = dbmodBaseline, DbmodAfterCommand1 = first?.DbmodAfter, DbmodAfterCommand2 = second?.DbmodAfter, DbmodAfterUndo = apply && terminal ? Dbmod() : null, Baseline = baseline, AfterCommand1 = first?.After, AfterCommand2 = second?.After, AfterUndo = afterUndo, CompletedCommands = runs.Where(x => x.Ended).Select(x => "PXP-OA").Concat(apply && commandEvents.Any(x => string.Equals(x, "Ended:UNDO", StringComparison.OrdinalIgnoreCase)) ? ["UNDO"] : []).ToArray(), PlanIdRun1 = first?.PlanId, PlanIdRun2 = second?.PlanId, PlanIdsEqual = first is not null && second is not null ? string.Equals(first.PlanId, second.PlanId, StringComparison.Ordinal) : null, EligibleCandidateCount = first?.Candidates.Count, SelectedCandidateHash = first?.Candidates.OrderBy(x => x, StringComparer.Ordinal).FirstOrDefault(), ViewHash = first?.View, CreateRun1 = first?.Create, CreateRun2 = second?.Create, SkipRun2 = second?.Skip, ApplyOutcomeRun1 = first?.ApplyOutcome, ApplyOutcomeRun2 = second?.ApplyOutcome, PermitAccepted = first?.PermitAccepted, CommandsCompleted = runs.Count == 2 && runs.All(x => x.Ended) && (!apply || commandEvents.Any(x => string.Equals(x, "Ended:UNDO", StringComparison.OrdinalIgnoreCase))), InspectorsCompleted = baseline is not null && first?.After is not null && second?.After is not null && (!apply || afterUndo is not null), EvidenceFlushed = true, ExceptionText = applyExceptionText, CurrentState = terminal ? (completed ? "Completed" : "Failed") : "Prepared", StateTransitions = transitions, PxpOaDiscovery = discovery, Sentinels = sentinels, CommandEvents = commandEvents, CommandCorrelations = correlations };
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(config.EvidencePath)!);
|
Directory.CreateDirectory(Path.GetDirectoryName(config.EvidencePath)!);
|
||||||
var temp = config.EvidencePath + ".tmp"; File.WriteAllText(temp, JsonSerializer.Serialize(evidence)); File.Move(temp, config.EvidencePath, true);
|
var temp = config.EvidencePath + ".tmp"; File.WriteAllText(temp, JsonSerializer.Serialize(evidence)); File.Move(temp, config.EvidencePath, true);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user