Add deploy + destroy server flow; migrate to t5_<mode>_<slug>_<n> naming
Full server lifecycle management from Electric Cherry: pick game/mode/map/pluto key in the Fleet page, EC provisions the deployment via Komodo, allocates a port in 28960-28980, creates the 0o777 host dir, and starts the container. Destroy button tears everything down (Komodo Destroy+Delete, rm -rf host dir, transactional DB cleanup) and surfaces any partial-failure warnings inline. Same phase cuts over to the new naming scheme (t5_zombie_cosmodrome_1 etc.). Komodo entity names now match EC server names 1:1 (no more _/- transform), and the map catalog carries friendly/slug/canonical for each of the 11 t5 zombie maps. Server rows gain game_mode, map_slug, host_dir, config_file, game_id, image, created_by, and destroyed_at columns for provisioning + audit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -8,13 +8,13 @@ let fixture: Fixture;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
fixture = await newFixture(['t5_kino_1']);
|
||||
fixture = await newFixture(['t5_zombie_theater_1']);
|
||||
delete process.env.KOMODO_ADDRESS;
|
||||
delete process.env.RCON_PASSWORD;
|
||||
// Ensure the three per-kind destinations exist under the fixture tree.
|
||||
await mkdir(join(fixture.t5, 't5_kino_1', 'runtime/plutonium/storage/t5/raw/scripts/sp'), { recursive: true });
|
||||
await mkdir(join(fixture.t5, 't5_kino_1', 'configs'), { recursive: true });
|
||||
await mkdir(join(fixture.t5, 't5_kino_1', 'runtime/plutonium/plugins'), { recursive: true });
|
||||
await mkdir(join(fixture.t5, 't5_zombie_theater_1', 'runtime/plutonium/storage/t5/raw/scripts/sp'), { recursive: true });
|
||||
await mkdir(join(fixture.t5, 't5_zombie_theater_1', 'configs'), { recursive: true });
|
||||
await mkdir(join(fixture.t5, 't5_zombie_theater_1', 'runtime/plutonium/plugins'), { recursive: true });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -28,7 +28,7 @@ describe('drift scanning', () => {
|
||||
await seedServerRegistry();
|
||||
|
||||
// Drop one stray file in each managed destination.
|
||||
const server = 't5_kino_1';
|
||||
const server = 't5_zombie_theater_1';
|
||||
await writeFile(
|
||||
join(fixture.t5, server, 'runtime/plutonium/storage/t5/raw/scripts/sp', 'stray.gsc'),
|
||||
'main(){}\n'
|
||||
@@ -84,7 +84,7 @@ async function promoteAndOrphan(
|
||||
const ver = getLatestVersion(created.script.id)!;
|
||||
const { betaId } = await openBeta({
|
||||
scripts: [{ scriptId: created.script.id, scriptVersionId: ver.id }],
|
||||
servers: ['t5_kino_1'],
|
||||
servers: ['t5_zombie_theater_1'],
|
||||
waitForHealth: false,
|
||||
waitForMatch: false
|
||||
});
|
||||
@@ -121,7 +121,7 @@ describe('missing artifact detection', () => {
|
||||
|
||||
const targetPath = join(
|
||||
fixture.t5,
|
||||
't5_kino_1',
|
||||
't5_zombie_theater_1',
|
||||
'runtime/plutonium/storage/t5/raw/scripts/sp',
|
||||
'hud.gsc'
|
||||
);
|
||||
@@ -131,13 +131,13 @@ describe('missing artifact detection', () => {
|
||||
// Simulate someone deleting the file out-of-band.
|
||||
await rm(targetPath, { force: true });
|
||||
|
||||
const drift = await scanServerDrift('t5_kino_1');
|
||||
const drift = await scanServerDrift('t5_zombie_theater_1');
|
||||
const missing = drift.filter((d) => d.driftKind === 'missing' && d.kind === 'script');
|
||||
expect(missing.length).toBe(1);
|
||||
expect(missing[0].relPath).toBe('hud.gsc');
|
||||
|
||||
const result = await runDriftDeployment({
|
||||
servers: ['t5_kino_1'],
|
||||
servers: ['t5_zombie_theater_1'],
|
||||
dryRun: false,
|
||||
triggeredBy: 'ui'
|
||||
});
|
||||
@@ -169,7 +169,7 @@ describe('missing artifact detection', () => {
|
||||
const bVer = getLatestVersion(baseline.script.id)!;
|
||||
const { betaId } = await openBeta({
|
||||
scripts: [{ scriptId: baseline.script.id, scriptVersionId: bVer.id }],
|
||||
servers: ['t5_kino_1'],
|
||||
servers: ['t5_zombie_theater_1'],
|
||||
waitForHealth: false,
|
||||
waitForMatch: false
|
||||
});
|
||||
@@ -179,11 +179,11 @@ describe('missing artifact detection', () => {
|
||||
db().prepare(`UPDATE betas SET state = 'ready-promote' WHERE id = ?`).run(betaId);
|
||||
await promoteBeta(betaId);
|
||||
|
||||
const cfgPath = join(fixture.t5, 't5_kino_1', 'configs', 'dedicated_sp.cfg');
|
||||
const cfgPath = join(fixture.t5, 't5_zombie_theater_1', 'configs', 'dedicated_sp.cfg');
|
||||
expect(existsSync(cfgPath)).toBe(true);
|
||||
await rm(cfgPath, { force: true });
|
||||
|
||||
const drift = await scanServerDrift('t5_kino_1');
|
||||
const drift = await scanServerDrift('t5_zombie_theater_1');
|
||||
const missingCfg = drift.filter((d) => d.driftKind === 'missing' && d.kind === 'config');
|
||||
expect(missingCfg.length).toBe(1);
|
||||
expect(missingCfg[0].relPath).toBe('dedicated_sp.cfg');
|
||||
|
||||
Reference in New Issue
Block a user