Skip to content

feat(network): add interface and route management via Netplan#349

Merged
retr0h merged 45 commits intomainfrom
feat/network-interface-route
Apr 6, 2026
Merged

feat(network): add interface and route management via Netplan#349
retr0h merged 45 commits intomainfrom
feat/network-interface-route

Conversation

@retr0h
Copy link
Copy Markdown
Collaborator

@retr0h retr0h commented Apr 3, 2026

Summary

Full CRUD for network interface configuration and route management via Netplan drop-in files, following the direct-write provider pattern.

  • Interface CRUD: list, get, create, update, delete — generates /etc/netplan/osapi-{name}.yaml
  • Route CRUD: list, get, create, update, delete — generates /etc/netplan/osapi-{name}-routes.yaml
  • DNS delete: remove managed DNS Netplan config
  • Safety: default route (0.0.0.0/0, ::/0) protection blocks accidental deletion
  • Validation: netplan generate validates merged config before netplan apply; rollback on failure
  • Managed flag: interface and route lists show managed: true/false; interface list includes down/unlinked interfaces with osapi config files
  • Idempotent Create/Delete: all providers (cron, certificate, service, NTP, sysctl, interface, route) now return changed: false instead of erroring when creating an already-managed resource or deleting an absent one
  • Netplan deep-merges separate files per interface (config, routes, DNS)
  • Standardized error messages to "already managed"/"not managed" across all providers

Layers implemented

  • Provider: internal/provider/network/netplan/ (interface + route CRUD, Debian impl, platform stubs)
  • Agent: processors for interface.* and route.* operations
  • API: 11 new handler endpoints under /node/{hostname}/network/
  • SDK: InterfaceService and RouteService with typed methods
  • CLI: osapi client node network interface and route command trees
  • Docs: feature page, 13 CLI reference pages, SDK docs, examples, cross-references
  • CLAUDE.md: added Idempotency (MANDATORY) section for provider development

Test plan

  • Provider: interface + route CRUD at 100% coverage
  • Agent: processor tests for all operations
  • Handlers: unit + HTTP validation + RBAC tests
  • SDK: all methods + type conversion tests
  • Idempotent Create/Delete tested across all 7 providers
  • Build clean, lint clean

🤖 Generated with Claude Code

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            Coverage Diff            @@
##             main     #349     +/-   ##
=========================================
  Coverage   99.91%   99.92%             
=========================================
  Files         443      469     +26     
  Lines       21606    23814   +2208     
=========================================
+ Hits        21587    23795   +2208     
  Misses         11       11             
  Partials        8        8             
Files with missing lines Coverage Δ
internal/agent/preflight.go 100.00% <ø> (ø)
internal/agent/processor_interface.go 100.00% <100.00%> (ø)
internal/agent/processor_network.go 100.00% <100.00%> (ø)
internal/agent/processor_route.go 100.00% <100.00%> (ø)
internal/controller/api/node/network/dns_delete.go 100.00% <100.00%> (ø)
internal/controller/api/node/network/dns_get.go 100.00% <100.00%> (ø)
internal/controller/api/node/network/dns_put.go 100.00% <100.00%> (ø)
...ntroller/api/node/network/interface_create_post.go 100.00% <100.00%> (ø)
...al/controller/api/node/network/interface_delete.go 100.00% <100.00%> (ø)
...ernal/controller/api/node/network/interface_get.go 100.00% <ø> (ø)
... and 81 more

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3c1ff98...64fca40. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

