up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics.Metrics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.SbomService.Models;
|
||||
using StellaOps.SbomService.Services;
|
||||
@@ -25,6 +24,13 @@ builder.Services.AddSingleton<ISbomEventStore, InMemorySbomEventStore>();
|
||||
builder.Services.AddSingleton<ISbomEventPublisher>(sp => sp.GetRequiredService<ISbomEventStore>());
|
||||
builder.Services.AddSingleton<ISbomQueryService, InMemorySbomQueryService>();
|
||||
builder.Services.AddSingleton<IEntrypointRepository, InMemoryEntrypointRepository>();
|
||||
builder.Services.AddSingleton<IOrchestratorRepository, InMemoryOrchestratorRepository>();
|
||||
builder.Services.AddSingleton<IOrchestratorControlRepository, InMemoryOrchestratorControlRepository>();
|
||||
builder.Services.AddSingleton<IOrchestratorControlService>(sp =>
|
||||
new OrchestratorControlService(
|
||||
sp.GetRequiredService<IOrchestratorControlRepository>(),
|
||||
SbomMetrics.Meter));
|
||||
builder.Services.AddSingleton<IWatermarkService, InMemoryWatermarkService>();
|
||||
|
||||
builder.Services.AddSingleton<IProjectionRepository>(sp =>
|
||||
{
|
||||
@@ -364,7 +370,30 @@ app.MapGet("/internal/sbom/events", async Task<IResult> (
|
||||
[FromServices] ISbomEventStore store,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
using var activity = SbomTracing.Source.StartActivity("events.list", ActivityKind.Server);
|
||||
var events = await store.ListAsync(cancellationToken);
|
||||
SbomMetrics.EventBacklogSize.Record(events.Count);
|
||||
|
||||
if (events.Count > 100)
|
||||
{
|
||||
app.Logger.LogWarning("sbom event backlog high: {Count}", events.Count);
|
||||
}
|
||||
return Results.Ok(events);
|
||||
});
|
||||
|
||||
app.MapGet("/internal/sbom/asset-events", async Task<IResult> (
|
||||
[FromServices] ISbomEventStore store,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
using var activity = SbomTracing.Source.StartActivity("asset-events.list", ActivityKind.Server);
|
||||
var events = await store.ListAssetsAsync(cancellationToken);
|
||||
SbomMetrics.EventBacklogSize.Record(events.Count);
|
||||
|
||||
if (events.Count > 100)
|
||||
{
|
||||
app.Logger.LogWarning("sbom asset event backlog high: {Count}", events.Count);
|
||||
}
|
||||
|
||||
return Results.Ok(events);
|
||||
});
|
||||
|
||||
@@ -390,9 +419,166 @@ app.MapPost("/internal/sbom/events/backfill", async Task<IResult> (
|
||||
}
|
||||
}
|
||||
|
||||
SbomMetrics.EventBacklogSize.Record(published);
|
||||
if (published > 0)
|
||||
{
|
||||
app.Logger.LogInformation("sbom events backfilled={Count}", published);
|
||||
}
|
||||
return Results.Ok(new { published });
|
||||
});
|
||||
|
||||
app.MapGet("/internal/sbom/inventory", async Task<IResult> (
|
||||
[FromServices] ISbomEventStore store,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
using var activity = SbomTracing.Source.StartActivity("inventory.list", ActivityKind.Server);
|
||||
var items = await store.ListInventoryAsync(cancellationToken);
|
||||
return Results.Ok(items);
|
||||
});
|
||||
|
||||
app.MapPost("/internal/sbom/inventory/backfill", async Task<IResult> (
|
||||
[FromServices] ISbomQueryService service,
|
||||
[FromServices] ISbomEventStore store,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
// clear existing inventory and replay by listing projections
|
||||
await store.ClearInventoryAsync(cancellationToken);
|
||||
var projections = new[] { ("snap-001", "tenant-a") };
|
||||
var published = 0;
|
||||
foreach (var (snapshot, tenant) in projections)
|
||||
{
|
||||
await service.GetProjectionAsync(snapshot, tenant, cancellationToken);
|
||||
published++;
|
||||
}
|
||||
return Results.Ok(new { published });
|
||||
});
|
||||
|
||||
app.MapGet("/internal/sbom/resolver-feed", async Task<IResult> (
|
||||
[FromServices] ISbomEventStore store,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var feed = await store.ListResolverAsync(cancellationToken);
|
||||
return Results.Ok(feed);
|
||||
});
|
||||
|
||||
app.MapPost("/internal/sbom/resolver-feed/backfill", async Task<IResult> (
|
||||
[FromServices] ISbomEventStore store,
|
||||
[FromServices] ISbomQueryService service,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
await store.ClearResolverAsync(cancellationToken);
|
||||
var projections = new[] { ("snap-001", "tenant-a") };
|
||||
foreach (var (snapshot, tenant) in projections)
|
||||
{
|
||||
await service.GetProjectionAsync(snapshot, tenant, cancellationToken);
|
||||
}
|
||||
var feed = await store.ListResolverAsync(cancellationToken);
|
||||
return Results.Ok(new { published = feed.Count });
|
||||
});
|
||||
|
||||
app.MapGet("/internal/sbom/resolver-feed/export", async Task<IResult> (
|
||||
[FromServices] ISbomEventStore store,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var feed = await store.ListResolverAsync(cancellationToken);
|
||||
var lines = feed.Select(candidate => JsonSerializer.Serialize(candidate));
|
||||
var ndjson = string.Join('\n', lines);
|
||||
return Results.Text(ndjson, "application/x-ndjson");
|
||||
});
|
||||
|
||||
app.MapGet("/internal/orchestrator/sources", async Task<IResult> (
|
||||
[FromQuery] string? tenant,
|
||||
[FromServices] IOrchestratorRepository repository,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenant))
|
||||
{
|
||||
return Results.BadRequest(new { error = "tenant required" });
|
||||
}
|
||||
|
||||
var sources = await repository.ListAsync(tenant.Trim(), cancellationToken);
|
||||
return Results.Ok(new { tenant = tenant.Trim(), items = sources });
|
||||
});
|
||||
|
||||
app.MapPost("/internal/orchestrator/sources", async Task<IResult> (
|
||||
RegisterOrchestratorSourceRequest request,
|
||||
[FromServices] IOrchestratorRepository repository,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.TenantId))
|
||||
{
|
||||
return Results.BadRequest(new { error = "tenant required" });
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(request.ArtifactDigest))
|
||||
{
|
||||
return Results.BadRequest(new { error = "artifactDigest required" });
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(request.SourceType))
|
||||
{
|
||||
return Results.BadRequest(new { error = "sourceType required" });
|
||||
}
|
||||
|
||||
var source = await repository.RegisterAsync(request, cancellationToken);
|
||||
return Results.Ok(source);
|
||||
});
|
||||
|
||||
app.MapGet("/internal/orchestrator/control", async Task<IResult> (
|
||||
[FromQuery] string? tenant,
|
||||
[FromServices] IOrchestratorControlService service,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenant))
|
||||
{
|
||||
return Results.BadRequest(new { error = "tenant required" });
|
||||
}
|
||||
|
||||
var state = await service.GetAsync(tenant.Trim(), cancellationToken);
|
||||
return Results.Ok(state);
|
||||
});
|
||||
|
||||
app.MapPost("/internal/orchestrator/control", async Task<IResult> (
|
||||
OrchestratorControlRequest request,
|
||||
[FromServices] IOrchestratorControlService service,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.TenantId))
|
||||
{
|
||||
return Results.BadRequest(new { error = "tenant required" });
|
||||
}
|
||||
|
||||
var updated = await service.UpdateAsync(request, cancellationToken);
|
||||
return Results.Ok(updated);
|
||||
});
|
||||
|
||||
app.MapGet("/internal/orchestrator/watermarks", async Task<IResult> (
|
||||
[FromQuery] string? tenant,
|
||||
[FromServices] IWatermarkService service,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenant))
|
||||
{
|
||||
return Results.BadRequest(new { error = "tenant required" });
|
||||
}
|
||||
|
||||
var state = await service.GetAsync(tenant.Trim(), cancellationToken);
|
||||
return Results.Ok(state);
|
||||
});
|
||||
|
||||
app.MapPost("/internal/orchestrator/watermarks", async Task<IResult> (
|
||||
[FromQuery] string? tenant,
|
||||
[FromQuery] string? watermark,
|
||||
[FromServices] IWatermarkService service,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenant))
|
||||
{
|
||||
return Results.BadRequest(new { error = "tenant required" });
|
||||
}
|
||||
|
||||
var updated = await service.SetAsync(tenant.Trim(), watermark ?? string.Empty, cancellationToken);
|
||||
return Results.Ok(updated);
|
||||
});
|
||||
|
||||
app.Run();
|
||||
|
||||
public partial class Program;
|
||||
|
||||
Reference in New Issue
Block a user