OPC # 0001: Extract OPC into standalone repo
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
using Scalar.Aspire;
|
||||
|
||||
var builder = DistributedApplication.CreateBuilder(args);
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Platform infrastructure (Keycloak, Vault, MinIO, Nginx, Dnsmasq) is
|
||||
// managed by infra/docker-compose.yml — NOT Aspire.
|
||||
// Run `docker compose up -d` from the infra/ folder before starting this host.
|
||||
//
|
||||
// Fixed dev URLs (hardcoded to match infra/docker-compose.yml):
|
||||
// Keycloak → http://localhost:8080
|
||||
// Vault → http://localhost:8200
|
||||
// MinIO → http://localhost:9000
|
||||
//
|
||||
// ControlPlane owns: opc-postgres (opcdb + giteadb), RabbitMQ, Gitea.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
// Shared paths
|
||||
var clientAssetsPath = Path.GetFullPath(Path.Combine(builder.AppHostDirectory, "..", "ClientAssets"));
|
||||
var nginxConfDPath = Path.GetFullPath(Path.Combine(builder.AppHostDirectory, "..", "infra", "nginx", "conf.d"));
|
||||
var vaultKeysFile = Path.GetFullPath(Path.Combine(builder.AppHostDirectory, "..", "infra", "vault", "data", "init.json"));
|
||||
|
||||
#region CONTROLPLANE POSTGRES
|
||||
// ControlPlane owns this — isolated from platform infra postgres.
|
||||
// Override via: dotnet user-secrets set "Parameters:cp-postgres-password" "yourpassword"
|
||||
var cpPostgresPassword = builder.AddParameter("cp-postgres-password", "controlplane-dev", secret: true);
|
||||
var cpPostgres = builder.AddPostgres("opc-postgres", password: cpPostgresPassword)
|
||||
.WithLifetime(ContainerLifetime.Persistent)
|
||||
.WithDataVolume("opc-postgres-data")
|
||||
.WithHostPort(5433)
|
||||
.WithPgAdmin();
|
||||
|
||||
var controlPlaneDb = cpPostgres.AddDatabase("opcdb");
|
||||
var giteaDb = cpPostgres.AddDatabase("giteadb");
|
||||
#endregion
|
||||
|
||||
#region GITEA
|
||||
// Gitea is ControlPlane's code management component — owns its own DB on opc-postgres.
|
||||
var gitea = builder.AddContainer("gitea", "gitea/gitea", "latest")
|
||||
.WithHttpEndpoint(port: 3000, targetPort: 3000, name: "http")
|
||||
.WithEndpoint(port: 2222, targetPort: 22, name: "ssh")
|
||||
.WithVolume("clarity-gitea-data", "/data")
|
||||
.WithEnvironment("GITEA__database__DB_TYPE", "postgres")
|
||||
.WithEnvironment("GITEA__database__HOST", "host.docker.internal:5433")
|
||||
.WithEnvironment("GITEA__database__NAME", "giteadb")
|
||||
.WithEnvironment("GITEA__database__USER", "postgres")
|
||||
.WithEnvironment("GITEA__database__PASSWD", "controlplane-dev")
|
||||
.WithEnvironment("GITEA__server__DOMAIN", "opc.clarity.test")
|
||||
.WithEnvironment("GITEA__server__ROOT_URL", "http://opc.clarity.test")
|
||||
.WithEnvironment("GITEA__server__SSH_DOMAIN", "opc.clarity.test")
|
||||
.WithEnvironment("GITEA__server__SSH_PORT", "2222")
|
||||
.WithEnvironment("GITEA__service__DISABLE_REGISTRATION", "true")
|
||||
.WaitFor(giteaDb)
|
||||
.WithLifetime(ContainerLifetime.Persistent);
|
||||
#endregion
|
||||
|
||||
#region RABBITMQ
|
||||
var rabbitPassword = builder.AddParameter("rabbitmq-password", "clarity-rabbit", secret: true);
|
||||
var rabbit = builder.AddRabbitMQ("rabbitmq", password: rabbitPassword)
|
||||
.WithLifetime(ContainerLifetime.Persistent)
|
||||
.WithManagementPlugin();
|
||||
#endregion
|
||||
|
||||
#region CONTROLPLANE API
|
||||
var api = builder.AddProject<Projects.ControlPlane_Api>("controlplane-api")
|
||||
.WithReference(rabbit)
|
||||
.WaitFor(rabbit)
|
||||
.WithReference(controlPlaneDb)
|
||||
.WaitFor(controlPlaneDb)
|
||||
.WithEnvironment("Gitea__BaseUrl", gitea.GetEndpoint("http"))
|
||||
.WithEnvironment("ClientAssets__Folder", clientAssetsPath)
|
||||
.WithEnvironment("Docker__RepoRoot", builder.AppHostDirectory.Replace("ControlPlane.AppHost", "").TrimEnd('\\', '/'))
|
||||
.WithExternalHttpEndpoints();
|
||||
#endregion
|
||||
|
||||
#region PROVISIONING WORKER
|
||||
builder.AddProject<Projects.ControlPlane_Worker>("controlplane-worker")
|
||||
.WithReference(rabbit)
|
||||
.WaitFor(rabbit)
|
||||
// Vault — fixed dev address from infra/docker-compose.yml
|
||||
.WithEnvironment("Vault__Address", "http://localhost:8200")
|
||||
.WithEnvironment("Vault__ContainerAddress", "http://vault:8200")
|
||||
.WithEnvironment("Vault__KeysFile", vaultKeysFile)
|
||||
// Keycloak — fixed dev address from infra/docker-compose.yml
|
||||
.WithEnvironment("Keycloak__AuthServerUrl", "http://localhost:8080")
|
||||
.WithEnvironment("Keycloak__ContainerUrl", "https://keycloak.clarity.test")
|
||||
.WithEnvironment("Keycloak__Realm", "master")
|
||||
.WithEnvironment("Keycloak__Resource", "admin-cli")
|
||||
.WithEnvironment("Keycloak__AdminUser", "admin")
|
||||
.WithEnvironment("Keycloak__AdminPassword", "Admin1234!")
|
||||
// Gateway
|
||||
.WithEnvironment("Gateway__TenantBaseUrl", "https://{subdomain}.clarity.test")
|
||||
// ClarityInfraOptions
|
||||
.WithEnvironment("Clarity__Domain", "clarity.test")
|
||||
.WithEnvironment("Clarity__Network", "clarity-net")
|
||||
.WithEnvironment("Clarity__KeycloakPublicUrl", "https://keycloak.clarity.test")
|
||||
.WithEnvironment("Clarity__KeycloakInternalUrl", "http://keycloak:8080")
|
||||
.WithEnvironment("Clarity__VaultInternalUrl", "http://vault:8200")
|
||||
.WithEnvironment("Clarity__NginxCertPath", "/etc/nginx/certs/clarity.test.crt")
|
||||
.WithEnvironment("Clarity__NginxCertKeyPath", "/etc/nginx/certs/clarity.test.key")
|
||||
// Nginx conf.d — points to infra/nginx/conf.d so platform nginx picks up tenant configs
|
||||
.WithEnvironment("Nginx__ConfDPath", nginxConfDPath)
|
||||
.WithEnvironment("ClientAssets__Folder", clientAssetsPath)
|
||||
// Platform Postgres connection string for tenant database provisioning (infra/docker-compose.yml)
|
||||
.WithEnvironment("ConnectionStrings__platformdb",
|
||||
"Host=localhost;Port=5432;Username=postgres;Password=postgres")
|
||||
.WithReference(controlPlaneDb)
|
||||
.WaitFor(controlPlaneDb);
|
||||
#endregion
|
||||
|
||||
#region CONTROLPLANE UI
|
||||
builder.AddViteApp("controlplane-ui", "../clarity.controlplane")
|
||||
.WithReference((IResourceBuilder<IResourceWithServiceDiscovery>)api)
|
||||
.WaitFor(api);
|
||||
#endregion
|
||||
|
||||
#region CLARITY-NET — connect RabbitMQ to platform network
|
||||
// Ensures RabbitMQ (the one container Aspire owns) is reachable from tenant containers
|
||||
// on clarity-net. All other platform containers are already on clarity-net via docker-compose.
|
||||
builder.Eventing.Subscribe<AfterResourcesCreatedEvent>(async (@event, ct) =>
|
||||
{
|
||||
const string network = "clarity-net";
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(4), ct);
|
||||
|
||||
var (inspectCode, _) = await DockerOutputAsync($"network inspect {network}", ct);
|
||||
if (inspectCode != 0)
|
||||
await DockerOutputAsync($"network create {network}", ct);
|
||||
|
||||
var (idCode, idOut) = await DockerOutputAsync("ps --filter name=rabbitmq --format {{.ID}}", ct);
|
||||
if (idCode == 0 && !string.IsNullOrWhiteSpace(idOut))
|
||||
{
|
||||
var containerId = idOut.Trim().Split('\n')[0].Trim();
|
||||
await DockerOutputAsync($"network connect --alias rabbitmq {network} {containerId}", ct);
|
||||
}
|
||||
});
|
||||
#endregion
|
||||
|
||||
#region SCALAR API DOCS
|
||||
var scalar = builder.AddScalarApiReference();
|
||||
scalar.WithApiReference(api);
|
||||
#endregion
|
||||
|
||||
builder.Build().Run();
|
||||
|
||||
static async Task<(int ExitCode, string Output)> DockerOutputAsync(string args, CancellationToken ct)
|
||||
{
|
||||
var psi = new System.Diagnostics.ProcessStartInfo("docker", args)
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
using var proc = System.Diagnostics.Process.Start(psi)!;
|
||||
var output = await proc.StandardOutput.ReadToEndAsync(ct);
|
||||
await proc.WaitForExitAsync(ct);
|
||||
return (proc.ExitCode, output);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user