Skip to content

Add V2 PhysicalContainerNetwork resource and controller - #226

Draft
David Negstad (danegsta) wants to merge 5 commits into
danegsta-v2-namespace-supportfrom
danegsta-danegsta-v2-physical-network
Draft

Add V2 PhysicalContainerNetwork resource and controller#226
David Negstad (danegsta) wants to merge 5 commits into
danegsta-v2-namespace-supportfrom
danegsta-danegsta-v2-physical-network

Conversation

@danegsta

@danegsta David Negstad (danegsta) commented Jul 31, 2026

Copy link
Copy Markdown
Member

Stacked on #223 — this PR targets danegsta-v2-namespace-support, not main. Review #223 first.

Implements follow-up roadmap item 1 from docs/v2-resource-plan.md: a V2 PhysicalContainerNetwork resource representing one concrete container runtime network. The name mirrors the existing PhysicalContainerImage; the resource path is physicalcontainernetworks and the short name is pcn.

What it adds

  • api/v2/physical_container_network_types.goPhysicalContainerNetwork with an immutable spec. Either networkName (create a new runtime network) or networkID (track an existing one), never both. preserveOnDeletion keeps the runtime network in place on deletion, and applies in track-only mode too, mirroring PhysicalContainer.
  • controllers/physical_container_network_controller.go + physical_container_network_data.go — reconciler using the established ObjectStateMap / deferred-op / queued-work pattern.
  • Registered with the scheme and API server (via PersistentTypes), the controller runner, both integration test environments, namespace cleanup, and resource cleanup ordering.

Design notes

  • Naming is a pass-through. networkName is required on the create path and is not generated. Uniquifying a user-facing name belongs in the logical layer, which does not exist yet. Validated against ^[a-zA-Z0-9][a-zA-Z0-9_.-]*$, which is Podman's NameRegex; Docker applies no pattern to network names. This is deliberately a separate constant from validContainerName, which requires at least two characters.
  • Three runtime operations only: CreateNetwork, InspectNetworks, RemoveNetworks. No connect/disconnect (that is container-side, roadmap item 2) and no WatchNetworks subscription.
  • Removal is confirmed by inspection, not by the error. Orchestrators report a partial failure both for a network that is already gone and for one that still has containers attached, so the controller re-inspects and only drops the finalizer when the network is genuinely absent. Otherwise it retries; containers detach as they are deleted.
  • Deletion waits for an in-flight create rather than cancelling it. A cancelled create can still produce a runtime network whose ID would be lost, leaving it reclaimable only by startup harvesting. (PhysicalContainerImage cancels pulls, but images are shared artifacts that are never removed on deletion, so there is nothing to orphan.)
  • No reconciliation churn. A ready network re-inspects on MonitoringDelay so a network removed outside of DCP is noticed, and the status projection returns no change when the inspection is unchanged, so steady state produces no writes.
  • Recoverable failures keep retrying; terminal failures do not. A repeated identical runtime failure produces no status change, and with no watch subscription and no cache resync anywhere in DCP that would otherwise leave the network wedged in Failed until an unrelated update arrived. Recoverable failures therefore request another reconciliation and pace it at LongDelay (5s ± 2s), matching how V1 handles an unhealthy runtime; a terminal creation failure still schedules nothing. The existing delay jitter keeps many retrying networks from polling the runtime in lockstep.
  • Namespace cleanup ordering puts networks after physical containers, because a network cannot be removed while containers are attached.

Testing

  • Unit tests for validation (api/v2) and for the controller's terminal-failure guard, creation labels, status projection idempotence, and progress record (controllers).
  • Ten integration tests covering create, track-by-ID, removal on deletion, preservation on deletion, removal of a tracked network, namespace-scoped cleanup, create de-duplication while a create is blocked, terminal create failure, steady-state stability, and recovery from a transient runtime failure.
  • The steady-state test was verified in both directions: it fails when the monitoring delay is replaced with the standard delay, and passes with the delay restored. This required adding InspectNetworkCallCount and BlockCreateNetwork/CreateNetworkCallCount to TestContainerOrchestrator, mirroring the existing container helpers.
  • A regression test covers recovery from a transient runtime failure. It uses a dedicated test environment because SetRuntimeHealth is global, and it was verified in both directions: it fails when the retry is reverted and passes with it restored.
  • removeRuntimeNetworkOnCleanup no longer blanket-tolerates ErrIncomplete. That error is returned both for a network that is already gone and for one that could not be removed, so the helper now re-inspects and only ignores the error once the network is confirmed absent.

make generate produces no uncommitted diff (the two new names_match warnings for IPv6 are the same shape as the pre-existing api/v1 ContainerNetworkSpec,IPv6 warnings), make lint reports 0 issues, and make test passes.

Docs

docs/v2-resource-plan.md moves PhysicalContainerNetwork into the current foundation and renumbers the remaining physical-layer items. A new item records that harvestAbandonedNetworks filters on withCreator rather than nonPersistentWithCreator, so a network with preserveOnDeletion: true is still reaped after a DCP crash. That asymmetry is inherited from V1 and is deliberately left unchanged here. Another item records that PhysicalContainerImage never retries a repeated identical inspection failure; that gap belongs to #223's code. The status guidelines also now describe the recoverable-versus-terminal failure pattern so future physical resources follow it. The roadmap's future PhysicalVolume is also renamed to PhysicalContainerVolume so the naming stays consistent.

