feat(docker): allow LXC as parent for docker_container

Previously docker_container could only attach to a docker_host via
virtual edge. Extend the rule so an LXC node can also act as parent,
enabling docker_container nesting under LXC without requiring an
intermediate docker_host.

Extracts the virtual-edge parent rule into a pure helper
(resolveVirtualEdgeParent) with unit tests covering all type pairs.

ha-relevant: yes
This commit is contained in:
Pouzor
2026-05-15 22:54:51 +02:00
parent cdc6091bd0
commit 280d5a2ae1
3 changed files with 106 additions and 11 deletions
@@ -0,0 +1,60 @@
import { describe, it, expect } from 'vitest'
import { resolveVirtualEdgeParent } from '../virtualEdgeParent'
describe('resolveVirtualEdgeParent', () => {
it('nests lxc under proxmox (container-mode parent)', () => {
const res = resolveVirtualEdgeParent(
{ id: 'lxc1', type: 'lxc' },
{ id: 'px1', type: 'proxmox' },
)
expect(res).toEqual({ childId: 'lxc1', parentId: 'px1' })
})
it('nests vm under proxmox regardless of edge direction', () => {
const res = resolveVirtualEdgeParent(
{ id: 'px1', type: 'proxmox' },
{ id: 'vm1', type: 'vm' },
)
expect(res).toEqual({ childId: 'vm1', parentId: 'px1' })
})
it('nests docker_container under docker_host', () => {
const res = resolveVirtualEdgeParent(
{ id: 'dc1', type: 'docker_container' },
{ id: 'dh1', type: 'docker_host' },
)
expect(res).toEqual({ childId: 'dc1', parentId: 'dh1' })
})
it('nests docker_container under lxc (reverse direction)', () => {
const res = resolveVirtualEdgeParent(
{ id: 'lxc1', type: 'lxc' },
{ id: 'dc1', type: 'docker_container' },
)
expect(res).toEqual({ childId: 'dc1', parentId: 'lxc1' })
})
it('nests docker_container under lxc (forward direction)', () => {
const res = resolveVirtualEdgeParent(
{ id: 'dc1', type: 'docker_container' },
{ id: 'lxc1', type: 'lxc' },
)
expect(res).toEqual({ childId: 'dc1', parentId: 'lxc1' })
})
it('returns null when docker_container links to non-host/lxc target', () => {
const res = resolveVirtualEdgeParent(
{ id: 'dc1', type: 'docker_container' },
{ id: 'srv1', type: 'server' },
)
expect(res).toBeNull()
})
it('returns null for unrelated type pairs', () => {
const res = resolveVirtualEdgeParent(
{ id: 'srv1', type: 'server' },
{ id: 'rt1', type: 'router' },
)
expect(res).toBeNull()
})
})
+37
View File
@@ -0,0 +1,37 @@
import type { NodeData } from '@/types'
export type NodeType = NodeData['type']
const CONTAINER_MODE_TYPES = new Set<NodeType>(['proxmox', 'vm', 'lxc', 'docker_host'])
export interface VirtualEdgeEndpoint {
id: string
type: NodeType
}
export interface ParentAssignment {
childId: string
parentId: string
}
export function resolveVirtualEdgeParent(
source: VirtualEdgeEndpoint,
target: VirtualEdgeEndpoint,
): ParentAssignment | null {
const { type: srcType, id: srcId } = source
const { type: tgtType, id: tgtId } = target
if ((srcType === 'lxc' || srcType === 'vm') && CONTAINER_MODE_TYPES.has(tgtType)) {
return { childId: srcId, parentId: tgtId }
}
if (CONTAINER_MODE_TYPES.has(srcType) && (tgtType === 'lxc' || tgtType === 'vm')) {
return { childId: tgtId, parentId: srcId }
}
if (srcType === 'docker_container' && (tgtType === 'docker_host' || tgtType === 'lxc')) {
return { childId: srcId, parentId: tgtId }
}
if (tgtType === 'docker_container' && (srcType === 'docker_host' || srcType === 'lxc')) {
return { childId: tgtId, parentId: srcId }
}
return null
}