feat: harden self-managed reconciliation

This commit is contained in:
2026-09-05 10:17:55 +00:00 Unverified
parent 8e9d207915
commit 321f4e807a
40 changed files with 4977 additions and 404 deletions
+42 -27
View File
@@ -2,10 +2,7 @@ import { createApp, cleanupExpiredSessions } from "./app";
import { RedactingAuditStore } from "./audit-store";
import { readFile } from "node:fs/promises";
import { IMAGE_REGISTRY } from "../const";
import {
BuildController,
buildImageName,
} from "./build-controller";
import { BuildController, buildImageName } from "./build-controller";
import {
KubernetesBuildOperations,
KubernetesBuildStore,
@@ -22,6 +19,7 @@ import {
KubernetesWorkspaceLeaseProvider,
KubernetesWorkspacePersistence,
KubernetesWorkspaceStore,
KubernetesTrustStore,
} from "./kubernetes-state";
import {
execUpgradeMatch,
@@ -51,23 +49,23 @@ const operationStore = new PersistentOperationStore(
const auditStore = new RedactingAuditStore(
new KubernetesAuditPersistence(clients.objects),
);
const trustStore = new KubernetesTrustStore(clients.objects);
const management = createManagementService(
createKubernetesManagementDependencies(clients),
);
const namespace = process.env.KUBER_SYSTEM_NAMESPACE?.trim() || "kuber-system";
const dataRoot = process.env.KUBER_DATA_ROOT?.trim() || "/data";
const registry = (process.env.KUBER_BUILD_REGISTRY?.trim() || IMAGE_REGISTRY).replace(
/\/+$/,
"",
);
const registry = (
process.env.KUBER_BUILD_REGISTRY?.trim() || IMAGE_REGISTRY
).replace(/\/+$/, "");
const registryConfigPath =
process.env.KUBER_REGISTRY_CONFIG?.trim() || "/etc/kuber/registry/config.json";
process.env.KUBER_REGISTRY_CONFIG?.trim() ||
"/etc/kuber/registry/config.json";
const registryResolveOrigin = process.env.KUBER_REGISTRY_RESOLVE_ORIGIN?.trim();
const internalRegistryHost = process.env.KUBER_INTERNAL_REGISTRY_HOST?.trim();
const internalRegistryInsecure =
process.env.KUBER_INTERNAL_REGISTRY_INSECURE?.trim() === "true";
const pushImagePrefix =
process.env.KUBER_PUSH_IMAGE_PREFIX?.trim() || "kuber/";
const pushImagePrefix = process.env.KUBER_PUSH_IMAGE_PREFIX?.trim() || "kuber/";
if (!process.env.KUBER_REGISTRY_SECRET?.trim()) {
console.warn(
"KUBER_REGISTRY_SECRET is unset; BuildKit will use anonymous registry access",
@@ -88,10 +86,15 @@ async function registryCredentials(): Promise<RegistryCredentials | undefined> {
config.auths?.[`https://${host}`] ??
config.auths?.[`https://${host}/v1/`];
if (!entry?.auth) return;
const separator = Buffer.from(entry.auth, "base64").toString("utf8").indexOf(":");
const separator = Buffer.from(entry.auth, "base64")
.toString("utf8")
.indexOf(":");
if (separator < 0) return;
const value = Buffer.from(entry.auth, "base64").toString("utf8");
return { username: value.slice(0, separator), password: value.slice(separator + 1) };
return {
username: value.slice(0, separator),
password: value.slice(separator + 1),
};
}
const buildStore = new KubernetesBuildStore(
@@ -105,10 +108,12 @@ const builds = new BuildController({
kubernetes: new KubernetesBuildOperations(clients.batch, clients.core),
namespace,
workspaceRoot: `${dataRoot}/workspaces`,
workspaceClaimName: process.env.KUBER_BUILD_DATA_CLAIM?.trim() || "kuber-build-data",
workspaceClaimName:
process.env.KUBER_BUILD_DATA_CLAIM?.trim() || "kuber-build-data",
cacheImage: (request) =>
`${(internalRegistryHost ?? registry)}/kuber/cache-${request.project}-${request.service}`,
imageName: (request) => buildImageName(registry, request.project, request.service),
`${internalRegistryHost ?? registry}/kuber/cache-${request.project}-${request.service}`,
imageName: (request) =>
buildImageName(registry, request.project, request.service),
pushImage: internalRegistryHost
? (request) =>
`${internalRegistryHost}/${pushImagePrefix}${request.project}-${request.service}:latest`
@@ -162,15 +167,18 @@ try {
const sessionCleanupIntervalMs = Number(
process.env.KUBER_SESSION_CLEANUP_MS ?? 10 * 60 * 1000,
);
const sessionCleanupTimer = setInterval(async () => {
try {
await cleanupExpiredSessions(store);
} catch (error) {
console.error("Expired session cleanup failed", error);
}
}, Number.isFinite(sessionCleanupIntervalMs) && sessionCleanupIntervalMs > 0
? sessionCleanupIntervalMs
: 10 * 60 * 1000);
const sessionCleanupTimer = setInterval(
async () => {
try {
await cleanupExpiredSessions(store);
} catch (error) {
console.error("Expired session cleanup failed", error);
}
},
Number.isFinite(sessionCleanupIntervalMs) && sessionCleanupIntervalMs > 0
? sessionCleanupIntervalMs
: 10 * 60 * 1000,
);
sessionCleanupTimer.unref?.();
const app = createApp({
@@ -178,6 +186,7 @@ const app = createApp({
workspaceStore,
operationStore,
auditStore,
trustStore,
management,
builds,
logs,
@@ -190,7 +199,11 @@ const app = createApp({
origin: registryResolveOrigin,
insecure: registryResolveOrigin?.startsWith("http://"),
});
return { image: image.replace(/:latest$/, ""), digest, reference: `${image.replace(/:latest$/, "")}@${digest}` };
return {
image: image.replace(/:latest$/, ""),
digest,
reference: `${image.replace(/:latest$/, "")}@${digest}`,
};
},
allowedOrigins: (
process.env.KUBER_ALLOWED_ORIGINS ?? "https://kuber.astrxl.dev"
@@ -242,7 +255,9 @@ const server = Bun.serve<ExecConnectionData>({
session?: WireExecSession;
};
state.session?.receive(
typeof message === "string" ? message : new TextDecoder().decode(message),
typeof message === "string"
? message
: new TextDecoder().decode(message),
);
},
close(ws) {