Keep agent profile identity local

This commit is contained in:
rcourtman
2026-03-25 11:09:11 +00:00
parent cb41473018
commit 86eb4ccaef
3 changed files with 57 additions and 3 deletions
@@ -598,6 +598,11 @@ runtime owner separate: `AgentProfilesPanel.tsx` is the surface shell, while
`useAgentProfilesPanelState.ts` owns license gating, AI availability, profile
load/save mutations, assignment resync, and modal form lifecycle so the panel
does not carry a second inline controller.
That same connected profile-assignment surface must also preserve canonical
local operator identity for monitored systems. When governed resources such as
PBS or PMG appear in the assignment list, the panel must keep the local
instance label for ordering and row display instead of substituting governed
summary text, so profile assignment remains instance-specific.
Canonical Proxmox auto-register must also preserve the legacy DHCP continuity
contract: when a node reruns registration from a new IP but presents the
same canonical node name and deterministic Pulse-managed token identity, Pulse
@@ -562,6 +562,54 @@ describe('AgentProfilesPanel V6 agent ID handling', () => {
});
});
it('keeps governed infrastructure assignments on local operator identity', async () => {
mockResources = [
makeAgentResource({
id: 'pbs-resource-id',
type: 'pbs',
name: 'redacted-pbs',
displayName: 'PBS Main',
platformId: 'pbs-main',
platformType: 'proxmox-pbs',
sourceType: 'api',
identity: { hostname: 'pbs.local' },
agent: { agentId: 'pbs-agent-1' },
policy: {
display: {
mode: 'governed',
summary: 'backup server resource; status online; sources pbs',
},
} as Record<string, unknown>,
}),
];
mockWsStore.state.connectedInfrastructure = [
makeHostInfrastructureItem({
id: 'pbs-resource-id',
name: 'PBS Main',
displayName: 'PBS Main',
hostname: 'pbs.local',
scopeAgentId: 'pbs-agent-1',
surfaces: [
{
id: 'agent:pbs-agent-1',
kind: 'agent',
label: 'PBS data',
controlId: 'pbs-agent-1',
},
],
}),
];
render(() => <AgentProfilesPanel />);
await waitFor(() => {
expect(screen.getByText('PBS Main')).toBeInTheDocument();
});
expect(
screen.queryByText('backup server resource; status online; sources pbs'),
).not.toBeInTheDocument();
});
it('surfaces malformed profile list responses instead of failing open to an empty state', async () => {
listProfilesMock.mockRejectedValueOnce(new Error('Invalid agent profile list response from Pulse.'));
@@ -38,6 +38,7 @@ import {
isAgentProfileAssignableResource,
} from '@/utils/agentResources';
import {
getPreferredInfrastructureDisplayName,
getPreferredNamedEntityLabel,
getPreferredResourceDisplayName,
getPreferredResourceHostname,
@@ -140,8 +141,8 @@ export const useAgentProfilesPanelState = () => {
.sort((a, b) => {
const byPriority = resourcePriority(a.resource) - resourcePriority(b.resource);
if (byPriority !== 0) return byPriority;
const aName = getPreferredResourceDisplayName(a.resource);
const bName = getPreferredResourceDisplayName(b.resource);
const aName = getPreferredInfrastructureDisplayName(a.resource);
const bName = getPreferredInfrastructureDisplayName(b.resource);
return aName.localeCompare(bName);
});
@@ -156,7 +157,7 @@ export const useAgentProfilesPanelState = () => {
id: assignmentId,
assignmentId,
hostname: getPreferredResourceHostname(resource) || 'Unknown',
displayName: resource.displayName,
displayName: getPreferredInfrastructureDisplayName(resource),
status: resource.status || 'unknown',
lastSeen: resource.lastSeen,
}))