Opened 3 years ago

Last modified 2 years ago

#63263 assigned defect

py-ansible-base 2.10.11: Path replacement breaks custom facts — at Version 1

Reported by: F30 (Felix Dreissig) Owned by:
Priority: Normal Milestone:
Component: ports Version:
Keywords: Cc: adfernandes (Andrew Fernandes), steenzout (Pedro Salgado)
Port: py-ansible-base

Description (last modified by F30 (Felix Dreissig))

Problem:

Local custom facts do not get picked up from "/etc/ansible/facts.d" if Ansible was installed via MacPorts.

To reproduce, place a JSON or INI file into "/etc/ansible/facts.d" on the remote host, e.g. "/etc/ansible/facts.d/demo.fact":

{
  "foo": "bar"
}

Then, try to read these facts by running something like ansible -m ansible.builtin.setup -a 'filter=ansible_local'. The output will not contain the custom fact.

Alternatively, try accessing ansible_local['demo']['foo'] from a playbook. That will cause Ansible to crash:

fatal: [<snip>]: FAILED! => 
  msg: |-
    The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'demo'

Cause:

The py-ansible-base Portfile does a global replacement of all "/etc/ansible" paths to "/opt/local/etc/ansible". This is valid for local paths of the machine running Ansible. However, it does not take into account that the Ansible code also contains paths relevant for remote hosts, which may have nothing to do with MacPorts.

The Ansible docs clearly state a default fact path of "/etc/ansible/facts.d" and (at least for me) it is very unexpected that MacPorts changes that.

This is a fundamental problem and might affect other places besides facts. The following files from the Ansible repository are currently edited by the path replacement:

examples/hosts.yaml
examples/scripts/uptime.py
lib/ansible/config/base.yml
lib/ansible/config/manager.py
lib/ansible/inventory/manager.py
lib/ansible/module_utils/urls.py
lib/ansible/modules/setup.py
lib/ansible/plugins/loader.py
test/integration/targets/ansible-galaxy/cleanup.yml
test/integration/targets/gathering/implicit.yml
test/integration/targets/gathering/smart.yml
test/integration/targets/gathering_facts/test_gathering_facts.yml
test/integration/targets/gathering_facts/test_run_once.yml
test/units/cli/galaxy/test_execute_list_collection.py
test/units/config/manager/test_find_ini_config_file.py
test/units/executor/test_play_iterator.py
test/units/playbook/role/test_include_role.py
test/units/playbook/role/test_role.py
test/units/playbook/test_included_file.py
test/units/playbook/test_play.py
test/units/vars/test_variable_manager.py

I suppose we can ignore everything under "examples" and "test". This leaves 30 changed lines across 6 files under "lib". Most of these affect the default paths of plugins, collections, roles and similar. These really are relevant for the local machine only.

In fact, the default fact_path from "lib/ansible/modules/setup.py" is the only remote path I could cursorily identify. Of course, it does not need to stay like that in future versions.

Workaround:

The default fact path may get adjusted by setting the ANSIBLE_FACT_PATH environment variable or adding [defaults]/fact_path to an Ansible config file.

However, as stated by the Ansible docs, this only works for implicit fact gathering (i.e. the playbook example from above). Explicit calls to ansible.builtin.setup (such as the non-playbook example above) will always use the built-in default or the path directly specified from the call.

Change History (1)

comment:1 Changed 3 years ago by F30 (Felix Dreissig)

Description: modified (diff)
Note: See TracTickets for help on using tickets.