up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Reachability Corpus Validation / validate-corpus (push) Has been cancelled
Reachability Corpus Validation / validate-ground-truths (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Reachability Corpus Validation / determinism-check (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-14 15:50:38 +02:00
parent f1a39c4ce3
commit 233873f620
249 changed files with 29746 additions and 154 deletions

View File

@@ -0,0 +1,42 @@
using StellaOps.Signals.Models;
namespace StellaOps.Signals.Persistence;
/// <summary>
/// Repository for persisting and querying proc snapshot documents.
/// </summary>
public interface IProcSnapshotRepository
{
/// <summary>
/// Upsert a proc snapshot document.
/// </summary>
Task<ProcSnapshotDocument> UpsertAsync(ProcSnapshotDocument document, CancellationToken cancellationToken);
/// <summary>
/// Get a proc snapshot by ID.
/// </summary>
Task<ProcSnapshotDocument?> GetByIdAsync(string id, CancellationToken cancellationToken);
/// <summary>
/// Get all proc snapshots for a specific image digest.
/// </summary>
Task<IReadOnlyList<ProcSnapshotDocument>> GetByImageDigestAsync(
string tenant,
string imageDigest,
int limit = 100,
CancellationToken cancellationToken = default);
/// <summary>
/// Get the most recent proc snapshot for an image digest and runtime type.
/// </summary>
Task<ProcSnapshotDocument?> GetLatestAsync(
string tenant,
string imageDigest,
string runtimeType,
CancellationToken cancellationToken);
/// <summary>
/// Delete expired proc snapshots.
/// </summary>
Task<int> DeleteExpiredAsync(CancellationToken cancellationToken);
}

View File

@@ -0,0 +1,95 @@
using System.Collections.Concurrent;
using StellaOps.Signals.Models;
namespace StellaOps.Signals.Persistence;
/// <summary>
/// In-memory implementation of <see cref="IProcSnapshotRepository"/> for testing and development.
/// </summary>
public sealed class InMemoryProcSnapshotRepository : IProcSnapshotRepository
{
private readonly ConcurrentDictionary<string, ProcSnapshotDocument> _documents = new(StringComparer.OrdinalIgnoreCase);
private readonly TimeProvider _timeProvider;
public InMemoryProcSnapshotRepository(TimeProvider timeProvider)
{
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
}
public Task<ProcSnapshotDocument> UpsertAsync(ProcSnapshotDocument document, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(document);
_documents[document.Id] = document;
return Task.FromResult(document);
}
public Task<ProcSnapshotDocument?> GetByIdAsync(string id, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
_documents.TryGetValue(id, out var document);
return Task.FromResult(document);
}
public Task<IReadOnlyList<ProcSnapshotDocument>> GetByImageDigestAsync(
string tenant,
string imageDigest,
int limit = 100,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(tenant);
ArgumentException.ThrowIfNullOrWhiteSpace(imageDigest);
var normalizedDigest = imageDigest.ToLowerInvariant();
var results = _documents.Values
.Where(d => string.Equals(d.Tenant, tenant, StringComparison.OrdinalIgnoreCase) &&
string.Equals(d.ImageDigest, normalizedDigest, StringComparison.OrdinalIgnoreCase))
.OrderByDescending(d => d.CapturedAt)
.Take(limit)
.ToList();
return Task.FromResult<IReadOnlyList<ProcSnapshotDocument>>(results);
}
public Task<ProcSnapshotDocument?> GetLatestAsync(
string tenant,
string imageDigest,
string runtimeType,
CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(tenant);
ArgumentException.ThrowIfNullOrWhiteSpace(imageDigest);
ArgumentException.ThrowIfNullOrWhiteSpace(runtimeType);
var normalizedDigest = imageDigest.ToLowerInvariant();
var result = _documents.Values
.Where(d => string.Equals(d.Tenant, tenant, StringComparison.OrdinalIgnoreCase) &&
string.Equals(d.ImageDigest, normalizedDigest, StringComparison.OrdinalIgnoreCase) &&
string.Equals(d.RuntimeType, runtimeType, StringComparison.OrdinalIgnoreCase))
.OrderByDescending(d => d.CapturedAt)
.FirstOrDefault();
return Task.FromResult(result);
}
public Task<int> DeleteExpiredAsync(CancellationToken cancellationToken)
{
var now = _timeProvider.GetUtcNow();
var expiredIds = _documents
.Where(kv => kv.Value.ExpiresAt.HasValue && kv.Value.ExpiresAt.Value < now)
.Select(kv => kv.Key)
.ToList();
var deletedCount = 0;
foreach (var id in expiredIds)
{
if (_documents.TryRemove(id, out _))
{
deletedCount++;
}
}
return Task.FromResult(deletedCount);
}
}