From a3b4390e4cf255d68e98127e0c19bb8746d82449 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 29 Mar 2026 22:45:56 +0100 Subject: [PATCH] fix(workloads): resolve truenas as canonical platform source --- .../v6/internal/subsystems/unified-resources.md | 6 ++++++ .../src/hooks/__tests__/useWorkloads.test.ts | 16 +++++++++++++++- .../src/utils/__tests__/sourcePlatforms.test.ts | 16 ++++++++++++++++ frontend-modern/src/utils/sourcePlatforms.ts | 15 +++++++++++++-- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/docs/release-control/v6/internal/subsystems/unified-resources.md b/docs/release-control/v6/internal/subsystems/unified-resources.md index 4265e7792..6a933ac3c 100644 --- a/docs/release-control/v6/internal/subsystems/unified-resources.md +++ b/docs/release-control/v6/internal/subsystems/unified-resources.md @@ -1143,6 +1143,12 @@ may render friendly string keys, but membership checks against available sources must normalize through the shared `frontend-modern/src/utils/sourcePlatforms.ts` helper before consulting `KnownSourcePlatform` sets. +That same shared source-platform boundary now also owns TrueNAS-backed hybrid +resources. When a canonical resource carries both `agent` and `truenas` +sources, `frontend-modern/src/utils/sourcePlatforms.ts` must still resolve the +platform as `truenas` and the source mode as `hybrid`, so workload and +infrastructure consumers do not collapse API-backed TrueNAS systems or apps +back onto the generic agent path just because host telemetry is also present. The route file `frontend-modern/src/pages/Infrastructure.tsx` is now only the navigation boundary for that surface; canonical infrastructure filter, search, deep-link, and expansion state now live behind the dedicated infrastructure diff --git a/frontend-modern/src/hooks/__tests__/useWorkloads.test.ts b/frontend-modern/src/hooks/__tests__/useWorkloads.test.ts index ece6105af..8d7f2fcac 100644 --- a/frontend-modern/src/hooks/__tests__/useWorkloads.test.ts +++ b/frontend-modern/src/hooks/__tests__/useWorkloads.test.ts @@ -137,6 +137,19 @@ describe('useWorkloads', () => { { ...sampleResource, id: 'vm-pve', sources: ['PROXMOX'] }, { ...sampleResource, id: 'vm-pbs', sources: ['pbs'], name: 'vm-pbs' }, { ...sampleResource, id: 'vm-pmg', sources: ['pmg'], name: 'vm-pmg' }, + { + ...sampleResource, + id: 'truenas-nextcloud', + type: 'app-container', + name: 'nextcloud', + status: 'healthy', + sources: ['agent', 'truenas'], + parentName: 'truenas-main', + docker: { + containerId: 'nextcloud-ctr', + image: 'ix-nextcloud:latest', + }, + }, ], meta: { totalPages: 1 }, }); @@ -150,12 +163,13 @@ describe('useWorkloads', () => { }); await flushAsync(); - await waitForWorkloadCount(() => result!.workloads().length, 3); + await waitForWorkloadCount(() => result!.workloads().length, 4); const byName = new Map(result!.workloads().map((workload) => [workload.name, workload])); expect(byName.get('vm-101')?.platformType).toBe('proxmox-pve'); expect(byName.get('vm-pbs')?.platformType).toBe('proxmox-pbs'); expect(byName.get('vm-pmg')?.platformType).toBe('proxmox-pmg'); + expect(byName.get('nextcloud')?.platformType).toBe('truenas'); dispose(); }); diff --git a/frontend-modern/src/utils/__tests__/sourcePlatforms.test.ts b/frontend-modern/src/utils/__tests__/sourcePlatforms.test.ts index ac5c5c722..cccd025e7 100644 --- a/frontend-modern/src/utils/__tests__/sourcePlatforms.test.ts +++ b/frontend-modern/src/utils/__tests__/sourcePlatforms.test.ts @@ -74,6 +74,7 @@ describe('sourcePlatforms', () => { hasKubernetes: false, hasPbs: true, hasPmg: false, + hasTrueNAS: false, }); }); @@ -85,6 +86,19 @@ describe('sourcePlatforms', () => { hasKubernetes: false, hasPbs: false, hasPmg: false, + hasTrueNAS: false, + }); + }); + + it('tracks TrueNAS as a canonical API-backed platform source', () => { + expect(readSourcePlatformFlags(['agent', 'truenas'])).toEqual({ + hasAgent: true, + hasProxmox: false, + hasDocker: false, + hasKubernetes: false, + hasPbs: false, + hasPmg: false, + hasTrueNAS: true, }); }); }); @@ -94,6 +108,7 @@ describe('sourcePlatforms', () => { expect(resolvePlatformTypeFromSources(['agent', 'docker'])).toBe('docker'); expect(resolvePlatformTypeFromSources(['agent', 'pbs'])).toBe('proxmox-pbs'); expect(resolvePlatformTypeFromSources(['pmg', 'docker'])).toBe('proxmox-pmg'); + expect(resolvePlatformTypeFromSources(['agent', 'truenas'])).toBe('truenas'); expect(resolvePlatformTypeFromSources(['agent'])).toBe('agent'); expect(resolvePlatformTypeFromSources(['custom-source'])).toBeUndefined(); }); @@ -104,6 +119,7 @@ describe('sourcePlatforms', () => { expect(resolveSourceTypeFromSources(['agent'])).toBe('agent'); expect(resolveSourceTypeFromSources(['pve'])).toBe('api'); expect(resolveSourceTypeFromSources(['agent', 'proxmox'])).toBe('hybrid'); + expect(resolveSourceTypeFromSources(['agent', 'truenas'])).toBe('hybrid'); expect(resolveSourceTypeFromSources(['custom-source'])).toBe('api'); }); }); diff --git a/frontend-modern/src/utils/sourcePlatforms.ts b/frontend-modern/src/utils/sourcePlatforms.ts index 07621592a..c790f68c7 100644 --- a/frontend-modern/src/utils/sourcePlatforms.ts +++ b/frontend-modern/src/utils/sourcePlatforms.ts @@ -30,6 +30,7 @@ export interface SourcePlatformFlags { hasKubernetes: boolean; hasPbs: boolean; hasPmg: boolean; + hasTrueNAS: boolean; } export const SOURCE_PLATFORM_PRESENTATION: Record = @@ -141,6 +142,7 @@ export const readSourcePlatformFlags = (sources?: string[]): SourcePlatformFlags hasKubernetes: false, hasPbs: false, hasPmg: false, + hasTrueNAS: false, }; if (!sources || sources.length === 0) { @@ -167,6 +169,9 @@ export const readSourcePlatformFlags = (sources?: string[]): SourcePlatformFlags case 'proxmox-pmg': flags.hasPmg = true; break; + case 'truenas': + flags.hasTrueNAS = true; + break; default: break; } @@ -180,8 +185,9 @@ export const resolvePlatformTypeFromSources = (sources?: string[]): PlatformType if (flags.hasProxmox) return 'proxmox-pve'; if (flags.hasPbs) return 'proxmox-pbs'; if (flags.hasPmg) return 'proxmox-pmg'; - if (flags.hasDocker) return 'docker'; + if (flags.hasTrueNAS) return 'truenas'; if (flags.hasKubernetes) return 'kubernetes'; + if (flags.hasDocker) return 'docker'; if (flags.hasAgent) return 'agent'; return undefined; }; @@ -189,7 +195,12 @@ export const resolvePlatformTypeFromSources = (sources?: string[]): PlatformType export const resolveSourceTypeFromSources = (sources?: string[]): SourceType => { const flags = readSourcePlatformFlags(sources); const hasOther = - flags.hasProxmox || flags.hasDocker || flags.hasKubernetes || flags.hasPbs || flags.hasPmg; + flags.hasProxmox || + flags.hasDocker || + flags.hasKubernetes || + flags.hasPbs || + flags.hasPmg || + flags.hasTrueNAS; if (flags.hasAgent && hasOther) return 'hybrid'; if (flags.hasAgent) return 'agent'; return 'api';