PSA/ee/appliance/host-service/metadata-engine.mjs
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

111 lines
3.5 KiB
JavaScript

#!/usr/bin/env node
import fs from 'node:fs';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
const DEFAULT_METADATA_FILE = '/var/lib/alga-appliance/maintenance-metadata.json';
function nowIso() {
return new Date().toISOString();
}
function readOsRelease(file = '/etc/os-release') {
if (!fs.existsSync(file)) {
return null;
}
const content = fs.readFileSync(file, 'utf8');
const lines = content.split(/\r?\n/).filter(Boolean);
const map = {};
for (const line of lines) {
const idx = line.indexOf('=');
if (idx <= 0) continue;
const key = line.slice(0, idx);
const raw = line.slice(idx + 1).trim();
map[key] = raw.replace(/^"|"$/g, '');
}
return {
id: map.ID || null,
versionId: map.VERSION_ID || null,
prettyName: map.PRETTY_NAME || null
};
}
function commandOutput(command) {
const result = spawnSync('sh', ['-c', command], { env: process.env, encoding: 'utf8' });
if (result.status !== 0) {
return null;
}
return (result.stdout || '').trim() || null;
}
function writeSecureJsonFile(targetFile, value) {
const dir = path.dirname(targetFile);
fs.mkdirSync(dir, { recursive: true, mode: 0o750 });
fs.writeFileSync(targetFile, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
fs.chmodSync(dir, 0o750);
fs.chmodSync(targetFile, 0o600);
}
function readJsonFile(file) {
if (!fs.existsSync(file)) {
return null;
}
try {
return JSON.parse(fs.readFileSync(file, 'utf8'));
} catch {
return null;
}
}
export function persistMaintenanceMetadata(options = {}) {
const metadataFile = options.metadataFile || DEFAULT_METADATA_FILE;
const releaseSelectionFile = options.releaseSelectionFile || '/etc/alga-appliance/release-selection.json';
const installStateFile = options.installStateFile || '/var/lib/alga-appliance/install-state.json';
const osReleaseFile = options.osReleaseFile || '/etc/os-release';
const k3sVersionCommand = options.k3sVersionCommand || 'k3s --version | head -n1';
const existing = readJsonFile(metadataFile) || {};
const releaseSelection = readJsonFile(releaseSelectionFile) || {};
const installState = readJsonFile(installStateFile) || {};
const osInfo = readOsRelease(osReleaseFile);
const k3sVersion = commandOutput(k3sVersionCommand);
const payload = {
updatedAt: nowIso(),
host: {
os: osInfo,
updatePolicy: 'manual-support-run-v1',
cveLiabilityNote: 'Ubuntu package and k3s updates are manual in v1; managed maintenance is planned for v2.'
},
k3s: {
version: k3sVersion,
updatePolicy: 'manual-support-run-v1'
},
app: {
selectedChannel: releaseSelection.selectedChannel || releaseSelection.channel || null,
selectedReleaseVersion: releaseSelection.selectedReleaseVersion || null,
registryHost: releaseSelection.registryHost || null,
repository: releaseSelection.repository || null,
manifestDigest: releaseSelection.manifestDigest || null,
lastKnownInstallStatus: installState.status || null,
lastKnownPhase: installState.phase || null,
lastAppUpdateAt: existing.app?.lastAppUpdateAt || null
},
maintenance: {
v2Direction: [
'add managed maintenance windows',
'preflight backup/snapshot checks',
'validated k3s upgrade paths',
'maintenance history and rollback guidance'
]
}
};
if ((installState.status || '').startsWith('update-')) {
payload.app.lastAppUpdateAt = nowIso();
}
writeSecureJsonFile(metadataFile, payload);
return { ok: true, metadataFile, metadata: payload };
}