feat: add d601 dev environment foundation
This commit is contained in:
@@ -37,6 +37,7 @@ UniDesk 是一个以主 server 为统一入口的分布式工作平台;本文
|
||||
- `bun scripts/cli.ts microservice list/status/health/diagnostics/tunnel-self-test/proxy`:管理和验证挂载在主 server、计算节点 Docker 或 k3s 控制面上的用户服务,`proxy` 支持受控 JSON body,OA Event Flow/Todo Note/Baidu Netdisk/Code Queue Manager on main-server、k3s Control/Code Queue 执行面/MDTODO/Decision Center/FindJob/Pipeline/MET Nonlinear on D601 的规则见 `docs/reference/microservices.md`。
|
||||
- `bun scripts/cli.ts decision upload/list/show/health`:通过 backend-core 用户服务代理上传会议记录/决议 Markdown、列出记录和查看详情;Decision Center 运行在 D601 k3s,规则见 `docs/reference/microservices.md`。
|
||||
- `bun scripts/cli.ts deploy check/plan/apply [--file deploy.json] [--service <id>]`:按根目录 `deploy.json` 的服务 repo 和 commit 期望状态校验或更新用户服务,目标侧自行 fetch、构建、部署和 live commit 验证;规则见 `docs/reference/deploy.md`。
|
||||
- `bun scripts/cli.ts dev-env validate [--manifest path] [--kubectl-dry-run]`:离线校验 D601 `unidesk-dev` namespace 与 dev PostgreSQL 底座 manifest 的生产隔离护栏,规则见 `docs/reference/deploy.md` 与 `docs/reference/microservices.md`。
|
||||
- `bun scripts/cli.ts ci install/status/run/logs`:在 D601 原生 k3s 上安装和运行 Tekton CI,只做每 commit 检查和 Code Queue 只读性能门禁,不部署 CD;规则见 `docs/reference/ci.md`。
|
||||
- `bun scripts/cli.ts codex deploy <commitId>`:Code Queue 兼容部署入口,会生成临时 desired manifest 并调用 `deploy apply --service code-queue` 的同一条 target-side build 与 live commit 验证路径;规则见 `docs/reference/codex-deploy.md`。
|
||||
- `bun scripts/cli.ts codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue <id>]`:通过 backend-core 私有代理提交 Code Queue 任务;控制面默认走主 server `code-queue-mgr` 写入 PostgreSQL,`--dry-run` 可只检查请求体不入队,规则见 `docs/reference/cli.md`。
|
||||
|
||||
@@ -22,6 +22,7 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定
|
||||
- `microservice list/status/health/diagnostics/tunnel-self-test/proxy` 通过 backend-core 内网 API 管理挂载在计算节点 Docker 或 k3s 控制面中的用户服务(底层命令名仍为 microservice);`health`、`diagnostics`、`tunnel-self-test` 和 `proxy` 会走真实 backend-core -> provider-gateway 或 k3sctl-adapter -> 节点服务链路,`proxy` 支持受控 JSON 请求体并对超大响应 body 默认输出有界预览,规则见 `docs/reference/microservices.md`。
|
||||
- `decision upload/list/show/health` 通过 backend-core 用户服务代理访问 D601 k3s Decision Center,用于上传会议记录/决议 Markdown、列出权威记录、查看详情和健康检查;它不得直连 D601 Service、NodePort 或 provider-gateway 业务 HTTP。
|
||||
- `deploy check/plan/apply` 默认从根目录 `deploy.json` 读取服务 repo 与 commit 期望状态,join `config.json` 和现有 manifest 后使用 target-side build 单一路径校验或更新直管服务与 k3s 代管服务;`deploy plan --env dev|prod` 在 Phase 0 只从固定 Git ref 读取 manifest 并输出 dry-run 环境计划,不使用本地 dirty worktree;规则见 `docs/reference/deploy.md`。
|
||||
- `dev-env validate [--manifest path] [--kubectl-dry-run]` 离线校验 D601 `unidesk-dev` namespace 和 dev PostgreSQL 底座 manifest。默认检查 `src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-foundation.k8s.yaml` 中所有 namespaced 对象都只落到 `unidesk-dev`,存在 `postgres-dev` StatefulSet/Service、dev secret/config、迁移 Job 和 DB URL guard,且 dev `DATABASE_URL` 只能指向 `postgres-dev.unidesk-dev.../unidesk_dev`。加 `--kubectl-dry-run` 时额外执行 `kubectl apply --dry-run=client --validate=false -f <manifest>`,仍不 apply 资源。
|
||||
- `codex deploy <commitId>` 是 Code Queue 兼容部署入口,会生成临时 desired manifest 并调用 `deploy apply --service code-queue` 的同一条 target-side build、k3s import、rollout 和 live commit 验证路径;详细规则见 `docs/reference/codex-deploy.md`。
|
||||
- `codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--reasoning-effort effort] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]` 通过 backend-core 私有代理向稳定 `code-queue` 用户服务路径提交任务;prompt 必须且只能来自位置参数、文件或 stdin 之一,`--dry-run` 只返回结构化请求且不实际入队。提交确认和 dry-run 必须返回完整 prompt、字符数和 `truncated=false`,不能套用任务详情的预览截断策略,否则长任务 prompt 无法被人工验收。backend-core 默认把提交、队列 CRUD、已读状态、历史摘要和轻量 Trace 读取分流到主 server `code-queue-mgr`,由它写入主 PostgreSQL;D601 scheduler 只轮询并执行已入库任务。
|
||||
- `codex task <taskId>` 通过 Code Queue 私有代理按任务 ID 查询结构化执行摘要;默认只返回有界 prompt/response 预览、执行 Provider、工作目录、最后 assistant message、最近工具调用摘要、attempt、judge、错误、耗时和 trace 翻页提示,适合在新队列任务中引用历史 session 且避免噪声爆炸。该摘要读取默认由主 server `code-queue-mgr` 从 PostgreSQL 返回,不依赖 D601 `code-queue-read` Service 可用。
|
||||
|
||||
@@ -33,6 +33,23 @@ The current Phase 0 implementation enables only dry-run `check` and `plan` for `
|
||||
|
||||
`config.json.microservices[].repository.commitId` is retained for catalog compatibility, but `deploy.json` is the deployment version authority for the reconciler.
|
||||
|
||||
## D601 Dev Foundation
|
||||
|
||||
Phase 2 of the D601 dev environment creates only the isolated namespace and database foundation. The authoritative manifest is `src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-foundation.k8s.yaml`.
|
||||
|
||||
It may create resources only in `unidesk-dev`:
|
||||
|
||||
- `Namespace unidesk-dev`, plus quota and default limits.
|
||||
- `Secret unidesk-dev-runtime-secrets` as a dev-only template for DB credentials, provider token, auth/session secret, and Code Queue model secret placeholders.
|
||||
- `ConfigMap unidesk-dev-runtime-config` for dev identity, fixed deploy ref `origin/deploy/dev`, provider id `D601-dev`, Code Queue dev paths, and non-secret runtime defaults.
|
||||
- `ConfigMap unidesk-dev-db-guard` with an executable guard script that rejects production-looking `DATABASE_URL` values.
|
||||
- `StatefulSet/Service postgres-dev` with a 5Gi persistent volume claim and bounded CPU/memory requests/limits.
|
||||
- `Job unidesk-dev-db-migrate`, which waits for `postgres-dev`, runs the guard, then prepares backend-core and Code Queue tables in the independent `unidesk_dev` database.
|
||||
|
||||
The manifest must not create, update, or delete production namespace resources, production DB objects, production PVCs, production Deployments/Services/Secrets, or main server Docker Compose services. Static validation is available through `bun scripts/cli.ts dev-env validate`; Kubernetes client dry-run is `bun scripts/cli.ts dev-env validate --kubectl-dry-run`. If applying manually during Phase 2, the only allowed apply target is this manifest and the post-check must prove production resources are unchanged, for example by comparing `kubectl -n unidesk get deploy,sts,svc,secret,pvc -o name` before and after.
|
||||
|
||||
Phase 2 guardrails are deliberately limited to the dev manifest and CLI validator. Runtime startup guards for dev backend-core, Code Queue and Code Queue Manager must be reviewed and shipped as a separate change before dev workloads are exposed beyond dry-run or controlled apply.
|
||||
|
||||
## CLI
|
||||
|
||||
`bun scripts/cli.ts deploy check [--file deploy.json] [--service <id>]` checks the live runtime against the desired repo and commit without changing the system.
|
||||
|
||||
@@ -151,6 +151,14 @@ Baidu Netdisk 在 UniDesk 语境中按纯后端服务管理:不得暴露百度
|
||||
- 拓扑健康:`expectedNodeIds` 负责展示计划内节点;当前 Code Queue 目标拓扑为 D601 原生 k3s 单节点多服务,`presentNodeIds` 应包含 `D601`、`missingNodeIds=[]`、`topologyComplete=true`、`status=healthy`。不能把未完成原生 k3s 接入或仍依赖 Docker 化 k3s 的节点列为 expected node;只有显式 `requireAllInstancesHealthy=true` 的服务才允许把缺失 standby/worker 节点提升为整体不健康。
|
||||
- 前端:`用户服务 / k3s Control` React 页面必须只通过 `/api/microservices/k3sctl-adapter/proxy/api/control-plane` 通信,展示控制面状态、manifest、D601 scheduler/read/write 实例、active instance、Kubernetes API service proxy/no-fallback 路径和显式原始 JSON 按钮;页面不得直接访问 provider-gateway、D601/D518 业务容器端口、NodePort 或 raw k3s/kubectl API。
|
||||
|
||||
### D601 Dev Namespace Foundation
|
||||
|
||||
D601 开发环境底座只允许创建 `unidesk-dev` namespace 与 dev 专用对象,manifest 固定为 `src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-foundation.k8s.yaml`。该 manifest 包含 `postgres-dev` 独立 PostgreSQL StatefulSet/Service/PVC、dev-only secret/config 模板、dev DB 初始化 SQL 和迁移 Job、ResourceQuota/LimitRange,以及 `unidesk-dev-db-guard`。它不得修改生产 `unidesk` namespace、生产 PostgreSQL、生产 PVC、生产 Deployment/Service/Secret 或主 server Docker Compose。
|
||||
|
||||
`postgres-dev` 是 dev backend-core 与 dev Code Queue 状态的默认唯一数据库。dev 运行时必须使用 `postgres-dev.unidesk-dev.svc.cluster.local:5432/unidesk_dev` 和 dev Provider 身份 `D601-dev`;不得共享生产 `d601-tcp-egress-gateway.../unidesk`。当前 Phase 2 只提供 manifest 脚本和 `dev-env validate` 的静态护栏;backend-core、Code Queue 和 Code Queue Manager 的运行时启动护栏需在后续阶段单独评审后接入。
|
||||
|
||||
验收入口:先运行 `bun scripts/cli.ts dev-env validate` 做静态资源与 DB URL 护栏检查;具备 D601 kubeconfig 时运行 `bun scripts/cli.ts dev-env validate --kubectl-dry-run` 做 Kubernetes client dry-run。若实际 apply,只能 apply 到 `unidesk-dev`,随后用 `kubectl -n unidesk-dev get pods,svc,pvc` 验证 dev DB ready,并对比 apply 前后的 `kubectl -n unidesk get deploy,sts,svc,secret,pvc -o name` 证明生产 workload 未变化。
|
||||
|
||||
### Code Queue k3s-Managed
|
||||
|
||||
当前对外 `id=code-queue` 是稳定用户服务 ID,实际按 master 控制面与 D601 执行面拆分。队列管理、提交、历史摘要、已读状态和轻量 Trace 读取默认由主 server `code-queue-mgr` 直管 PostgreSQL;D601 k3s Code Queue 作为执行面代管,负责 scheduler/runner、dev-container、active run steer/interrupt、judge、输出/attempt/通知写回,并接入统一 `oa-event-flow` 发布 Trace/STEP 事实事件与读取统计中心:
|
||||
|
||||
@@ -16,6 +16,7 @@ import { runScheduleCommand } from "./src/schedules";
|
||||
import { parseNetworkPerfOptions, runNetworkPerf } from "./src/network-perf";
|
||||
import { runCiCommand } from "./src/ci";
|
||||
import { runSwapCommand } from "./src/swap";
|
||||
import { runDevEnvCommand } from "./src/dev-env";
|
||||
|
||||
const remoteOptions = extractRemoteCliOptions(process.argv.slice(2));
|
||||
const args = remoteOptions.args;
|
||||
@@ -54,6 +55,7 @@ function help(): unknown {
|
||||
{ command: "decision list [--type ...] [--status ...] [--level ...] [--linked-goal-id id] [--limit N]", description: "List Decision Center records through the user-service proxy." },
|
||||
{ command: "decision show <id>", description: "Show one Decision Center record." },
|
||||
{ command: "deploy check|plan|apply [--file deploy.json|--env dev|prod] [--service id] [--dry-run] [--force]", description: "Reconcile services from a repo+commit manifest; --env uses fixed environment refs for dry-run planning in Phase 0." },
|
||||
{ command: "dev-env validate [--manifest path] [--kubectl-dry-run]", description: "Validate the D601 unidesk-dev namespace/database foundation manifest and production DB URL guardrails without applying resources." },
|
||||
{ command: "schedule list|get|runs|run|delete", description: "Manage backend-core scheduled tasks and run history; schedule run <id> supports --wait-ms N." },
|
||||
{ command: "schedule upsert-pgdata-backup [--time HH:MM] [--remote-base /SERVER_DATA/UNIDESK_PG_DATA]", description: "Create or update the daily PGDATA physical backup task that uploads monthly rotated archives to Baidu Netdisk." },
|
||||
{ command: "codex deploy <commitId> [--provider-id D601] [--timeout-ms N]", description: "Compatibility wrapper for deploy apply --service code-queue with a temporary repo+commit manifest." },
|
||||
@@ -155,6 +157,14 @@ async function main(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (top === "dev-env") {
|
||||
const result = runDevEnvCommand(args.slice(1));
|
||||
const ok = (result as { ok?: unknown }).ok !== false;
|
||||
emitJson(commandName, result, ok);
|
||||
if (!ok) process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const config = readConfig();
|
||||
|
||||
if (top === "ssh") {
|
||||
|
||||
@@ -16,6 +16,7 @@ const syntaxFiles = [
|
||||
"scripts/src/code-queue.ts",
|
||||
"scripts/src/command.ts",
|
||||
"scripts/src/decision-center.ts",
|
||||
"scripts/src/dev-env.ts",
|
||||
"scripts/src/deploy.ts",
|
||||
"scripts/src/docker.ts",
|
||||
"scripts/src/e2e.ts",
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { runCommand } from "./command";
|
||||
import { repoRoot, rootPath } from "./config";
|
||||
|
||||
const defaultManifest = "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-foundation.k8s.yaml";
|
||||
const devNamespace = "unidesk-dev";
|
||||
const prodNamespace = "unidesk";
|
||||
const requiredKinds = new Set([
|
||||
"Namespace/unidesk-dev",
|
||||
"Secret/unidesk-dev-runtime-secrets",
|
||||
"ConfigMap/unidesk-dev-runtime-config",
|
||||
"ConfigMap/unidesk-dev-db-guard",
|
||||
"ConfigMap/unidesk-dev-db-init",
|
||||
"Service/postgres-dev",
|
||||
"StatefulSet/postgres-dev",
|
||||
"Job/unidesk-dev-db-migrate",
|
||||
]);
|
||||
|
||||
interface ManifestDocument {
|
||||
index: number;
|
||||
raw: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
namespace: string | null;
|
||||
}
|
||||
|
||||
interface DevEnvOptions {
|
||||
manifestPath: string;
|
||||
kubectlDryRun: boolean;
|
||||
}
|
||||
|
||||
function isHelpArg(arg: string | undefined): boolean {
|
||||
return arg === "help" || arg === "--help" || arg === "-h";
|
||||
}
|
||||
|
||||
function parseOptions(args: string[]): DevEnvOptions {
|
||||
const options: DevEnvOptions = { manifestPath: defaultManifest, kubectlDryRun: false };
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
if (arg === "--manifest") {
|
||||
const value = args[index + 1];
|
||||
if (value === undefined || value.length === 0) throw new Error("--manifest requires a path");
|
||||
options.manifestPath = value;
|
||||
index += 1;
|
||||
} else if (arg === "--kubectl-dry-run") {
|
||||
options.kubectlDryRun = true;
|
||||
} else if (!isHelpArg(arg)) {
|
||||
throw new Error(`unknown dev-env option: ${arg}`);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
function scalarAfter(text: string, key: string): string | null {
|
||||
const match = text.match(new RegExp(`^\\s*${key}:\\s*"?([^"\\n#]+)"?\\s*(?:#.*)?$`, "mu"));
|
||||
return match?.[1]?.trim() ?? null;
|
||||
}
|
||||
|
||||
function namespaceFromDoc(text: string): string | null {
|
||||
const metadataIndex = text.search(/^metadata:\s*$/mu);
|
||||
if (metadataIndex < 0) return null;
|
||||
const metadataBlock = text.slice(metadataIndex);
|
||||
const match = metadataBlock.match(/^ {2}namespace:\s*"?([^"\n#]+)"?\s*(?:#.*)?$/mu);
|
||||
return match?.[1]?.trim() ?? null;
|
||||
}
|
||||
|
||||
function parseManifestDocuments(text: string): ManifestDocument[] {
|
||||
return text.split(/^---\s*$/mu)
|
||||
.map((raw, index) => ({ raw: raw.trim(), index }))
|
||||
.filter((doc) => doc.raw.length > 0)
|
||||
.map(({ raw, index }) => {
|
||||
const kind = scalarAfter(raw, "kind") ?? "";
|
||||
const name = (() => {
|
||||
const metadataIndex = raw.search(/^metadata:\s*$/mu);
|
||||
if (metadataIndex < 0) return "";
|
||||
const metadataBlock = raw.slice(metadataIndex);
|
||||
const match = metadataBlock.match(/^ {2}name:\s*"?([^"\n#]+)"?\s*(?:#.*)?$/mu);
|
||||
return match?.[1]?.trim() ?? "";
|
||||
})();
|
||||
return { index, raw, kind, name, namespace: namespaceFromDoc(raw) };
|
||||
});
|
||||
}
|
||||
|
||||
function databaseUrls(text: string): string[] {
|
||||
const urls: string[] = [];
|
||||
const pattern = /postgres(?:ql)?:\/\/[^\s"']+/gu;
|
||||
for (const match of text.matchAll(pattern)) urls.push(match[0] ?? "");
|
||||
return urls.filter((url) => url.length > 0 && !url.includes("*") && !url.includes("$"));
|
||||
}
|
||||
|
||||
function validateDatabaseUrl(url: string): { ok: boolean; url: string; reason: string | null } {
|
||||
if (url.includes("d601-tcp-egress-gateway") || url.includes("74.48.78.17:15432") || url.includes("database:5432/unidesk")) {
|
||||
return { ok: false, url, reason: "matches production database route" };
|
||||
}
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(url);
|
||||
} catch {
|
||||
return { ok: false, url, reason: "invalid URL" };
|
||||
}
|
||||
const hostOk = [
|
||||
"postgres-dev",
|
||||
"postgres-dev.unidesk-dev",
|
||||
"postgres-dev.unidesk-dev.svc",
|
||||
"postgres-dev.unidesk-dev.svc.cluster.local",
|
||||
].includes(parsed.hostname);
|
||||
const database = parsed.pathname.replace(/^\/+/u, "");
|
||||
if (!hostOk) return { ok: false, url, reason: `host ${parsed.hostname} is not postgres-dev` };
|
||||
if (database !== "unidesk_dev") return { ok: false, url, reason: `database ${database} is not unidesk_dev` };
|
||||
return { ok: true, url, reason: null };
|
||||
}
|
||||
|
||||
function kubectlDryRun(manifestPath: string): unknown {
|
||||
const result = runCommand(["kubectl", "apply", "--dry-run=client", "--validate=false", "-f", manifestPath], repoRoot, { timeoutMs: 60_000 });
|
||||
return {
|
||||
command: result.command,
|
||||
exitCode: result.exitCode,
|
||||
signal: result.signal,
|
||||
timedOut: result.timedOut,
|
||||
ok: result.exitCode === 0,
|
||||
stdoutTail: result.stdout.slice(-4000),
|
||||
stderrTail: result.stderr.slice(-4000),
|
||||
};
|
||||
}
|
||||
|
||||
function devEnvHelp(): Record<string, unknown> {
|
||||
return {
|
||||
ok: true,
|
||||
command: "dev-env validate",
|
||||
usage: "bun scripts/cli.ts dev-env validate [--manifest path] [--kubectl-dry-run]",
|
||||
defaultManifest,
|
||||
checks: [
|
||||
"all namespaced resources must target unidesk-dev",
|
||||
"required dev namespace, postgres-dev, secret/config, guard, migration resources must exist",
|
||||
"dev DATABASE_URL values must target postgres-dev/unidesk_dev and not production routes",
|
||||
"--kubectl-dry-run optionally asks kubectl to client-dry-run the manifest without applying it",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function runDevEnvCommand(args: string[]): unknown {
|
||||
const action = args[0];
|
||||
if (action === undefined || isHelpArg(action)) return devEnvHelp();
|
||||
if (action !== "validate") throw new Error("dev-env usage: bun scripts/cli.ts dev-env validate [--manifest path] [--kubectl-dry-run]");
|
||||
|
||||
const options = parseOptions(args.slice(1));
|
||||
const manifestPath = rootPath(options.manifestPath);
|
||||
const manifestText = readFileSync(manifestPath, "utf8");
|
||||
const docs = parseManifestDocuments(manifestText);
|
||||
const resources = docs.map((doc) => `${doc.kind}/${doc.name}`);
|
||||
const namespacedViolations = docs
|
||||
.filter((doc) => doc.kind !== "Namespace")
|
||||
.filter((doc) => doc.namespace !== devNamespace)
|
||||
.map((doc) => ({ index: doc.index, kind: doc.kind, name: doc.name, namespace: doc.namespace }));
|
||||
const namespaceObjectViolations = docs
|
||||
.filter((doc) => doc.kind === "Namespace")
|
||||
.filter((doc) => doc.name !== devNamespace)
|
||||
.map((doc) => ({ index: doc.index, kind: doc.kind, name: doc.name }));
|
||||
const productionNamespaceTouches = docs
|
||||
.filter((doc) => doc.namespace === prodNamespace || (doc.kind === "Namespace" && doc.name === prodNamespace))
|
||||
.map((doc) => ({ kind: doc.kind, name: doc.name }));
|
||||
const missingRequiredResources = Array.from(requiredKinds).filter((resource) => !resources.includes(resource));
|
||||
const urlChecks = databaseUrls(manifestText).map(validateDatabaseUrl);
|
||||
const badUrls = urlChecks.filter((check) => !check.ok);
|
||||
const forbiddenProductionTextHits = [
|
||||
"namespace: unidesk\n",
|
||||
"d601-tcp-egress-gateway.unidesk.svc.cluster.local:15432/unidesk",
|
||||
"74.48.78.17:15432/unidesk",
|
||||
].filter((needle) => manifestText.includes(needle));
|
||||
|
||||
const staticOk = namespacedViolations.length === 0
|
||||
&& namespaceObjectViolations.length === 0
|
||||
&& productionNamespaceTouches.length === 0
|
||||
&& missingRequiredResources.length === 0
|
||||
&& badUrls.length === 0
|
||||
&& forbiddenProductionTextHits.length === 0;
|
||||
const kubectl = options.kubectlDryRun ? kubectlDryRun(manifestPath) : { skipped: true, enableWith: "--kubectl-dry-run" };
|
||||
const kubectlOk = typeof kubectl === "object" && kubectl !== null && "ok" in kubectl ? (kubectl as { ok: boolean }).ok : true;
|
||||
|
||||
return {
|
||||
ok: staticOk && kubectlOk,
|
||||
manifest: options.manifestPath,
|
||||
namespace: devNamespace,
|
||||
staticChecks: {
|
||||
ok: staticOk,
|
||||
resources,
|
||||
namespacedViolations,
|
||||
namespaceObjectViolations,
|
||||
productionNamespaceTouches,
|
||||
missingRequiredResources,
|
||||
databaseUrlChecks: urlChecks,
|
||||
forbiddenProductionTextHits,
|
||||
},
|
||||
kubectlDryRun: kubectl,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,568 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
unidesk.ai/k3s-cluster: d601-native-k3s
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ResourceQuota
|
||||
metadata:
|
||||
name: unidesk-dev-quota
|
||||
namespace: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
spec:
|
||||
hard:
|
||||
pods: "30"
|
||||
requests.cpu: "4"
|
||||
requests.memory: 8Gi
|
||||
requests.storage: 30Gi
|
||||
limits.cpu: "8"
|
||||
limits.memory: 12Gi
|
||||
persistentvolumeclaims: "8"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: LimitRange
|
||||
metadata:
|
||||
name: unidesk-dev-default-limits
|
||||
namespace: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
spec:
|
||||
limits:
|
||||
- type: Container
|
||||
defaultRequest:
|
||||
cpu: 50m
|
||||
memory: 96Mi
|
||||
default:
|
||||
memory: 512Mi
|
||||
max:
|
||||
cpu: "2"
|
||||
memory: 2Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: unidesk-dev-runtime-secrets
|
||||
namespace: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
annotations:
|
||||
unidesk.ai/template: "true"
|
||||
unidesk.ai/description: "Dev-only placeholder secret; replace values on D601 before exposing dev services."
|
||||
type: Opaque
|
||||
stringData:
|
||||
POSTGRES_USER: unidesk_dev
|
||||
POSTGRES_PASSWORD: change-me-dev-postgres-password
|
||||
POSTGRES_DB: unidesk_dev
|
||||
DATABASE_URL: postgres://unidesk_dev:change-me-dev-postgres-password@postgres-dev.unidesk-dev.svc.cluster.local:5432/unidesk_dev
|
||||
PROVIDER_TOKEN: change-me-D601-dev-provider-token
|
||||
AUTH_USERNAME: admin-dev
|
||||
AUTH_PASSWORD: change-me-dev-auth-password
|
||||
SESSION_SECRET: change-me-dev-session-secret-minimum-32-characters
|
||||
OPENAI_API_KEY: replace-me-openai-api-key
|
||||
CRS_OAI_KEY: replace-me-crs-oai-key
|
||||
MINIMAX_API_KEY: replace-me-minimax-api-key
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: unidesk-dev-runtime-config
|
||||
namespace: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
data:
|
||||
UNIDESK_ENV: dev
|
||||
UNIDESK_NAMESPACE: unidesk-dev
|
||||
UNIDESK_DEPLOY_REF: origin/deploy/dev
|
||||
UNIDESK_PROVIDER_ID: D601-dev
|
||||
UNIDESK_NODE_ID: D601
|
||||
UNIDESK_DEV_DATABASE_NAME: unidesk_dev
|
||||
UNIDESK_DEV_DATABASE_ALLOWED_HOSTS: postgres-dev,postgres-dev.unidesk-dev,postgres-dev.unidesk-dev.svc,postgres-dev.unidesk-dev.svc.cluster.local
|
||||
UNIDESK_DEV_DATABASE_FORBIDDEN_PATTERNS: d601-tcp-egress-gateway,database:5432/unidesk,74.48.78.17:15432
|
||||
DATABASE_VOLUME_NAME: postgres-dev-data
|
||||
DATABASE_VOLUME_SIZE: 5Gi
|
||||
HEARTBEAT_TIMEOUT_MS: "30000"
|
||||
TASK_PENDING_TIMEOUT_MS: "600000"
|
||||
DATABASE_POOL_MAX: "2"
|
||||
MICROSERVICES_JSON: "[]"
|
||||
SESSION_TTL_SECONDS: "28800"
|
||||
CODE_QUEUE_MAIN_PROVIDER_ID: D601-dev
|
||||
CODE_QUEUE_EXECUTION_PROVIDER_IDS: D601-dev
|
||||
CODE_QUEUE_WORKDIR: /workspace-dev
|
||||
CODE_QUEUE_REMOTE_WORKDIR: /home/ubuntu/unidesk-dev-workspace
|
||||
CODE_QUEUE_DATA_DIR: /var/lib/unidesk-dev/code-queue
|
||||
CODE_QUEUE_CODEX_HOME: /var/lib/unidesk-dev/code-queue/codex-home
|
||||
CODE_QUEUE_OPENCODE_XDG_DIR: /var/lib/unidesk-dev/code-queue/opencode-xdg
|
||||
CODE_QUEUE_DEFAULT_MODEL: gpt-5.5
|
||||
CODE_QUEUE_MODELS: gpt-5.5,gpt-5.4-mini,gpt-5.4,minimax-m2.7
|
||||
CODE_QUEUE_MODEL_REASONING_EFFORTS: gpt-5.5=xhigh
|
||||
CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED: "false"
|
||||
CODE_QUEUE_STARTUP_OA_BACKFILL_ENABLED: "false"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: unidesk-dev-db-guard
|
||||
namespace: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
data:
|
||||
guard-dev-db-url.sh: |
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
database_url="${1:-${DATABASE_URL:-}}"
|
||||
expected_name="${UNIDESK_DEV_DATABASE_NAME:-unidesk_dev}"
|
||||
|
||||
if [ -z "$database_url" ]; then
|
||||
echo "DATABASE_URL is required for dev DB guard" >&2
|
||||
exit 42
|
||||
fi
|
||||
|
||||
case "$database_url" in
|
||||
*d601-tcp-egress-gateway*|*74.48.78.17:15432*|*database:5432/unidesk*|*/unidesk|*/unidesk\?*)
|
||||
echo "refusing production-looking DATABASE_URL in unidesk-dev: $database_url" >&2
|
||||
exit 42
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$database_url" in
|
||||
postgres://*@postgres-dev:5432/"$expected_name"|\
|
||||
postgresql://*@postgres-dev:5432/"$expected_name"|\
|
||||
postgres://*@postgres-dev.unidesk-dev:5432/"$expected_name"|\
|
||||
postgresql://*@postgres-dev.unidesk-dev:5432/"$expected_name"|\
|
||||
postgres://*@postgres-dev.unidesk-dev.svc:5432/"$expected_name"|\
|
||||
postgresql://*@postgres-dev.unidesk-dev.svc:5432/"$expected_name"|\
|
||||
postgres://*@postgres-dev.unidesk-dev.svc.cluster.local:5432/"$expected_name"|\
|
||||
postgresql://*@postgres-dev.unidesk-dev.svc.cluster.local:5432/"$expected_name")
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "DATABASE_URL must target postgres-dev in unidesk-dev and database $expected_name: $database_url" >&2
|
||||
exit 42
|
||||
;;
|
||||
esac
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: unidesk-dev-db-init
|
||||
namespace: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
data:
|
||||
001_unidesk_dev_schema.sql: |
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_environment_identity (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
INSERT INTO unidesk_environment_identity (key, value, updated_at)
|
||||
VALUES
|
||||
('environment', 'dev', now()),
|
||||
('namespace', 'unidesk-dev', now()),
|
||||
('database', 'unidesk_dev', now()),
|
||||
('provider_id', 'D601-dev', now()),
|
||||
('deploy_ref', 'origin/deploy/dev', now())
|
||||
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_nodes (
|
||||
provider_id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
labels JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
status TEXT NOT NULL,
|
||||
connected_at TIMESTAMPTZ,
|
||||
last_heartbeat TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
type TEXT NOT NULL,
|
||||
source TEXT NOT NULL,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
provider_id TEXT NOT NULL,
|
||||
command TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
result JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_scheduled_tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
enabled BOOLEAN NOT NULL DEFAULT true,
|
||||
schedule_json JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
action_json JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
concurrency_policy TEXT NOT NULL DEFAULT 'skip',
|
||||
next_run_at TIMESTAMPTZ,
|
||||
last_run_at TIMESTAMPTZ,
|
||||
last_run_id TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_scheduled_task_runs (
|
||||
id TEXT PRIMARY KEY,
|
||||
schedule_id TEXT NOT NULL REFERENCES unidesk_scheduled_tasks(id) ON DELETE CASCADE,
|
||||
trigger_type TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
task_id TEXT,
|
||||
result JSONB,
|
||||
error TEXT,
|
||||
started_at TIMESTAMPTZ,
|
||||
finished_at TIMESTAMPTZ,
|
||||
duration_ms BIGINT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_node_docker_status (
|
||||
provider_id TEXT PRIMARY KEY,
|
||||
status JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
collected_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_node_system_status (
|
||||
provider_id TEXT PRIMARY KEY,
|
||||
status JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
collected_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_node_metric_samples (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
provider_id TEXT NOT NULL,
|
||||
collected_at TIMESTAMPTZ NOT NULL,
|
||||
cpu_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
memory_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
disk_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
sample JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
queue_id TEXT NOT NULL DEFAULT 'default',
|
||||
status TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL DEFAULT 'D601-dev',
|
||||
execution_mode TEXT NOT NULL DEFAULT 'default',
|
||||
model TEXT NOT NULL,
|
||||
cwd TEXT NOT NULL,
|
||||
prompt TEXT NOT NULL,
|
||||
base_prompt TEXT NOT NULL DEFAULT '',
|
||||
reference_task_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
reference_injection JSONB,
|
||||
reasoning_effort TEXT,
|
||||
max_attempts INTEGER NOT NULL,
|
||||
current_attempt INTEGER NOT NULL DEFAULT 0,
|
||||
current_mode TEXT,
|
||||
codex_thread_id TEXT,
|
||||
active_turn_id TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
started_at TIMESTAMPTZ,
|
||||
finished_at TIMESTAMPTZ,
|
||||
read_at TIMESTAMPTZ,
|
||||
last_error TEXT,
|
||||
last_judge JSONB,
|
||||
output_count INTEGER NOT NULL DEFAULT 0,
|
||||
event_count INTEGER NOT NULL DEFAULT 0,
|
||||
attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_output_seq BIGINT NOT NULL DEFAULT 0,
|
||||
task_json JSONB NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_queues (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_workdirs (
|
||||
provider_id TEXT NOT NULL,
|
||||
execution_mode TEXT NOT NULL DEFAULT 'default',
|
||||
path TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (provider_id, execution_mode, path)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_notifications (
|
||||
id TEXT PRIMARY KEY,
|
||||
kind TEXT NOT NULL,
|
||||
dedup_key TEXT NOT NULL,
|
||||
target TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
attempts INTEGER NOT NULL DEFAULT 0,
|
||||
next_attempt_at TIMESTAMPTZ NOT NULL,
|
||||
last_error TEXT,
|
||||
sent_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS queue_id TEXT NOT NULL DEFAULT 'default';
|
||||
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS provider_id TEXT NOT NULL DEFAULT 'D601-dev';
|
||||
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS execution_mode TEXT NOT NULL DEFAULT 'default';
|
||||
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS base_prompt TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS reference_task_ids JSONB NOT NULL DEFAULT '[]'::jsonb;
|
||||
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS reference_injection JSONB;
|
||||
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS read_at TIMESTAMPTZ;
|
||||
ALTER TABLE unidesk_code_queue_queues ADD COLUMN IF NOT EXISTS name TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE unidesk_code_queue_workdirs ADD COLUMN IF NOT EXISTS execution_mode TEXT NOT NULL DEFAULT 'default';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_nodes_status ON unidesk_nodes(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_events_created_at ON unidesk_events(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_tasks_provider_status ON unidesk_tasks(provider_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_tasks_updated_at ON unidesk_tasks(updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_tasks_status_updated_at ON unidesk_tasks(status, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_scheduled_tasks_next_run ON unidesk_scheduled_tasks(enabled, next_run_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_scheduled_task_runs_schedule_updated ON unidesk_scheduled_task_runs(schedule_id, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_scheduled_task_runs_status_updated ON unidesk_scheduled_task_runs(status, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_node_docker_status_updated_at ON unidesk_node_docker_status(updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_node_system_status_updated_at ON unidesk_node_system_status(updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_node_metric_samples_provider_time ON unidesk_node_metric_samples(provider_id, collected_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_status_updated ON unidesk_code_queue_tasks(status, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_queue_status_updated ON unidesk_code_queue_tasks(queue_id, status, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_provider_updated ON unidesk_code_queue_tasks(provider_id, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_execution_mode_updated ON unidesk_code_queue_tasks(execution_mode, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_created ON unidesk_code_queue_tasks(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_queue_created ON unidesk_code_queue_tasks(queue_id, created_at DESC, id DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_unread_terminal ON unidesk_code_queue_tasks(queue_id, updated_at DESC) WHERE read_at IS NULL AND status IN ('succeeded', 'failed', 'canceled');
|
||||
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_model_updated ON unidesk_code_queue_tasks(model, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_code_queue_tasks_list ON unidesk_code_queue_tasks(status, updated_at DESC, id DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_code_queue_tasks_queue_updated ON unidesk_code_queue_tasks(queue_id, updated_at DESC, id DESC);
|
||||
|
||||
INSERT INTO unidesk_code_queue_queues (id, name, created_at, updated_at)
|
||||
VALUES ('default', 'default', now(), now())
|
||||
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, updated_at = GREATEST(unidesk_code_queue_queues.updated_at, EXCLUDED.updated_at);
|
||||
|
||||
COMMIT;
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgres-dev
|
||||
namespace: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-dev
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/name: postgres-dev
|
||||
unidesk.ai/environment: dev
|
||||
ports:
|
||||
- name: pg
|
||||
port: 5432
|
||||
targetPort: pg
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: postgres-dev
|
||||
namespace: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-dev
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
spec:
|
||||
serviceName: postgres-dev
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: postgres-dev
|
||||
unidesk.ai/environment: dev
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-dev
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
unidesk.ai/node-id: D601
|
||||
spec:
|
||||
nodeSelector:
|
||||
unidesk.ai/node-id: D601
|
||||
terminationGracePeriodSeconds: 30
|
||||
securityContext:
|
||||
fsGroup: 70
|
||||
containers:
|
||||
- name: postgres
|
||||
image: postgres:16-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- -c
|
||||
- shared_buffers=64MB
|
||||
- -c
|
||||
- max_connections=30
|
||||
- -c
|
||||
- work_mem=4MB
|
||||
- -c
|
||||
- maintenance_work_mem=64MB
|
||||
- -c
|
||||
- effective_cache_size=256MB
|
||||
ports:
|
||||
- name: pg
|
||||
containerPort: 5432
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: unidesk-dev-runtime-secrets
|
||||
key: POSTGRES_USER
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: unidesk-dev-runtime-secrets
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: POSTGRES_DB
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: unidesk-dev-runtime-secrets
|
||||
key: POSTGRES_DB
|
||||
volumeMounts:
|
||||
- name: postgres-dev-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -ec
|
||||
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 18
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -ec
|
||||
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 6
|
||||
startupProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -ec
|
||||
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 60
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: postgres-dev-data
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-dev
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: unidesk-dev-db-migrate
|
||||
namespace: unidesk-dev
|
||||
labels:
|
||||
app.kubernetes.io/name: unidesk-dev-db-migrate
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
spec:
|
||||
backoffLimit: 6
|
||||
ttlSecondsAfterFinished: 86400
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: unidesk-dev-db-migrate
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/environment: dev
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
nodeSelector:
|
||||
unidesk.ai/node-id: D601
|
||||
containers:
|
||||
- name: migrate
|
||||
image: postgres:16-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: unidesk-dev-runtime-secrets
|
||||
key: DATABASE_URL
|
||||
- name: UNIDESK_DEV_DATABASE_NAME
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: unidesk-dev-runtime-config
|
||||
key: UNIDESK_DEV_DATABASE_NAME
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
args:
|
||||
- |
|
||||
/etc/unidesk-dev-guard/guard-dev-db-url.sh "$DATABASE_URL"
|
||||
until pg_isready -d "$DATABASE_URL"; do
|
||||
echo "waiting for postgres-dev"
|
||||
sleep 2
|
||||
done
|
||||
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f /migrations/001_unidesk_dev_schema.sql
|
||||
volumeMounts:
|
||||
- name: db-guard
|
||||
mountPath: /etc/unidesk-dev-guard
|
||||
readOnly: true
|
||||
- name: db-init
|
||||
mountPath: /migrations
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 25m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
volumes:
|
||||
- name: db-guard
|
||||
configMap:
|
||||
name: unidesk-dev-db-guard
|
||||
defaultMode: 0555
|
||||
- name: db-init
|
||||
configMap:
|
||||
name: unidesk-dev-db-init
|
||||
Reference in New Issue
Block a user