Make profile shutdown idempotent
This commit is contained in:
@@ -963,8 +963,12 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle {
|
|||||||
private async stopProfile(profile: GatewayProfileRecord): Promise<void> {
|
private async stopProfile(profile: GatewayProfileRecord): Promise<void> {
|
||||||
const apiName = buildProcessName(profile.profileName, 'api');
|
const apiName = buildProcessName(profile.profileName, 'api');
|
||||||
const daemonName = buildProcessName(profile.profileName, 'daemon');
|
const daemonName = buildProcessName(profile.profileName, 'daemon');
|
||||||
|
const existingNames = new Set((await this.processManager.list()).map((process) => process.name));
|
||||||
const failures: string[] = [];
|
const failures: string[] = [];
|
||||||
for (const name of [apiName, daemonName]) {
|
for (const name of [apiName, daemonName]) {
|
||||||
|
if (!existingNames.has(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await this.processManager.stop(name);
|
await this.processManager.stop(name);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ const buildOperation = (type: 'START' | 'STOP'): GatewayOperationRecord => ({
|
|||||||
updatedAt: '2026-07-25T01:00:00.000Z',
|
updatedAt: '2026-07-25T01:00:00.000Z',
|
||||||
});
|
});
|
||||||
|
|
||||||
const createHarness = (operation: GatewayOperationRecord, failStart = false, failStop = false) => {
|
const createHarness = (
|
||||||
|
operation: GatewayOperationRecord,
|
||||||
|
failStart = false,
|
||||||
|
failStop = false,
|
||||||
|
processesPresent = true
|
||||||
|
) => {
|
||||||
let nextOperation: GatewayOperationRecord | null = operation;
|
let nextOperation: GatewayOperationRecord | null = operation;
|
||||||
const statuses: string[] = [];
|
const statuses: string[] = [];
|
||||||
const completions: GatewayOperationStatus[] = [];
|
const completions: GatewayOperationStatus[] = [];
|
||||||
@@ -77,7 +82,13 @@ const createHarness = (operation: GatewayOperationRecord, failStart = false, fai
|
|||||||
retryOperation: async () => null,
|
retryOperation: async () => null,
|
||||||
};
|
};
|
||||||
const processManager: ProcessManager = {
|
const processManager: ProcessManager = {
|
||||||
list: async () => [],
|
list: async () =>
|
||||||
|
processesPresent
|
||||||
|
? [
|
||||||
|
{ name: 'sammo:che:2:game-api', status: 'online' },
|
||||||
|
{ name: 'sammo:che:2:turn-daemon', status: 'online' },
|
||||||
|
]
|
||||||
|
: [],
|
||||||
start: async (definition) => {
|
start: async (definition) => {
|
||||||
if (failStart) {
|
if (failStart) {
|
||||||
throw new Error('pm2 unavailable');
|
throw new Error('pm2 unavailable');
|
||||||
@@ -145,6 +156,17 @@ describe('GatewayOrchestrator first-class operations', () => {
|
|||||||
expect(harness.completions).toEqual(['SUCCEEDED']);
|
expect(harness.completions).toEqual(['SUCCEEDED']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('treats an already stopped profile as a successful idempotent stop', async () => {
|
||||||
|
const harness = createHarness(buildOperation('STOP'), false, false, false);
|
||||||
|
|
||||||
|
await harness.orchestrator.runOperationsNow();
|
||||||
|
|
||||||
|
expect(harness.statuses).toEqual(['STOPPED']);
|
||||||
|
expect(harness.stopped).toEqual([]);
|
||||||
|
expect(harness.deleted).toEqual([]);
|
||||||
|
expect(harness.completions).toEqual(['SUCCEEDED']);
|
||||||
|
});
|
||||||
|
|
||||||
it('records a failed start instead of reporting a false success', async () => {
|
it('records a failed start instead of reporting a false success', async () => {
|
||||||
const harness = createHarness(buildOperation('START'), true);
|
const harness = createHarness(buildOperation('START'), true);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user