Scenarios install their Galaxy dependencies with `force: true`, so two roles running at once re-extract the same collections and roles into ~/.ansible and pull them out from under each other mid-play. It surfaces as a collection that was working moments earlier going missing: the connection plugin 'community.docker.docker' was not found Found while running five scenarios in parallel, where it cost a run. ANSIBLE_HOME relocates both `collections/` and `roles/`, so one variable covers both halves; the scenarios' ANSIBLE_ROLES_PATH workaround now follows it rather than hardcoding ~/.ansible/roles. Left alone if already set, and unset in CI, where each role runs in its own job and has nothing to collide with. Verified by removing var/molecule-ansible-home entirely and running matrix-alertmanager-receiver from cold: green through idempotence, with the collections and roles landing under the per-role directory - which also shows nothing was quietly relying on the shared ~/.ansible being populated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SEH3vxYSQ5SV4N5z61eyGTpull/5575/head
| @@ -70,6 +70,20 @@ if [ ! -x "${venv_dir}/bin/molecule" ]; then | |||||
| "${venv_dir}/bin/pip" install --quiet -r "${repo_dir}/molecule-shared/requirements.txt" | "${venv_dir}/bin/pip" install --quiet -r "${repo_dir}/molecule-shared/requirements.txt" | ||||
| fi | fi | ||||
| # Galaxy content is installed with `force: true` on every run, so two scenarios | |||||
| # running at once will re-extract collections and roles into the same directory | |||||
| # and pull them out from under each other mid-play. It shows up as a collection | |||||
| # that was working moments earlier going missing: | |||||
| # | |||||
| # the connection plugin 'community.docker.docker' was not found | |||||
| # | |||||
| # ANSIBLE_HOME relocates both `collections/` and `roles/`, so one variable is | |||||
| # enough to give each role its own. The scenarios' ANSIBLE_ROLES_PATH follows it. | |||||
| # | |||||
| # Unset in CI, where it falls back to ~/.ansible - each role runs in its own job | |||||
| # there, so there is nothing to collide with and nothing to gain from isolation. | |||||
| export ANSIBLE_HOME="${ANSIBLE_HOME:-${repo_dir}/var/molecule-ansible-home/${role}}" | |||||
| export MOLECULE_DISTRO="${MOLECULE_DISTRO:-ubuntu2604}" | export MOLECULE_DISTRO="${MOLECULE_DISTRO:-ubuntu2604}" | ||||
| export PY_COLORS="${PY_COLORS:-1}" | export PY_COLORS="${PY_COLORS:-1}" | ||||
| export ANSIBLE_FORCE_COLOR="${ANSIBLE_FORCE_COLOR:-1}" | export ANSIBLE_FORCE_COLOR="${ANSIBLE_FORCE_COLOR:-1}" | ||||
| @@ -69,3 +69,24 @@ A suite that only waits for the systemd unit to become `active` proves very litt | |||||
| Give the scenario values that differ from both the role's defaults and the component's own defaults. Otherwise a passing assertion cannot distinguish "the role configured this" from "it would have happened anyway". | Give the scenario values that differ from both the role's defaults and the component's own defaults. Otherwise a passing assertion cannot distinguish "the role configured this" from "it would have happened anyway". | ||||
| Then try to break it. If a scenario cannot be made to fail by deliberately breaking the thing it checks, it is not testing that thing. | Then try to break it. If a scenario cannot be made to fail by deliberately breaking the thing it checks, it is not testing that thing. | ||||
| ## Running more than one scenario at once | |||||
| `bin/molecule.sh` points `ANSIBLE_HOME` at `var/molecule-ansible-home/<role>/`, so each role gets | |||||
| its own copy of the Galaxy collections and roles. | |||||
| This is not an optimisation - it is a correctness fix. Scenarios install their dependencies with | |||||
| `force: true`, so two runs sharing `~/.ansible` re-extract the same collections underneath each | |||||
| other. The symptom is a collection that was working moments earlier going missing mid-play: | |||||
| ``` | |||||
| the connection plugin 'community.docker.docker' was not found | |||||
| ``` | |||||
| If you see that, a concurrent run took the collection out from under you. | |||||
| `ANSIBLE_HOME` is left alone if you have already set it, and is unset in CI - each role runs in its | |||||
| own job there, so there is nothing to collide with. | |||||
| The directories are disposable; `var/` is gitignored. Delete `var/molecule-ansible-home/` to force | |||||
| a fresh install. | |||||
| @@ -59,7 +59,7 @@ provisioner: | |||||
| molecule-room: "!molecule-room-id:molecule.local" | molecule-room: "!molecule-room-id:molecule.local" | ||||
| env: | env: | ||||
| # Workaround for https://github.com/ansible/molecule/issues/4391 | # Workaround for https://github.com/ansible/molecule/issues/4391 | ||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles | |||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:${ANSIBLE_HOME:-~/.ansible}/roles | |||||
| scenario: | scenario: | ||||
| test_sequence: | test_sequence: | ||||
| - dependency | - dependency | ||||
| @@ -75,7 +75,7 @@ provisioner: | |||||
| matrix_bot_baibot_config_agents_static_definitions_anthropic_config_text_generation_max_context_tokens: 56789 | matrix_bot_baibot_config_agents_static_definitions_anthropic_config_text_generation_max_context_tokens: 56789 | ||||
| env: | env: | ||||
| # Workaround for https://github.com/ansible/molecule/issues/4391 | # Workaround for https://github.com/ansible/molecule/issues/4391 | ||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles | |||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:${ANSIBLE_HOME:-~/.ansible}/roles | |||||
| scenario: | scenario: | ||||
| test_sequence: | test_sequence: | ||||
| - dependency | - dependency | ||||
| @@ -80,7 +80,7 @@ provisioner: | |||||
| matrix_bot_matrix_reminder_bot_data_store_path: /matrix/matrix-reminder-bot/data/store | matrix_bot_matrix_reminder_bot_data_store_path: /matrix/matrix-reminder-bot/data/store | ||||
| env: | env: | ||||
| # Workaround for https://github.com/ansible/molecule/issues/4391 | # Workaround for https://github.com/ansible/molecule/issues/4391 | ||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles | |||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:${ANSIBLE_HOME:-~/.ansible}/roles | |||||
| scenario: | scenario: | ||||
| test_sequence: | test_sequence: | ||||
| - dependency | - dependency | ||||
| @@ -57,7 +57,7 @@ provisioner: | |||||
| matrix_bridge_heisenbridge_base_path: /matrix/heisenbridge | matrix_bridge_heisenbridge_base_path: /matrix/heisenbridge | ||||
| env: | env: | ||||
| # Workaround for https://github.com/ansible/molecule/issues/4391 | # Workaround for https://github.com/ansible/molecule/issues/4391 | ||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles | |||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:${ANSIBLE_HOME:-~/.ansible}/roles | |||||
| scenario: | scenario: | ||||
| test_sequence: | test_sequence: | ||||
| - dependency | - dependency | ||||
| @@ -105,7 +105,7 @@ provisioner: | |||||
| matrix_bridge_hookshot_identifier: matrix-hookshot | matrix_bridge_hookshot_identifier: matrix-hookshot | ||||
| env: | env: | ||||
| # Workaround for https://github.com/ansible/molecule/issues/4391 | # Workaround for https://github.com/ansible/molecule/issues/4391 | ||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles | |||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:${ANSIBLE_HOME:-~/.ansible}/roles | |||||
| scenario: | scenario: | ||||
| test_sequence: | test_sequence: | ||||
| - dependency | - dependency | ||||
| @@ -88,7 +88,7 @@ provisioner: | |||||
| matrix_bridge_mautrix_discord_data_path: /matrix/mautrix-discord/data | matrix_bridge_mautrix_discord_data_path: /matrix/mautrix-discord/data | ||||
| env: | env: | ||||
| # Workaround for https://github.com/ansible/molecule/issues/4391 | # Workaround for https://github.com/ansible/molecule/issues/4391 | ||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles | |||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:${ANSIBLE_HOME:-~/.ansible}/roles | |||||
| scenario: | scenario: | ||||
| test_sequence: | test_sequence: | ||||
| - dependency | - dependency | ||||
| @@ -101,7 +101,7 @@ provisioner: | |||||
| matrix_bridge_mautrix_meta_messenger_data_path: /matrix/mautrix-meta-messenger/data | matrix_bridge_mautrix_meta_messenger_data_path: /matrix/mautrix-meta-messenger/data | ||||
| env: | env: | ||||
| # Workaround for https://github.com/ansible/molecule/issues/4391 | # Workaround for https://github.com/ansible/molecule/issues/4391 | ||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles | |||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:${ANSIBLE_HOME:-~/.ansible}/roles | |||||
| scenario: | scenario: | ||||
| test_sequence: | test_sequence: | ||||
| - dependency | - dependency | ||||
| @@ -63,7 +63,7 @@ provisioner: | |||||
| matrix_bridge_mautrix_whatsapp_data_path: /matrix/mautrix-whatsapp/data | matrix_bridge_mautrix_whatsapp_data_path: /matrix/mautrix-whatsapp/data | ||||
| env: | env: | ||||
| # Workaround for https://github.com/ansible/molecule/issues/4391 | # Workaround for https://github.com/ansible/molecule/issues/4391 | ||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/roles | |||||
| ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/../..:/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:${ANSIBLE_HOME:-~/.ansible}/roles | |||||
| scenario: | scenario: | ||||
| test_sequence: | test_sequence: | ||||
| - dependency | - dependency | ||||