Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -16,7 +16,8 @@ interface RestartSlot {
|
||||
}
|
||||
|
||||
function slotFor(server: string): RestartSlot | undefined {
|
||||
return db().prepare('SELECT * FROM restart_rollout WHERE server = ?').get(server) as RestartSlot | undefined;
|
||||
return db().prepare('SELECT * FROM restart_rollout WHERE server = ?').get(server) as
|
||||
RestartSlot | undefined;
|
||||
}
|
||||
|
||||
export function restartRecoveryPending(server: string): boolean {
|
||||
@@ -24,51 +25,107 @@ export function restartRecoveryPending(server: string): boolean {
|
||||
}
|
||||
|
||||
type SettlingSlot = Pick<RestartSlot, 'requested_at' | 'healthy_at' | 'operation_id'>;
|
||||
interface SettlingOperation { id: string; action: string; state: string }
|
||||
interface SettlingOperation {
|
||||
id: string;
|
||||
action: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
/** Submitted lifecycle work stays Starting until verification ends, including slow restarts. */
|
||||
export function recoverySettling(slot: SettlingSlot | undefined, operation?: SettlingOperation | null): boolean {
|
||||
export function recoverySettling(
|
||||
slot: SettlingSlot | undefined,
|
||||
operation?: SettlingOperation | null
|
||||
): boolean {
|
||||
if (!slot || slot.healthy_at) return false;
|
||||
if (slot.operation_id) return operation?.id === slot.operation_id
|
||||
&& ['start', 'restart', 'rebuild'].includes(operation.action)
|
||||
&& ['running', 'awaiting-health'].includes(operation.state);
|
||||
if (slot.operation_id)
|
||||
return (
|
||||
operation?.id === slot.operation_id &&
|
||||
['start', 'restart', 'rebuild'].includes(operation.action) &&
|
||||
['running', 'awaiting-health'].includes(operation.state)
|
||||
);
|
||||
// Legacy recovery slots have no operation whose verification can be followed.
|
||||
return Date.now() - Date.parse(slot.requested_at) < HEALTH_POLL_MS;
|
||||
}
|
||||
|
||||
export function restartSettling(server: string): boolean {
|
||||
const slot = slotFor(server);
|
||||
const operation = slot?.operation_id ? db().prepare(`SELECT id, action, state FROM server_deployments
|
||||
WHERE server = ? AND server_deleted_at IS NULL ORDER BY rowid DESC LIMIT 1`).get(server) as SettlingOperation | undefined : undefined;
|
||||
const operation = slot?.operation_id
|
||||
? (db()
|
||||
.prepare(
|
||||
`SELECT id, action, state FROM server_deployments
|
||||
WHERE server = ? AND server_deleted_at IS NULL ORDER BY rowid DESC LIMIT 1`
|
||||
)
|
||||
.get(server) as SettlingOperation | undefined)
|
||||
: undefined;
|
||||
return recoverySettling(slot, operation);
|
||||
}
|
||||
|
||||
export function rolloutWaitReason(server: string, operationId = '', action: RuntimeAction = 'restart'): string | null {
|
||||
export function rolloutWaitReason(
|
||||
server: string,
|
||||
operationId = '',
|
||||
action: RuntimeAction = 'restart'
|
||||
): string | null {
|
||||
return rolloutBlocker(server, operationId, action)?.message ?? null;
|
||||
}
|
||||
|
||||
export function rolloutBlocker(server: string, operationId = '', action: RuntimeAction = 'restart'): ProvisioningBlocker | null {
|
||||
const conflict = db().prepare(`SELECT id FROM server_deployments WHERE server = ? AND id != ? AND server_deleted_at IS NULL
|
||||
export function rolloutBlocker(
|
||||
server: string,
|
||||
operationId = '',
|
||||
action: RuntimeAction = 'restart'
|
||||
): ProvisioningBlocker | null {
|
||||
const conflict = db()
|
||||
.prepare(
|
||||
`SELECT id FROM server_deployments WHERE server = ? AND id != ? AND server_deleted_at IS NULL
|
||||
AND ((action IN ('start','stop','rebuild') AND state IN ('running','awaiting-health'))
|
||||
OR (action = 'destroy' AND state IN ('waiting','running')))`).get(server, operationId) as { id: string } | undefined;
|
||||
OR (action = 'destroy' AND state IN ('waiting','running')))`
|
||||
)
|
||||
.get(server, operationId) as { id: string } | undefined;
|
||||
if (conflict) {
|
||||
return { code: 'operation-conflict', message: 'Waiting for the current provisioning operation', automatic: true,
|
||||
href: `/provisioning/${encodeURIComponent(conflict.id)}`, label: 'Blocking operation' };
|
||||
return {
|
||||
code: 'operation-conflict',
|
||||
message: 'Waiting for the current provisioning operation',
|
||||
automatic: true,
|
||||
href: `/provisioning/${encodeURIComponent(conflict.id)}`,
|
||||
label: 'Blocking operation'
|
||||
};
|
||||
}
|
||||
const slot = slotFor(server);
|
||||
const ownHandoff = slot?.operation_id === operationId && !!db().prepare(`SELECT 1 FROM server_deployments d
|
||||
const ownHandoff =
|
||||
slot?.operation_id === operationId &&
|
||||
!!db()
|
||||
.prepare(
|
||||
`SELECT 1 FROM server_deployments d
|
||||
WHERE d.id = ? AND d.server_deleted_at IS NULL AND d.action IN ('restart','rebuild') AND EXISTS (
|
||||
SELECT 1 FROM provisioning_sources s WHERE s.operation_id = d.id AND s.kind = 'watchdog')`).get(operationId);
|
||||
SELECT 1 FROM provisioning_sources s WHERE s.operation_id = d.id AND s.kind = 'watchdog')`
|
||||
)
|
||||
.get(operationId);
|
||||
if (slot?.healthy_at === null && !ownHandoff) {
|
||||
return { code: 'previous-recovery', message: `Waiting for this server to recover from its previous operation${slot.error ? `: ${slot.error}` : ''}`, automatic: true,
|
||||
href: slot.operation_id ? `/provisioning/${encodeURIComponent(slot.operation_id)}` : null, label: 'Previous operation' };
|
||||
return {
|
||||
code: 'previous-recovery',
|
||||
message: `Waiting for this server to recover from its previous operation${slot.error ? `: ${slot.error}` : ''}`,
|
||||
automatic: true,
|
||||
href: slot.operation_id
|
||||
? `/provisioning/${encodeURIComponent(slot.operation_id)}`
|
||||
: null,
|
||||
label: 'Previous operation'
|
||||
};
|
||||
}
|
||||
const { n } = db().prepare('SELECT COUNT(*) AS n FROM restart_rollout WHERE healthy_at IS NULL').get() as { n: number };
|
||||
return n - (ownHandoff && slot?.healthy_at === null ? 1 : 0) >= fleetSettings().restartConcurrency
|
||||
? { code: 'restart-capacity', message: `Queued to ${action}; waiting for another server to recover`, automatic: true } : null;
|
||||
const { n } = db()
|
||||
.prepare('SELECT COUNT(*) AS n FROM restart_rollout WHERE healthy_at IS NULL')
|
||||
.get() as { n: number };
|
||||
return n - (ownHandoff && slot?.healthy_at === null ? 1 : 0) >=
|
||||
fleetSettings().restartConcurrency
|
||||
? {
|
||||
code: 'restart-capacity',
|
||||
message: `Queued to ${action}; waiting for another server to recover`,
|
||||
automatic: true
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
export function rolloutHealth(server: string): { ok: true; players: number } | { ok: false; reason: string } {
|
||||
export function rolloutHealth(
|
||||
server: string
|
||||
): { ok: true; players: number } | { ok: false; reason: string } {
|
||||
const slot = slotFor(server);
|
||||
if (slot?.healthy_at) return { ok: true, players: slot.players ?? 0 };
|
||||
return { ok: false, reason: slot?.error ?? 'Waiting for restart recovery and health checks' };
|
||||
|
||||
Reference in New Issue
Block a user