release: v1.32.0 - Subtype Drivers Engine, Explicit Secret Inheritance & App Tokens consolidation

This commit is contained in:
2026-08-08 15:38:35 -04:00
parent 5b1302bc6f
commit a442dc9921
37 changed files with 1543 additions and 191 deletions
+74 -1
View File
@@ -19,9 +19,42 @@ function isDescendant(candidateId, rootId, edges) {
}
class DiscoveryReconciler {
static async reconcile(sourceName, payload) {
static async reconcile(sourceName, payload, options = {}) {
const { resources = [], edges = [] } = payload;
let newDevices = 0;
const location = options.location || options.site || null;
const autoPromote = !!options.autoPromote;
let targetSite = null;
if (location && String(location).trim()) {
const sites = await Resource.list({ where: { kind: 'site' } });
const locStr = String(location).trim().toLowerCase();
targetSite = sites.find(s => s.name.toLowerCase() === locStr || s.slug.toLowerCase() === locStr);
if (!targetSite) {
const locSlug = `site-${locStr.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}`;
targetSite = await Resource.create({
id: crypto.randomUUID(),
kind: 'site',
name: String(location).trim(),
slug: locSlug,
created_on: Math.floor(Date.now() / 1000)
}).catch(() => null);
}
}
if (!targetSite) {
const sites = await Resource.list({ where: { kind: 'site' } });
if (sites && sites.length > 0) {
targetSite = sites[0];
} else {
targetSite = await Resource.create({
id: crypto.randomUUID(),
kind: 'site',
name: 'Default Site',
slug: 'site-default',
created_on: Math.floor(Date.now() / 1000)
}).catch(() => null);
}
}
const normalizeMac = (m) => (m || '').toLowerCase().replace(/[^a-f0-9]/g, '');
const normalizeHost = (h) => (h || '').toLowerCase().split('.')[0].trim();
@@ -35,6 +68,7 @@ class DiscoveryReconciler {
for (const res of resources) {
if (!res.metadata) res.metadata = {};
if (autoPromote) res.metadata.managed = true;
res._originalSlug = res.slug; // Keep track for edge mapping
let existing = null;
@@ -255,6 +289,45 @@ class DiscoveryReconciler {
}
}
if (targetSite) {
const childSlugs = new Set(edges.map(e => e.childSlug));
for (const res of resources) {
if (res._actualId && res._actualId !== targetSite.id && !childSlugs.has(res._originalSlug || res.slug)) {
const edgeExists = existingEdges.find(e => e.childId === res._actualId);
if (!edgeExists) {
const created = await ResourceEdge.create({
id: crypto.randomUUID(),
parentId: targetSite.id,
childId: res._actualId,
relation: 'hosts'
}).catch(() => null);
if (created) existingEdges.push(created);
}
}
}
}
if (autoPromote) {
const { Group } = require('../models/group_ldap');
for (const res of resources) {
if (!res._actualId) continue;
const accessGroup = `${res.slug}_access`;
const adminGroup = `${res.slug}_admin`;
try {
await Group.get(accessGroup).catch(async (e) => {
if (e.status === 404) await Group.add({ name: accessGroup, description: `Access to ${res.name}`, owner: 'cn=admin' });
});
await Group.get(adminGroup).catch(async (e) => {
if (e.status === 404) await Group.add({ name: adminGroup, description: `Admin access to ${res.name}`, owner: 'cn=admin' });
});
await ResourceGroup.create({ id: crypto.randomUUID(), resourceId: res._actualId, groupCn: accessGroup, accessLevel: 'user' }).catch(() => {});
await ResourceGroup.create({ id: crypto.randomUUID(), resourceId: res._actualId, groupCn: adminGroup, accessLevel: 'admin' }).catch(() => {});
} catch (err) {
console.error(`[DiscoveryReconciler] autoPromote failed for ${res.slug}:`, err.message);
}
}
}
if (newDevices > 0) {
console.log(`[DiscoveryReconciler] Source ${sourceName} discovered ${newDevices} new devices.`);
}
+115
View File
@@ -0,0 +1,115 @@
'use strict';
const BaseDriver = require('../drivers/base_driver');
const ThetaAgentDriver = require('../drivers/theta_agent_driver');
const ProxmoxDriver = require('../drivers/proxmox_driver');
const DockerSocketDriver = require('../drivers/docker_socket_driver');
const DbDriver = require('../drivers/db_driver');
const NetworkDriver = require('../drivers/network_driver');
const K8sDriver = require('../drivers/k8s_driver');
const AgentManager = require('../utils/agent_manager');
/**
* Registry & Resolution Engine for Subtype Management and Metrics Drivers.
*/
class DriverRegistry {
constructor() {
this.drivers = [];
this.defaultDriver = new BaseDriver('unmanaged');
this.initDefaultDrivers();
}
initDefaultDrivers() {
this.thetaAgentDriver = new ThetaAgentDriver();
this.proxmoxDriver = new ProxmoxDriver();
this.dockerSocketDriver = new DockerSocketDriver();
this.dbDriver = new DbDriver();
this.networkDriver = new NetworkDriver();
this.k8sDriver = new K8sDriver();
// Register drivers in priority order
this.register(this.thetaAgentDriver);
this.register(this.proxmoxDriver);
this.register(this.dockerSocketDriver);
this.register(this.dbDriver);
this.register(this.networkDriver);
this.register(this.k8sDriver);
}
/**
* Register a new subtype driver.
* @param {BaseDriver} driver
*/
register(driver) {
if (driver && typeof driver.getMetrics === 'function') {
this.drivers.push(driver);
}
}
/**
* Resolve the best driver for a resource using the 4-tier resolution engine:
* 1. Direct theta-agent (if agent connected)
* 2. Subtype-specific driver (Proxmox, Docker, DB, Network, K8s)
* 3. Parent Provider Fallback (e.g. Proxmox hypervisor host for un-agentized LXC/KVM guest)
* 4. Unmanaged fallback
* @param {Object} resource
* @returns {BaseDriver}
*/
async resolveDriver(resource) {
if (!resource) return this.defaultDriver;
// 1. Direct Theta Agent Check
const agent = await AgentManager.getAgentForResource(resource.id).catch(() => null);
if (agent && agent.isOnline) {
return this.thetaAgentDriver;
}
// 2. Specialized Subtype Driver Check
for (const driver of this.drivers) {
if (driver !== this.thetaAgentDriver && driver.supports(resource)) {
return driver;
}
}
// 3. Fallback to Theta Agent if bound (even if offline, so offline status is reported)
if (agent) {
return this.thetaAgentDriver;
}
// 4. Fallback to Proxmox driver if it's an LXC/KVM guest
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
if (['lxc', 'kvm'].includes(subType)) {
return this.proxmoxDriver;
}
return this.defaultDriver;
}
/**
* Get operational telemetry for a resource.
*/
async getMetrics(resource, options = {}) {
const driver = await this.resolveDriver(resource);
return await driver.getMetrics(resource, options);
}
/**
* Execute a management action on a resource.
*/
async execAction(resource, action, params = {}) {
const driver = await this.resolveDriver(resource);
return await driver.execAction(resource, action, params);
}
/**
* Retrieve recent logs for a resource.
*/
async getLogs(resource, lines = 100) {
const driver = await this.resolveDriver(resource);
return await driver.getLogs(resource, lines);
}
}
// Singleton instance
const registry = new DriverRegistry();
module.exports = registry;
+1 -1
View File
@@ -82,7 +82,7 @@ async function runPluginJob(instanceId) {
};
const payload = await runFn(cfg);
if (instance.category === 'discovery') {
await DiscoveryReconciler.reconcile(instance.slug, payload);
await DiscoveryReconciler.reconcile(instance.slug, payload, cfg);
}
await instance.update({ lastStatus: STATUS.OK, lastError: null, lastLog: logs.join('\n') });
} catch (err) {