⚠️ Repo CI does not run on this PR

Every validation workflow in .github/workflows is gated to main, release/*, and production on pull_request:

Workflow pull_request branches Runs here?
build-test.yml main, release/*, production No
lint.yml main, release/*, production No
validate-generated.yml main, release/*, production No
aspire-regression.yml main No

Because this PR targets danegsta-v2-namespace-support, the only check that will ever report is license/cla. A green checkmark on this PR does not mean it was validated. That matters here in particular: this change touches container-runtime code paths, where platform differences surface, and it regenerates deepcopy/model-name/openapi files that validate-generated.yml would normally verify.

Validation performed instead:

Known flake: TestV2NamespaceControllerCleansUpPhysicalContainers

The two red macOS runs above both failed the same test from #223, and the same SHA passed on the same platform, so this was timing-dependent rather than a deterministic break. ubuntu-latest and windows-latest were green on every one of those runs. I could not reproduce it locally in eight full-suite runs, including the exact CI configuration (-race, no -parallel, and a constrained GOMAXPROCS).

In both failures the namespace remained at phase: Active with a deletionTimestamp and its finalizer for the full 180s, which means the deletion path never made progress. The exact lost-event mechanism was not conclusively reproduced or proven. However, manageNamespace returned noChange for an Active namespace, so nothing was requeued and the controller depended entirely on watch events from that point, while DCP configures no cache resync. That left no safety net for a missed watch event or a reconciliation that observed cached state predating deletion.

The defensive fix landed where the code belongs, in base PR #223 commit 29c3fe1. Active namespaces now receive a slow MonitoringDelay fallback reconciliation, while deleting namespaces retain StandardDelay. This branch was rebased onto that fix, roadmap item 9 was removed as resolved, and the subsequent Build and Test run passed all three platforms.

Please do not retarget this PR to main to obtain CI — that would pull PR #223's diff into this one. Once #223 merges, this PR retargets to main automatically and picks up real CI.

Out of scope

PhysicalContainerVolume (item 1 of the remaining physical-layer work) and rewiring PhysicalContainer to reference PhysicalContainerNetwork (item 2).

@danegsta David Negstad (danegsta) changed the title Add V2 PhysicalNetwork resource and controller Add V2 PhysicalContainerNetwork resource and controller Jul 31, 2026
@danegsta
David Negstad (danegsta) marked this pull request as draft July 31, 2026 21:39
Adds the first V2 physical network primitive, following the patterns
established by PhysicalContainer and PhysicalContainerImage.

PhysicalNetwork either creates a runtime container network from an
explicit networkName or tracks an existing one by networkID, and reports
the observed network identity, driver, IPv6 setting, and address
allocations. The spec is immutable after create. Created networks are
stamped with the persistent and creator-process labels so startup
harvesting can reclaim them after a DCP crash.

The controller performs three runtime operations: create (queued, so a
blocking runtime call does not stall reconciliation), inspect, and
remove. Creation is guarded by an active namespace; deletion bypasses
that gate so cleanup can finish after the namespace is gone. Removal is
confirmed by re-inspecting rather than by interpreting the orchestrator
error, because a partial-failure result covers both an already-absent
network and one that still has containers attached.

A ready network re-inspects on the monitoring delay so a network removed
outside of DCP is noticed, and the status projection reports no change
when the inspection is unchanged, so steady state produces no writes.

Registers the type with the scheme and API server, the controller with
the controller runner and both integration test environments, and the
kind with namespace cleanup, ordered after physical containers because a
network cannot be removed while containers are attached to it.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2bcfab76-f43d-4564-a714-303ce7779074
Aligns the resource name with the existing PhysicalContainerImage naming
convention. Resource path becomes physicalcontainernetworks and the short
name becomes pcn.

The roadmap's future PhysicalVolume is renamed to PhysicalContainerVolume
in docs/v2-resource-plan.md for the same consistency.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2bcfab76-f43d-4564-a714-303ce7779074
A repeated identical runtime inspection failure produces no status change, and
with no watch subscription or cache resync the resource was left with nothing
scheduled to retry it, wedging it in Failed until an unrelated update arrived.

Recoverable failures now request another reconciliation and pace it at
LongDelay, matching how V1 handles an unhealthy runtime; terminal creation
failures continue to not requeue. Existing delay jitter keeps retrying networks
from polling the runtime in lockstep.

Also narrows the shared network cleanup helper so it confirms absence instead of
blanket-tolerating ErrIncomplete, which is also returned when removal fails.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2bcfab76-f43d-4564-a714-303ce7779074
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2bcfab76-f43d-4564-a714-303ce7779074
The Active namespace fallback reconcile landed in the base branch, so
the recorded gap no longer applies.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2bcfab76-f43d-4564-a714-303ce7779074
@danegsta
David Negstad (danegsta) force-pushed the danegsta-danegsta-v2-physical-network branch from 5a8a703 to aedb6ff Compare August 11, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant