Files
proxdeploy/src/worker/ablaeufe/template.ts
T
florian.wildeandClaude Opus 5.5 00712dc6ce feat(templates): Profile ins Template einbacken
Beim Template-Bau lassen sich Profile wählen (nur solche ohne
Pflichtvariablen). Nach dem Vorbereitungsstart laufen sie über den
Guest-Agent; danach werden Maschinen-ID, SSH-Host-Keys, Docker-Engine-ID
und apt-Cache entfernt. Ausrollen nimmt das Template mit den meisten
passenden Profilen und überspringt, was schon eingebacken ist.

Auf pve-demo: Provisionierung mit base+docker 141 s ohne, ~30 s mit
Template. Dabei gefunden: docker nimmt den Benutzer per guest.user in
die Gruppe auf – Profile mit guest.* laufen deshalb beim Deploy noch
einmal (idempotent, schnell).

Closes #022

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:20:32 +02:00

94 lines
3.3 KiB
TypeScript

import { phasenAusfuehren } from "@/engine/ablauf/ablauf";
import { templateBauSchritte, templateName, type TemplateBauStand } from "@/engine/ablaeufe/template-bau";
import type { Distribution } from "@/engine/katalog/schema";
import { ProxmoxClient } from "@/engine/proxmox/client";
import { prisma } from "@/lib/db";
import { TemplateBauAnfrage } from "@/lib/schemas/template";
import { melden } from "@/server/ereignisse/senke";
import { verbindungAus } from "@/server/services/server";
import { nodeBefehlFuer } from "@/server/services/ssh-zugang";
import { templateProfileAufloesen } from "@/server/services/templates";
import { vmidReservieren } from "@/server/services/vmid";
import { ablaufRegistrieren } from "../ausfuehren";
import { aufrufLogger } from "../aufruf-log";
ablaufRegistrieren("TEMPLATE_BAUEN", async ({ auftrag, senke, abbruchGewuenscht, angelegt }) => {
const anfrage = TemplateBauAnfrage.parse(auftrag.anfrage);
const server = await prisma.server.findUniqueOrThrow({ where: { id: auftrag.serverId } });
const zeile = await prisma.distribution.findUniqueOrThrow({ where: { id: anfrage.distribution } });
const distribution = zeile.definition as Distribution;
const profile = await templateProfileAufloesen(anfrage.profile, distribution.family);
await prisma.auftrag.update({
where: { id: auftrag.id },
data: {
kopf: `Server ${server.name} · Node ${anfrage.knoten} · Template ${distribution.name}${
anfrage.profile.length > 0 ? ` mit ${anfrage.profile.join(", ")}` : ""
}`,
},
});
await melden(auftrag.id);
const client = new ProxmoxClient(verbindungAus(server), { beobachter: aufrufLogger(senke) });
const stand: TemplateBauStand = {};
try {
const ergebnis = await phasenAusfuehren(
templateBauSchritte(
{
client,
distribution,
profile,
knoten: anfrage.knoten,
storage: anfrage.storage,
bridge: anfrage.bridge,
vlan: anfrage.vlan,
ssh: nodeBefehlFuer(server, anfrage.knoten),
auftragKurzId: auftrag.kurzId,
vmidReservieren: async (belegt) => {
const vmid = await vmidReservieren({
serverId: server.id,
auftragId: auftrag.id,
belegt,
von: server.templateVmidVon,
bis: server.templateVmidBis,
});
await prisma.auftrag.update({ where: { id: auftrag.id }, data: { vmid } });
await angelegt({ vmid, knoten: anfrage.knoten, typ: "VM" });
return vmid;
},
},
stand,
),
senke,
{ abbruchGewuenscht },
);
if (!ergebnis.ok) return ergebnis;
await prisma.template.create({
data: {
serverId: server.id,
knoten: anfrage.knoten,
vmid: stand.vmid!,
distributionId: distribution.id,
storage: anfrage.storage,
imageDatei: stand.imageDatei!,
pruefsumme: stand.pruefsumme!,
profile: profile.map((p) => p.profil.id),
gebautVonAuftragId: auftrag.id,
},
});
return {
ok: true,
ergebnis: {
vmid: stand.vmid,
template: templateName(
distribution,
profile.map((p) => p.profil.id),
),
knoten: anfrage.knoten,
},
};
} finally {
await client.schliessen();
}
});