Cancel active provisioning jobs when rolling back betas

This commit is contained in:
2026-10-05 17:48:46 -04:00
parent 6ad5e7f4c6
commit 701066aaf8
25 changed files with 564 additions and 37 deletions
+44 -3
View File
@@ -20,7 +20,7 @@ vi.mock('../../src/lib/server/integrations/rcon.js', () => ({
rconStatusResult: vi.fn(async () => ({ ok: true, endpoint: { host: 'localhost', port: 27016 }, rawReply: '',
status: { players: 0, map: 'zombie_cosmodrome', gametype: null, roster: [] } }))
}));
vi.mock('../../src/lib/server/pipeline/worker.js', () => ({ kick: vi.fn(async () => {}) }));
vi.mock('../../src/lib/server/pipeline/worker.js', () => ({ kick: vi.fn(async () => {}), kickProvisioning: vi.fn(async () => {}) }));
let fixture: Fixture;
const serverName = 't5_zombie_cosmodrome_1';
@@ -68,9 +68,9 @@ describe('queued removal cancellation', () => {
resolveCount(0);
await work;
expect(komodoDestroyDeployment).not.toHaveBeenCalled();
expect(getDeployment(queued.deploymentId!)?.deployment).toMatchObject({ state: 'cancelled', server_exists: 1, stage: 'Removal cancelled' });
expect(getDeployment(queued.deploymentId!)?.deployment).toMatchObject({ state: 'cancelled', server_exists: 1, stage: 'Cancelled by operator' });
expect(getDeployment(queued.deploymentId!)?.canCancelRemoval).toBe(false);
expect((await POST({ params: { id: queued.deploymentId! } })).status).toBe(409);
expect((await POST({ params: { id: queued.deploymentId! } })).status).toBe(200);
});
it('does not overwrite cancellation when the in-flight player check rejects', async () => {
@@ -760,3 +760,44 @@ describe('persistent server keys', () => {
.toMatchObject({ ok: true, name: serverName });
});
});
describe('cancelling submitted creation and destruction', () => {
it('retains a remotely created identity after cancellation without starting its container', async () => {
const { queueCreateServer, processCreations, nextAvailablePort, nextLobbyNumber } = await import('../../src/lib/server/provisioning/instances.js');
const { cancelProvisioningJob } = await import('../../src/lib/server/provisioning/cancellation.js');
const { getDeployment } = await import('../../src/lib/server/provisioning/history.js');
const { getServer } = await import('../../src/lib/server/fleet/servers.js');
const { komodoCreateDeployment, komodoDeploy } = await import('../../src/lib/server/integrations/komodo.js');
const queued = queueCreateServer(input); if (!queued.ok) throw new Error(queued.message);
let release!: (value: {ok:true}) => void;
vi.mocked(komodoCreateDeployment).mockImplementationOnce(() => new Promise(resolve => {release=resolve;}));
const work = processCreations();
await vi.waitFor(() => expect(release).toBeTypeOf('function'));
expect(cancelProvisioningJob(queued.deploymentId!)).toBe(true);
expect(nextAvailablePort()).not.toBe(queued.port);
expect(nextLobbyNumber('t5_zombie_cosmodrome')).toBe(2);
release({ok:true}); await work;
expect(getServer(queued.name)).toBeTruthy();
expect(komodoDeploy).not.toHaveBeenCalled();
expect(getDeployment(queued.deploymentId!)?.deployment.state).toBe('cancelled');
});
it('does not delete deployment metadata or files after cancelling container teardown', async () => {
const { createServer, destroyServer, processDestructions } = await import('../../src/lib/server/provisioning/instances.js');
const { cancelProvisioningJob } = await import('../../src/lib/server/provisioning/cancellation.js');
const { getDeployment } = await import('../../src/lib/server/provisioning/history.js');
const { getServer } = await import('../../src/lib/server/fleet/servers.js');
const { komodoDestroyDeployment, komodoDeleteDeployment } = await import('../../src/lib/server/integrations/komodo.js');
await createServer(input);
let release!: (value: {ok:true}) => void;
vi.mocked(komodoDestroyDeployment).mockImplementationOnce(() => new Promise(resolve => {release=resolve;}));
const queued=destroyServer(serverName); const work=processDestructions();
await vi.waitFor(() => expect(release).toBeTypeOf('function'));
expect(cancelProvisioningJob(queued.deploymentId!)).toBe(true);
release({ok:true}); await work;
expect(komodoDeleteDeployment).not.toHaveBeenCalled();
expect(getServer(serverName)).toBeTruthy();
expect(existsSync(join(fixture.t5,serverName))).toBe(true);
expect(getDeployment(queued.deploymentId!)?.deployment.state).toBe('cancelled');
});
});