retr0h and others added 25 commits April 5, 2026 23:04
11 tasks: provider types, interface CRUD, route CRUD, agent
processors, wiring, OpenAPI spec, handlers, SDK, CLI, docs,
verification.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Define InterfaceProvider and RouteProvider interfaces with associated
entry, result, and route types. Generate gomock mocks for testing.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add Debian provider with List, Get, Create, Update, Delete for
network interface configuration via Netplan YAML files. Includes
YAML generation, name validation, platform stubs (Darwin/Linux),
and comprehensive tests with 100% coverage on all new files.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add job operation constants for network interface and route CRUD
operations. Create processor_interface.go and processor_route.go
with dispatch functions following the existing sysctl/service
pattern. Wire both into NewNetworkProcessor with new case branches
for "interface" and "route" base operations.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add createNetplanProviders() to agent_setup.go that creates
platform-specific Netplan interface and route providers (Debian
with file-state KV, stubs on Darwin/Linux). Pass both providers
to NewNetworkProcessor and include them in the FactsAware wiring
list for the network registry entry.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add network interface CRUD endpoints (list, get, create, update,
delete), route CRUD endpoints (list, get by interface, create, update,
delete), and DNS delete endpoint to the network OpenAPI spec. Fix
ping handler enum constants that changed due to new status types.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement all StrictServerInterface methods for the new network
endpoints: interface list/get/create/update/delete, route
list/get/create/update/delete, and DNS delete. Each handler
validates hostname and body, supports broadcast targets, and
follows the existing DNS get/put patterns. Adds OpNetworkDNSDelete
operation constant.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add CLI commands for network interface management (list, get, create,
update, delete), network route management (list, get, create, update,
delete), and DNS delete. All commands support --json output and
broadcast targeting.

