Add resilient upload queue and client health
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using OCSentinelCli.Configuration;
|
||||
using OCSentinelCli.Models;
|
||||
using System.Text.Json;
|
||||
using OCSentinelCli.Security;
|
||||
using OCSentinelCli.Transport;
|
||||
@@ -56,37 +57,103 @@ internal static class UploadCommand
|
||||
|
||||
string reportFullPath = Path.GetFullPath(reportPath);
|
||||
string json = File.ReadAllText(reportFullPath);
|
||||
string secret = ProtectedSecretStore.LoadSecret(resolvedSecretPath);
|
||||
var client = new N8nUploadClient();
|
||||
var queue = new UploadQueue(config.UploadQueueMaxReports);
|
||||
UploadHealth health = queue.LoadHealth();
|
||||
DateTimeOffset attemptTime = DateTimeOffset.UtcNow;
|
||||
ScanResult? parsedReport = JsonSerializer.Deserialize<ScanResult>(json, JsonOptions.Default);
|
||||
|
||||
if (parsedReport is not null)
|
||||
{
|
||||
parsedReport = parsedReport with
|
||||
{
|
||||
Runtime = parsedReport.Runtime with
|
||||
{
|
||||
UploadAttempted = true
|
||||
UploadAttempted = true,
|
||||
UploadStatus = "attempting",
|
||||
QueuedReportCount = health.QueuedReportCount,
|
||||
LastSuccessfulUploadUtc = health.LastSuccessfulUploadUtc
|
||||
}
|
||||
};
|
||||
|
||||
json = JsonSerializer.Serialize(parsedReport, JsonOptions.Default);
|
||||
File.WriteAllText(reportFullPath, json);
|
||||
}
|
||||
|
||||
string secret = ProtectedSecretStore.LoadSecret(resolvedSecretPath);
|
||||
var client = new N8nUploadClient();
|
||||
var result = client.UploadJson(config.N8nWebhookUrl, Environment.MachineName, BuildMetadata.Version, json, secret, config.UploadTimeoutSeconds);
|
||||
UploadResult? deferredFailure = queue.Drain(
|
||||
client,
|
||||
config.N8nWebhookUrl,
|
||||
Environment.MachineName,
|
||||
BuildMetadata.Version,
|
||||
secret,
|
||||
config.UploadTimeoutSeconds);
|
||||
|
||||
if (!result.Success)
|
||||
if (deferredFailure is null)
|
||||
{
|
||||
Console.Error.WriteLine($"Upload failed ({result.StatusCode}): {result.Message}");
|
||||
return 1;
|
||||
var result = client.UploadJson(config.N8nWebhookUrl, Environment.MachineName, BuildMetadata.Version, json, secret, config.UploadTimeoutSeconds);
|
||||
if (result.Success)
|
||||
{
|
||||
UploadHealth successHealth = new()
|
||||
{
|
||||
LastUploadAttemptUtc = attemptTime,
|
||||
LastSuccessfulUploadUtc = DateTimeOffset.UtcNow,
|
||||
LastUploadStatus = "ok",
|
||||
LastUploadError = string.Empty,
|
||||
QueuedReportCount = queue.GetQueueDepth()
|
||||
};
|
||||
queue.SaveHealth(successHealth);
|
||||
WriteReportRuntime(reportFullPath, parsedReport, successHealth, true);
|
||||
|
||||
Console.WriteLine($"Upload succeeded ({result.StatusCode})");
|
||||
Console.WriteLine($"Nonce: {result.Nonce}");
|
||||
Console.WriteLine($"Payload SHA256: {result.PayloadSha256}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
deferredFailure = result;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Upload succeeded ({result.StatusCode})");
|
||||
Console.WriteLine($"Nonce: {result.Nonce}");
|
||||
Console.WriteLine($"Payload SHA256: {result.PayloadSha256}");
|
||||
UploadResult failure = deferredFailure ?? throw new InvalidOperationException("Upload failed without a result.");
|
||||
int queuedCount = queue.Enqueue(json);
|
||||
UploadHealth queuedHealth = new()
|
||||
{
|
||||
LastUploadAttemptUtc = attemptTime,
|
||||
LastSuccessfulUploadUtc = health.LastSuccessfulUploadUtc,
|
||||
LastUploadStatus = "queued",
|
||||
LastUploadError = failure.Message,
|
||||
QueuedReportCount = queuedCount
|
||||
};
|
||||
queue.SaveHealth(queuedHealth);
|
||||
WriteReportRuntime(reportFullPath, parsedReport, queuedHealth, true);
|
||||
|
||||
Console.WriteLine($"Upload deferred ({failure.StatusCode}): {failure.Message}");
|
||||
Console.WriteLine($"Queued reports: {queuedCount}");
|
||||
Console.WriteLine("The report will be retried automatically on the next scheduled run.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void WriteReportRuntime(string reportPath, ScanResult? report, UploadHealth health, bool attempted)
|
||||
{
|
||||
if (report is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ScanResult updated = report with
|
||||
{
|
||||
Runtime = report.Runtime with
|
||||
{
|
||||
UploadAttempted = attempted,
|
||||
UploadSucceeded = string.Equals(health.LastUploadStatus, "ok", StringComparison.Ordinal),
|
||||
UploadStatus = health.LastUploadStatus,
|
||||
QueuedReportCount = health.QueuedReportCount,
|
||||
LastSuccessfulUploadUtc = health.LastSuccessfulUploadUtc,
|
||||
LastUploadError = health.LastUploadError
|
||||
}
|
||||
};
|
||||
File.WriteAllText(reportPath, JsonSerializer.Serialize(updated, JsonOptions.Default));
|
||||
}
|
||||
|
||||
private static string ReadValue(string[] args, ref int index, string argName)
|
||||
{
|
||||
if (index + 1 >= args.Length)
|
||||
|
||||
Reference in New Issue
Block a user