From 01ce0bd6090276645402016c12a7b554336f5d45 Mon Sep 17 00:00:00 2001 From: Amritanshu Date: Wed, 23 Sep 2026 00:18:04 +0530 Subject: [PATCH] Create each Site's network with its own subnet The deploy failed on hngmohali with 'all predefined address pools have been fully subnetted': every 172.17-172.30/16 is in use, 172.31/16 is the host's own VPC so Docker will not allocate from it, and all sixteen 192.168.0-240/20 blocks are taken. Docker therefore refuses a network created without a subnet. Each Site now names a free network_subnet (10.210.0.0/16, one /24 each), the role looks the network up first and only creates it when it is absent and with that subnet, and it fails with an explanation when a Site declares none. Existing networks are left alone, because Docker cannot change a subnet in place and the Sites already running have theirs. The test checks every Site declares one, and the deployment readme says a new Site needs a free subnet. deploy.sh now deploys every Site by default, with SITE= to narrow it, which is what the local edit was reaching for. --- ansible/readme.md | 5 ++++- ansible/roles/network/tasks/main.yaml | 26 +++++++++++++++++++++++++- ansible/vars/default.yml | 3 +++ ansible/vars/greatbear.yml | 4 ++++ ansible/vars/hngmohali.yml | 3 +++ ansible/vars/hopsngrains.yml | 4 ++++ deploy.sh | 10 +++++++--- test/site-configs.test.ts | 2 ++ 8 files changed, 52 insertions(+), 5 deletions(-) diff --git a/ansible/readme.md b/ansible/readme.md index 639bafc..cdae033 100644 --- a/ansible/readme.md +++ b/ansible/readme.md @@ -52,7 +52,10 @@ machine's `~/.ssh/config` says. (`ssh` on some machines complains about a local file-permission bug whose fix is `sudo chown -R root:root /etc/ssh/ssh_config.d`, not something this playbook works around.) -Adding a Site is one line in `hosts` and a copy of `vars/hopsngrains.yml`. +Adding a Site is one line in `hosts`, a copy of `vars/hopsngrains.yml`, a +`config/.site.json`, and a **free subnet** — this host's Docker address +pools are exhausted, so `network_subnet` (a free `/24` from `10.210.0.0/16`) is +required and a network is only created with it. ## Checking it landed diff --git a/ansible/roles/network/tasks/main.yaml b/ansible/roles/network/tasks/main.yaml index 979dde4..3f16862 100644 --- a/ansible/roles/network/tasks/main.yaml +++ b/ansible/roles/network/tasks/main.yaml @@ -1,5 +1,29 @@ --- -- name: 'Ensure {{ docker_network }} exists and Caddy is on it' +- name: Look for the app network + docker_network_info: + name: '{{ docker_network }}' + register: menu_network + +- name: Fail when this Site declares no subnet + fail: + msg: >- + {{ inventory_hostname }} needs a network_subnet in its vars file. This host's + default address pools are exhausted (every 172.17-172.30/16 and every + 192.168.0-240/20 is taken), so Docker refuses to allocate one and a network + has to be created with an explicit subnet. + when: network_subnet | default('') == '' + +# An existing network is left exactly as it is: Docker cannot change a network's +# subnet in place, and the Sites already running have theirs. +- name: Create the app network with this Site's subnet + docker_network: + name: '{{ docker_network }}' + ipam_config: + - subnet: '{{ network_subnet }}' + state: present + when: not menu_network.exists + +- name: Put Caddy on the app network docker_network: name: '{{ docker_network }}' state: present diff --git a/ansible/vars/default.yml b/ansible/vars/default.yml index ab4ba28..2e78a5a 100644 --- a/ansible/vars/default.yml +++ b/ansible/vars/default.yml @@ -8,6 +8,9 @@ site: '{{ inventory_hostname }}' image_name: registry.tanshu.com/menu-admin image_tag: "{{ tag | default('latest') }}" +# Every Site sets network_subnet: the host's Docker address pools are exhausted, +# so a network created without an explicit subnet is refused. Use a free /24 from +# 10.210.0.0/16, one per Site. app_uid: 1000 app_gid: 1000 diff --git a/ansible/vars/greatbear.yml b/ansible/vars/greatbear.yml index 96aa27c..f1100f6 100644 --- a/ansible/vars/greatbear.yml +++ b/ansible/vars/greatbear.yml @@ -11,3 +11,7 @@ docker_network: '{{ docker_container }}_net' # Everything the app writes — data/ and the untracked site.env. Deliberately the # directory the old static menus were served from. app_root: '/var/lib/{{ host }}' + +# The host's default Docker pools are exhausted, so every Site names a subnet. +# This one already exists from the pool; the value is what a rebuild would use. +network_subnet: 10.210.2.0/24 diff --git a/ansible/vars/hngmohali.yml b/ansible/vars/hngmohali.yml index 986b0d3..91e4f49 100644 --- a/ansible/vars/hngmohali.yml +++ b/ansible/vars/hngmohali.yml @@ -11,3 +11,6 @@ docker_network: '{{ docker_container }}_net' # Everything the app writes — data/ and the untracked site.env. Deliberately the # directory the old static menus were served from. app_root: '/var/lib/{{ host }}' + +# The host's default Docker pools are exhausted, so every Site names a subnet. +network_subnet: 10.210.3.0/24 diff --git a/ansible/vars/hopsngrains.yml b/ansible/vars/hopsngrains.yml index 4ee10c7..7858107 100644 --- a/ansible/vars/hopsngrains.yml +++ b/ansible/vars/hopsngrains.yml @@ -19,3 +19,7 @@ docker_network: '{{ docker_container }}_net' # writes its own data/ inside it; the old menu files can be emptied out when # they are no longer wanted, and the directory itself must stay. app_root: '/var/lib/{{ host }}' + +# The host's default Docker pools are exhausted, so every Site names a subnet. +# This one already exists from the pool; the value is what a rebuild would use. +network_subnet: 10.210.1.0/24 diff --git a/deploy.sh b/deploy.sh index 71651e6..543137f 100755 --- a/deploy.sh +++ b/deploy.sh @@ -40,7 +40,11 @@ if [[ -n "${SKIP_DEPLOY:-}" ]]; then exit 0 fi -SITE="${SITE:-hopsngrains}" -echo "==> deploying $SITE" cd ansible -ansible-playbook --inventory=hosts playbook.yml --limit "$SITE" -e "tag=$TAG" +if [[ -n "${SITE:-}" ]]; then + echo "==> deploying $SITE" + ansible-playbook --inventory=hosts playbook.yml --limit "$SITE" -e "tag=$TAG" +else + echo "==> deploying every Site" + ansible-playbook --inventory=hosts playbook.yml -e "tag=$TAG" +fi diff --git a/test/site-configs.test.ts b/test/site-configs.test.ts index cd703cd..5df66ec 100644 --- a/test/site-configs.test.ts +++ b/test/site-configs.test.ts @@ -52,6 +52,8 @@ describe('every Site in the inventory is complete', () => { assert.match(varsRaw, /^title: /m, `${name}: ${varFile} has no title`); assert.match(varsRaw, /^host: 'menu\.\{\{ title \}\}'/m, `${name}: host is not quoted`); assert.match(varsRaw, /^app_root: /m, `${name}: no app_root`); + // Without this the play fails on a host whose Docker pools are full. + assert.match(varsRaw, /^network_subnet: /m, `${name}: no network_subnet`); } });