Make the multitenant branch use a clean PLATFORM_*/TENANT_* model, remove active AGENT_NAME runtime usage, collapse hostd ownership into the shared platform, add operator audit surfaces, and add read-only tenant lifecycle commands. --- Build: pass | Tests: pass — 151 passed (14 files)
91 lines
2.7 KiB
TypeScript
91 lines
2.7 KiB
TypeScript
import { TENANT_ID } from '../src/config.js';
|
|
import { formatDisplayDate } from '../src/display-date.js';
|
|
import {
|
|
collectPlatformAuditReport,
|
|
summarizeServiceProbe,
|
|
} from '../src/platform-audit-report.js';
|
|
|
|
function printSection(title: string): void {
|
|
console.log(`\n== ${title} ==`);
|
|
}
|
|
|
|
function formatList(items: string[]): string {
|
|
return items.length > 0 ? items.join(', ') : 'none';
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
const report = await collectPlatformAuditReport({
|
|
tenantId: TENANT_ID,
|
|
caller: 'operator',
|
|
});
|
|
const { registry } = report;
|
|
|
|
console.log('=== PLATFORM AUDIT ===');
|
|
console.log(`Generated: ${formatDisplayDate(new Date())}`);
|
|
console.log(
|
|
`Platform: ${registry.platform.id} (${registry.platform.displayName})`,
|
|
);
|
|
|
|
printSection('Declared Shared');
|
|
console.log(`Services: ${formatList(registry.shared.services)}`);
|
|
console.log(`Jails: ${formatList(registry.shared.jails)}`);
|
|
console.log(`Datasets: ${formatList(registry.shared.datasets)}`);
|
|
|
|
const tenantIds = Object.keys(registry.tenants).sort((a, b) =>
|
|
a.localeCompare(b),
|
|
);
|
|
|
|
for (const tenantId of tenantIds) {
|
|
const tenant = registry.tenants[tenantId];
|
|
printSection(`Tenant ${tenant.id}`);
|
|
console.log(`Display name: ${tenant.displayName}`);
|
|
console.log(`Services: ${tenant.service}`);
|
|
console.log(`Jails: ${formatList(tenant.workerJails)}`);
|
|
console.log(`Datasets: ${formatList(tenant.datasets)}`);
|
|
}
|
|
|
|
printSection('Observed Services');
|
|
for (const service of report.observedServices) {
|
|
console.log(
|
|
`${service.name}: ${summarizeServiceProbe(service)} [${service.owner}]`,
|
|
);
|
|
}
|
|
|
|
printSection('Observed Jails');
|
|
if (!report.observedJails) {
|
|
console.log(
|
|
`Unavailable: ${report.observedJailsError || 'hostd unreachable'}`,
|
|
);
|
|
} else {
|
|
const buckets = report.observedJails;
|
|
console.log(`Shared: ${formatList(buckets.shared)}`);
|
|
for (const tenantId of tenantIds) {
|
|
console.log(
|
|
`${tenantId}: ${formatList(buckets.tenants[tenantId] || [])}`,
|
|
);
|
|
}
|
|
console.log(`Unknown: ${formatList(buckets.unknown)}`);
|
|
}
|
|
|
|
printSection('Observed Datasets');
|
|
if (!report.observedDatasets) {
|
|
console.log(
|
|
`Unavailable: ${report.observedDatasetsError || 'hostd unreachable'}`,
|
|
);
|
|
return;
|
|
}
|
|
|
|
const datasetBuckets = report.observedDatasets;
|
|
console.log(`Shared: ${formatList(datasetBuckets.shared)}`);
|
|
for (const tenantId of tenantIds) {
|
|
console.log(
|
|
`${tenantId}: ${formatList(datasetBuckets.tenants[tenantId] || [])}`,
|
|
);
|
|
}
|
|
console.log(`Unknown: ${formatList(datasetBuckets.unknown)}`);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error('Error:', err instanceof Error ? err.message : String(err));
|
|
process.exit(1);
|
|
});
|