up
Some checks failed
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
This commit is contained in:
@@ -4,7 +4,6 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Driver;
|
||||
using NetEscapades.Configuration.Yaml;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
@@ -82,49 +81,7 @@ builder.Services.AddProblemDetails();
|
||||
builder.Services.AddHealthChecks();
|
||||
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
||||
|
||||
builder.Services.AddSingleton<IMongoClient>(sp =>
|
||||
{
|
||||
var opts = sp.GetRequiredService<IOptions<SignalsOptions>>().Value;
|
||||
return new MongoClient(opts.Mongo.ConnectionString);
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<IMongoDatabase>(sp =>
|
||||
{
|
||||
var opts = sp.GetRequiredService<IOptions<SignalsOptions>>().Value;
|
||||
var mongoClient = sp.GetRequiredService<IMongoClient>();
|
||||
var mongoUrl = MongoUrl.Create(opts.Mongo.ConnectionString);
|
||||
var databaseName = string.IsNullOrWhiteSpace(mongoUrl.DatabaseName) ? opts.Mongo.Database : mongoUrl.DatabaseName;
|
||||
return mongoClient.GetDatabase(databaseName);
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<IMongoCollection<CallgraphDocument>>(sp =>
|
||||
{
|
||||
var opts = sp.GetRequiredService<IOptions<SignalsOptions>>().Value;
|
||||
var database = sp.GetRequiredService<IMongoDatabase>();
|
||||
var collection = database.GetCollection<CallgraphDocument>(opts.Mongo.CallgraphsCollection);
|
||||
EnsureCallgraphIndexes(collection);
|
||||
return collection;
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<IMongoCollection<ReachabilityFactDocument>>(sp =>
|
||||
{
|
||||
var opts = sp.GetRequiredService<IOptions<SignalsOptions>>().Value;
|
||||
var database = sp.GetRequiredService<IMongoDatabase>();
|
||||
var collection = database.GetCollection<ReachabilityFactDocument>(opts.Mongo.ReachabilityFactsCollection);
|
||||
EnsureReachabilityFactIndexes(collection);
|
||||
return collection;
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<IMongoCollection<UnknownSymbolDocument>>(sp =>
|
||||
{
|
||||
var opts = sp.GetRequiredService<IOptions<SignalsOptions>>().Value;
|
||||
var database = sp.GetRequiredService<IMongoDatabase>();
|
||||
var collection = database.GetCollection<UnknownSymbolDocument>(opts.Mongo.UnknownsCollection);
|
||||
EnsureUnknownsIndexes(collection);
|
||||
return collection;
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<ICallgraphRepository, MongoCallgraphRepository>();
|
||||
builder.Services.AddSingleton<ICallgraphRepository, InMemoryCallgraphRepository>();
|
||||
builder.Services.AddSingleton<ICallgraphNormalizationService, CallgraphNormalizationService>();
|
||||
|
||||
// Configure callgraph artifact storage based on driver
|
||||
@@ -160,7 +117,6 @@ builder.Services.AddSingleton<ICallgraphParser>(new SimpleJsonCallgraphParser("p
|
||||
builder.Services.AddSingleton<ICallgraphParser>(new SimpleJsonCallgraphParser("go"));
|
||||
builder.Services.AddSingleton<ICallgraphParserResolver, CallgraphParserResolver>();
|
||||
builder.Services.AddSingleton<ICallgraphIngestionService, CallgraphIngestionService>();
|
||||
builder.Services.AddSingleton<MongoReachabilityFactRepository>();
|
||||
builder.Services.AddSingleton<IReachabilityCache>(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<SignalsOptions>>().Value;
|
||||
@@ -168,6 +124,14 @@ builder.Services.AddSingleton<IReachabilityCache>(sp =>
|
||||
});
|
||||
builder.Services.AddSingleton<IRedisConnectionFactory, RedisConnectionFactory>();
|
||||
builder.Services.AddSingleton<ReachabilityFactEventBuilder>();
|
||||
builder.Services.AddSingleton<InMemoryReachabilityFactRepository>();
|
||||
builder.Services.AddSingleton<IReachabilityFactRepository>(sp =>
|
||||
{
|
||||
var inner = sp.GetRequiredService<InMemoryReachabilityFactRepository>();
|
||||
var cache = sp.GetRequiredService<IReachabilityCache>();
|
||||
return new ReachabilityFactCacheDecorator(inner, cache);
|
||||
});
|
||||
builder.Services.AddSingleton<IUnknownsRepository, InMemoryUnknownsRepository>();
|
||||
builder.Services.AddHttpClient<RouterEventsPublisher>((sp, client) =>
|
||||
{
|
||||
var opts = sp.GetRequiredService<SignalsOptions>().Events.Router;
|
||||
@@ -221,18 +185,12 @@ builder.Services.AddSingleton<IEventsPublisher>(sp =>
|
||||
sp.GetRequiredService<ILogger<InMemoryEventsPublisher>>(),
|
||||
eventBuilder);
|
||||
});
|
||||
builder.Services.AddSingleton<IReachabilityFactRepository>(sp =>
|
||||
{
|
||||
var inner = sp.GetRequiredService<MongoReachabilityFactRepository>();
|
||||
var cache = sp.GetRequiredService<IReachabilityCache>();
|
||||
return new ReachabilityFactCacheDecorator(inner, cache);
|
||||
});
|
||||
builder.Services.AddSingleton<IReachabilityScoringService, ReachabilityScoringService>();
|
||||
builder.Services.AddSingleton<IRuntimeFactsProvenanceNormalizer, RuntimeFactsProvenanceNormalizer>();
|
||||
builder.Services.AddSingleton<IRuntimeFactsIngestionService, RuntimeFactsIngestionService>();
|
||||
builder.Services.AddSingleton<IReachabilityUnionIngestionService, ReachabilityUnionIngestionService>();
|
||||
builder.Services.AddSingleton<IUnknownsRepository, MongoUnknownsRepository>();
|
||||
builder.Services.AddSingleton<IUnknownsIngestionService, UnknownsIngestionService>();
|
||||
builder.Services.AddSingleton<SyntheticRuntimeProbeBuilder>();
|
||||
|
||||
if (bootstrap.Authority.Enabled)
|
||||
{
|
||||
@@ -488,6 +446,56 @@ signalsGroup.MapPost("/runtime-facts", async Task<IResult> (
|
||||
}
|
||||
}).WithName("SignalsRuntimeIngest");
|
||||
|
||||
signalsGroup.MapPost("/runtime-facts/synthetic", async Task<IResult> (
|
||||
HttpContext context,
|
||||
SignalsOptions options,
|
||||
SyntheticRuntimeProbeRequest request,
|
||||
ICallgraphRepository callgraphRepository,
|
||||
IRuntimeFactsIngestionService ingestionService,
|
||||
SyntheticRuntimeProbeBuilder probeBuilder,
|
||||
SignalsSealedModeMonitor sealedModeMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure))
|
||||
{
|
||||
return authFailure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
{
|
||||
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.CallgraphId))
|
||||
{
|
||||
return Results.BadRequest(new { error = "callgraphId is required." });
|
||||
}
|
||||
|
||||
var callgraph = await callgraphRepository.GetByIdAsync(request.CallgraphId.Trim(), cancellationToken).ConfigureAwait(false);
|
||||
if (callgraph is null)
|
||||
{
|
||||
return Results.NotFound(new { error = "callgraph not found." });
|
||||
}
|
||||
|
||||
var subject = request.Subject ?? new ReachabilitySubject { ScanId = $"synthetic-{callgraph.Id}" };
|
||||
var events = probeBuilder.BuildEvents(callgraph, request.EventCount);
|
||||
var metadata = request.Metadata is null
|
||||
? new Dictionary<string, string?>(StringComparer.Ordinal)
|
||||
: new Dictionary<string, string?>(request.Metadata, StringComparer.Ordinal);
|
||||
metadata.TryAdd("source", "synthetic-probe");
|
||||
|
||||
var ingestRequest = new RuntimeFactsIngestRequest
|
||||
{
|
||||
CallgraphId = callgraph.Id,
|
||||
Subject = subject,
|
||||
Events = events,
|
||||
Metadata = metadata
|
||||
};
|
||||
|
||||
var response = await ingestionService.IngestAsync(ingestRequest, cancellationToken).ConfigureAwait(false);
|
||||
return Results.Accepted($"/signals/runtime-facts/{response.SubjectKey}", response);
|
||||
}).WithName("SignalsRuntimeIngestSynthetic");
|
||||
|
||||
signalsGroup.MapPost("/reachability/union", async Task<IResult> (
|
||||
HttpContext context,
|
||||
SignalsOptions options,
|
||||
@@ -808,55 +816,6 @@ public partial class Program
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void EnsureCallgraphIndexes(IMongoCollection<CallgraphDocument> collection)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(collection);
|
||||
|
||||
try
|
||||
{
|
||||
var indexKeys = Builders<CallgraphDocument>.IndexKeys
|
||||
.Ascending(document => document.Component)
|
||||
.Ascending(document => document.Version)
|
||||
.Ascending(document => document.Language);
|
||||
|
||||
var model = new CreateIndexModel<CallgraphDocument>(indexKeys, new CreateIndexOptions
|
||||
{
|
||||
Name = "callgraphs_component_version_language_unique",
|
||||
Unique = true
|
||||
});
|
||||
|
||||
collection.Indexes.CreateOne(model);
|
||||
}
|
||||
catch (MongoCommandException ex) when (string.Equals(ex.CodeName, "IndexOptionsConflict", StringComparison.Ordinal))
|
||||
{
|
||||
// Index already exists with different options – ignore to keep startup idempotent.
|
||||
}
|
||||
}
|
||||
|
||||
internal static void EnsureReachabilityFactIndexes(IMongoCollection<ReachabilityFactDocument> collection)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(collection);
|
||||
|
||||
try
|
||||
{
|
||||
var subjectIndex = new CreateIndexModel<ReachabilityFactDocument>(
|
||||
Builders<ReachabilityFactDocument>.IndexKeys.Ascending(doc => doc.SubjectKey),
|
||||
new CreateIndexOptions { Name = "reachability_subject_key_unique", Unique = true });
|
||||
|
||||
collection.Indexes.CreateOne(subjectIndex);
|
||||
|
||||
var callgraphIndex = new CreateIndexModel<ReachabilityFactDocument>(
|
||||
Builders<ReachabilityFactDocument>.IndexKeys.Ascending(doc => doc.CallgraphId),
|
||||
new CreateIndexOptions { Name = "reachability_callgraph_lookup" });
|
||||
|
||||
collection.Indexes.CreateOne(callgraphIndex);
|
||||
}
|
||||
catch (MongoCommandException ex) when (string.Equals(ex.CodeName, "IndexOptionsConflict", StringComparison.Ordinal))
|
||||
{
|
||||
// Ignore when indexes already exist with different options to keep startup idempotent.
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryEnsureSealedMode(SignalsSealedModeMonitor monitor, out IResult? failure)
|
||||
{
|
||||
if (!monitor.EnforcementEnabled)
|
||||
@@ -876,31 +835,4 @@ public partial class Program
|
||||
statusCode: StatusCodes.Status503ServiceUnavailable);
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void EnsureUnknownsIndexes(IMongoCollection<UnknownSymbolDocument> collection)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(collection);
|
||||
|
||||
try
|
||||
{
|
||||
var subjectIndex = new CreateIndexModel<UnknownSymbolDocument>(
|
||||
Builders<UnknownSymbolDocument>.IndexKeys.Ascending(doc => doc.SubjectKey),
|
||||
new CreateIndexOptions { Name = "unknowns_subject_lookup" });
|
||||
|
||||
var dedupeIndex = new CreateIndexModel<UnknownSymbolDocument>(
|
||||
Builders<UnknownSymbolDocument>.IndexKeys
|
||||
.Ascending(doc => doc.SubjectKey)
|
||||
.Ascending(doc => doc.SymbolId)
|
||||
.Ascending(doc => doc.Purl)
|
||||
.Ascending(doc => doc.EdgeFrom)
|
||||
.Ascending(doc => doc.EdgeTo),
|
||||
new CreateIndexOptions { Name = "unknowns_subject_symbol_edge_unique", Unique = true });
|
||||
|
||||
collection.Indexes.CreateMany(new[] { subjectIndex, dedupeIndex });
|
||||
}
|
||||
catch (MongoCommandException ex) when (string.Equals(ex.CodeName, "IndexOptionsConflict", StringComparison.Ordinal))
|
||||
{
|
||||
// Ignore to keep startup idempotent when index options differ.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user