fix: filter domain assembly scans to Default ALC to prevent type identity mismatches
Plugin assemblies loaded via PluginHost into isolated AssemblyLoadContexts produce distinct types even from the same DLL. When AppDomain.GetAssemblies() returns both Default and plugin-ALC copies, DI registration and IOptions<T> resolution silently fail (e.g. ValkeyTransportOptions defaulting to localhost). Applied AssemblyLoadContext.Default filter to all 7 assembly discovery sites: - MessagingServiceCollectionExtensions (transport plugin scan) - StellaRouterIntegrationHelper (transport plugin loader) - Gateway.WebService Program.cs (startup transport scan) - GeneratedEndpointDiscoveryProvider (endpoint provider scan) - ReflectionEndpointDiscoveryProvider (endpoint attribute scan) - ServiceCollectionExtensions (schema provider scan) - MigrationModulePluginDiscovery (migration plugin scan) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
116
src/Scanner/StellaOps.Scanner.Cartographer/Program.cs
Normal file
116
src/Scanner/StellaOps.Scanner.Cartographer/Program.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Cartographer.Options;
|
||||
|
||||
using StellaOps.Router.AspNet;
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Configuration
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddEnvironmentVariables("CARTOGRAPHER_");
|
||||
|
||||
builder.Services.AddOptions();
|
||||
builder.Services.AddLogging();
|
||||
|
||||
var authoritySection = builder.Configuration.GetSection("Cartographer:Authority");
|
||||
builder.Services.AddOptions<CartographerAuthorityOptions>()
|
||||
.Bind(authoritySection)
|
||||
.PostConfigure(CartographerAuthorityOptionsConfigurator.ApplyDefaults)
|
||||
.ValidateOnStart();
|
||||
builder.Services.AddSingleton<IValidateOptions<CartographerAuthorityOptions>, CartographerAuthorityOptionsValidator>();
|
||||
|
||||
var authorityOptions = authoritySection.Get<CartographerAuthorityOptions>() ?? new CartographerAuthorityOptions();
|
||||
CartographerAuthorityOptionsConfigurator.ApplyDefaults(authorityOptions);
|
||||
authorityOptions.Validate();
|
||||
|
||||
if (authorityOptions.Enabled)
|
||||
{
|
||||
builder.Services.AddStellaOpsResourceServerAuthentication(
|
||||
builder.Configuration,
|
||||
configurationSection: null,
|
||||
configure: resourceOptions =>
|
||||
{
|
||||
resourceOptions.Authority = authorityOptions.Issuer;
|
||||
resourceOptions.RequireHttpsMetadata = authorityOptions.RequireHttpsMetadata;
|
||||
resourceOptions.MetadataAddress = authorityOptions.MetadataAddress;
|
||||
resourceOptions.BackchannelTimeout = TimeSpan.FromSeconds(authorityOptions.BackchannelTimeoutSeconds);
|
||||
resourceOptions.TokenClockSkew = TimeSpan.FromSeconds(authorityOptions.TokenClockSkewSeconds);
|
||||
|
||||
resourceOptions.Audiences.Clear();
|
||||
foreach (var audience in authorityOptions.Audiences)
|
||||
{
|
||||
resourceOptions.Audiences.Add(audience);
|
||||
}
|
||||
|
||||
resourceOptions.RequiredScopes.Clear();
|
||||
foreach (var scope in authorityOptions.RequiredScopes)
|
||||
{
|
||||
resourceOptions.RequiredScopes.Add(scope);
|
||||
}
|
||||
});
|
||||
|
||||
builder.Services.AddAuthorization(options =>
|
||||
{
|
||||
if (authorityOptions.AllowAnonymousFallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
options.FallbackPolicy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.AddAuthenticationSchemes(StellaOpsAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddRequirements(new StellaOpsScopeRequirement(authorityOptions.RequiredScopes.ToArray()))
|
||||
.Build();
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: register Cartographer graph builders, overlay workers, and Authority client once implementations land.
|
||||
builder.Services.AddHealthChecks()
|
||||
.AddCheck("cartographer_ready", () => HealthCheckResult.Healthy(), tags: new[] { "ready" });
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
|
||||
// Stella Router integration
|
||||
var routerEnabled = builder.Services.AddRouterMicroservice(
|
||||
builder.Configuration,
|
||||
serviceName: "cartographer",
|
||||
version: System.Reflection.CustomAttributeExtensions.GetCustomAttribute<System.Reflection.AssemblyInformationalVersionAttribute>(System.Reflection.Assembly.GetExecutingAssembly())?.InformationalVersion ?? "1.0.0",
|
||||
routerOptionsSection: "Router");
|
||||
builder.TryAddStellaOpsLocalBinding("cartographer");
|
||||
var app = builder.Build();
|
||||
app.LogStellaOpsLocalHostname("cartographer");
|
||||
|
||||
if (!authorityOptions.Enabled)
|
||||
{
|
||||
app.Logger.LogWarning("Cartographer Authority authentication is disabled; enable it before production deployments.");
|
||||
}
|
||||
else if (authorityOptions.AllowAnonymousFallback)
|
||||
{
|
||||
app.Logger.LogWarning("Cartographer Authority allows anonymous fallback; disable fallback before production rollout.");
|
||||
}
|
||||
|
||||
app.UseStellaOpsCors();
|
||||
if (authorityOptions.Enabled)
|
||||
{
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
}
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
|
||||
app.MapHealthChecks("/healthz").AllowAnonymous();
|
||||
app.MapHealthChecks("/readyz", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
|
||||
{
|
||||
Predicate = check => check.Tags.Contains("ready")
|
||||
}).AllowAnonymous();
|
||||
|
||||
app.TryRefreshStellaRouterEndpoints(routerEnabled);
|
||||
app.Run();
|
||||
|
||||
public partial class Program;
|
||||
|
||||
Reference in New Issue
Block a user