Add Policy DSL Validator, Schema Exporter, and Simulation Smoke tools
- 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.
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user