Queue native collection launches before async placement

This commit is contained in:
2026-10-05 11:35:50 -04:00
parent 1405114a14
commit 7c76851252
34 changed files with 1324 additions and 244 deletions
+41
View File
@@ -77,6 +77,9 @@ describe('host deletion', () => {
const queued = hosts.deleteHost(hosts.host.id);
expect(queued.deleted).toBe(false);
expect(hosts.getHost(hosts.host.id)?.decommission?.stage).toBe('queued');
const { nativeLaunchSelection } = await import('../../src/lib/server/provisioning/native.js');
expect(() => nativeLaunchSelection({ hostId: hosts.host.id, libraryId: 'version', image: 'plutainer:test' }, 't5'))
.toThrow('The selected host is being removed');
expect(hosts.authenticateCredential(hosts.credentials.credential)?.id).toBe(hosts.host.id);
await finishCleanup(hosts);
@@ -120,6 +123,44 @@ describe('host deletion', () => {
expect(hosts.authenticateCredential(hosts.credentials.credential)?.id).toBe(hosts.host.id);
});
it('blocks an accepted unplaced pinned launch until it is cancelled', async () => {
const hosts = await enrolled();
const { queueNativeCreationIntent } = await import('../../src/lib/server/provisioning/native.js');
const { cancelCreation } = await import('../../src/lib/server/provisioning/cancel-creation.js');
const { db } = await import('../../src/lib/server/db/index.js');
const queued = queueNativeCreationIntent({ server: 't5_zombie_theater_1', game: 't5', mapSlug: 'theater',
plutoKey: 'saved-key', selection: { hostId: hosts.host.id, libraryId: 'library', image: 'plutainer:test' } });
expect(queued.ok).toBe(true);
if (!queued.ok) throw new Error(queued.message);
expect(db().prepare('SELECT id FROM native_instances').all()).toEqual([]);
expect(db().prepare('SELECT instance_id FROM ec_host_ports').all()).toEqual([]);
expect(hosts.hostDeletionStatus(hosts.host.id)).toEqual({ allowed: false,
blockers: [expect.stringContaining('Cancel the queued server launches pinned to this host')] });
expect(() => hosts.deleteHost(hosts.host.id)).toThrow(expect.objectContaining({ status: 409 }));
expect(hosts.getHost(hosts.host.id)?.decommission).toBeNull();
expect(cancelCreation(queued.deploymentId!)).toBeTruthy();
expect(hosts.hostDeletionStatus(hosts.host.id)).toEqual({ allowed: true, blockers: [] });
await finishCleanup(hosts);
expect(hosts.getHost(hosts.host.id)).toBeNull();
});
it('does not pin a host for automatic launches or launches assigned elsewhere', async () => {
const hosts = await enrolled();
const other = hosts.createHost({ name: 'Other host' });
const { queueNativeCreationIntent } = await import('../../src/lib/server/provisioning/native.js');
for (const [index, selection] of [
{ hostId: hosts.host.id, automatic: true },
{ hostId: other.id, automatic: false }
].entries()) {
expect(queueNativeCreationIntent({ server: `t5_zombie_theater_${index + 1}`, game: 't5', mapSlug: 'theater',
plutoKey: 'saved-key', selection: { ...selection, libraryId: 'library', image: 'plutainer:test' } }).ok).toBe(true);
}
expect(hosts.hostDeletionStatus(hosts.host.id)).toEqual({ allowed: true, blockers: [] });
expect(hosts.hostDeletionStatus(other.id).allowed).toBe(false);
await finishCleanup(hosts);
expect(hosts.getHost(hosts.host.id)).toBeNull();
});
it('blocks residual port reservations independently of live instances', async () => {
const hosts = await enrolled();
hosts.reserveHostPort(hosts.host.id, 'unresolved-instance');