docs re-org, audit fixes, build fixes
This commit is contained in:
@@ -9,13 +9,16 @@ public sealed class ExportRetentionService : IExportRetentionService
|
||||
{
|
||||
private readonly IExportRetentionStore _retentionStore;
|
||||
private readonly ILogger<ExportRetentionService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ExportRetentionService(
|
||||
IExportRetentionStore retentionStore,
|
||||
ILogger<ExportRetentionService> logger)
|
||||
ILogger<ExportRetentionService> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_retentionStore = retentionStore ?? throw new ArgumentNullException(nameof(retentionStore));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -26,7 +29,7 @@ public sealed class ExportRetentionService : IExportRetentionService
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
|
||||
var retention = request.OverrideRetention ?? new ExportRetentionConfig();
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
|
||||
_logger.LogInformation(
|
||||
"Starting retention prune for tenant {TenantId}, profile {ProfileId}, execute={Execute}",
|
||||
|
||||
@@ -11,6 +11,12 @@ public sealed class InMemoryExportScheduleStore : IExportScheduleStore
|
||||
private readonly ConcurrentDictionary<Guid, Guid> _runToProfile = new();
|
||||
private readonly ConcurrentDictionary<Guid, List<ScheduledProfileInfo>> _profilesByTenant = new();
|
||||
private readonly object _lock = new();
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public InMemoryExportScheduleStore(TimeProvider? timeProvider = null)
|
||||
{
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a profile for testing.
|
||||
@@ -106,7 +112,7 @@ public sealed class InMemoryExportScheduleStore : IExportScheduleStore
|
||||
{
|
||||
if (_statusByProfile.TryGetValue(profileId, out var existing))
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
var newFailureCount = success ? 0 : existing.ConsecutiveFailures + 1;
|
||||
|
||||
_statusByProfile[profileId] = existing with
|
||||
|
||||
@@ -32,12 +32,14 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
};
|
||||
|
||||
private readonly ILogger<LineageEvidencePackService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly ConcurrentDictionary<Guid, CachedPack> _packCache = new();
|
||||
private readonly string _tempDirectory;
|
||||
|
||||
public LineageEvidencePackService(ILogger<LineageEvidencePackService> logger)
|
||||
public LineageEvidencePackService(ILogger<LineageEvidencePackService> logger, TimeProvider? timeProvider = null)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_tempDirectory = Path.Combine(Path.GetTempPath(), "stellaops-evidence-packs");
|
||||
Directory.CreateDirectory(_tempDirectory);
|
||||
}
|
||||
@@ -187,7 +189,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
Entries = entries.ToImmutableArray(),
|
||||
TotalSizeBytes = entries.Sum(e => e.SizeBytes),
|
||||
FileCount = entries.Count,
|
||||
CreatedAt = DateTimeOffset.UtcNow
|
||||
CreatedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
|
||||
// Write manifest
|
||||
@@ -205,7 +207,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
VexVerdictDigests = vexDocuments.Select(v => v.Digest).ToImmutableArray(),
|
||||
PolicyVerdictDigest = policyVerdict?.Digest,
|
||||
ReplayHash = ComputeReplayHash(artifactDigest, sbomDigest, manifest.MerkleRoot),
|
||||
GeneratedAt = DateTimeOffset.UtcNow,
|
||||
GeneratedAt = _timeProvider.GetUtcNow(),
|
||||
Attestations = attestations.ToImmutableArray(),
|
||||
SbomDocuments = sbomDocuments.ToImmutableArray(),
|
||||
VexDocuments = vexDocuments.ToImmutableArray(),
|
||||
@@ -224,7 +226,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
{
|
||||
Pack = pack,
|
||||
ZipPath = zipPath,
|
||||
ExpiresAt = DateTimeOffset.UtcNow.AddHours(24)
|
||||
ExpiresAt = _timeProvider.GetUtcNow().AddHours(24)
|
||||
};
|
||||
|
||||
// Clean up temp directory
|
||||
@@ -246,7 +248,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
Success = true,
|
||||
Pack = pack,
|
||||
DownloadUrl = $"/api/v1/lineage/export/{packId}/download",
|
||||
ExpiresAt = DateTimeOffset.UtcNow.AddHours(24),
|
||||
ExpiresAt = _timeProvider.GetUtcNow().AddHours(24),
|
||||
SizeBytes = zipInfo.Length,
|
||||
Warnings = warnings.ToImmutableArray()
|
||||
};
|
||||
@@ -268,7 +270,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
string tenantId,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (_packCache.TryGetValue(packId, out var cached) && cached.ExpiresAt > DateTimeOffset.UtcNow)
|
||||
if (_packCache.TryGetValue(packId, out var cached) && cached.ExpiresAt > _timeProvider.GetUtcNow())
|
||||
{
|
||||
if (cached.Pack.TenantId == tenantId)
|
||||
{
|
||||
@@ -285,7 +287,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
string tenantId,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (_packCache.TryGetValue(packId, out var cached) && cached.ExpiresAt > DateTimeOffset.UtcNow)
|
||||
if (_packCache.TryGetValue(packId, out var cached) && cached.ExpiresAt > _timeProvider.GetUtcNow())
|
||||
{
|
||||
if (cached.Pack.TenantId == tenantId && File.Exists(cached.ZipPath))
|
||||
{
|
||||
@@ -347,7 +349,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
bomFormat = "CycloneDX",
|
||||
specVersion = "1.6",
|
||||
version = 1,
|
||||
metadata = new { timestamp = DateTimeOffset.UtcNow.ToString("O") },
|
||||
metadata = new { timestamp = _timeProvider.GetUtcNow().ToString("O") },
|
||||
components = Array.Empty<object>()
|
||||
}, JsonOptions);
|
||||
|
||||
@@ -383,7 +385,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
dataLicense = "CC0-1.0",
|
||||
name = artifactDigest,
|
||||
documentNamespace = $"https://stellaops.io/spdx/{artifactDigest}",
|
||||
creationInfo = new { created = DateTimeOffset.UtcNow.ToString("O") },
|
||||
creationInfo = new { created = _timeProvider.GetUtcNow().ToString("O") },
|
||||
packages = Array.Empty<object>()
|
||||
}, JsonOptions);
|
||||
|
||||
@@ -418,7 +420,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
context = "https://openvex.dev/ns/v0.2.0",
|
||||
id = $"urn:stellaops:vex:{artifactDigest}",
|
||||
author = "StellaOps",
|
||||
timestamp = DateTimeOffset.UtcNow.ToString("O"),
|
||||
timestamp = _timeProvider.GetUtcNow().ToString("O"),
|
||||
statements = Array.Empty<object>()
|
||||
}, JsonOptions);
|
||||
|
||||
@@ -457,7 +459,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
tenantId,
|
||||
verdict = "pass",
|
||||
policyVersion = "1.0.0",
|
||||
evaluatedAt = DateTimeOffset.UtcNow.ToString("O"),
|
||||
evaluatedAt = _timeProvider.GetUtcNow().ToString("O"),
|
||||
rules = new { total = 0, passed = 0, failed = 0, warned = 0 }
|
||||
}, JsonOptions);
|
||||
|
||||
@@ -477,7 +479,7 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
RulesPassed = 0,
|
||||
RulesFailed = 0,
|
||||
RulesWarned = 0,
|
||||
EvaluatedAt = DateTimeOffset.UtcNow,
|
||||
EvaluatedAt = _timeProvider.GetUtcNow(),
|
||||
FileName = fileName
|
||||
};
|
||||
}
|
||||
@@ -528,9 +530,9 @@ public sealed class LineageEvidencePackService : ILineageEvidencePackService
|
||||
return ComputeHash(combined);
|
||||
}
|
||||
|
||||
private static string ComputeReplayHash(string artifactDigest, string sbomDigest, string merkleRoot)
|
||||
private string ComputeReplayHash(string artifactDigest, string sbomDigest, string merkleRoot)
|
||||
{
|
||||
var input = $"{artifactDigest}|{sbomDigest}|{merkleRoot}|{DateTimeOffset.UtcNow:O}";
|
||||
var input = $"{artifactDigest}|{sbomDigest}|{merkleRoot}|{_timeProvider.GetUtcNow():O}";
|
||||
return $"sha256:{ComputeHash(input)}";
|
||||
}
|
||||
|
||||
|
||||
@@ -149,14 +149,15 @@ public sealed record ExportVerificationResult
|
||||
/// <summary>
|
||||
/// When verification was performed.
|
||||
/// </summary>
|
||||
public DateTimeOffset VerifiedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
public required DateTimeOffset VerifiedAt { get; init; }
|
||||
|
||||
public static ExportVerificationResult Failed(Guid runId, params VerificationError[] errors)
|
||||
public static ExportVerificationResult Failed(Guid runId, DateTimeOffset verifiedAt, params VerificationError[] errors)
|
||||
=> new()
|
||||
{
|
||||
Status = VerificationStatus.Invalid,
|
||||
RunId = runId,
|
||||
Errors = errors
|
||||
Errors = errors,
|
||||
VerifiedAt = verifiedAt
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,22 +14,26 @@ public sealed class ExportVerificationService : IExportVerificationService
|
||||
private readonly IExportArtifactStore _artifactStore;
|
||||
private readonly IPackRunAttestationStore? _packRunStore;
|
||||
private readonly ILogger<ExportVerificationService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ExportVerificationService(
|
||||
IExportArtifactStore artifactStore,
|
||||
ILogger<ExportVerificationService> logger)
|
||||
: this(artifactStore, null, logger)
|
||||
ILogger<ExportVerificationService> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
: this(artifactStore, null, logger, timeProvider)
|
||||
{
|
||||
}
|
||||
|
||||
public ExportVerificationService(
|
||||
IExportArtifactStore artifactStore,
|
||||
IPackRunAttestationStore? packRunStore,
|
||||
ILogger<ExportVerificationService> logger)
|
||||
ILogger<ExportVerificationService> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_artifactStore = artifactStore ?? throw new ArgumentNullException(nameof(artifactStore));
|
||||
_packRunStore = packRunStore;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -52,6 +56,7 @@ public sealed class ExportVerificationService : IExportVerificationService
|
||||
{
|
||||
return ExportVerificationResult.Failed(
|
||||
request.RunId,
|
||||
_timeProvider.GetUtcNow(),
|
||||
new VerificationError
|
||||
{
|
||||
Code = VerificationErrorCodes.ManifestNotFound,
|
||||
@@ -64,6 +69,7 @@ public sealed class ExportVerificationService : IExportVerificationService
|
||||
{
|
||||
return ExportVerificationResult.Failed(
|
||||
request.RunId,
|
||||
_timeProvider.GetUtcNow(),
|
||||
new VerificationError
|
||||
{
|
||||
Code = VerificationErrorCodes.TenantMismatch,
|
||||
@@ -234,7 +240,8 @@ public sealed class ExportVerificationService : IExportVerificationService
|
||||
Encryption = encryptionResult,
|
||||
Attestation = attestationStatus,
|
||||
Errors = errors,
|
||||
Warnings = warnings
|
||||
Warnings = warnings,
|
||||
VerifiedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ public sealed class ExceptionReportGenerator : IExceptionReportGenerator
|
||||
private readonly IExceptionApplicationRepository _applicationRepository;
|
||||
private readonly ConcurrentDictionary<string, ReportJob> _jobs = new();
|
||||
private readonly ILogger<ExceptionReportGenerator> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
@@ -30,11 +31,13 @@ public sealed class ExceptionReportGenerator : IExceptionReportGenerator
|
||||
public ExceptionReportGenerator(
|
||||
IExceptionRepository exceptionRepository,
|
||||
IExceptionApplicationRepository applicationRepository,
|
||||
ILogger<ExceptionReportGenerator> logger)
|
||||
ILogger<ExceptionReportGenerator> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_exceptionRepository = exceptionRepository;
|
||||
_applicationRepository = applicationRepository;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<ExceptionReportJobResponse> CreateReportAsync(
|
||||
@@ -42,7 +45,7 @@ public sealed class ExceptionReportGenerator : IExceptionReportGenerator
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var jobId = $"exc-rpt-{Guid.NewGuid():N}";
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
|
||||
var job = new ReportJob
|
||||
{
|
||||
@@ -151,7 +154,7 @@ public sealed class ExceptionReportGenerator : IExceptionReportGenerator
|
||||
try
|
||||
{
|
||||
job.Status = "running";
|
||||
job.StartedAt = DateTimeOffset.UtcNow;
|
||||
job.StartedAt = _timeProvider.GetUtcNow();
|
||||
|
||||
var filter = job.Request.Filter ?? new ExceptionFilter
|
||||
{
|
||||
@@ -232,7 +235,7 @@ public sealed class ExceptionReportGenerator : IExceptionReportGenerator
|
||||
var document = new ExceptionReportDocument
|
||||
{
|
||||
ReportId = job.JobId,
|
||||
GeneratedAt = DateTimeOffset.UtcNow,
|
||||
GeneratedAt = _timeProvider.GetUtcNow(),
|
||||
TenantId = job.TenantId,
|
||||
RequesterId = job.RequesterId,
|
||||
Title = job.Request.Title ?? "Exception Report",
|
||||
@@ -289,7 +292,7 @@ public sealed class ExceptionReportGenerator : IExceptionReportGenerator
|
||||
job.ContentHash = $"sha256:{Convert.ToHexString(SHA256.HashData(content)).ToLowerInvariant()}";
|
||||
job.Progress = 100;
|
||||
job.Status = "completed";
|
||||
job.CompletedAt = DateTimeOffset.UtcNow;
|
||||
job.CompletedAt = _timeProvider.GetUtcNow();
|
||||
|
||||
_logger.LogInformation(
|
||||
"Completed exception report {JobId} with {Count} exceptions, {Size} bytes",
|
||||
@@ -300,7 +303,7 @@ public sealed class ExceptionReportGenerator : IExceptionReportGenerator
|
||||
_logger.LogError(ex, "Failed to generate exception report {JobId}", job.JobId);
|
||||
job.Status = "failed";
|
||||
job.ErrorMessage = ex.Message;
|
||||
job.CompletedAt = DateTimeOffset.UtcNow;
|
||||
job.CompletedAt = _timeProvider.GetUtcNow();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user