fix: use PK01 public PostgreSQL endpoint
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { createHash, randomBytes } from "node:crypto";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { dirname, isAbsolute, join } from "node:path";
|
||||
import type { UniDeskConfig } from "./config";
|
||||
@@ -248,10 +249,23 @@ interface RemoteFacts {
|
||||
appConnectionOk: boolean | null;
|
||||
appConnectionSsl: boolean | null;
|
||||
appConnectionHost: string | null;
|
||||
appConnectionError: string | null;
|
||||
};
|
||||
raw?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface ControllerConnectionProbe {
|
||||
attempted: boolean;
|
||||
ok: boolean | null;
|
||||
ssl: boolean | null;
|
||||
host: string;
|
||||
port: number;
|
||||
clientAddr: string | null;
|
||||
serverAddr: string | null;
|
||||
psqlVersion: string | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
interface RemoteJobStart {
|
||||
ok: boolean;
|
||||
remoteJobId: string | null;
|
||||
@@ -367,6 +381,8 @@ async function status(config: UniDeskConfig, options: PlatformDbOptions): Promis
|
||||
const secrets = inspectSecrets(pg, false);
|
||||
const remote = await remoteFacts(config, pg, secrets);
|
||||
const facts = remote.parsed;
|
||||
const controllerConnection = controllerConnectionProbe(pg, secrets);
|
||||
const endpointHealthy = controllerConnection.ok === true && controllerConnection.ssl === true;
|
||||
const deploymentHealthy = facts !== null
|
||||
&& facts.postgres.packageInstalled
|
||||
&& facts.postgres.serviceActive
|
||||
@@ -376,7 +392,14 @@ async function status(config: UniDeskConfig, options: PlatformDbOptions): Promis
|
||||
&& facts.network.port5432Listening
|
||||
&& facts.postgres.appConnectionOk === true
|
||||
&& facts.postgres.appConnectionSsl === true;
|
||||
const cutoverReady = deploymentHealthy && facts.network.dns.resolves;
|
||||
const cutoverReady = deploymentHealthy && secrets.ok && endpointHealthy;
|
||||
const cutoverBlockers = cutoverReady
|
||||
? []
|
||||
: [
|
||||
...(deploymentHealthy ? [] : ["deployment-unhealthy"]),
|
||||
...(secrets.ok ? [] : ["secrets-unhealthy"]),
|
||||
...(endpointHealthy ? [] : [controllerConnection.attempted ? "connection-host-probe-failed" : "connection-host-probe-unavailable"]),
|
||||
];
|
||||
return {
|
||||
ok: remote.capture.exitCode === 0 && facts !== null && deploymentHealthy && secrets.ok,
|
||||
action: "platform-db-postgres-status",
|
||||
@@ -386,7 +409,9 @@ async function status(config: UniDeskConfig, options: PlatformDbOptions): Promis
|
||||
healthy: deploymentHealthy,
|
||||
deploymentHealthy,
|
||||
cutoverReady,
|
||||
cutoverBlockers: cutoverReady ? [] : ["dns-db-pikapython-unresolved"],
|
||||
cutoverBlockers,
|
||||
connectionHost: pg.postgres.network.connectionHost,
|
||||
publicDns: pg.postgres.network.publicDns,
|
||||
pgVersion: pg.postgres.package.version,
|
||||
packageInstalled: facts.postgres.packageInstalled,
|
||||
serviceActive: facts.postgres.serviceActive,
|
||||
@@ -399,10 +424,13 @@ async function status(config: UniDeskConfig, options: PlatformDbOptions): Promis
|
||||
appConnectionOk: facts.postgres.appConnectionOk,
|
||||
appConnectionSsl: facts.postgres.appConnectionSsl,
|
||||
appConnectionHost: facts.postgres.appConnectionHost,
|
||||
appConnectionError: facts.postgres.appConnectionError,
|
||||
connectionHostProbe: controllerConnection,
|
||||
port5432Listening: facts.network.port5432Listening,
|
||||
serverVersion: facts.postgres.serverVersion,
|
||||
secretsOk: secrets.ok,
|
||||
dnsRequiredBeforeSub2ApiCutover: true,
|
||||
dnsRequiredBeforeSub2ApiCutover: false,
|
||||
dnsDisposition: facts.network.dns.resolves ? "optional-alias-resolves" : "optional-alias-unresolved",
|
||||
},
|
||||
remoteFacts: facts,
|
||||
secrets: secretSummary(secrets),
|
||||
@@ -1042,6 +1070,10 @@ function quoteEnv(value: string): string {
|
||||
return `'${value.replaceAll("'", "'\"'\"'")}'`;
|
||||
}
|
||||
|
||||
function compactText(value: string): string {
|
||||
return value.replace(/\s+/gu, " ").trim().slice(0, 500);
|
||||
}
|
||||
|
||||
function fingerprintValues(values: Record<string, string>, keys: string[]): string {
|
||||
const material = keys
|
||||
.slice()
|
||||
@@ -1127,7 +1159,7 @@ function desiredChecks(pg: PostgresHostConfig, facts: RemoteFacts, secrets: Secr
|
||||
{ name: "pgdg-repo-release", ok: facts.repo.reachable, releaseUrl: facts.repo.releaseUrl, firstLine: facts.repo.firstLine },
|
||||
{ name: "secrets", ok: secrets.ok, entries: secrets.entries.map((entry) => ({ sourceRef: entry.sourceRef, action: entry.action, missingKeys: entry.missingKeys })) },
|
||||
{ name: "pg16-required", ok: pg.postgres.package.version === "16", detail: "Sub2API deployment requires PostgreSQL 16 per issue #281 update." },
|
||||
{ name: "dns-db-pikapython", ok: facts.network.dns.resolves, host: facts.network.dns.hostname, addresses: facts.network.dns.addresses, requiredBeforeSub2ApiCutover: true },
|
||||
{ name: "dns-db-pikapython", ok: true, host: facts.network.dns.hostname, addresses: facts.network.dns.addresses, requiredBeforeSub2ApiCutover: false, disposition: facts.network.dns.resolves ? "optional-alias-resolves" : "optional-alias-unresolved" },
|
||||
{ name: "postgres-tls-required", ok: pg.postgres.network.tls.enabled && pg.postgres.network.sslmode === "require", transport: pg.postgres.network.transport, sslmode: pg.postgres.network.sslmode },
|
||||
{ name: "remote-pg-hba-hostssl", ok: pg.postgres.auth.pgHba.filter((rule) => rule.type !== "local" && rule.address !== "127.0.0.1/32").every((rule) => rule.type === "hostssl"), detail: "remote PostgreSQL access must use hostssl so plaintext remote connections are refused" },
|
||||
{ name: "postgres-ssl-runtime", ok: !facts.postgres.packageInstalled || facts.postgres.sslOn, observed: facts.postgres.sslOn, disposition: facts.postgres.packageInstalled ? "must-be-on" : "will-enable-on-apply" },
|
||||
@@ -1151,6 +1183,50 @@ function appProbe(pg: PostgresHostConfig, secrets: SecretInspection | null): { u
|
||||
return { user: role.name, password, database: database.name };
|
||||
}
|
||||
|
||||
function controllerConnectionProbe(pg: PostgresHostConfig, secrets: SecretInspection): ControllerConnectionProbe {
|
||||
const host = pg.postgres.network.connectionHost;
|
||||
const port = pg.postgres.network.port;
|
||||
const base = { host, port, clientAddr: null, serverAddr: null };
|
||||
if (!secrets.ok) {
|
||||
return { ...base, attempted: false, ok: null, ssl: null, psqlVersion: null, error: "secrets-unhealthy" };
|
||||
}
|
||||
const probe = appProbe(pg, secrets);
|
||||
if (probe === null) {
|
||||
return { ...base, attempted: false, ok: null, ssl: null, psqlVersion: null, error: "probe-secret-material-unavailable" };
|
||||
}
|
||||
const version = spawnSync("psql", ["--version"], { encoding: "utf8", timeout: 5_000 });
|
||||
if (version.error !== undefined || version.status !== 0) {
|
||||
return {
|
||||
...base,
|
||||
attempted: false,
|
||||
ok: null,
|
||||
ssl: null,
|
||||
psqlVersion: null,
|
||||
error: compactText(version.error?.message ?? version.stderr ?? "psql-unavailable"),
|
||||
};
|
||||
}
|
||||
const conn = `host=${host} port=${port} user=${probe.user} dbname=${probe.database} sslmode=require connect_timeout=8`;
|
||||
const result = spawnSync("psql", ["-Atq", "-F", "\t", conn, "-c", "SELECT inet_client_addr()::text, inet_server_addr()::text, ssl::text FROM pg_stat_ssl WHERE pid = pg_backend_pid();"], {
|
||||
encoding: "utf8",
|
||||
timeout: 12_000,
|
||||
env: { ...process.env, PGPASSWORD: probe.password },
|
||||
});
|
||||
const line = result.stdout.trim().split(/\r?\n/u).find((item) => item.trim().length > 0);
|
||||
const fields = line?.split("\t") ?? [];
|
||||
const sslText = (fields[2] ?? "").toLowerCase();
|
||||
const ssl = ["t", "true", "1", "on"].includes(sslText) ? true : ["f", "false", "0", "off"].includes(sslText) ? false : null;
|
||||
return {
|
||||
...base,
|
||||
attempted: true,
|
||||
ok: result.status === 0 && ssl === true,
|
||||
ssl,
|
||||
clientAddr: fields[0] ?? null,
|
||||
serverAddr: fields[1] ?? null,
|
||||
psqlVersion: version.stdout.trim() || null,
|
||||
error: result.status === 0 ? null : compactText(result.stderr || result.error?.message || "psql-connection-failed"),
|
||||
};
|
||||
}
|
||||
|
||||
function factsScript(pg: PostgresHostConfig, probe: { user: string; password: string; database: string } | null): string {
|
||||
const release = releaseUrl(pg);
|
||||
const dataDir = pg.postgres.paths.dataDir;
|
||||
@@ -1280,6 +1356,7 @@ payload = {
|
||||
"appConnectionOk": None if text("appConnRc") == "" else text("appConnRc") == "0",
|
||||
"appConnectionSsl": None if text("appSsl") == "" else text("appSsl").lower() in {"t", "true", "1", "on"},
|
||||
"appConnectionHost": text("appConnHost") or None,
|
||||
"appConnectionError": text("appConnErr") or None,
|
||||
},
|
||||
}
|
||||
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
||||
|
||||
Reference in New Issue
Block a user