OPC # 0001: Extract OPC into standalone repo
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
using ControlPlane.Core.Config;
|
||||
using ControlPlane.Core.Interfaces;
|
||||
using ControlPlane.Core.Models;
|
||||
using ControlPlane.Worker.Services;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace ControlPlane.Worker.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Final saga step — launches the clarity-server Docker container with the fully
|
||||
/// enriched SagaContext (connection strings, Keycloak realm, etc. all known).
|
||||
/// Runs LAST so all env vars are available at container start.
|
||||
/// </summary>
|
||||
public class LaunchStep(
|
||||
ILogger<LaunchStep> logger,
|
||||
IConfiguration config,
|
||||
IOptions<ClarityInfraOptions> infraOptions,
|
||||
ClarityContainerService containers) : ISagaStep
|
||||
{
|
||||
public string StepName => "Container Launch";
|
||||
|
||||
public async Task ExecuteAsync(SagaContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var job = context.Job;
|
||||
|
||||
logger.LogInformation("[{JobId}] Launching container {Env}-app-clarity-{Site}",
|
||||
job.Id, job.Environment, job.SiteCode);
|
||||
|
||||
var containerName = await containers.StartTenantContainerAsync(
|
||||
environment: job.Environment,
|
||||
siteCode: job.SiteCode,
|
||||
subdomain: job.Subdomain,
|
||||
keycloakRealm: $"clarity-{job.Subdomain.ToLowerInvariant()}",
|
||||
postgresConnectionString: context.TenantConnectionString,
|
||||
vaultToken: ReadVaultToken(config),
|
||||
jobId: job.Id,
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
context.ContainerName = containerName;
|
||||
context.TenantApiBaseUrl = infraOptions.Value.TenantPublicUrl(job.Subdomain);
|
||||
|
||||
logger.LogInformation("[{JobId}] Container {Name} live at {Url}",
|
||||
job.Id, containerName, context.TenantApiBaseUrl);
|
||||
|
||||
context.Job.CompletedSteps |= CompletedSteps.InfrastructureProvisioned;
|
||||
}
|
||||
|
||||
public async Task CompensateAsync(SagaContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(context.ContainerName)) return;
|
||||
|
||||
logger.LogWarning("[{JobId}] Compensating: removing container {Name}", context.Job.Id, context.ContainerName);
|
||||
await containers.StopAndRemoveAsync(context.ContainerName, cancellationToken);
|
||||
await containers.RemoveNginxConfigAsync(context.Job.Subdomain, cancellationToken);
|
||||
}
|
||||
|
||||
// Reads the Vault root token from the persisted init.json on the Vault volume.
|
||||
// Falls back to config["Vault:Token"] then "root" for local dev.
|
||||
private static string? ReadVaultToken(IConfiguration config)
|
||||
{
|
||||
var keysFile = config["Vault:KeysFile"];
|
||||
if (!string.IsNullOrWhiteSpace(keysFile) && File.Exists(keysFile))
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(keysFile);
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(json);
|
||||
if (doc.RootElement.TryGetProperty("root_token", out var tok))
|
||||
return tok.GetString();
|
||||
}
|
||||
catch { /* fall through */ }
|
||||
}
|
||||
|
||||
return config["Vault:Token"] ?? "root";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user