Add fleet and per-host concurrency controls
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest';
|
||||
import { newFixture, type Fixture } from '../helpers/fixture.js';
|
||||
let fixture: Fixture;
|
||||
beforeEach(async () => { vi.resetModules(); fixture = await newFixture(['t5_test']); });
|
||||
afterEach(async () => { await fixture.cleanup(); });
|
||||
|
||||
it('counts rebuilds once across pools and keeps their capacity through migration handoff and lower limits', async () => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { fleetSettings, saveFleetSettings } = await import('../../src/lib/server/settings.js');
|
||||
const { concurrencyBlocker, lifecycleHolders, preparingJobs } = await import('../../src/lib/server/provisioning/concurrency.js');
|
||||
const h = db();
|
||||
h.prepare("INSERT INTO native_instances(id,server,host_id,generation,library_id,image,port,created_at) VALUES('i','t5_test','source',1,'lib','img',1,'2026-01-01')").run();
|
||||
h.prepare("INSERT INTO server_deployments(id,server,action,state,stage,started_at,triggered_by) VALUES('a','t5_test','rebuild','waiting','migrating','2026-01-01','test')").run();
|
||||
h.prepare("INSERT INTO native_provisioning(deployment_id,instance_id,generation) VALUES('a','i',1)").run();
|
||||
h.prepare("INSERT INTO ec_host_migrations(deployment_id,source_host_id,instance_id,target_host_id,remove_operation_id,removed_at) VALUES('a','source','i','target','remove','2026-01-02')").run();
|
||||
saveFleetSettings({ ...fleetSettings(), lifecycleConcurrency: 1, rebuildConcurrency: 1 });
|
||||
expect(lifecycleHolders()).toHaveLength(1);
|
||||
expect(concurrencyBlocker('b','stop','other')).toContain('lifecycle');
|
||||
expect(concurrencyBlocker('a','rebuild','target')).toBeNull();
|
||||
saveFleetSettings({ ...fleetSettings(), lifecycleConcurrency: 0 });
|
||||
expect(concurrencyBlocker('b','rebuild','other')).toContain('rebuild');
|
||||
saveFleetSettings({ ...fleetSettings(), rebuildConcurrency: 0, createConcurrencyPerHost: 1 });
|
||||
expect(concurrencyBlocker('b','create','target')).toContain('startup');
|
||||
expect(concurrencyBlocker('b','create','other')).toBeNull();
|
||||
preparingJobs.add('a');
|
||||
expect(lifecycleHolders()).toHaveLength(1);
|
||||
preparingJobs.clear();
|
||||
});
|
||||
|
||||
it('enforces per-host removal and recovery limits independently of other hosts', async () => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { fleetSettings, saveFleetSettings } = await import('../../src/lib/server/settings.js');
|
||||
const { concurrencyBlocker } = await import('../../src/lib/server/provisioning/concurrency.js');
|
||||
const h = db();
|
||||
h.prepare("INSERT INTO servers(name) VALUES('t5_test')").run();
|
||||
h.prepare("INSERT INTO native_instances(id,server,host_id,generation,library_id,image,port,created_at) VALUES('i','t5_test','host',1,'lib','img',1,'2026-01-01')").run();
|
||||
h.prepare("INSERT INTO server_deployments(id,server,action,state,stage,started_at,triggered_by) VALUES('a','t5_test','destroy','running','removing','2026-01-01','test')").run();
|
||||
h.prepare("INSERT INTO native_provisioning(deployment_id,instance_id,generation) VALUES('a','i',1)").run();
|
||||
saveFleetSettings({ ...fleetSettings(), destroyConcurrencyPerHost: 1, restartConcurrencyPerHost: 1 });
|
||||
expect(concurrencyBlocker('b','destroy','host')).toContain('destruction');
|
||||
expect(concurrencyBlocker('b','destroy','other')).toBeNull();
|
||||
h.prepare("INSERT INTO restart_rollout(server,requested_at,operation_id) VALUES('t5_test','2026-01-01','a')").run();
|
||||
expect(concurrencyBlocker('b','restart','host')).toContain('recovery');
|
||||
expect(concurrencyBlocker('b','restart','other')).toBeNull();
|
||||
});
|
||||
Reference in New Issue
Block a user