Fix: Add port allocation and EdgePublisher integration

- Allocate ports using PortAllocationService.reserve()
- Pass ports to agent payload builders (game and dev)
- Generate proper FQDN (slotHostname) for DNS records
- Include all required fields in enqueuePublishEdge (ports, slotHostname, txnId)
- Commit ports after successful provisioning
- Rollback ports on error
- Fixes DNS record creation, Velocity registration, and EdgePublisher jobs
This commit is contained in:
jester 2025-12-20 17:47:52 +00:00
parent 167246dfc6
commit 9eb678ea25

View File

@ -43,6 +43,7 @@ const AGENT_TEMPLATE_VMID = Number(
const AGENT_PORT = Number(process.env.ZLH_AGENT_PORT || 18888); const AGENT_PORT = Number(process.env.ZLH_AGENT_PORT || 18888);
const AGENT_TOKEN = process.env.ZLH_AGENT_TOKEN || null; const AGENT_TOKEN = process.env.ZLH_AGENT_TOKEN || null;
const ZONE = process.env.TECHNITIUM_ZONE || "zerolaghub.quest";
/* ------------------------------------------------------------- /* -------------------------------------------------------------
VERSION PARSER (Minecraft only) VERSION PARSER (Minecraft only)
@ -107,7 +108,7 @@ function generateAdminPassword() {
} }
/* ------------------------------------------------------------- /* -------------------------------------------------------------
GAME PAYLOAD (UNCHANGED) GAME PAYLOAD
------------------------------------------------------------- */ ------------------------------------------------------------- */
function buildGameAgentPayload({ function buildGameAgentPayload({
vmid, vmid,
@ -186,9 +187,9 @@ function buildGameAgentPayload({
} }
/* ------------------------------------------------------------- /* -------------------------------------------------------------
DEV PAYLOAD (NEW, MINIMAL, CANONICAL) DEV PAYLOAD
------------------------------------------------------------- */ ------------------------------------------------------------- */
function buildDevAgentPayload({ vmid, runtime, version, memoryMiB }) { function buildDevAgentPayload({ vmid, runtime, version, memoryMiB, ports }) {
if (!runtime) throw new Error("runtime required for dev container"); if (!runtime) throw new Error("runtime required for dev container");
if (!version) throw new Error("version required for dev container"); if (!version) throw new Error("version required for dev container");
@ -198,6 +199,7 @@ function buildDevAgentPayload({ vmid, runtime, version, memoryMiB }) {
runtime, runtime,
version, version,
memory_mb: Number(memoryMiB) || 2048, memory_mb: Number(memoryMiB) || 2048,
ports: Array.isArray(ports) ? ports : [ports].filter(Boolean),
}; };
} }
@ -283,10 +285,35 @@ export async function provisionAgentInstance(body = {}) {
let vmid; let vmid;
let ctIp; let ctIp;
let allocatedPorts = [];
let txnId = null;
try { try {
vmid = await allocateVmid(ctype); vmid = await allocateVmid(ctype);
// Allocate ports if needed
if (req.portsNeeded && req.portsNeeded > 0) {
console.log(`[agentProvision] Allocating ${req.portsNeeded} port(s)`);
txnId = crypto.randomUUID();
const portObjs = await PortAllocationService.reserve({
game: req.game,
variant: req.variant,
customerId: req.customerId,
vmid,
purpose: ctype === 'game' ? 'game_main' : 'dev',
txnId,
count: req.portsNeeded,
});
// Extract port numbers from objects
allocatedPorts = Array.isArray(portObjs)
? portObjs.map(p => typeof p === 'object' ? p.port : p)
: [portObjs];
console.log(`[agentProvision] Allocated ports: ${allocatedPorts.join(', ')}`);
}
const hostname = generateSystemHostname({ const hostname = generateSystemHostname({
ctype, ctype,
game: req.game, game: req.game,
@ -294,6 +321,9 @@ export async function provisionAgentInstance(body = {}) {
vmid, vmid,
}); });
// Generate FQDN for DNS/EdgePublisher
const slotHostname = `${hostname}.${ZONE}`;
await cloneContainer({ await cloneContainer({
templateVmid: AGENT_TEMPLATE_VMID, templateVmid: AGENT_TEMPLATE_VMID,
vmid, vmid,
@ -312,6 +342,7 @@ export async function provisionAgentInstance(body = {}) {
ctIp = await getCtIpWithRetry(vmid); ctIp = await getCtIpWithRetry(vmid);
// Build payload WITH ports
const payload = const payload =
ctype === "dev" ctype === "dev"
? buildDevAgentPayload({ ? buildDevAgentPayload({
@ -319,10 +350,12 @@ export async function provisionAgentInstance(body = {}) {
runtime: body.runtime, runtime: body.runtime,
version: body.version, version: body.version,
memoryMiB: req.memoryMiB, memoryMiB: req.memoryMiB,
ports: allocatedPorts,
}) })
: buildGameAgentPayload({ : buildGameAgentPayload({
vmid, vmid,
...req, ...req,
ports: allocatedPorts,
}); });
await sendAgentConfig({ ip: ctIp, payload }); await sendAgentConfig({ ip: ctIp, payload });
@ -335,23 +368,47 @@ export async function provisionAgentInstance(body = {}) {
ctype, ctype,
hostname, hostname,
ip: ctIp, ip: ctIp,
ports: allocatedPorts,
payload, payload,
agentState: "running", agentState: "running",
agentLastSeen: new Date(), agentLastSeen: new Date(),
}, },
}); });
await enqueuePublishEdge({ // Enqueue EdgePublisher with ALL required fields
vmid, if (allocatedPorts.length > 0) {
instanceHostname: hostname, console.log(`[agentProvision] Enqueuing EdgePublisher for vmid=${vmid}`);
ctIp, await enqueuePublishEdge({
game: req.game, vmid,
}); slotHostname, // ← FQDN for DNS records
instanceHostname: hostname, // ← Short hostname
ports: allocatedPorts, // ← CRITICAL
ctIp,
game: req.game,
txnId,
});
// Commit ports to mark them as in-use
await PortAllocationService.commit({ vmid, ports: allocatedPorts });
console.log(`[agentProvision] Ports committed for vmid=${vmid}`);
}
await confirmVmidAllocated(vmid); await confirmVmidAllocated(vmid);
return { vmid, hostname, ip: ctIp }; return { vmid, hostname, ip: ctIp, ports: allocatedPorts };
} catch (err) { } catch (err) {
console.error(`[agentProvision] ERROR for vmid=${vmid}:`, err.message);
// Rollback ports on failure
if (vmid && allocatedPorts.length > 0) {
try {
await PortAllocationService.releaseByVmid(vmid);
console.log(`[agentProvision] Rolled back ports for vmid=${vmid}`);
} catch (rollbackErr) {
console.error(`[agentProvision] Port rollback failed:`, rollbackErr.message);
}
}
if (vmid) { if (vmid) {
try { await deleteContainer(vmid); } catch {} try { await deleteContainer(vmid); } catch {}
try { await releaseVmid(vmid); } catch {} try { await releaseVmid(vmid); } catch {}