Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
4.5 kB · 140 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141import { InstallationConflict } from "./installation-errors.ts";import { parse, parseInstallation, sameRelease, type Installation, type InstallationChanges, type ReleaseIdentity,} from "./installation-metadata.ts";import { operationSchema, type InstallationOperation, type InstallationRecovery, type UpgradeBaseline,} from "./operation.ts";
interface InstallationStart { desired: ReleaseIdentity; deadline: number; recovery: InstallationRecovery | null; upgrade: UpgradeBaseline | null; operationId: string; now: number;}
export function beginInstallation( current: Installation, previous: InstallationOperation | null, command: InstallationStart,) { const { desired, deadline, recovery, upgrade, operationId, now } = command; if ( current.status === "installing" || current.status === "updating" || !!current.installedRelease !== !!upgrade || (upgrade && (!sameRelease(upgrade.fromRelease, current.installedRelease) || !sameRelease(upgrade.toRelease, desired))) || (current.desiredRelease && current.status !== "ready" && !sameRelease(current.desiredRelease, desired)) ) throw new InstallationConflict(); if ( recovery && (current.status !== "failed" || current.errorCode !== "recovery_required" || current.revision !== recovery.expectedRevision) ) throw new InstallationConflict(); if ( recovery?.clearWorker && !upgrade && (current.resources.personalAgentNamespaceId || current.resources.sandboxNamespaceId) ) throw new InstallationConflict(); if (recovery?.clearContainer && current.resources.sandboxApplicationId) throw new InstallationConflict(); const continuing = current.status === "ready" ? null : previous; const operation = parse(operationSchema, { upgrade, rolloutId: recovery?.clearRollout ? null : (continuing?.rolloutId ?? null), operationId, deadline, workerIntent: recovery?.clearWorker ? null : (continuing?.workerIntent ?? null), containerIntent: recovery?.clearContainer ? false : (continuing?.containerIntent ?? false), containerUpdate: continuing?.containerUpdate ?? false, rolloutIntent: recovery?.clearRollout ? null : (continuing?.rolloutIntent ?? null), }); const installation = parseInstallation({ ...current, desiredRelease: desired, operationId, status: upgrade ? "updating" : "installing", progress: "preparing", errorCode: null, revision: current.revision + 1, updatedAt: now, }); return { installation, operation };}
export function changeInstallation( current: Installation, expectedRevision: number, changes: InstallationChanges, now: number,): Installation { if (current.revision !== expectedRevision) throw new InstallationConflict(); const nextStatus = changes.status ?? current.status; const active = current.status === "installing" || current.status === "updating"; // FLA9/12 own execution. This only prevents stale/overlapping operations // and distinguishes the desired artifact from the last verified install. if (active) { if ( ![current.status, "failed", "ready"].includes(nextStatus) || (changes.operationId !== undefined && changes.operationId !== current.operationId) || (changes.desiredRelease !== undefined && !sameRelease(changes.desiredRelease, current.desiredRelease)) ) throw new InstallationConflict(); } else if ( nextStatus !== (current.installedRelease ? "updating" : "installing") || !changes.operationId || !changes.desiredRelease ) throw new InstallationConflict(); for (const [key, newValue] of Object.entries(changes.resources ?? {})) { const previous = current.resources[key as keyof Installation["resources"]]; if (previous !== null && previous !== newValue) throw new InstallationConflict(); } const next = parseInstallation({ ...current, ...changes, resources: { ...current.resources, ...changes.resources }, revision: current.revision + 1, updatedAt: now, errorCode: changes.errorCode ?? (nextStatus === "failed" ? current.errorCode : null), progress: nextStatus === "ready" ? "complete" : (changes.progress ?? (active ? current.progress : "preparing")), installedRelease: nextStatus === "ready" ? { ...current.desiredRelease!, installedAt: now } : current.installedRelease, }); return next;}