feat(templates): Template-Bau ohne SSH über eine Bau-Adresse
Ohne SSH zum Node kommt der Guest-Agent jetzt über eine feste Bau-Adresse ins Template: cloud-init setzt Adresse und den Bereitstellungsschlüssel für den Standardbenutzer, ProxDeploy meldet sich per SSH in der VM an, installiert den Agent und entfernt danach Schlüssel, Adresse und cloud-init-Angaben. Dabei korrigiert: der Verbindungstest meldete „Template-Bau per API“ auch ohne Weg, den Agent einzubauen. Im Protokoll erscheinen unveränderte JSON-Felder nicht mehr als geändert. Refs #021 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -360,3 +360,74 @@ describe("Profile einbacken (#022)", () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Guest-Agent über die Bau-Adresse (#021)", () => {
|
||||
it("ohne Node-SSH: feste Adresse und Bereitstellungsschlüssel per cloud-init, SSH in die VM, danach zurückgesetzt", async () => {
|
||||
const { mock, client } = await aufbauen({ version: "9.2.1" });
|
||||
mock.route("POST", /\/status\/shutdown$/, () => ({ data: UPID("qmshutdown") }));
|
||||
const gast: Array<{ host: string; benutzer: string; skript: string }> = [];
|
||||
const ergebnis = await phasenAusfuehren(
|
||||
templateBauSchritte({
|
||||
client,
|
||||
distribution: await debian13(),
|
||||
knoten: "pve01",
|
||||
storage: "local-zfs",
|
||||
bridge: "vmbr0",
|
||||
ssh: null,
|
||||
auftragKurzId: "t",
|
||||
holen: async () => `${HASH} debian-13-genericcloud-amd64.qcow2`,
|
||||
vmidReservieren: async () => 9000,
|
||||
warten: async () => {},
|
||||
bauNetz: { ip: "10.0.10.250/24", gateway: "10.0.10.1", dns: ["10.0.10.1"] },
|
||||
bereitstellung: async () => ({
|
||||
privat: "p",
|
||||
oeffentlich: "ssh-ed25519 AAAAbau proxdeploy-bereitstellung",
|
||||
}),
|
||||
gastSsh: async (ziel, skript) => {
|
||||
gast.push({ host: ziel.host, benutzer: ziel.benutzer, skript });
|
||||
return 0;
|
||||
},
|
||||
}),
|
||||
stumm,
|
||||
);
|
||||
expect(ergebnis).toEqual({ ok: true });
|
||||
expect(gast).toEqual([
|
||||
{
|
||||
host: "10.0.10.250",
|
||||
benutzer: "debian",
|
||||
skript: expect.stringContaining("apt-get -y -q -o DPkg::Lock::Timeout=600 install qemu-guest-agent"),
|
||||
},
|
||||
]);
|
||||
expect(gast[0]!.skript).toContain("rm -f /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys");
|
||||
const configs = mock.anfragen.filter((a) => a.methode === "PUT").map((a) => Object.fromEntries(a.rumpf));
|
||||
expect(configs[0]).toMatchObject({
|
||||
ciuser: "debian",
|
||||
ipconfig0: "ip=10.0.10.250/24,gw=10.0.10.1",
|
||||
nameserver: "10.0.10.1",
|
||||
});
|
||||
expect(configs[0]!["sshkeys"]).toBe("ssh-ed25519%20AAAAbau%20proxdeploy-bereitstellung");
|
||||
expect(configs.at(-1)).toEqual({ ipconfig0: "ip=dhcp", delete: "sshkeys,ciuser,nameserver" });
|
||||
// Kein Snippet ohne Node-SSH.
|
||||
expect(mock.anfragen.some((a) => a.rumpf.get("cicustom"))).toBe(false);
|
||||
});
|
||||
|
||||
it("ohne Node-SSH und ohne Bau-Adresse sagt schon die Prüfung, was fehlt", async () => {
|
||||
const { client } = await aufbauen({ version: "9.2.1" });
|
||||
const ergebnis = await phasenAusfuehren(
|
||||
templateBauSchritte({
|
||||
client,
|
||||
distribution: await debian13(),
|
||||
knoten: "pve01",
|
||||
storage: "local-zfs",
|
||||
bridge: "vmbr0",
|
||||
ssh: null,
|
||||
auftragKurzId: "t",
|
||||
holen: async () => "",
|
||||
vmidReservieren: async () => 9000,
|
||||
}),
|
||||
stumm,
|
||||
);
|
||||
expect(ergebnis).toMatchObject({ ok: false, phase: "pruefungen" });
|
||||
if (!ergebnis.ok) expect(ergebnis.fehler.hinweis).toMatch(/Bau-Adresse/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user