mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
added agent_shutdown event
This commit is contained in:
+2
-11
@@ -589,17 +589,8 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
// Run shutdown analyzers before final event (captures end-state for delta detection)
|
||||
RunShutdownAnalyzers();
|
||||
|
||||
// Emit final event
|
||||
EmitEvent(new EnrollmentEvent
|
||||
{
|
||||
SessionId = _configuration.SessionId,
|
||||
TenantId = _configuration.TenantId,
|
||||
EventType = "agent_stopped",
|
||||
Severity = EventSeverity.Info,
|
||||
Source = "Agent",
|
||||
Phase = _lastPhase ?? EnrollmentPhase.Unknown,
|
||||
Message = "Autopilot Monitor Agent stopped"
|
||||
});
|
||||
// Emit agent_shutdown as counterpart to agent_started
|
||||
EmitShutdownEvent("manual_stop", "Autopilot Monitor Agent stopped");
|
||||
|
||||
// Final upload attempt
|
||||
UploadEventsAsync().Wait(TimeSpan.FromSeconds(10));
|
||||
|
||||
+109
@@ -191,6 +191,38 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emits an agent_shutdown event as the counterpart to agent_started.
|
||||
/// Called at every controlled exit point so the timeline shows why and how the agent terminated.
|
||||
/// </summary>
|
||||
private void EmitShutdownEvent(string reason, string message, Dictionary<string, object> extraData = null)
|
||||
{
|
||||
var data = new Dictionary<string, object>
|
||||
{
|
||||
{ "reason", reason },
|
||||
{ "agentVersion", _agentVersion },
|
||||
{ "uptimeMinutes", Math.Round((DateTime.UtcNow - _agentStartTimeUtc).TotalMinutes, 1) }
|
||||
};
|
||||
if (extraData != null)
|
||||
{
|
||||
foreach (var kvp in extraData)
|
||||
data[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
EmitEvent(new EnrollmentEvent
|
||||
{
|
||||
SessionId = _configuration.SessionId,
|
||||
TenantId = _configuration.TenantId,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
EventType = "agent_shutdown",
|
||||
Severity = EventSeverity.Info,
|
||||
Source = "Agent",
|
||||
Phase = _lastPhase ?? EnrollmentPhase.Unknown,
|
||||
Message = message,
|
||||
Data = data
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when new events are available in the spool (FileSystemWatcher detected new files)
|
||||
/// Uses debouncing to batch events before uploading
|
||||
@@ -264,6 +296,34 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
_uploadTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
_debounceTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
|
||||
// Emit agent_shutdown event and try to upload it directly (we hold the upload
|
||||
// semaphore so a normal UploadEventsAsync would skip — send inline instead)
|
||||
EmitShutdownEvent(
|
||||
"remote_kill_signal",
|
||||
"Agent terminated by remote kill signal from administrator",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "pendingEvents", _spool.GetCount() },
|
||||
{ "selfDestruct", true }
|
||||
});
|
||||
try
|
||||
{
|
||||
var shutdownEvents = _spool.GetBatch(_configuration.MaxBatchSize);
|
||||
if (shutdownEvents.Count > 0)
|
||||
{
|
||||
await _apiClient.IngestEventsAsync(new IngestEventsRequest
|
||||
{
|
||||
SessionId = _configuration.SessionId,
|
||||
TenantId = _configuration.TenantId,
|
||||
Events = shutdownEvents
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warning($"Failed to upload shutdown event before kill: {ex.Message}");
|
||||
}
|
||||
|
||||
// Force self-destruct regardless of current config, respecting KeepLogFile from tenant config
|
||||
_configuration.SelfDestructOnComplete = true;
|
||||
_cleanupService.ExecuteSelfDestruct();
|
||||
@@ -376,6 +436,17 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
_logger.Error($"=== AGENT SHUTDOWN: {_consecutiveAuthFailures} consecutive authentication failures (401/403). " +
|
||||
"The device is not authorized to send data to Autopilot Monitor. " +
|
||||
"Check client certificate and Autopilot device validation in your tenant configuration. ===");
|
||||
|
||||
// Emit shutdown event (stays in spool for diagnostics — upload not possible due to auth failure)
|
||||
EmitShutdownEvent(
|
||||
"auth_failure",
|
||||
$"Agent terminated: {_consecutiveAuthFailures} consecutive authentication failures exceeded threshold ({_configuration.MaxAuthFailures})",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "consecutiveFailures", _consecutiveAuthFailures },
|
||||
{ "shutdownTrigger", "max_attempts" },
|
||||
{ "maxAuthFailures", _configuration.MaxAuthFailures }
|
||||
});
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
@@ -389,6 +460,18 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
$"(timeout: {_configuration.AuthFailureTimeoutMinutes} min). " +
|
||||
"The device is not authorized to send data to Autopilot Monitor. " +
|
||||
"Check client certificate and Autopilot device validation in your tenant configuration. ===");
|
||||
|
||||
// Emit shutdown event (stays in spool for diagnostics — upload not possible due to auth failure)
|
||||
EmitShutdownEvent(
|
||||
"auth_failure",
|
||||
$"Agent terminated: authentication failures persisted for {elapsed.TotalMinutes:F0} minutes (timeout: {_configuration.AuthFailureTimeoutMinutes} min)",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "consecutiveFailures", _consecutiveAuthFailures },
|
||||
{ "shutdownTrigger", "timeout" },
|
||||
{ "authFailureTimeoutMinutes", _configuration.AuthFailureTimeoutMinutes },
|
||||
{ "elapsedMinutes", Math.Round(elapsed.TotalMinutes, 1) }
|
||||
});
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
@@ -547,6 +630,18 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
Process.Start(psi);
|
||||
}
|
||||
|
||||
// Emit agent_shutdown as counterpart to agent_started
|
||||
EmitShutdownEvent(
|
||||
enrollmentSucceeded ? "enrollment_complete" : "enrollment_failed",
|
||||
$"Agent shutting down after enrollment {(enrollmentSucceeded ? "completion" : "failure")}",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "enrollmentSucceeded", enrollmentSucceeded },
|
||||
{ "selfDestruct", _configuration.SelfDestructOnComplete },
|
||||
{ "reboot", _configuration.RebootOnComplete }
|
||||
});
|
||||
await UploadEventsAsync();
|
||||
|
||||
_logger.Info("Shutdown sequence complete. Agent will now exit.");
|
||||
|
||||
// Exit the application
|
||||
@@ -603,6 +698,20 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
|
||||
// No DeleteSessionId, no WriteEnrollmentCompleteMarker, no self-destruct.
|
||||
// The Windows Autopilot shutdown will power off the device;
|
||||
// the Scheduled Task restarts the agent on the user's first boot.
|
||||
// Emit agent_shutdown as counterpart to agent_started
|
||||
EmitShutdownEvent(
|
||||
"whiteglove_complete",
|
||||
"Agent shutting down after WhiteGlove Part 1 — no cleanup, session preserved for Part 2 on next boot",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "sessionPreserved", true },
|
||||
{ "selfDestruct", false },
|
||||
{ "reboot", false },
|
||||
{ "cleanup", false },
|
||||
{ "willResumeOnNextBoot", true }
|
||||
});
|
||||
await UploadEventsAsync();
|
||||
|
||||
_logger.Info("WhiteGlove graceful shutdown complete. Agent exiting.");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
483
|
||||
485
|
||||
|
||||
Reference in New Issue
Block a user