Skip to content

Attach docker nodes to their declared networks - #104

Merged
bgrewell merged 1 commit into
mainfrom
feature/docker-networks
Aug 9, 2026
Merged

Attach docker nodes to their declared networks#104
bgrewell merged 1 commit into
mainfrom
feature/docker-networks

Conversation

@bgrewell

@bgrewell bgrewell commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Closes #93.

The problem

networks: on a docker node was decoded into DockerNodeOpts.Networks and then never read. Containers were created with an empty NetworkingConfig, so every node landed on Docker's default bridge.

Three consequences, in increasing order of seriousness:

  • A documented static ip: had no effect.
  • The default bridge has no embedded DNS, so containers could not resolve each other by node name.
  • A suite asserting that two nodes are isolated passed because they were never separated. Same false-green family as the rest of this series — the assertion looked configured and tested nothing.

The shipped examples/docker/docker.yaml relies on it: two containers on test_net at 192.168.200.10 and .100. Those addresses were silently ignored.

The fix

Declared networks now reach the container. Docker accepts a single endpoint at creation time, so the first entry is applied there and any others are connected immediately afterwards with NetworkConnect, preserving list order. A static address is passed as the endpoint's IPAM configuration.

subnet on a node-level entry is now a configuration error pointing at docker.networks — a node joins a network rather than defining one. That matches the rule already applied to lxd nodes in #97.

Testability change

Wrapper.cli was the concrete *client.Client, which made the create path impossible to test without a daemon. It is now client.APIClient; GetClient and RemoveImage widen to match, and both only ever needed the interface — NewComposeStack already took it.

Verification — please read

No Docker daemon was reachable from this machine or from any datacenter host I tried (services-01/02/03, control-ws), so this is not verified end to end against a real daemon. Given that, I tested the wiring against a recording client that captures the create call and follow-up connects:

  • a declared network reaches EndpointsConfig and needs no follow-up connect
  • a static address arrives as IPAMConfig.IPv4Address
  • with three networks, the first is at creation and the other two are connected in order against the created container, with and without addresses
  • a node declaring no networks produces the previous behaviour exactly

That covers my logic but not Docker's acceptance of it. The parts I would want confirmed on a real daemon before trusting this in anger:

  1. that a static IP is accepted on a network whose subnet was declared by the suite (it requires a user-defined subnet — the platform block provides one, but I have not seen the daemon accept it);
  2. that examples/docker/docker.yaml now actually reaches .10 and .100 and its tests pass.

If you have a host with Docker, that example is the fastest way to confirm both. Build, vet, gofmt, and the full test suite are green, and every example still passes --check.

networks: on a docker node was decoded and dropped. Containers were created
with an empty networking configuration, so every node landed on the default
bridge: a documented static ip: had no effect, containers could not resolve
each other by name, and a suite asserting that two nodes were isolated passed
because they had never been separated.

The declared networks now reach the container. Docker accepts one endpoint at
creation, so the first entry is applied there and the rest are connected
immediately afterwards, preserving list order. A static address is passed as
the endpoint's IPAM configuration.

subnet on a node-level entry is now a configuration error pointing at
docker.networks, which is what creates the network — a node joins one rather
than defining it. This matches the rule already applied to lxd nodes.

Wrapper.cli is now the client interface rather than the concrete type, so the
call sequence can be asserted without a daemon; GetClient and RemoveImage
widen to match, both of which only ever needed the interface.

Note: verified against a recording client rather than a live daemon — no
Docker was reachable from this machine or the datacenter hosts.
@bgrewell
bgrewell merged commit c6a708e into main Aug 9, 2026
2 checks passed
@bgrewell
bgrewell deleted the feature/docker-networks branch August 9, 2026 00:50
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.

networks: on a docker node is parsed and then dropped

1 participant