feat: Add initial implementation of Vulnerability Resolver Jobs
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Created project for StellaOps.Scanner.Analyzers.Native.Tests with necessary dependencies. - Documented roles and guidelines in AGENTS.md for Scheduler module. - Implemented IResolverJobService interface and InMemoryResolverJobService for handling resolver jobs. - Added ResolverBacklogNotifier and ResolverBacklogService for monitoring job metrics. - Developed API endpoints for managing resolver jobs and retrieving metrics. - Defined models for resolver job requests and responses. - Integrated dependency injection for resolver job services. - Implemented ImpactIndexSnapshot for persisting impact index data. - Introduced SignalsScoringOptions for configurable scoring weights in reachability scoring. - Added unit tests for ReachabilityScoringService and RuntimeFactsIngestionService. - Created dotnet-filter.sh script to handle command-line arguments for dotnet. - Established nuget-prime project for managing package downloads.
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
namespace StellaOps.Orchestrator.Core;
|
||||
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Orchestrator.Core;
|
||||
|
||||
public sealed record EventEnvelope(
|
||||
[property: JsonPropertyName("schemaVersion")] string SchemaVersion,
|
||||
[property: JsonPropertyName("eventId")] string EventId,
|
||||
[property: JsonPropertyName("eventType")] string EventType,
|
||||
[property: JsonPropertyName("occurredAt")] DateTimeOffset OccurredAt,
|
||||
[property: JsonPropertyName("idempotencyKey")] string IdempotencyKey,
|
||||
[property: JsonPropertyName("correlationId")] string? CorrelationId,
|
||||
[property: JsonPropertyName("tenantId")] string TenantId,
|
||||
[property: JsonPropertyName("projectId")] string? ProjectId,
|
||||
[property: JsonPropertyName("actor")] EventActor Actor,
|
||||
[property: JsonPropertyName("job")] EventJob Job,
|
||||
[property: JsonPropertyName("metrics")] EventMetrics? Metrics,
|
||||
[property: JsonPropertyName("notifier")] EventNotifier? Notifier)
|
||||
{
|
||||
public static EventEnvelope Create(
|
||||
string eventType,
|
||||
string tenantId,
|
||||
EventJob job,
|
||||
EventActor actor,
|
||||
string schemaVersion = "orch.event.v1",
|
||||
string? correlationId = null,
|
||||
string? projectId = null,
|
||||
EventMetrics? metrics = null,
|
||||
EventNotifier? notifier = null,
|
||||
DateTimeOffset? occurredAt = null,
|
||||
string? eventId = null,
|
||||
string? idempotencyKey = null)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(eventType);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(tenantId);
|
||||
ArgumentNullException.ThrowIfNull(job);
|
||||
ArgumentNullException.ThrowIfNull(actor);
|
||||
|
||||
var occurred = occurredAt ?? DateTimeOffset.UtcNow;
|
||||
var evtId = string.IsNullOrWhiteSpace(eventId) ? Guid.NewGuid().ToString() : eventId!;
|
||||
var key = string.IsNullOrWhiteSpace(idempotencyKey)
|
||||
? ComputeIdempotencyKey(eventType, job.Id, job.Attempt)
|
||||
: idempotencyKey!;
|
||||
|
||||
return new EventEnvelope(
|
||||
schemaVersion,
|
||||
evtId,
|
||||
eventType,
|
||||
occurred,
|
||||
key,
|
||||
string.IsNullOrWhiteSpace(correlationId) ? null : correlationId,
|
||||
tenantId,
|
||||
string.IsNullOrWhiteSpace(projectId) ? null : projectId,
|
||||
actor,
|
||||
job,
|
||||
metrics,
|
||||
notifier);
|
||||
}
|
||||
|
||||
public static string ComputeIdempotencyKey(string eventType, string jobId, int attempt)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(eventType);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(jobId);
|
||||
return $"orch-{eventType}-{jobId}-{attempt}".ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record EventActor(
|
||||
[property: JsonPropertyName("subject")] string Subject,
|
||||
[property: JsonPropertyName("scopes")] ImmutableArray<string> Scopes);
|
||||
|
||||
public sealed record EventJob(
|
||||
[property: JsonPropertyName("id")] string Id,
|
||||
[property: JsonPropertyName("type")] string Type,
|
||||
[property: JsonPropertyName("runId")] string? RunId,
|
||||
[property: JsonPropertyName("attempt")] int Attempt,
|
||||
[property: JsonPropertyName("leaseId")] string? LeaseId,
|
||||
[property: JsonPropertyName("taskRunnerId")] string? TaskRunnerId,
|
||||
[property: JsonPropertyName("status")] string Status,
|
||||
[property: JsonPropertyName("reason")] string? Reason,
|
||||
[property: JsonPropertyName("payloadDigest")] string? PayloadDigest,
|
||||
[property: JsonPropertyName("artifacts")] ImmutableArray<EventArtifact> Artifacts,
|
||||
[property: JsonPropertyName("provenance")] ImmutableDictionary<string, string>? Provenance);
|
||||
|
||||
public sealed record EventArtifact(
|
||||
[property: JsonPropertyName("uri")] string Uri,
|
||||
[property: JsonPropertyName("digest")] string Digest,
|
||||
[property: JsonPropertyName("mime")] string? Mime);
|
||||
|
||||
public sealed record EventMetrics(
|
||||
[property: JsonPropertyName("durationSeconds")] double? DurationSeconds,
|
||||
[property: JsonPropertyName("logStreamLagSeconds")] double? LogStreamLagSeconds,
|
||||
[property: JsonPropertyName("backoffSeconds")] double? BackoffSeconds);
|
||||
|
||||
public sealed record EventNotifier(
|
||||
[property: JsonPropertyName("channel")] string Channel,
|
||||
[property: JsonPropertyName("delivery")] string Delivery,
|
||||
[property: JsonPropertyName("replay")] EventReplayInfo? Replay);
|
||||
|
||||
public sealed record EventReplayInfo(
|
||||
[property: JsonPropertyName("ordinal")] int Ordinal,
|
||||
[property: JsonPropertyName("total")] int Total);
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json;
|
||||
using StellaOps.Orchestrator.Core;
|
||||
|
||||
namespace StellaOps.Orchestrator.Tests;
|
||||
|
||||
public class EventEnvelopeTests
|
||||
{
|
||||
[Fact]
|
||||
public void ComputeIdempotencyKey_IsDeterministicAndLowercase()
|
||||
{
|
||||
var key = EventEnvelope.ComputeIdempotencyKey("Job.Completed", "job_abc", 3);
|
||||
Assert.Equal("orch-job.completed-job_abc-3", key);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_PopulatesDefaultsAndSerializes()
|
||||
{
|
||||
var job = new EventJob(
|
||||
Id: "job_123",
|
||||
Type: "pack-run",
|
||||
RunId: "run_123",
|
||||
Attempt: 2,
|
||||
LeaseId: "lease_1",
|
||||
TaskRunnerId: "tr_9",
|
||||
Status: "completed",
|
||||
Reason: null,
|
||||
PayloadDigest: "sha256:deadbeef",
|
||||
Artifacts: ImmutableArray.Create(new EventArtifact("s3://bucket/obj", "sha256:beef", "application/json")),
|
||||
Provenance: ImmutableDictionary<string, string>.Empty);
|
||||
|
||||
var actor = new EventActor("worker-sdk-go", ImmutableArray.Create("orch:quota"));
|
||||
|
||||
var envelope = EventEnvelope.Create(
|
||||
eventType: "job.completed",
|
||||
tenantId: "tenant-alpha",
|
||||
job: job,
|
||||
actor: actor,
|
||||
projectId: "proj-1",
|
||||
correlationId: "corr-123");
|
||||
|
||||
Assert.False(string.IsNullOrWhiteSpace(envelope.EventId));
|
||||
Assert.Equal("orch-job.completed-job_123-2", envelope.IdempotencyKey);
|
||||
Assert.Equal("orch.event.v1", envelope.SchemaVersion);
|
||||
Assert.Equal("tenant-alpha", envelope.TenantId);
|
||||
Assert.Equal("proj-1", envelope.ProjectId);
|
||||
|
||||
var json = JsonSerializer.Serialize(envelope);
|
||||
var roundtrip = JsonSerializer.Deserialize<EventEnvelope>(json);
|
||||
Assert.NotNull(roundtrip);
|
||||
Assert.Equal(envelope.IdempotencyKey, roundtrip!.IdempotencyKey);
|
||||
Assert.Equal(envelope.Job.Id, roundtrip.Job.Id);
|
||||
Assert.Equal(envelope.Actor.Subject, roundtrip.Actor.Subject);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace StellaOps.Orchestrator.Tests;
|
||||
|
||||
public class UnitTest1
|
||||
{
|
||||
[Fact]
|
||||
public void Test1()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,19 @@
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
var summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
app.MapGet("/weatherforecast", () =>
|
||||
{
|
||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray();
|
||||
return forecast;
|
||||
})
|
||||
.WithName("GetWeatherForecast");
|
||||
|
||||
app.Run();
|
||||
|
||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
}
|
||||
|
||||
app.MapGet("/healthz", () => Results.Json(new { status = "ok" }));
|
||||
app.MapGet("/readyz", () => Results.Json(new { status = "ready" }));
|
||||
|
||||
app.Run();
|
||||
|
||||
public partial class Program;
|
||||
|
||||
Reference in New Issue
Block a user