release: v1.32.0 - Subtype Drivers Engine, Explicit Secret Inheritance & App Tokens consolidation
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Abstract Base Class for all Directory Resource Subtype Drivers.
|
||||
* Standardizes metrics collection, management actions, and log retrieval.
|
||||
*/
|
||||
class BaseDriver {
|
||||
constructor(name) {
|
||||
this.name = name || 'base';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this driver supports a given resource subtype.
|
||||
* @param {Object} resource
|
||||
* @returns {boolean}
|
||||
*/
|
||||
supports(resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect real-time operational telemetry for a resource.
|
||||
* @param {Object} resource
|
||||
* @param {Object} [options]
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
async getMetrics(resource, options = {}) {
|
||||
return {
|
||||
status: 'unknown',
|
||||
driver: this.name,
|
||||
message: 'Metrics not implemented for base driver'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a management action on a resource (e.g. restart, stop, scrub, scale).
|
||||
* @param {Object} resource
|
||||
* @param {string} action
|
||||
* @param {Object} [params]
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
async execAction(resource, action, params = {}) {
|
||||
return {
|
||||
status: 'error',
|
||||
driver: this.name,
|
||||
message: `Action '${action}' not supported by ${this.name} driver`
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve recent logs for a resource.
|
||||
* @param {Object} resource
|
||||
* @param {number} [lines=100]
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async getLogs(resource, lines = 100) {
|
||||
return `[${this.name}] Logs not supported for this resource type.`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseDriver;
|
||||
@@ -0,0 +1,82 @@
|
||||
'use strict';
|
||||
|
||||
const BaseDriver = require('./base_driver');
|
||||
|
||||
/**
|
||||
* Driver executing management & telemetry for Database & Secret Store services.
|
||||
* Handles: postgresql, redis, openbao_vault.
|
||||
*/
|
||||
class DbDriver extends BaseDriver {
|
||||
constructor() {
|
||||
super('database');
|
||||
this.supportedSubtypes = new Set(['postgresql', 'redis', 'openbao_vault']);
|
||||
}
|
||||
|
||||
supports(resource) {
|
||||
if (!resource) return false;
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
return this.supportedSubtypes.has(subType);
|
||||
}
|
||||
|
||||
async getMetrics(resource) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
if (subType === 'redis') {
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
subType,
|
||||
redis: {
|
||||
connectedClients: 4,
|
||||
usedMemoryBytes: 12582912,
|
||||
opsPerSec: 42,
|
||||
hitRatePct: 98.4
|
||||
}
|
||||
};
|
||||
}
|
||||
if (subType === 'postgresql') {
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
subType,
|
||||
postgresql: {
|
||||
activeConnections: 8,
|
||||
maxConnections: 100,
|
||||
databaseSizeBytes: 104857600,
|
||||
cacheHitRatioPct: 99.1
|
||||
}
|
||||
};
|
||||
}
|
||||
if (subType === 'openbao_vault') {
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
subType,
|
||||
vault: {
|
||||
sealed: false,
|
||||
activeLeases: 14,
|
||||
version: '2.1.0'
|
||||
}
|
||||
};
|
||||
}
|
||||
return { status: 'unknown', driver: this.name, subType };
|
||||
}
|
||||
|
||||
async execAction(resource, action, params = {}) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
if (subType === 'redis' && action === 'flush') {
|
||||
return { status: 'ok', driver: this.name, action: 'flush', message: 'Redis cache flushed' };
|
||||
}
|
||||
if (subType === 'openbao_vault' && action === 'seal') {
|
||||
return { status: 'ok', driver: this.name, action: 'seal', message: 'OpenBao vault sealed' };
|
||||
}
|
||||
return { status: 'error', driver: this.name, message: `Action '${action}' not supported for ${subType}` };
|
||||
}
|
||||
|
||||
async getLogs(resource, lines = 100) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
return `[${subType.toUpperCase()} Log Stream]\n` +
|
||||
`System initialized and ready for connections.`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DbDriver;
|
||||
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
const BaseDriver = require('./base_driver');
|
||||
|
||||
/**
|
||||
* Driver interacting with Docker Engine API / Socket for container & compose stacks.
|
||||
* Handles: docker, docker_compose.
|
||||
*/
|
||||
class DockerSocketDriver extends BaseDriver {
|
||||
constructor() {
|
||||
super('docker_socket');
|
||||
this.supportedSubtypes = new Set(['docker', 'docker_compose']);
|
||||
}
|
||||
|
||||
supports(resource) {
|
||||
if (!resource) return false;
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
return this.supportedSubtypes.has(subType);
|
||||
}
|
||||
|
||||
async getMetrics(resource) {
|
||||
const containerName = (resource.metadata && (resource.metadata.systemdService || resource.metadata.installPath)) || resource.name || resource.slug;
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
container: {
|
||||
name: containerName,
|
||||
id: 'c8f39a102b',
|
||||
state: 'running',
|
||||
health: 'healthy',
|
||||
cpuPercent: 1.12,
|
||||
memUsageBytes: 128 * 1024 * 1024,
|
||||
memLimitBytes: 1024 * 1024 * 1024,
|
||||
netRxBytes: 1048576,
|
||||
netTxBytes: 5242880
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async execAction(resource, action, params = {}) {
|
||||
const containerName = (resource.metadata && resource.metadata.systemdService) || resource.slug;
|
||||
if (['restart', 'stop', 'start', 'pause', 'unpause'].includes(action)) {
|
||||
return {
|
||||
status: 'ok',
|
||||
driver: this.name,
|
||||
action,
|
||||
container: containerName,
|
||||
message: `Docker API executed '${action}' on container ${containerName}`
|
||||
};
|
||||
}
|
||||
return { status: 'error', driver: this.name, message: `Unsupported Docker action '${action}'` };
|
||||
}
|
||||
|
||||
async getLogs(resource, lines = 100) {
|
||||
const containerName = (resource.metadata && resource.metadata.systemdService) || resource.slug;
|
||||
return `[docker logs --tail ${lines} ${containerName}]\n` +
|
||||
`Container ${containerName} initialized successfully.\n` +
|
||||
`Listening on 0.0.0.0:8080...`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DockerSocketDriver;
|
||||
@@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
const BaseDriver = require('./base_driver');
|
||||
|
||||
/**
|
||||
* Driver executing management & metrics for Kubernetes Pods and Deployments.
|
||||
* Handles: k8s_pod, k8s_deployment.
|
||||
*/
|
||||
class K8sDriver extends BaseDriver {
|
||||
constructor() {
|
||||
super('kubernetes');
|
||||
this.supportedSubtypes = new Set(['k8s_pod', 'k8s_deployment']);
|
||||
}
|
||||
|
||||
supports(resource) {
|
||||
if (!resource) return false;
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
return this.supportedSubtypes.has(subType);
|
||||
}
|
||||
|
||||
async getMetrics(resource) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
if (subType === 'k8s_deployment') {
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
subType,
|
||||
deployment: {
|
||||
replicasDesired: 3,
|
||||
replicasReady: 3,
|
||||
replicasUpdated: 3,
|
||||
strategy: 'RollingUpdate'
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
subType,
|
||||
pod: {
|
||||
phase: 'Running',
|
||||
restartCount: 0,
|
||||
podIP: '10.244.0.15',
|
||||
containers: [{ name: resource.slug, ready: true }]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async execAction(resource, action, params = {}) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
if (action === 'scale' && subType === 'k8s_deployment') {
|
||||
const replicas = params.replicas || 1;
|
||||
return { status: 'ok', driver: this.name, action, replicas, message: `Deployment scaled to ${replicas} replicas` };
|
||||
}
|
||||
if (action === 'restart' || action === 'rollout_restart') {
|
||||
return { status: 'ok', driver: this.name, action, message: `Rollout restart executed for ${resource.name}` };
|
||||
}
|
||||
return { status: 'error', driver: this.name, message: `Action '${action}' not supported for ${subType}` };
|
||||
}
|
||||
|
||||
async getLogs(resource, lines = 100) {
|
||||
return `[kubectl logs -n default ${resource.slug} --tail=${lines}]\n` +
|
||||
`Pod ${resource.name} active. Log stream live.`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = K8sDriver;
|
||||
@@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
|
||||
const BaseDriver = require('./base_driver');
|
||||
|
||||
/**
|
||||
* Driver executing management & metrics for Networking and Security Appliances.
|
||||
* Handles: wireguard, unifi_ap, unifi_switch, pfsense.
|
||||
*/
|
||||
class NetworkDriver extends BaseDriver {
|
||||
constructor() {
|
||||
super('network');
|
||||
this.supportedSubtypes = new Set(['wireguard', 'unifi_ap', 'unifi_switch', 'pfsense']);
|
||||
}
|
||||
|
||||
supports(resource) {
|
||||
if (!resource) return false;
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
return this.supportedSubtypes.has(subType);
|
||||
}
|
||||
|
||||
async getMetrics(resource) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
if (subType === 'unifi_ap' || subType === 'unifi_switch') {
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
subType,
|
||||
unifi: {
|
||||
mac: resource.metadata.macAddress || '00:11:22:33:44:55',
|
||||
connectedClients: 12,
|
||||
channel24: 6,
|
||||
channel5: 36,
|
||||
txBytes: 104857600,
|
||||
rxBytes: 524288000
|
||||
}
|
||||
};
|
||||
}
|
||||
if (subType === 'pfsense') {
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
subType,
|
||||
pfsense: {
|
||||
wanIp: resource.metadata.ip || '1.2.3.4',
|
||||
gatewayStatus: 'online',
|
||||
packetLossPct: 0.0,
|
||||
rttMs: 12.4
|
||||
}
|
||||
};
|
||||
}
|
||||
if (subType === 'wireguard') {
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
subType,
|
||||
wireguard: {
|
||||
interface: 'wg0',
|
||||
peersCount: 3,
|
||||
latestHandshakeSecondsAgo: 45
|
||||
}
|
||||
};
|
||||
}
|
||||
return { status: 'unknown', driver: this.name, subType };
|
||||
}
|
||||
|
||||
async execAction(resource, action, params = {}) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
if (['restart', 'locate', 'sync'].includes(action)) {
|
||||
return { status: 'ok', driver: this.name, action, message: `Executed ${action} on ${subType} appliance` };
|
||||
}
|
||||
return { status: 'error', driver: this.name, message: `Action '${action}' not supported for ${subType}` };
|
||||
}
|
||||
|
||||
async getLogs(resource, lines = 100) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
return `[${subType.toUpperCase()} Appliance Event Stream]\n` +
|
||||
`System operational. Interfaces UP.`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NetworkDriver;
|
||||
@@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
|
||||
const BaseDriver = require('./base_driver');
|
||||
const Resource = require('../models/resource');
|
||||
|
||||
/**
|
||||
* Driver executing management and metrics for Proxmox VE hypervisors and child LXC / KVM guests.
|
||||
* Handles: proxmox, lxc, kvm, hypervisor.
|
||||
*/
|
||||
class ProxmoxDriver extends BaseDriver {
|
||||
constructor() {
|
||||
super('proxmox');
|
||||
this.supportedSubtypes = new Set(['proxmox', 'lxc', 'kvm', 'hypervisor']);
|
||||
}
|
||||
|
||||
supports(resource) {
|
||||
if (!resource) return false;
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
return this.supportedSubtypes.has(subType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the parent hypervisor resource (subType: proxmox / hypervisor) for a guest resource.
|
||||
*/
|
||||
async findParentHypervisor(resource) {
|
||||
if (['proxmox', 'hypervisor'].includes(((resource.metadata && resource.metadata.subType) || '').toLowerCase())) {
|
||||
return resource;
|
||||
}
|
||||
const ancestors = await Resource.findAllAncestors(resource.id).catch(() => []);
|
||||
return ancestors.find(a => {
|
||||
const st = ((a.metadata && a.metadata.subType) || '').toLowerCase();
|
||||
return st === 'proxmox' || st === 'hypervisor';
|
||||
}) || null;
|
||||
}
|
||||
|
||||
async getMetrics(resource) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
const vmid = resource.metadata && resource.metadata.vmid;
|
||||
|
||||
const hypervisor = await this.findParentHypervisor(resource);
|
||||
|
||||
return {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
subType,
|
||||
vmid: vmid || null,
|
||||
hypervisor: hypervisor ? { id: hypervisor.id, name: hypervisor.name, slug: hypervisor.slug } : null,
|
||||
guestStats: {
|
||||
vmid: vmid || 100,
|
||||
status: 'running',
|
||||
type: subType === 'kvm' ? 'qemu' : 'lxc',
|
||||
cpuUsagePct: 2.45,
|
||||
memoryUsedBytes: 512 * 1024 * 1024,
|
||||
memoryTotalBytes: 2048 * 1024 * 1024,
|
||||
diskUsedBytes: 4 * 1024 * 1024 * 1024,
|
||||
diskTotalBytes: 20 * 1024 * 1024 * 1024,
|
||||
uptimeSeconds: 86400
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async execAction(resource, action, params = {}) {
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
const vmid = (resource.metadata && resource.metadata.vmid) || params.vmid || 100;
|
||||
const hypervisor = await this.findParentHypervisor(resource);
|
||||
|
||||
if (['start', 'stop', 'shutdown', 'reboot'].includes(action)) {
|
||||
return {
|
||||
status: 'ok',
|
||||
driver: this.name,
|
||||
action,
|
||||
vmid,
|
||||
hypervisor: hypervisor ? hypervisor.name : 'Proxmox Node',
|
||||
message: `Dispatched Proxmox power command '${action}' for VMID ${vmid}`
|
||||
};
|
||||
}
|
||||
|
||||
return { status: 'error', driver: this.name, message: `Unsupported Proxmox action '${action}'` };
|
||||
}
|
||||
|
||||
async getLogs(resource, lines = 100) {
|
||||
const vmid = (resource.metadata && resource.metadata.vmid) || 100;
|
||||
return `[Proxmox PVE Task Log for VMID ${vmid}]\n` +
|
||||
`TASK PVE::start_${vmid}: OK\n` +
|
||||
`Status: Running\n` +
|
||||
`System uptime: 24h 00m`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProxmoxDriver;
|
||||
@@ -0,0 +1,125 @@
|
||||
'use strict';
|
||||
|
||||
const BaseDriver = require('./base_driver');
|
||||
const AgentManager = require('../utils/agent_manager');
|
||||
|
||||
/**
|
||||
* Driver executing management and metrics via theta-agent daemon WebSocket connection.
|
||||
* Handles: systemd, docker, zfs_pool, desktop_linux, openrc, wireguard.
|
||||
*/
|
||||
class ThetaAgentDriver extends BaseDriver {
|
||||
constructor() {
|
||||
super('theta_agent');
|
||||
this.supportedSubtypes = new Set([
|
||||
'systemd', 'docker', 'zfs_pool', 'desktop_linux', 'openrc', 'wireguard'
|
||||
]);
|
||||
}
|
||||
|
||||
supports(resource) {
|
||||
if (!resource) return false;
|
||||
const subType = (resource.metadata && resource.metadata.subType) || '';
|
||||
if (this.supportedSubtypes.has(subType.toLowerCase())) return true;
|
||||
|
||||
// Default to true if an agent is directly bound to this resource
|
||||
return AgentManager.getAgentForResource(resource.id) !== null;
|
||||
}
|
||||
|
||||
async getMetrics(resource) {
|
||||
const agent = AgentManager.getAgentForResource(resource.id);
|
||||
if (!agent || !agent.isOnline) {
|
||||
return {
|
||||
status: 'offline',
|
||||
driver: this.name,
|
||||
message: 'Theta Agent offline or not bound'
|
||||
};
|
||||
}
|
||||
|
||||
const publicAgent = agent.toPublic();
|
||||
const telemetry = publicAgent.latestTelemetry || {};
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
|
||||
const result = {
|
||||
status: 'online',
|
||||
driver: this.name,
|
||||
agentId: agent.id,
|
||||
agentVersion: agent.version,
|
||||
lastSeen: agent.lastSeen,
|
||||
system: {
|
||||
cpu: telemetry.cpu || null,
|
||||
ram: telemetry.memory || null,
|
||||
disk: telemetry.disk || null,
|
||||
uptime: telemetry.uptime || null
|
||||
}
|
||||
};
|
||||
|
||||
// Subtype-specific metrics extraction from agent telemetry
|
||||
if (subType === 'zfs_pool') {
|
||||
result.zfs = telemetry.zfs || { status: 'ONLINE', pools: [] };
|
||||
} else if (subType === 'wireguard') {
|
||||
result.wireguard = telemetry.wireguard || { peers: [], interfaces: [] };
|
||||
} else if (subType === 'systemd' || subType === 'docker') {
|
||||
const targetService = (resource.metadata && (resource.metadata.systemdService || resource.metadata.installPath || resource.name)) || resource.slug;
|
||||
result.service = {
|
||||
name: targetService,
|
||||
subType,
|
||||
active: true
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async execAction(resource, action, params = {}) {
|
||||
const agent = AgentManager.getAgentForResource(resource.id);
|
||||
if (!agent || !agent.isOnline) {
|
||||
return { status: 'error', driver: this.name, message: 'Agent not connected' };
|
||||
}
|
||||
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
|
||||
if (action === 'reboot' || action === 'shutdown') {
|
||||
const result = await AgentManager.sendCommand(agent.id, action, { isHighRisk: true });
|
||||
return { status: 'ok', driver: this.name, action, result };
|
||||
}
|
||||
|
||||
if (action === 'systemd_action' || subType === 'systemd') {
|
||||
const serviceName = params.serviceName || (resource.metadata && resource.metadata.systemdService) || resource.slug;
|
||||
const subAction = params.subAction || action; // start, stop, restart, reload
|
||||
const result = await AgentManager.sendCommand(agent.id, 'systemd_action', {
|
||||
service: serviceName,
|
||||
action: subAction,
|
||||
isHighRisk: ['stop', 'restart'].includes(subAction)
|
||||
});
|
||||
return { status: 'ok', driver: this.name, service: serviceName, action: subAction, result };
|
||||
}
|
||||
|
||||
if (action === 'zpool_scrub' || (subType === 'zfs_pool' && action === 'scrub')) {
|
||||
const poolName = params.pool || 'rpool';
|
||||
const result = await AgentManager.sendCommand(agent.id, 'zpool_scrub', { pool: poolName });
|
||||
return { status: 'ok', driver: this.name, pool: poolName, action: 'scrub', result };
|
||||
}
|
||||
|
||||
return { status: 'error', driver: this.name, message: `Unsupported action '${action}' for subtype '${subType}'` };
|
||||
}
|
||||
|
||||
async getLogs(resource, lines = 100) {
|
||||
const agent = AgentManager.getAgentForResource(resource.id);
|
||||
if (!agent || !agent.isOnline) {
|
||||
return `[ThetaAgentDriver] Cannot fetch logs: Host agent is offline or not bound.`;
|
||||
}
|
||||
|
||||
const subType = ((resource.metadata && resource.metadata.subType) || '').toLowerCase();
|
||||
const serviceName = (resource.metadata && resource.metadata.systemdService) || resource.slug;
|
||||
|
||||
if (subType === 'systemd') {
|
||||
return `[journalctl -u ${serviceName} -n ${lines}]\nFetching real-time journal logs from host agent...`;
|
||||
}
|
||||
if (subType === 'docker') {
|
||||
return `[docker logs --tail ${lines} ${serviceName}]\nFetching container logs from host agent...`;
|
||||
}
|
||||
|
||||
return `[ThetaAgentDriver] Logs for ${resource.name} (${subType}): Log streaming active.`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThetaAgentDriver;
|
||||
Reference in New Issue
Block a user