Co-Authored-By: Claude <noreply@anthropic.com>
Add feature page, CLI docs, SDK docs, and SDK examples for network
interface and route management. Update cross-reference files including
features landing page, network management feature page, architecture
overview, API guidelines path table, SDK client overview, and
Docusaurus navbar config.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Split flat netplan package into netplan/ (shared helpers), netif/
(interface provider), and route/ (route provider). Each sub-package
has its own marshalJSON var and test suite, eliminating data races
under -parallel -race.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Rename netif → netplan/iface, move route → netplan/route. Each
sub-package has its own marshalJSON var eliminating test races
under -parallel -race. Update all imports and regenerate mocks.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add back the marshalJSON package var and marshal error test to the
netplan package. Now safe because iface and route are in separate
sub-packages with their own marshalJSON vars — no shared state.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add test cases for broadcast paths, optional field builders,
conversion edge cases, and validation error branches across all
11 network handler files.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add marshalJSON error tests for route Create/Update, and all-fields
test for SDK interfaceConfigRequestFromOpts. All new network code
now at 100% coverage.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
DNS writes Netplan config via ApplyConfig, so it logically belongs
under netplan/ alongside iface/ and route/. Update all imports.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Match the sysctl/service pattern — one Println before Job ID, none
after. Also fix operation constants to use network. prefix.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Interface list and get now return system info from netinfo provider
and a primary flag from agent facts.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add read-only system fields to InterfaceInfo schema, handler
conversion, SDK types, and CLI display. Primary interface marked
with * in CLI list output.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Drop IPv6 column, add dedicated PRIMARY column, use cleaner layout.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Scan /etc/netplan/*.yaml files to detect if an interface uses DHCP.
Add DHCP column to CLI interface list output. Use Glob instead of
ReadDir+filter for cleaner file discovery.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Delete now returns an error with "not managed" instead of silently
returning Changed: false when the target interface or route set has
no OSAPI-managed Netplan config file.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace netinfo provider calls with netplan status --format json
parsing. Single command provides interfaces, addresses with DHCP
flags, routes with protocol, MAC, type, and state. Remove netinfo
dependency from iface and route providers. Add shared GetStatus
helper and InterfaceStatus methods. 100% coverage across all
netplan packages.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove domain-specific prefixes from ErrUnsupported returns across
all providers. The error message is now consistently just
"operation not supported on this OS family". Also drop MAC column
from interface list CLI for cleaner output.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Use netplan status to determine if interface is wifi, bridge, etc.
and generate the correct YAML section (wifis vs ethernets). Fix
file permissions to 0600 for netplan configs.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
retr0h and others added 18 commits April 5, 2026 23:04
Add dhcp4-overrides and dhcp6-overrides with use-dns: false to the
generated DNS Netplan YAML. When explicitly setting DNS servers,
DHCP-provided servers should not be merged in.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add capabilities needed for netplan file ownership (CAP_CHOWN) and
network configuration (CAP_NET_ADMIN). Update docs, preflight
checks, and test fixtures.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add overrideDHCP bool parameter to DNSService.Update() SDK method,
--override-dhcp flag to the CLI update command, and test coverage
for both. Update CLI docs, feature docs, and SDK docs.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
When override_dhcp is true, the generated Netplan YAML includes
dhcp4-overrides and dhcp6-overrides with use-dns: false, ensuring
only explicitly configured DNS servers are used. Default false
preserves DHCP DNS merge behavior.

Updated across all layers: OpenAPI spec, handler, agent processor,
provider, SDK, CLI (--override-dhcp flag), tests, and docs.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add DeleteNetplanConfig method to DNS provider that removes the
osapi-dns.yaml file and applies netplan. Wire delete operation in
the agent processor to distinguish from update. Add platform stubs.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
…lete)

The operation string is "dns.delete" not "network.dns.delete" — the
category prefix is already stripped. Fix parts index from [2] to [1].

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Document in the provider that accept-ra: false would break IPv6
connectivity (default route, global addresses, prefix info) so
RA-provided DNS servers may still appear alongside configured ones.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix three gaps found in audit: DNS Delete method missing from SDK
doc page, SDK example, and network management permissions table.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Move interface type detection to shared netplan.SectionForInterface
and netplan.SectionForType. Remove duplicated logic from DNS and
route providers. All YAML generators now accept ifaceSection param.

WIP: tests need updating for new netplan status mock expectation.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Create and Update now call SectionForInterface which queries
netplan status. Add RunCmd mock expectations to all affected
test cases.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Route list now cross-references state KV to mark managed routes.
MANAGED column added to CLI output. Strip "netplan" prefix from
all provider error messages — users don't need implementation
details. Update OpenAPI spec and SDK types.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace "already exists"/"does not exist" with "already managed"/
"not managed" across certificate, service, and cron providers.
Strip "netplan" prefix from interface errors. Consistent wording
across all CRUD providers.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
…viders

Create now returns Changed: false when the resource already exists
instead of erroring. Delete returns Changed: false when the resource
doesn't exist. This matches Ansible's desired-state semantics where
state: present/absent are always idempotent.

Also fixes interface List to scan /etc/netplan/ for osapi-*.yaml
files, including managed interfaces that are down/unlinked and not
visible in netplan status output.

Providers updated: cron, certificate, service, NTP, sysctl,
interface, and route.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add Idempotency (MANDATORY) section to CLAUDE.md documenting the
Create/Update/Delete contract. Update OpenAPI specs, feature docs,
CLI docs, type comments, and agent processor tests to reflect that
Create returns Changed: false (not an error) when already managed.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add test cases for managed route matching in List via loadManagedRoutes
(KV happy path, undeployed entries, Keys/Get errors, invalid JSON,
missing metadata keys). Add test cases for scanManagedInterfaces
(multiple managed-only interfaces sorting, directory/non-yaml skips,
ReadDir failure).

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
@retr0h retr0h force-pushed the feat/network-interface-route branch from 24c5cb1 to 2676219 Compare April 6, 2026 06:04
retr0h and others added 2 commits April 5, 2026 23:12
…t processor

Add tests for DNS DeleteNetplanConfig on all platforms (Debian,
Darwin, DebianDocker, Linux), overrideDHCP branch in YAML generation,
SectionForInterface/SectionForType in netplan status, and DNS delete
operation in the agent network processor.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove mixed wantErr/wantErrMsg + validateFunc pattern. All test
cases now use a single validateFunc(result, error) callback,
matching the convention used across all other provider tests.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
@retr0h retr0h merged commit 8fccd74 into main Apr 6, 2026
11 checks passed
@retr0h retr0h deleted the feat/network-interface-route branch April 6, 2026 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant