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>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { Distribution } from "@/engine/katalog/schema";
|
||||
import type { Distribution, Profil } from "@/engine/katalog/schema";
|
||||
import { profileAufloesen, type AufgeloestesProfil } from "@/engine/profile/aufloesen";
|
||||
import { ProxmoxClient } from "@/engine/proxmox/client";
|
||||
import { ProxmoxFehler } from "@/engine/proxmox/fehler";
|
||||
import { prisma } from "@/lib/db";
|
||||
@@ -27,6 +28,7 @@ export async function templateBauStarten(
|
||||
if (!(distribution.definition as Distribution).vm) {
|
||||
throw new AnwendungsFehler(400, "ungueltig", `${distribution.name} bietet kein VM-Image.`);
|
||||
}
|
||||
await templateProfileAufloesen(anfrage.profile, (distribution.definition as Distribution).family);
|
||||
return await auftragAnlegen(
|
||||
{
|
||||
art: "TEMPLATE_BAUEN",
|
||||
@@ -42,6 +44,33 @@ export async function templateBauStarten(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Profile fürs Template: aufgelöst mit Standardwerten. Ein Profil mit Pflichtvariable (z. B. der
|
||||
* Zabbix-Server) ist gerätespezifisch und gehört nicht ins Template.
|
||||
*/
|
||||
export async function templateProfileAufloesen(
|
||||
auswahl: string[],
|
||||
familie: string,
|
||||
): Promise<AufgeloestesProfil[]> {
|
||||
if (auswahl.length === 0) return [];
|
||||
const verfuegbar = new Map(
|
||||
(await prisma.profil.findMany({ where: { aktiv: true, entferntAm: null } })).map((p) => [
|
||||
p.id,
|
||||
p.definition as Profil,
|
||||
]),
|
||||
);
|
||||
const aufgeloest = profileAufloesen(auswahl, verfuegbar, familie);
|
||||
if (!aufgeloest.ok) {
|
||||
throw new AnwendungsFehler(
|
||||
400,
|
||||
"ungueltig",
|
||||
aufgeloest.fehler.map((f) => `${f.profil}: ${f.meldung}`).join(" "),
|
||||
"Profile mit Pflichtvariablen gehören nicht ins Template – sie laufen beim Ausrollen.",
|
||||
);
|
||||
}
|
||||
return aufgeloest.profile;
|
||||
}
|
||||
|
||||
export type TemplateSicht = {
|
||||
vmid: number;
|
||||
name: string;
|
||||
@@ -49,6 +78,7 @@ export type TemplateSicht = {
|
||||
distributionId: string | null;
|
||||
gebautAm: string | null;
|
||||
imageDatei: string | null;
|
||||
profile: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -74,9 +104,13 @@ export async function templatesListe(serverName: string): Promise<TemplateSicht[
|
||||
vmid: r.vmid,
|
||||
name: r.name ?? `VM ${r.vmid}`,
|
||||
knoten: r.node,
|
||||
distributionId: db?.distributionId ?? tags.find((t) => t !== "proxdeploy-template") ?? null,
|
||||
distributionId:
|
||||
db?.distributionId ??
|
||||
tags.find((t) => t !== "proxdeploy-template" && !t.startsWith("profil-")) ??
|
||||
null,
|
||||
gebautAm: db?.gebautAm.toISOString() ?? null,
|
||||
imageDatei: db?.imageDatei ?? null,
|
||||
profile: tags.filter((t) => t.startsWith("profil-")).map((t) => t.slice("profil-".length)),
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.vmid - b.vmid);
|
||||
|
||||
Reference in New Issue
Block a user