306 lines
11 KiB
TypeScript
306 lines
11 KiB
TypeScript
import * as fs from 'node:fs';
|
|
import * as path from 'node:path';
|
|
|
|
import {
|
|
decideMatrixPlacementBind,
|
|
type AuthorityCoordinatorLease,
|
|
type MatrixMountCardinality,
|
|
type MatrixPlacementBindDecision,
|
|
type MatrixPlacementBindRequest,
|
|
type MatrixPlacementPolicy,
|
|
type RuntimeInventoryPlacementMetadata,
|
|
} from '@open-matrix/contracts/placement';
|
|
import type { MatrixActor } from '@open-matrix/core/core/MatrixActor.js';
|
|
import { MatrixRuntime } from '@open-matrix/core/runtime/MatrixRuntime.js';
|
|
import type { ILoadedMatrixPackageEnvironment } from './package-environment.js';
|
|
import type { ILoadedMatrixServiceManifest } from './service-manifest.js';
|
|
|
|
export const MATRIX_PLACEMENT_POLICY_SERVICE = 'matrix-placement-policy';
|
|
export const MATRIX_PLACEMENT_STANDBY_RESULTS_SERVICE = 'matrix-placement-standby-results';
|
|
|
|
export interface RunnerPlacementStandbyResult {
|
|
readonly mount: string;
|
|
readonly runtimeId: string;
|
|
readonly packageName: string;
|
|
readonly placement: RuntimeInventoryPlacementMetadata;
|
|
}
|
|
|
|
interface PlacementDeclaration {
|
|
readonly cardinality: MatrixMountCardinality;
|
|
readonly physicalMount?: string;
|
|
}
|
|
|
|
interface NodeTopologySnapshot {
|
|
readonly kind?: string;
|
|
readonly nodeId?: string;
|
|
readonly authorityRoot: string;
|
|
readonly authorityCoordinatorActive: boolean;
|
|
readonly authorityLease?: AuthorityCoordinatorLease | null;
|
|
}
|
|
|
|
type SupervisedCreate = MatrixRuntime['createSupervised'];
|
|
|
|
export function installRunnerPlacementPolicy(options: {
|
|
readonly runtime: MatrixRuntime;
|
|
readonly manifest: ILoadedMatrixServiceManifest;
|
|
readonly environment: ILoadedMatrixPackageEnvironment | undefined;
|
|
readonly runtimeId: string;
|
|
readonly authorityRoot: string | undefined;
|
|
}): void {
|
|
const declarations = readPlacementDeclarations(options.manifest);
|
|
const topology = resolveTopologySnapshot(options.runtime, options.environment, options.authorityRoot);
|
|
const standbyResults: RunnerPlacementStandbyResult[] = [];
|
|
const policy: MatrixPlacementPolicy = {
|
|
canBind(input: MatrixPlacementBindRequest): MatrixPlacementBindDecision {
|
|
return decideMatrixPlacementBind(input);
|
|
},
|
|
};
|
|
|
|
options.runtime.registerService(MATRIX_PLACEMENT_POLICY_SERVICE, policy);
|
|
options.runtime.registerService(MATRIX_PLACEMENT_STANDBY_RESULTS_SERVICE, standbyResults);
|
|
|
|
const originalCreateSupervised = options.runtime.createSupervised.bind(options.runtime) as SupervisedCreate;
|
|
options.runtime.createSupervised = (async <T extends MatrixActor>(
|
|
ComponentClass: new () => T,
|
|
mount: string,
|
|
props?: Record<string, unknown>,
|
|
): Promise<T> => {
|
|
const trimmedMount = mount.trim();
|
|
const declaration = declarations.get(trimmedMount);
|
|
if (!declaration) {
|
|
return await originalCreateSupervised(ComponentClass, mount, props);
|
|
}
|
|
const physicalMount = declaration.physicalMount ?? trimmedMount;
|
|
const request: MatrixPlacementBindRequest = {
|
|
authorityRoot: topology.authorityRoot,
|
|
runtimeId: options.runtimeId,
|
|
packageName: options.manifest.packageName,
|
|
mount: trimmedMount,
|
|
cardinality: declaration.cardinality,
|
|
...(topology.kind ? { topologyKind: topology.kind } : {}),
|
|
authorityCoordinatorActive: topology.authorityCoordinatorActive,
|
|
...(topology.nodeId ? { ownerNodeId: topology.nodeId } : {}),
|
|
...(topology.authorityLease !== undefined ? { authorityLease: topology.authorityLease } : {}),
|
|
};
|
|
const decision = policy.canBind(request);
|
|
if (decision.allowed) {
|
|
return await originalCreateSupervised(ComponentClass, mount, props);
|
|
}
|
|
standbyResults.push({
|
|
mount: trimmedMount,
|
|
runtimeId: options.runtimeId,
|
|
packageName: options.manifest.packageName,
|
|
placement: {
|
|
cardinality: declaration.cardinality,
|
|
claimMode: claimModeForCardinality(declaration.cardinality),
|
|
logicalMount: trimmedMount,
|
|
physicalMount,
|
|
callable: decision.callable,
|
|
...(decision.reason ? { standbyReason: decision.reason } : {}),
|
|
},
|
|
});
|
|
throw new Error(`Placement denied for ${trimmedMount}: ${decision.reason ?? 'UNKNOWN_PLACEMENT'}`);
|
|
}) as SupervisedCreate;
|
|
}
|
|
|
|
export function readRunnerPlacementStandbyResults(runtime: MatrixRuntime): readonly RunnerPlacementStandbyResult[] {
|
|
return runtime.getRootContext().getService<readonly RunnerPlacementStandbyResult[]>(
|
|
MATRIX_PLACEMENT_STANDBY_RESULTS_SERVICE,
|
|
) ?? [];
|
|
}
|
|
|
|
function resolveTopologySnapshot(
|
|
runtime: MatrixRuntime,
|
|
environment: ILoadedMatrixPackageEnvironment | undefined,
|
|
authorityRoot: string | undefined,
|
|
): NodeTopologySnapshot {
|
|
const currentRoot = authorityRoot?.trim()
|
|
|| environment?.environment.runtime.root?.trim()
|
|
|| runtime.transport.root.trim();
|
|
const topology = readTopologyRecord(runtime.getRootContext().getService<unknown>('matrix-node-topology'))
|
|
?? readTopologyRecord(environment?.environment.topology);
|
|
const topologyAuthorityRoot = readAuthorityRoot(topology) ?? currentRoot;
|
|
const active = readAuthorityCoordinatorActive(topology);
|
|
const authorityLease = readAuthorityLease(runtime, environment, topology);
|
|
return {
|
|
...(readString(topology?.kind) ? { kind: readString(topology?.kind) } : {}),
|
|
...(readString(topology?.nodeId) ? { nodeId: readString(topology?.nodeId) } : {}),
|
|
authorityRoot: topologyAuthorityRoot,
|
|
authorityCoordinatorActive: active ?? true,
|
|
...(authorityLease !== undefined ? { authorityLease } : {}),
|
|
};
|
|
}
|
|
|
|
function readAuthorityLease(
|
|
runtime: MatrixRuntime,
|
|
environment: ILoadedMatrixPackageEnvironment | undefined,
|
|
topology: Record<string, unknown> | undefined,
|
|
): AuthorityCoordinatorLease | null | undefined {
|
|
const serviceLease = readAuthorityLeaseRecord(runtime.getRootContext().getService<unknown>('matrix-authority-coordinator-lease'));
|
|
if (serviceLease !== undefined) {
|
|
return serviceLease;
|
|
}
|
|
const topologyLease = readAuthorityLeaseRecord(topology?.authorityLease);
|
|
if (topologyLease !== undefined) {
|
|
return topologyLease;
|
|
}
|
|
return readAuthorityLeaseRecord(environment?.environment.authorityLease);
|
|
}
|
|
|
|
function readPlacementDeclarations(manifest: ILoadedMatrixServiceManifest): ReadonlyMap<string, PlacementDeclaration> {
|
|
const declarations = new Map<string, PlacementDeclaration>();
|
|
const manifestRecord = readJsonObjectIfPresent(path.join(manifest.packageDir, 'matrix.json'));
|
|
if (!manifestRecord) {
|
|
return declarations;
|
|
}
|
|
addPlacementDeclaration(declarations, manifestRecord.root);
|
|
const components = manifestRecord.components;
|
|
if (Array.isArray(components)) {
|
|
for (const component of components) {
|
|
addPlacementDeclaration(declarations, component);
|
|
}
|
|
}
|
|
return declarations;
|
|
}
|
|
|
|
function addPlacementDeclaration(declarations: Map<string, PlacementDeclaration>, value: unknown): void {
|
|
if (!isRecord(value)) {
|
|
return;
|
|
}
|
|
const mount = readString(value.mount);
|
|
const placement = isRecord(value.placement) ? value.placement : undefined;
|
|
const cardinality = readMountCardinality(placement?.cardinality);
|
|
if (!mount || !cardinality) {
|
|
return;
|
|
}
|
|
const physicalMount = readString(placement?.physicalMount);
|
|
declarations.set(mount, {
|
|
cardinality,
|
|
...(physicalMount ? { physicalMount } : {}),
|
|
});
|
|
}
|
|
|
|
function readTopologyRecord(value: unknown): Record<string, unknown> | undefined {
|
|
return isRecord(value) ? value : undefined;
|
|
}
|
|
|
|
function readAuthorityRoot(topology: Record<string, unknown> | undefined): string | undefined {
|
|
const authority = isRecord(topology?.authority) ? topology.authority : undefined;
|
|
return readString(authority?.authorityRoot);
|
|
}
|
|
|
|
function readAuthorityCoordinatorActive(topology: Record<string, unknown> | undefined): boolean | undefined {
|
|
const roles = isRecord(topology?.roles) ? topology.roles : undefined;
|
|
const authorityCoordinator = isRecord(roles?.authorityCoordinator) ? roles.authorityCoordinator : undefined;
|
|
return typeof authorityCoordinator?.active === 'boolean' ? authorityCoordinator.active : undefined;
|
|
}
|
|
|
|
function readAuthorityLeaseRecord(value: unknown): AuthorityCoordinatorLease | null | undefined {
|
|
if (value === null) {
|
|
return null;
|
|
}
|
|
if (!isRecord(value)) {
|
|
return undefined;
|
|
}
|
|
if (
|
|
value.version !== 1
|
|
|| typeof value.authorityRoot !== 'string'
|
|
|| typeof value.leaseId !== 'string'
|
|
|| typeof value.ownerNodeId !== 'string'
|
|
|| typeof value.placementEpoch !== 'number'
|
|
|| typeof value.acquiredAt !== 'string'
|
|
|| typeof value.heartbeatAt !== 'string'
|
|
|| typeof value.expiresAt !== 'string'
|
|
) {
|
|
return undefined;
|
|
}
|
|
if (
|
|
value.status !== 'active'
|
|
&& value.status !== 'released'
|
|
&& value.status !== 'expired'
|
|
&& value.status !== 'fenced'
|
|
) {
|
|
return undefined;
|
|
}
|
|
if (
|
|
value.scope !== 'local-home'
|
|
&& value.scope !== 'hub-authority'
|
|
&& value.scope !== 'remote-projection'
|
|
) {
|
|
return undefined;
|
|
}
|
|
if (
|
|
value.leaseAuthority !== 'local'
|
|
&& value.leaseAuthority !== 'hub'
|
|
) {
|
|
return undefined;
|
|
}
|
|
if (
|
|
value.source !== 'local-file'
|
|
&& value.source !== 'hub-projection'
|
|
) {
|
|
return undefined;
|
|
}
|
|
return {
|
|
version: 1,
|
|
authorityRoot: value.authorityRoot,
|
|
leaseId: value.leaseId,
|
|
ownerNodeId: value.ownerNodeId,
|
|
...(readString(value.ownerHostId) ? { ownerHostId: readString(value.ownerHostId) } : {}),
|
|
...(readString(value.ownerInstallId) ? { ownerInstallId: readString(value.ownerInstallId) } : {}),
|
|
...(readString(value.ownerHostLinkId) ? { ownerHostLinkId: readString(value.ownerHostLinkId) } : {}),
|
|
placementEpoch: value.placementEpoch,
|
|
acquiredAt: value.acquiredAt,
|
|
heartbeatAt: value.heartbeatAt,
|
|
expiresAt: value.expiresAt,
|
|
status: value.status,
|
|
scope: value.scope,
|
|
leaseAuthority: value.leaseAuthority,
|
|
source: value.source,
|
|
...(readString(value.reason) ? { reason: readString(value.reason) } : {}),
|
|
};
|
|
}
|
|
|
|
function claimModeForCardinality(cardinality: MatrixMountCardinality): RuntimeInventoryPlacementMetadata['claimMode'] {
|
|
if (cardinality === 'authority-singleton') {
|
|
return 'authoritative';
|
|
}
|
|
if (cardinality === 'queue-service') {
|
|
return 'pool-member';
|
|
}
|
|
if (cardinality === 'replicated-read') {
|
|
return 'mirror';
|
|
}
|
|
return 'local-only';
|
|
}
|
|
|
|
function readMountCardinality(value: unknown): MatrixMountCardinality | undefined {
|
|
return value === 'authority-singleton'
|
|
|| value === 'device-singleton'
|
|
|| value === 'device-local-control'
|
|
|| value === 'queue-service'
|
|
|| value === 'named-instance'
|
|
|| value === 'replicated-read'
|
|
? value
|
|
: undefined;
|
|
}
|
|
|
|
function readJsonObjectIfPresent(filePath: string): Record<string, unknown> | undefined {
|
|
if (!fs.existsSync(filePath)) {
|
|
return undefined;
|
|
}
|
|
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '')) as unknown;
|
|
if (!isRecord(parsed)) {
|
|
throw new Error(`Expected JSON object at ${filePath}`);
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
function readString(value: unknown): string {
|
|
return typeof value === 'string' ? value.trim() : '';
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
}
|