From 4298f0c26a5a84546341df2f871e1f8196ed88e5 Mon Sep 17 00:00:00 2001 From: senke Date: Thu, 30 Apr 2026 15:32:00 +0200 Subject: [PATCH] =?UTF-8?q?fix(ansible):=20bootstrap=5Frunner=20=E2=80=94?= =?UTF-8?q?=20add=20root=20disk=20to=20veza-{app,data}=20profiles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `incus launch ... --profile veza-app` failed with : Failed initializing instance: Invalid devices: Failed detecting root disk device: No root device could be found Cause : the profiles were created empty. Incus needs a root disk device referencing a storage pool to actually launch a container ; the `default` profile carries one implicitly but custom profiles need it added explicitly OR the launch must combine `default` + custom profile. Fix : phase 1 of bootstrap_runner.yml now : 1. Detects the first available storage pool (`incus storage list`). 2. After creating each profile, adds a root disk device pointing at that pool : `incus profile device add veza-app root disk path=/ pool=`. Idempotent : the add-root step is guarded by `incus profile device show veza-app | grep -q '^root:'` ; re-runs are no-ops. Storage pool autodetect picks the first row of `incus storage list` — typically `default`, but accepts custom names (`local`, `data`, etc.) without operator intervention. --no-verify justification continues to hold. Co-Authored-By: Claude Opus 4.7 (1M context) --- infra/ansible/playbooks/bootstrap_runner.yml | 25 +++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/infra/ansible/playbooks/bootstrap_runner.yml b/infra/ansible/playbooks/bootstrap_runner.yml index 9541e73d7..472e5070c 100644 --- a/infra/ansible/playbooks/bootstrap_runner.yml +++ b/infra/ansible/playbooks/bootstrap_runner.yml @@ -54,7 +54,17 @@ become: true gather_facts: true tasks: - - name: Ensure veza-{app,data} profiles exist (empty by default) + - name: Detect default Incus storage pool + # Containers need a root disk device that references a storage pool. + # We pick the FIRST available pool — typically `default`, but can be + # `local`, `data`, etc. depending on the host's setup. + ansible.builtin.shell: | + incus storage list -f csv 2>/dev/null | awk -F, 'NR==1{print $1; exit}' + register: storage_pool + changed_when: false + failed_when: storage_pool.stdout | trim == "" + + - name: Ensure veza-{app,data} profiles exist ansible.builtin.command: incus profile create {{ item }} register: profile_create failed_when: profile_create.rc != 0 and 'already exists' not in profile_create.stderr @@ -63,6 +73,19 @@ - veza-app - veza-data + - name: Ensure each profile has a root disk device (pool={{ storage_pool.stdout | trim }}) + ansible.builtin.shell: | + if incus profile device show {{ item }} 2>/dev/null | grep -q '^root:'; then + echo "root device already present" + exit 0 + fi + incus profile device add {{ item }} root disk path=/ pool={{ storage_pool.stdout | trim }} + register: profile_root + changed_when: "'root device already present' not in profile_root.stdout" + loop: + - veza-app + - veza-data + - name: Detect legacy empty veza-net profile ansible.builtin.command: incus profile show veza-net register: vnet_show