Implement MongoDB-based storage for Pack Run approval, artifact, log, and state management
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Added MongoPackRunApprovalStore for managing approval states with MongoDB.
- Introduced MongoPackRunArtifactUploader for uploading and storing artifacts.
- Created MongoPackRunLogStore to handle logging of pack run events.
- Developed MongoPackRunStateStore for persisting and retrieving pack run states.
- Implemented unit tests for MongoDB stores to ensure correct functionality.
- Added MongoTaskRunnerTestContext for setting up MongoDB test environment.
- Enhanced PackRunStateFactory to correctly initialize state with gate reasons.
This commit is contained in:
master
2025-11-07 10:01:35 +02:00
parent e5ffcd6535
commit a1ce3f74fa
122 changed files with 8730 additions and 914 deletions

View File

@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using StellaOps.Scanner.Worker.Processing;
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using StellaOps.Scanner.Surface.Secrets;
using StellaOps.Scanner.Worker.Processing;
namespace StellaOps.Scanner.Worker.Diagnostics;
@@ -9,11 +10,18 @@ public sealed class ScannerWorkerMetrics
{
private readonly Histogram<double> _queueLatencyMs;
private readonly Histogram<double> _jobDurationMs;
private readonly Histogram<double> _stageDurationMs;
private readonly Histogram<double> _stageDurationMs;
private readonly Counter<long> _jobsCompleted;
private readonly Counter<long> _jobsFailed;
private readonly Counter<long> _languageCacheHits;
private readonly Counter<long> _languageCacheMisses;
private readonly Counter<long> _registrySecretRequests;
private readonly Histogram<double> _registrySecretTtlSeconds;
private readonly Counter<long> _surfaceManifestsPublished;
private readonly Counter<long> _surfaceManifestSkipped;
private readonly Counter<long> _surfaceManifestFailures;
private readonly Counter<long> _surfacePayloadPersisted;
private readonly Histogram<double> _surfaceManifestPublishDurationMs;
public ScannerWorkerMetrics()
{
@@ -41,6 +49,29 @@ public sealed class ScannerWorkerMetrics
_languageCacheMisses = ScannerWorkerInstrumentation.Meter.CreateCounter<long>(
"scanner_worker_language_cache_misses_total",
description: "Number of language analyzer cache misses encountered by the worker.");
_registrySecretRequests = ScannerWorkerInstrumentation.Meter.CreateCounter<long>(
"scanner_worker_registry_secret_requests_total",
description: "Number of registry secret resolution attempts performed by the worker.");
_registrySecretTtlSeconds = ScannerWorkerInstrumentation.Meter.CreateHistogram<double>(
"scanner_worker_registry_secret_ttl_seconds",
unit: "s",
description: "Time-to-live in seconds for resolved registry secrets (earliest expiration).");
_surfaceManifestsPublished = ScannerWorkerInstrumentation.Meter.CreateCounter<long>(
"scanner_worker_surface_manifests_published_total",
description: "Number of surface manifests successfully published by the worker.");
_surfaceManifestSkipped = ScannerWorkerInstrumentation.Meter.CreateCounter<long>(
"scanner_worker_surface_manifests_skipped_total",
description: "Number of surface manifest publish attempts skipped due to missing payloads.");
_surfaceManifestFailures = ScannerWorkerInstrumentation.Meter.CreateCounter<long>(
"scanner_worker_surface_manifests_failed_total",
description: "Number of surface manifest publish attempts that failed.");
_surfacePayloadPersisted = ScannerWorkerInstrumentation.Meter.CreateCounter<long>(
"scanner_worker_surface_payload_persisted_total",
description: "Number of surface payload artefacts persisted to the local cache.");
_surfaceManifestPublishDurationMs = ScannerWorkerInstrumentation.Meter.CreateHistogram<double>(
"scanner_worker_surface_manifest_publish_duration_ms",
unit: "ms",
description: "Duration in milliseconds to persist and publish surface manifests.");
}
public void RecordQueueLatency(ScanJobContext context, TimeSpan latency)
@@ -63,15 +94,15 @@ public sealed class ScannerWorkerMetrics
_jobDurationMs.Record(duration.TotalMilliseconds, CreateTags(context));
}
public void RecordStageDuration(ScanJobContext context, string stage, TimeSpan duration)
{
if (duration <= TimeSpan.Zero)
{
return;
}
_stageDurationMs.Record(duration.TotalMilliseconds, CreateTags(context, stage: stage));
}
public void RecordStageDuration(ScanJobContext context, string stage, TimeSpan duration)
{
if (duration <= TimeSpan.Zero)
{
return;
}
_stageDurationMs.Record(duration.TotalMilliseconds, CreateTags(context, stage: stage));
}
public void IncrementJobCompleted(ScanJobContext context)
{
@@ -93,9 +124,130 @@ public sealed class ScannerWorkerMetrics
_languageCacheMisses.Add(1, CreateTags(context, analyzerId: analyzerId));
}
private static KeyValuePair<string, object?>[] CreateTags(ScanJobContext context, string? stage = null, string? failureReason = null, string? analyzerId = null)
public void RecordRegistrySecretResolved(
ScanJobContext context,
string secretName,
RegistryAccessSecret secret,
TimeProvider timeProvider)
{
var tags = new List<KeyValuePair<string, object?>>(stage is null ? 5 : 6)
var tags = CreateTags(
context,
secretName: secretName,
secretResult: "resolved",
secretEntryCount: secret.Entries.Count);
_registrySecretRequests.Add(1, tags);
if (ComputeTtlSeconds(secret, timeProvider) is double ttlSeconds)
{
_registrySecretTtlSeconds.Record(ttlSeconds, tags);
}
}
public void RecordRegistrySecretMissing(ScanJobContext context, string secretName)
{
var tags = CreateTags(context, secretName: secretName, secretResult: "missing");
_registrySecretRequests.Add(1, tags);
}
public void RecordRegistrySecretFailure(ScanJobContext context, string secretName)
{
var tags = CreateTags(context, secretName: secretName, secretResult: "failure");
_registrySecretRequests.Add(1, tags);
}
public void RecordSurfaceManifestPublished(ScanJobContext context, int payloadCount, TimeSpan duration)
{
if (payloadCount < 0)
{
payloadCount = 0;
}
var tags = CreateTags(
context,
surfaceAction: "manifest",
surfaceResult: "published",
surfacePayloadCount: payloadCount);
_surfaceManifestsPublished.Add(1, tags);
if (duration > TimeSpan.Zero)
{
_surfaceManifestPublishDurationMs.Record(duration.TotalMilliseconds, tags);
}
}
public void RecordSurfaceManifestSkipped(ScanJobContext context)
{
var tags = CreateTags(context, surfaceAction: "manifest", surfaceResult: "skipped");
_surfaceManifestSkipped.Add(1, tags);
}
public void RecordSurfaceManifestFailed(ScanJobContext context, string failureReason)
{
var tags = CreateTags(
context,
surfaceAction: "manifest",
surfaceResult: "failed",
failureReason: failureReason);
_surfaceManifestFailures.Add(1, tags);
}
public void RecordSurfacePayloadPersisted(ScanJobContext context, string surfaceKind)
{
var normalizedKind = string.IsNullOrWhiteSpace(surfaceKind)
? "unknown"
: surfaceKind.Trim().ToLowerInvariant();
var tags = CreateTags(
context,
surfaceAction: "payload",
surfaceKind: normalizedKind,
surfaceResult: "cached");
_surfacePayloadPersisted.Add(1, tags);
}
private static double? ComputeTtlSeconds(RegistryAccessSecret secret, TimeProvider timeProvider)
{
DateTimeOffset? earliest = null;
foreach (var entry in secret.Entries)
{
if (entry.ExpiresAt is null)
{
continue;
}
if (earliest is null || entry.ExpiresAt < earliest)
{
earliest = entry.ExpiresAt;
}
}
if (earliest is null)
{
return null;
}
var now = timeProvider.GetUtcNow();
var ttl = (earliest.Value - now).TotalSeconds;
return ttl < 0 ? 0 : ttl;
}
private static KeyValuePair<string, object?>[] CreateTags(
ScanJobContext context,
string? stage = null,
string? failureReason = null,
string? analyzerId = null,
string? secretName = null,
string? secretResult = null,
int? secretEntryCount = null,
string? surfaceAction = null,
string? surfaceKind = null,
string? surfaceResult = null,
int? surfacePayloadCount = null)
{
var tags = new List<KeyValuePair<string, object?>>(8)
{
new("job.id", context.JobId),
new("scan.id", context.ScanId),
@@ -113,10 +265,10 @@ public sealed class ScannerWorkerMetrics
}
if (!string.IsNullOrWhiteSpace(stage))
{
tags.Add(new KeyValuePair<string, object?>("stage", stage));
}
{
tags.Add(new KeyValuePair<string, object?>("stage", stage));
}
if (!string.IsNullOrWhiteSpace(failureReason))
{
tags.Add(new KeyValuePair<string, object?>("reason", failureReason));
@@ -127,6 +279,41 @@ public sealed class ScannerWorkerMetrics
tags.Add(new KeyValuePair<string, object?>("analyzer.id", analyzerId));
}
if (!string.IsNullOrWhiteSpace(secretName))
{
tags.Add(new KeyValuePair<string, object?>("secret.name", secretName));
}
if (!string.IsNullOrWhiteSpace(secretResult))
{
tags.Add(new KeyValuePair<string, object?>("secret.result", secretResult));
}
if (secretEntryCount is not null)
{
tags.Add(new KeyValuePair<string, object?>("secret.entries", secretEntryCount.Value));
}
if (!string.IsNullOrWhiteSpace(surfaceAction))
{
tags.Add(new KeyValuePair<string, object?>("surface.action", surfaceAction));
}
if (!string.IsNullOrWhiteSpace(surfaceKind))
{
tags.Add(new KeyValuePair<string, object?>("surface.kind", surfaceKind));
}
if (!string.IsNullOrWhiteSpace(surfaceResult))
{
tags.Add(new KeyValuePair<string, object?>("surface.result", surfaceResult));
}
if (surfacePayloadCount is not null)
{
tags.Add(new KeyValuePair<string, object?>("surface.payload_count", surfacePayloadCount.Value));
}
return tags.ToArray();
}
}