feat(ausrollen): VM ausrollen mit Assistent, Dry-Run und CLI-Befehl
Eine VM aus dem ProxDeploy-Template ausrollen, vollständig per API: Klonen, Hardware, cloud-init, Start, Guest-Agent und Provisionierung der Profile über den Agent – ohne SSH. Der Fortschritt kommt aus der Logdatei im Gast, je Profil eine Marke. Der Dry-Run läuft durch dieselben Phasen mit einem Trocken-Client und zeigt jeden geplanten Proxmox-Aufruf und den passenden CLI-Befehl. Der Assistent hat 9 Schritte, den Zustand in der URL und bietet an, ein fehlendes Template im selben Auftrag zu bauen. Auf pve-demo abgenommen: pd-test01 (base, docker) per API, pd-test02 (base, zabbix-agent) über den Assistenten, beide per SSH geprüft. Dabei gefunden: file-write scheitert an Umlauten – Skripte gehen jetzt base64-kodiert hinüber. Closes #008 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { POST as ausrollenPost } from "@/app/api/v1/auftraege/route";
|
||||
import { verschluesseln } from "@/lib/geheimnisse";
|
||||
import { cliBefehl, AusrollAnfrage } from "@/lib/schemas/ausrollen";
|
||||
import { katalogSynchronisieren } from "@/server/services/katalog";
|
||||
import { datenbankLeeren, datenbankVerfuegbar, testPrisma } from "./helpers/db";
|
||||
import { angemeldet, anfrage } from "./helpers/anfragen";
|
||||
|
||||
const SCHLUESSEL =
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEFTdkCuqQ6mF7I8QzmkNaQpKj7S/I2+yuxWeYasJjLd florian@test";
|
||||
const gueltig = {
|
||||
typ: "vm",
|
||||
server: "rz1",
|
||||
distribution: "debian-13",
|
||||
name: "web01",
|
||||
storage: "local-zfs",
|
||||
netz: { bridge: "vmbr0" },
|
||||
zugang: { benutzer: "florian", sshSchluessel: [SCHLUESSEL] },
|
||||
profile: ["docker"],
|
||||
};
|
||||
|
||||
describe("CLI-Befehl aus der Anfrage", () => {
|
||||
it("nennt nur Abweichungen vom Standard und entspricht der Vorgabe", () => {
|
||||
const a = AusrollAnfrage.parse({
|
||||
...gueltig,
|
||||
profile: ["base", "docker"],
|
||||
netz: { bridge: "vmbr0", vlan: 20, modus: "statisch", ip: "10.0.20.15/24", gateway: "10.0.20.1" },
|
||||
cores: 2,
|
||||
speicherMb: 4096,
|
||||
diskGb: 40,
|
||||
});
|
||||
expect(cliBefehl(a)).toBe(
|
||||
"proxdeploy vm web01 --server rz1 --distro debian-13 --profile base,docker --ip 10.0.20.15/24 --gw 10.0.20.1 --vlan 20 --memory 4096 --disk 40G --storage local-zfs --user florian",
|
||||
);
|
||||
});
|
||||
|
||||
it("quotet Variablenwerte mit Leerzeichen", () => {
|
||||
const a = AusrollAnfrage.parse({ ...gueltig, variablen: { "zabbix-agent": { server: "a b" } } });
|
||||
expect(cliBefehl(a)).toContain("--var zabbix-agent.server='a b'");
|
||||
});
|
||||
});
|
||||
|
||||
describe.runIf(datenbankVerfuegbar)("POST /api/v1/auftraege", () => {
|
||||
let cookie: string;
|
||||
beforeEach(async () => {
|
||||
await datenbankLeeren();
|
||||
await katalogSynchronisieren();
|
||||
cookie = (await angemeldet("OPERATOR")).cookie;
|
||||
await testPrisma.server.create({
|
||||
data: {
|
||||
name: "rz1",
|
||||
titel: "RZ 1",
|
||||
url: "https://127.0.0.1:8006",
|
||||
tokenId: "a@pve!b",
|
||||
tokenGeheimnis: verschluesseln("x"),
|
||||
fingerabdruck: "AA",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const senden = async (rumpf: unknown) => {
|
||||
const antwort = await ausrollenPost(anfrage("POST", "/api/v1/auftraege", cookie, rumpf));
|
||||
return { status: antwort.status, inhalt: await antwort.json() };
|
||||
};
|
||||
|
||||
it("meldet Feldfehler einzeln: Name, Schlüssel, root", async () => {
|
||||
const { status, inhalt } = await senden({
|
||||
...gueltig,
|
||||
name: "Web_01",
|
||||
zugang: { benutzer: "root", sshSchluessel: ["kein schlüssel"] },
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
expect(Object.keys(inhalt.fehler.felder)).toEqual(
|
||||
expect.arrayContaining(["name", "zugang.benutzer", "zugang.sshSchluessel.0"]),
|
||||
);
|
||||
});
|
||||
|
||||
it("fehlende Profil-Variable landet am richtigen Feld", async () => {
|
||||
const { status, inhalt } = await senden({ ...gueltig, profile: ["zabbix-agent"] });
|
||||
expect(status).toBe(400);
|
||||
expect(inhalt.fehler.felder).toEqual({ "variablen.zabbix-agent.server": "Zabbix-Server fehlt." });
|
||||
});
|
||||
|
||||
it("deaktivierte Distribution und unbekannter Server werden abgewiesen", async () => {
|
||||
await testPrisma.distribution.update({ where: { id: "debian-13" }, data: { aktiv: false } });
|
||||
expect((await senden(gueltig)).status).toBe(400);
|
||||
expect((await senden({ ...gueltig, server: "gibtsnicht" })).status).toBe(404);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user