fix(gateway): finish lifecycle action audits
This commit is contained in:
@@ -1332,11 +1332,6 @@ export const adminRouter = router({
|
||||
SHUTDOWN: 'DISABLED',
|
||||
} as const;
|
||||
const mappedStatus = statusMap[input.action as keyof typeof statusMap];
|
||||
if (mappedStatus) {
|
||||
await ctx.profiles.updateStatus(input.profileName, mappedStatus);
|
||||
await ctx.orchestrator.reconcileNow();
|
||||
}
|
||||
|
||||
const meta = readMetaObject(profile.meta);
|
||||
const actionLog = Array.isArray(meta.adminActions)
|
||||
? meta.adminActions.filter((entry) => entry && typeof entry === 'object')
|
||||
@@ -1354,6 +1349,23 @@ export const adminRouter = router({
|
||||
adminActions: [...actionLog, actionRecord],
|
||||
};
|
||||
await ctx.profiles.updateMeta(input.profileName, nextMeta);
|
||||
if (mappedStatus) {
|
||||
await ctx.profiles.updateStatus(input.profileName, mappedStatus);
|
||||
await ctx.orchestrator.reconcileNow();
|
||||
const appliedActionRecord = {
|
||||
...actionRecord,
|
||||
status: 'APPLIED',
|
||||
handledAt: new Date().toISOString(),
|
||||
handler: 'gateway-api',
|
||||
detail: `profile status reconciled as ${mappedStatus}`,
|
||||
};
|
||||
await ctx.profiles.updateMeta(input.profileName, {
|
||||
...nextMeta,
|
||||
adminActions: [...actionLog, appliedActionRecord],
|
||||
adminActionsUpdatedAt: appliedActionRecord.handledAt,
|
||||
});
|
||||
return { ok: true, action: appliedActionRecord };
|
||||
}
|
||||
return { ok: true, action: actionRecord };
|
||||
}),
|
||||
requestBuild: profileAdminProcedure
|
||||
|
||||
@@ -42,6 +42,7 @@ const buildCaller = async (
|
||||
const createdRuntimeActions: Array<Record<string, unknown>> = [];
|
||||
const flushes: Array<{ userId: string; reason?: string; iconRevision?: string }> = [];
|
||||
const updatedStatuses: GatewayProfileRecord['status'][] = [];
|
||||
const updatedMetas: Record<string, unknown>[] = [];
|
||||
let reconcileCount = 0;
|
||||
let storedNotice = options.initialNotice ?? '';
|
||||
const profile = {
|
||||
@@ -65,7 +66,10 @@ const buildCaller = async (
|
||||
return { ...profile, status };
|
||||
},
|
||||
updateBuildStatus: async () => profile,
|
||||
updateMeta: async () => profile,
|
||||
updateMeta: async (_profileName, meta) => {
|
||||
updatedMetas.push(meta);
|
||||
return profile;
|
||||
},
|
||||
listReservedToStart: async () => [],
|
||||
findQueuedBuild: async () => null,
|
||||
updateLastError: async () => {},
|
||||
@@ -171,6 +175,7 @@ const buildCaller = async (
|
||||
admin,
|
||||
flushes,
|
||||
updatedStatuses,
|
||||
updatedMetas,
|
||||
getReconcileCount: () => reconcileCount,
|
||||
getStoredNotice: () => storedNotice,
|
||||
setStoredNotice: (notice: string) => {
|
||||
@@ -360,6 +365,47 @@ describe('admin runtime clock action API', () => {
|
||||
).resolves.toMatchObject({ ok: true });
|
||||
expect(harness.updatedStatuses).toEqual(['RUNNING']);
|
||||
expect(harness.getReconcileCount()).toBe(1);
|
||||
expect(harness.updatedMetas).toHaveLength(2);
|
||||
expect(harness.updatedMetas.at(-1)).toMatchObject({
|
||||
adminActions: [
|
||||
{
|
||||
action: 'RESUME',
|
||||
status: 'APPLIED',
|
||||
handler: 'gateway-api',
|
||||
detail: 'profile status reconciled as RUNNING',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
['STOP', 'STOPPED'],
|
||||
['SHUTDOWN', 'DISABLED'],
|
||||
] as const)('records %s as terminal only after runtime reconciliation', async (action, expectedStatus) => {
|
||||
const harness = await buildCaller(unusedCreateOperation, { initialProfileStatus: 'RUNNING' });
|
||||
|
||||
const result = await harness.caller.admin.profiles.requestAction({
|
||||
profileName: 'che:2',
|
||||
action,
|
||||
});
|
||||
|
||||
expect(harness.updatedStatuses).toEqual([expectedStatus]);
|
||||
expect(harness.getReconcileCount()).toBe(1);
|
||||
expect(harness.updatedMetas).toHaveLength(2);
|
||||
expect(harness.updatedMetas[0]).toMatchObject({
|
||||
adminActions: [{ action, status: 'REQUESTED' }],
|
||||
});
|
||||
expect(harness.updatedMetas[1]).toMatchObject({
|
||||
adminActions: [
|
||||
{
|
||||
action,
|
||||
status: 'APPLIED',
|
||||
handler: 'gateway-api',
|
||||
detail: `profile status reconciled as ${expectedStatus}`,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(result.action).toMatchObject({ action, status: 'APPLIED', handledAt: expect.any(String) });
|
||||
});
|
||||
|
||||
it('rejects resume outside stopped and paused states without reconciliation', async () => {
|
||||
|
||||
Reference in New Issue
Block a user