/** * `cele2e host` — bring the Docker host up and down in the shape the rig needs. * * On Linux this is a no-op: docker runs on the host kernel. On macOS docker * runs inside a colima VM whose memory and mount transport dominate how long a * suite takes, and both are set at `colima start` — so "start colima" is not a * neutral act, and everyone who typed it by hand got whatever colima's defaults * or the last person's flags left behind. That is what this command replaces: * one invocation, one policy, derived from the host it is running on. * * Three verbs, and the split between them is deliberate. * * `status` reads and reports. No side effects. * `up` starts the VM, or reports precisely why a running one cannot be * brought into policy. It NEVER destroys anything. * `reset` destroys the VM and recreates it. The only way to change the * mount type, and it costs the whole image cache, so it demands * `--yes` and refuses while the run-lock is held. * * `up` cannot fix a wrong mount type, and pretending otherwise would be worse * than useless: colima accepts `--mount-type` on an existing VM, prints * `'volume mount type' cannot be updated after initial setup, discarded`, and * carries on with the old one. A command that appeared to fix it and did not * would be exactly the "check that cannot reach its subject" failure this rig * has a whole section about. */ import { spawnSync } from 'node:child_process'; import { type HostVmBudget, activeProfile, evaluateHostVm, readHostFacts, readHostVmFacts, recommendedBudget, } from '../host-vm'; import { lockStatus } from '../run-lock'; const bold = '\x1b[1m'; const dim = '\x1b[2m'; const green = '\x1b[32m'; const red = '\x1b[31m'; const yellow = '\x1b[33m'; const reset = '\x1b[0m'; /** Is there a colima binary to drive? */ function haveColima(): boolean { return spawnSync('colima', ['version'], { stdio: 'ignore' }).status === 0; } function colimaRunning(profile: string): boolean { return spawnSync('colima', ['status', '-p', profile], { stdio: 'ignore' }).status === 0; } /** * The `colima start` a fresh VM should get. * * `vz` (Apple Virtualization) rather than qemu, because virtiofs needs it. * Exported so the docs and the tests quote the same string the command runs * rather than a transcription of it. */ export function colimaStartArgs(budget: HostVmBudget, profile: string): string[] { return [ 'start', '-p', profile, '--vm-type', 'vz', '--cpu', String(budget.cpus), '--memory', String(budget.memoryGiB), '--mount-type', budget.mountType, ]; } function reportStatus(): number { const profile = activeProfile(); const host = readHostFacts(); const facts = readHostVmFacts(profile); console.log(`${bold}=== Docker host ===${reset}`); console.log(` host ${host.cpus} CPU, ${host.memoryGiB} GiB`); if (!facts) { console.log(' VM none — docker runs natively, no host policy applies'); return 0; } const budget = recommendedBudget(host, facts.vmType); console.log( ` VM colima/${facts.profile} (${facts.vmType}): ${facts.cpus} CPU, ${facts.memoryGiB} GiB, ${facts.mountType} mounts`, ); console.log( ` policy ${budget.cpus} CPU, ${budget.memoryGiB} GiB, ${budget.mountType} mounts`, ); console.log(` running ${colimaRunning(profile) ? 'yes' : 'no'}`); console.log(''); const { problems, needsRecreate } = evaluateHostVm(facts, host, budget); if (problems.length === 0) { console.log(`${green}The VM matches the policy.${reset}`); return 0; } for (const problem of problems) console.log(` ${yellow}!${reset} ${problem}`); console.log(''); console.log( needsRecreate ? ` fix: ${bold}cele2e host reset --yes${reset} ${dim}(destroys the VM and its image cache; budget one \`cele2e build-infra\`)${reset}` : ` fix: ${bold}cele2e host up${reset} ${dim}(restarts the VM; images survive)${reset}`, ); return 1; } function bringUp(): number { const profile = activeProfile(); const host = readHostFacts(); const existing = readHostVmFacts(profile); const budget = recommendedBudget(host, existing?.vmType ?? 'vz'); if (!existing) { console.log( `${bold}Creating the colima VM${reset} ${dim}(${budget.cpus} CPU, ${budget.memoryGiB} GiB, ${budget.mountType} mounts)${reset}`, ); const result = spawnSync('colima', colimaStartArgs(budget, profile), { stdio: 'inherit' }); return result.status ?? 1; } const { problems, needsRecreate } = evaluateHostVm(existing, host, budget); if (needsRecreate) { console.error(`${red}The running VM cannot be brought into policy by restarting it.${reset}`); for (const problem of problems) console.error(` ! ${problem}`); console.error(''); console.error('colima discards a mount-type change on an existing VM, so the only fix is to'); console.error('recreate it. That deletes the local image cache and costs one full'); console.error(`${bold}cele2e build-infra${reset}. When you are ready:`); console.error(''); console.error(` ${bold}cele2e host reset --yes${reset}`); return 1; } if (colimaRunning(profile) && problems.length === 0) { console.log(`${green}Docker host already up and in policy.${reset}`); return 0; } // Memory and CPU DO take on a restart, so a plain `colima start` with the // budget is the fix for those. console.log( `${bold}Starting the colima VM${reset} ${dim}(${budget.cpus} CPU, ${budget.memoryGiB} GiB)${reset}`, ); const result = spawnSync( 'colima', ['start', '-p', profile, '--cpu', String(budget.cpus), '--memory', String(budget.memoryGiB)], { stdio: 'inherit' }, ); return result.status ?? 1; } /** * Refuse to touch the VM while a run holds the lock. * * Not hypothetical: stopping the VM under a live run killed a colleague's * suite mid-flight on 2026-09-05, and because every per-test container carries * `restart: unless-stopped`, the next VM boot resurrected that dead run's * containers as orphans. */ function lockBlocks(action: string): boolean { const lock = lockStatus(); if (lock.free || !lock.holder) return false; console.error(`${red}Refusing to ${action}: the e2e run-lock is held.${reset}`); console.error( ` ${lock.holder.session} — ${lock.holder.state} ${lock.holder.test} (pid ${lock.holder.pid})`, ); console.error(''); console.error('Poll with `cele2e status --json` (exit 0 = free, 3 = busy), or release a stack'); console.error('you know is abandoned with `cele2e down`.'); return true; } function bringDown(): number { if (lockBlocks('stop the Docker host')) return 3; const profile = activeProfile(); if (!readHostVmFacts(profile)) { console.log('No colima VM to stop.'); return 0; } console.log(`${bold}Stopping the colima VM${reset}`); return spawnSync('colima', ['stop', '-p', profile], { stdio: 'inherit' }).status ?? 1; } function resetVm(args: string[]): number { if (!args.includes('--yes')) { console.error(`${red}cele2e host reset DESTROYS the VM, including every built image.${reset}`); console.error(''); console.error('It is the only way to change the mount transport, which colima fixes at'); console.error('creation. After it you must run `cele2e build-infra` before any test.'); console.error(''); console.error(`Re-run as ${bold}cele2e host reset --yes${reset} when that is what you want.`); return 1; } if (lockBlocks('reset the Docker host')) return 3; const profile = activeProfile(); const host = readHostFacts(); const budget = recommendedBudget(host, 'vz'); console.log( `${bold}Deleting the colima VM${reset} ${dim}(every local image goes with it)${reset}`, ); const deleted = spawnSync('colima', ['delete', '-p', profile, '--force'], { stdio: 'inherit' }); if ((deleted.status ?? 1) !== 0) return deleted.status ?? 1; console.log( `${bold}Creating it again${reset} ${dim}(${budget.cpus} CPU, ${budget.memoryGiB} GiB, ${budget.mountType} mounts)${reset}`, ); const created = spawnSync('colima', colimaStartArgs(budget, profile), { stdio: 'inherit' }); if ((created.status ?? 1) !== 0) return created.status ?? 1; console.log(''); console.log( `${yellow}The image cache is empty. Run \`cele2e build-infra\` before any test.${reset}`, ); return 0; } /** Route a `cele2e host `; returns the process exit code. */ export function runHost(args: string[]): number { const verb = args[0] ?? 'status'; if (!haveColima() && verb !== 'status') { console.error('No `colima` binary found — nothing for `cele2e host` to drive.'); console.error('On Linux docker runs natively and there is no host VM to shape.'); return 1; } switch (verb) { case 'status': return reportStatus(); case 'up': return bringUp(); case 'down': return bringDown(); case 'reset': return resetVm(args.slice(1)); default: console.error(`Unknown host verb: ${verb}`); console.error('Usage: cele2e host '); return 1; } }