feat: harden self-managed reconciliation
This commit is contained in:
+42
-27
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user