- Implemented PolicyDslValidator with command-line options for strict mode and JSON output. - Created PolicySchemaExporter to generate JSON schemas for policy-related models. - Developed PolicySimulationSmoke tool to validate policy simulations against expected outcomes. - Added project files and necessary dependencies for each tool. - Ensured proper error handling and usage instructions across tools.
205 lines
7.0 KiB
C#
205 lines
7.0 KiB
C#
using System.Collections.Immutable;
|
|
using Microsoft.Extensions.Logging;
|
|
using StellaOps.Scheduler.Models;
|
|
using StellaOps.Scheduler.Storage.Mongo.Documents;
|
|
using StellaOps.Scheduler.Storage.Mongo.Projections;
|
|
using StellaOps.Scheduler.Storage.Mongo.Repositories;
|
|
|
|
namespace StellaOps.Scheduler.Storage.Mongo.Services;
|
|
|
|
internal sealed class RunSummaryService : IRunSummaryService
|
|
{
|
|
private const int RecentLimit = 20;
|
|
|
|
private readonly IRunSummaryRepository _repository;
|
|
private readonly TimeProvider _timeProvider;
|
|
private readonly ILogger<RunSummaryService> _logger;
|
|
|
|
public RunSummaryService(
|
|
IRunSummaryRepository repository,
|
|
TimeProvider? timeProvider,
|
|
ILogger<RunSummaryService> logger)
|
|
{
|
|
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
|
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
}
|
|
|
|
public async Task<RunSummaryProjection> ProjectAsync(
|
|
Run run,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(run);
|
|
if (string.IsNullOrWhiteSpace(run.ScheduleId))
|
|
{
|
|
throw new ArgumentException("Run must contain a scheduleId to project summary data.", nameof(run));
|
|
}
|
|
|
|
var document = await _repository
|
|
.GetAsync(run.TenantId, run.ScheduleId!, cancellationToken)
|
|
.ConfigureAwait(false)
|
|
?? new RunSummaryDocument
|
|
{
|
|
TenantId = run.TenantId,
|
|
ScheduleId = run.ScheduleId!,
|
|
};
|
|
|
|
UpdateDocument(document, run);
|
|
document.UpdatedAt = _timeProvider.GetUtcNow().UtcDateTime;
|
|
|
|
await _repository.UpsertAsync(document, cancellationToken).ConfigureAwait(false);
|
|
_logger.LogDebug(
|
|
"Projected run summary for tenant {TenantId} schedule {ScheduleId} using run {RunId}.",
|
|
run.TenantId,
|
|
run.ScheduleId,
|
|
run.Id);
|
|
return ToProjection(document);
|
|
}
|
|
|
|
public async Task<RunSummaryProjection?> GetAsync(
|
|
string tenantId,
|
|
string scheduleId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var document = await _repository
|
|
.GetAsync(tenantId, scheduleId, cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
return document is null ? null : ToProjection(document);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<RunSummaryProjection>> ListAsync(
|
|
string tenantId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var documents = await _repository.ListAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
|
return documents.Select(ToProjection).ToArray();
|
|
}
|
|
|
|
private static void UpdateDocument(RunSummaryDocument document, Run run)
|
|
{
|
|
var entry = document.Recent.FirstOrDefault(item => string.Equals(item.RunId, run.Id, StringComparison.Ordinal));
|
|
if (entry is null)
|
|
{
|
|
entry = new RunSummaryEntryDocument
|
|
{
|
|
RunId = run.Id,
|
|
};
|
|
document.Recent.Add(entry);
|
|
}
|
|
|
|
entry.Trigger = run.Trigger;
|
|
entry.State = run.State;
|
|
entry.CreatedAt = run.CreatedAt.UtcDateTime;
|
|
entry.StartedAt = run.StartedAt?.UtcDateTime;
|
|
entry.FinishedAt = run.FinishedAt?.UtcDateTime;
|
|
entry.Error = run.Error;
|
|
entry.Stats = run.Stats;
|
|
|
|
document.Recent = document.Recent
|
|
.OrderByDescending(item => item.CreatedAt)
|
|
.ThenByDescending(item => item.RunId, StringComparer.Ordinal)
|
|
.Take(RecentLimit)
|
|
.ToList();
|
|
|
|
document.LastRun = document.Recent.FirstOrDefault();
|
|
document.Counters = ComputeCounters(document.Recent);
|
|
}
|
|
|
|
private static RunSummaryCountersDocument ComputeCounters(IEnumerable<RunSummaryEntryDocument> entries)
|
|
{
|
|
var counters = new RunSummaryCountersDocument();
|
|
|
|
foreach (var entry in entries)
|
|
{
|
|
counters.Total++;
|
|
switch (entry.State)
|
|
{
|
|
case RunState.Planning:
|
|
counters.Planning++;
|
|
break;
|
|
case RunState.Queued:
|
|
counters.Queued++;
|
|
break;
|
|
case RunState.Running:
|
|
counters.Running++;
|
|
break;
|
|
case RunState.Completed:
|
|
counters.Completed++;
|
|
break;
|
|
case RunState.Error:
|
|
counters.Error++;
|
|
break;
|
|
case RunState.Cancelled:
|
|
counters.Cancelled++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
counters.TotalDeltas += entry.Stats.Deltas;
|
|
counters.TotalNewCriticals += entry.Stats.NewCriticals;
|
|
counters.TotalNewHigh += entry.Stats.NewHigh;
|
|
counters.TotalNewMedium += entry.Stats.NewMedium;
|
|
counters.TotalNewLow += entry.Stats.NewLow;
|
|
}
|
|
|
|
return counters;
|
|
}
|
|
|
|
private static RunSummaryProjection ToProjection(RunSummaryDocument document)
|
|
{
|
|
var updatedAt = new DateTimeOffset(DateTime.SpecifyKind(document.UpdatedAt, DateTimeKind.Utc));
|
|
var lastRun = document.LastRun is null
|
|
? null
|
|
: ToSnapshot(document.LastRun);
|
|
|
|
var recent = document.Recent
|
|
.Select(ToSnapshot)
|
|
.ToImmutableArray();
|
|
|
|
var counters = new RunSummaryCounters(
|
|
document.Counters.Total,
|
|
document.Counters.Planning,
|
|
document.Counters.Queued,
|
|
document.Counters.Running,
|
|
document.Counters.Completed,
|
|
document.Counters.Error,
|
|
document.Counters.Cancelled,
|
|
document.Counters.TotalDeltas,
|
|
document.Counters.TotalNewCriticals,
|
|
document.Counters.TotalNewHigh,
|
|
document.Counters.TotalNewMedium,
|
|
document.Counters.TotalNewLow);
|
|
|
|
return new RunSummaryProjection(
|
|
document.TenantId,
|
|
document.ScheduleId,
|
|
updatedAt,
|
|
lastRun,
|
|
recent,
|
|
counters);
|
|
}
|
|
|
|
private static RunSummarySnapshot ToSnapshot(RunSummaryEntryDocument entry)
|
|
{
|
|
var createdAt = new DateTimeOffset(DateTime.SpecifyKind(entry.CreatedAt, DateTimeKind.Utc));
|
|
DateTimeOffset? startedAt = entry.StartedAt is null
|
|
? null
|
|
: new DateTimeOffset(DateTime.SpecifyKind(entry.StartedAt.Value, DateTimeKind.Utc));
|
|
DateTimeOffset? finishedAt = entry.FinishedAt is null
|
|
? null
|
|
: new DateTimeOffset(DateTime.SpecifyKind(entry.FinishedAt.Value, DateTimeKind.Utc));
|
|
|
|
return new RunSummarySnapshot(
|
|
entry.RunId,
|
|
entry.Trigger,
|
|
entry.State,
|
|
createdAt,
|
|
startedAt,
|
|
finishedAt,
|
|
entry.Stats,
|
|
entry.Error);
|
|
}
|
|
}
|