From d7fbde4ea9db6cc44e81c39d699595330f4fa3c6 Mon Sep 17 00:00:00 2001 From: htol Date: Mon, 11 Apr 2022 21:17:00 +0300 Subject: [PATCH] Make "~/.ansible" path configurable (#76114) * replace hardcoded '~/.ansible' to C.ANSIBLE_HOME * rename previously existing env ANSIBLE_HOME in env-setup script * modify cache dir monkeypatching in galaxy api unit tests * update "version_added" to 2.14 for ANSIBLE_HOME * fix description of collections with proper use of ANSIBLE_HOME Co-authored-by: htol Co-authored-by: stefanwascoding --- hacking/env-setup | 16 +++--- lib/ansible/cli/__init__.py | 2 +- lib/ansible/cli/arguments/option_helpers.py | 2 +- lib/ansible/cli/pull.py | 2 +- lib/ansible/config/base.yml | 64 ++++++++++++--------- lib/ansible/galaxy/api.py | 2 +- lib/ansible/plugins/loader.py | 2 +- test/units/galaxy/test_api.py | 2 +- 8 files changed, 52 insertions(+), 40 deletions(-) diff --git a/hacking/env-setup b/hacking/env-setup index b02c7e6e07c..0a86e0fe4fb 100644 --- a/hacking/env-setup +++ b/hacking/env-setup @@ -45,12 +45,12 @@ fi # The below is an alternative to readlink -fn which doesn't exist on macOS # Source: http://stackoverflow.com/a/1678636 FULL_PATH=$("$PYTHON_BIN" -c "import os; print(os.path.realpath('$HACKING_DIR'))") -export ANSIBLE_HOME="$(dirname "$FULL_PATH")" +export ANSIBLE_DEV_HOME="$(dirname "$FULL_PATH")" -PREFIX_PYTHONPATH="$ANSIBLE_HOME/lib" -ANSIBLE_TEST_PREFIX_PYTHONPATH="$ANSIBLE_HOME/test/lib" -PREFIX_PATH="$ANSIBLE_HOME/bin" -PREFIX_MANPATH="$ANSIBLE_HOME/docs/man" +PREFIX_PYTHONPATH="$ANSIBLE_DEV_HOME/lib" +ANSIBLE_TEST_PREFIX_PYTHONPATH="$ANSIBLE_DEV_HOME/test/lib" +PREFIX_PATH="$ANSIBLE_DEV_HOME/bin" +PREFIX_MANPATH="$ANSIBLE_DEV_HOME/docs/man" expr "$PYTHONPATH" : "${PREFIX_PYTHONPATH}.*" > /dev/null || prepend_path PYTHONPATH "$PREFIX_PYTHONPATH" expr "$PYTHONPATH" : "${ANSIBLE_TEST_PREFIX_PYTHONPATH}.*" > /dev/null || prepend_path PYTHONPATH "$ANSIBLE_TEST_PREFIX_PYTHONPATH" @@ -73,13 +73,13 @@ gen_egg_info() "$PYTHON_BIN" setup.py egg_info } -if [ "$ANSIBLE_HOME" != "$PWD" ] ; then +if [ "$ANSIBLE_DEV_HOME" != "$PWD" ] ; then current_dir="$PWD" else - current_dir="$ANSIBLE_HOME" + current_dir="$ANSIBLE_DEV_HOME" fi ( - cd "$ANSIBLE_HOME" + cd "$ANSIBLE_DEV_HOME" if [ "$verbosity" = silent ] ; then gen_egg_info > /dev/null 2>&1 & find . -type f -name "*.pyc" -exec rm -f {} \; > /dev/null 2>&1 diff --git a/lib/ansible/cli/__init__.py b/lib/ansible/cli/__init__.py index dc15f4bd6a6..975cb34792a 100644 --- a/lib/ansible/cli/__init__.py +++ b/lib/ansible/cli/__init__.py @@ -579,7 +579,7 @@ class CLI(ABC): try: display.debug("starting run") - ansible_dir = Path("~/.ansible").expanduser() + ansible_dir = Path(C.ANSIBLE_HOME).expanduser() try: ansible_dir.mkdir(mode=0o700) except OSError as exc: diff --git a/lib/ansible/cli/arguments/option_helpers.py b/lib/ansible/cli/arguments/option_helpers.py index 8c6444f602b..ad7cb240114 100644 --- a/lib/ansible/cli/arguments/option_helpers.py +++ b/lib/ansible/cli/arguments/option_helpers.py @@ -225,7 +225,7 @@ def add_async_options(parser): def add_basedir_options(parser): """Add options for commands which can set a playbook basedir""" - parser.add_argument('--playbook-dir', default=C.config.get_config_value('PLAYBOOK_DIR'), dest='basedir', action='store', + parser.add_argument('--playbook-dir', default=C.PLAYBOOK_DIR, dest='basedir', action='store', help="Since this tool does not use playbooks, use this as a substitute playbook directory. " "This sets the relative path for many features including roles/ group_vars/ etc.", type=unfrack_path()) diff --git a/lib/ansible/cli/pull.py b/lib/ansible/cli/pull.py index 3db5a22fbaa..99da8c4f0f2 100755 --- a/lib/ansible/cli/pull.py +++ b/lib/ansible/cli/pull.py @@ -130,7 +130,7 @@ class PullCLI(CLI): if not options.dest: hostname = socket.getfqdn() # use a hostname dependent directory, in case of $HOME on nfs - options.dest = os.path.join('~/.ansible/pull', hostname) + options.dest = os.path.join(C.ANSIBLE_HOME, 'pull', hostname) options.dest = os.path.expandvars(os.path.expanduser(options.dest)) if os.path.exists(options.dest) and not os.path.isdir(options.dest): diff --git a/lib/ansible/config/base.yml b/lib/ansible/config/base.yml index 4cd0f9e7f2a..9801f857349 100644 --- a/lib/ansible/config/base.yml +++ b/lib/ansible/config/base.yml @@ -1,6 +1,18 @@ # Copyright (c) 2017 Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) --- +ANSIBLE_HOME: + name: The Ansible home path + description: + - The default root path for Ansible config files on the controller. + default: ~/.ansible + env: + - name: ANSIBLE_HOME + ini: + - key: home + section: defaults + type: path + version_added: '2.14' ANSIBLE_CONNECTION_PATH: name: Path of ansible-connection script default: null @@ -193,10 +205,10 @@ COLLECTIONS_PATHS: description: > Colon separated paths in which Ansible will search for collections content. Collections must be in nested *subdirectories*, not directly in these directories. - For example, if ``COLLECTIONS_PATHS`` includes ``~/.ansible/collections``, + For example, if ``COLLECTIONS_PATHS`` includes ``'{{ ANSIBLE_HOME ~ "/collections" }}'``, and you want to add ``my.collection`` to that directory, it must be saved as - ``~/.ansible/collections/ansible_collections/my/collection``. - default: ~/.ansible/collections:/usr/share/ansible/collections + ``'{{ ANSIBLE_HOME} ~ "/collections/ansible_collections/my/collection" }}'``. + default: '{{ ANSIBLE_HOME ~ "/collections:/usr/share/ansible/collections" }}' type: pathspec env: - name: ANSIBLE_COLLECTIONS_PATHS # TODO: Deprecate this and ini once PATH has been in a few releases. @@ -408,7 +420,7 @@ INVENTORY_UNPARSED_WARNING: version_added: "2.14" DOC_FRAGMENT_PLUGIN_PATH: name: documentation fragment plugins path - default: ~/.ansible/plugins/doc_fragments:/usr/share/ansible/plugins/doc_fragments + default: '{{ ANSIBLE_HOME ~ "/plugins/doc_fragments:/usr/share/ansible/plugins/doc_fragments" }}' description: Colon separated paths in which Ansible will search for Documentation Fragments Plugins. env: [{name: ANSIBLE_DOC_FRAGMENT_PLUGINS}] ini: @@ -416,7 +428,7 @@ DOC_FRAGMENT_PLUGIN_PATH: type: pathspec DEFAULT_ACTION_PLUGIN_PATH: name: Action plugins path - default: ~/.ansible/plugins/action:/usr/share/ansible/plugins/action + default: '{{ ANSIBLE_HOME ~ "/plugins/action:/usr/share/ansible/plugins/action" }}' description: Colon separated paths in which Ansible will search for Action Plugins. env: [{name: ANSIBLE_ACTION_PLUGINS}] ini: @@ -497,7 +509,7 @@ DEFAULT_BECOME_FLAGS: - {key: become_flags, section: privilege_escalation} BECOME_PLUGIN_PATH: name: Become plugins path - default: ~/.ansible/plugins/become:/usr/share/ansible/plugins/become + default: '{{ ANSIBLE_HOME ~ "/plugins/become:/usr/share/ansible/plugins/become" }}' description: Colon separated paths in which Ansible will search for Become Plugins. env: [{name: ANSIBLE_BECOME_PLUGINS}] ini: @@ -515,7 +527,7 @@ DEFAULT_BECOME_USER: yaml: {key: become.user} DEFAULT_CACHE_PLUGIN_PATH: name: Cache Plugins Path - default: ~/.ansible/plugins/cache:/usr/share/ansible/plugins/cache + default: '{{ ANSIBLE_HOME ~ "/plugins/cache:/usr/share/ansible/plugins/cache" }}' description: Colon separated paths in which Ansible will search for Cache Plugins. env: [{name: ANSIBLE_CACHE_PLUGINS}] ini: @@ -523,7 +535,7 @@ DEFAULT_CACHE_PLUGIN_PATH: type: pathspec DEFAULT_CALLBACK_PLUGIN_PATH: name: Callback Plugins Path - default: ~/.ansible/plugins/callback:/usr/share/ansible/plugins/callback + default: '{{ ANSIBLE_HOME ~ "/plugins/callback:/usr/share/ansible/plugins/callback" }}' description: Colon separated paths in which Ansible will search for Callback Plugins. env: [{name: ANSIBLE_CALLBACK_PLUGINS}] ini: @@ -557,7 +569,7 @@ CALLBACKS_ENABLED: type: list DEFAULT_CLICONF_PLUGIN_PATH: name: Cliconf Plugins Path - default: ~/.ansible/plugins/cliconf:/usr/share/ansible/plugins/cliconf + default: '{{ ANSIBLE_HOME ~ "/plugins/cliconf:/usr/share/ansible/plugins/cliconf" }}' description: Colon separated paths in which Ansible will search for Cliconf Plugins. env: [{name: ANSIBLE_CLICONF_PLUGINS}] ini: @@ -565,7 +577,7 @@ DEFAULT_CLICONF_PLUGIN_PATH: type: pathspec DEFAULT_CONNECTION_PLUGIN_PATH: name: Connection Plugins Path - default: ~/.ansible/plugins/connection:/usr/share/ansible/plugins/connection + default: '{{ ANSIBLE_HOME ~ "/plugins/connection:/usr/share/ansible/plugins/connection" }}' description: Colon separated paths in which Ansible will search for Connection Plugins. env: [{name: ANSIBLE_CONNECTION_PLUGINS}] ini: @@ -614,7 +626,7 @@ DEFAULT_FACT_PATH: # alternatives: module_defaults DEFAULT_FILTER_PLUGIN_PATH: name: Jinja2 Filter Plugins Path - default: ~/.ansible/plugins/filter:/usr/share/ansible/plugins/filter + default: '{{ ANSIBLE_HOME ~ "/plugins/filter:/usr/share/ansible/plugins/filter" }}' description: Colon separated paths in which Ansible will search for Jinja2 Filter Plugins. env: [{name: ANSIBLE_FILTER_PLUGINS}] ini: @@ -731,7 +743,7 @@ DEFAULT_HOST_LIST: yaml: {key: defaults.inventory} DEFAULT_HTTPAPI_PLUGIN_PATH: name: HttpApi Plugins Path - default: ~/.ansible/plugins/httpapi:/usr/share/ansible/plugins/httpapi + default: '{{ ANSIBLE_HOME ~ "/plugins/httpapi:/usr/share/ansible/plugins/httpapi" }}' description: Colon separated paths in which Ansible will search for HttpApi Plugins. env: [{name: ANSIBLE_HTTPAPI_PLUGINS}] ini: @@ -753,7 +765,7 @@ DEFAULT_INTERNAL_POLL_INTERVAL: - "The default corresponds to the value hardcoded in Ansible <= 2.1" DEFAULT_INVENTORY_PLUGIN_PATH: name: Inventory Plugins Path - default: ~/.ansible/plugins/inventory:/usr/share/ansible/plugins/inventory + default: '{{ ANSIBLE_HOME ~ "/plugins/inventory:/usr/share/ansible/plugins/inventory" }}' description: Colon separated paths in which Ansible will search for Inventory Plugins. env: [{name: ANSIBLE_INVENTORY_PLUGINS}] ini: @@ -815,7 +827,7 @@ DEFAULT_LOAD_CALLBACK_PLUGINS: version_added: "1.8" DEFAULT_LOCAL_TMP: name: Controller temporary directory - default: ~/.ansible/tmp + default: '{{ ANSIBLE_HOME ~ "/tmp" }}' description: Temporary directory for Ansible to use on the controller. env: [{name: ANSIBLE_LOCAL_TEMP}] ini: @@ -840,7 +852,7 @@ DEFAULT_LOG_FILTER: DEFAULT_LOOKUP_PLUGIN_PATH: name: Lookup Plugins Path description: Colon separated paths in which Ansible will search for Lookup Plugins. - default: ~/.ansible/plugins/lookup:/usr/share/ansible/plugins/lookup + default: '{{ ANSIBLE_HOME ~ "/plugins/lookup:/usr/share/ansible/plugins/lookup" }}' env: [{name: ANSIBLE_LOOKUP_PLUGINS}] ini: - {key: lookup_plugins, section: defaults} @@ -881,7 +893,7 @@ DEFAULT_MODULE_NAME: DEFAULT_MODULE_PATH: name: Modules Path description: Colon separated paths in which Ansible will search for Modules. - default: ~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules + default: '{{ ANSIBLE_HOME ~ "/plugins/modules:/usr/share/ansible/plugins/modules" }}' env: [{name: ANSIBLE_LIBRARY}] ini: - {key: library, section: defaults} @@ -889,14 +901,14 @@ DEFAULT_MODULE_PATH: DEFAULT_MODULE_UTILS_PATH: name: Module Utils Path description: Colon separated paths in which Ansible will search for Module utils files, which are shared by modules. - default: ~/.ansible/plugins/module_utils:/usr/share/ansible/plugins/module_utils + default: '{{ ANSIBLE_HOME ~ "/plugins/module_utils:/usr/share/ansible/plugins/module_utils" }}' env: [{name: ANSIBLE_MODULE_UTILS}] ini: - {key: module_utils, section: defaults} type: pathspec DEFAULT_NETCONF_PLUGIN_PATH: name: Netconf Plugins Path - default: ~/.ansible/plugins/netconf:/usr/share/ansible/plugins/netconf + default: '{{ ANSIBLE_HOME ~ "/plugins/netconf:/usr/share/ansible/plugins/netconf" }}' description: Colon separated paths in which Ansible will search for Netconf Plugins. env: [{name: ANSIBLE_NETCONF_PLUGINS}] ini: @@ -985,7 +997,7 @@ DEFAULT_REMOTE_USER: - {key: remote_user, section: defaults} DEFAULT_ROLES_PATH: name: Roles path - default: ~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles + default: '{{ ANSIBLE_HOME ~ "/roles:/usr/share/ansible/roles:/etc/ansible/roles" }}' description: Colon separated paths in which Ansible will search for Roles. env: [{name: ANSIBLE_ROLES_PATH}] expand_relative_paths: True @@ -1051,7 +1063,7 @@ DEFAULT_STRATEGY: DEFAULT_STRATEGY_PLUGIN_PATH: name: Strategy Plugins Path description: Colon separated paths in which Ansible will search for Strategy Plugins. - default: ~/.ansible/plugins/strategy:/usr/share/ansible/plugins/strategy + default: '{{ ANSIBLE_HOME ~ "/plugins/strategy:/usr/share/ansible/plugins/strategy" }}' env: [{name: ANSIBLE_STRATEGY_PLUGINS}] ini: - {key: strategy_plugins, section: defaults} @@ -1073,7 +1085,7 @@ DEFAULT_SYSLOG_FACILITY: - {key: syslog_facility, section: defaults} DEFAULT_TERMINAL_PLUGIN_PATH: name: Terminal Plugins Path - default: ~/.ansible/plugins/terminal:/usr/share/ansible/plugins/terminal + default: '{{ ANSIBLE_HOME ~ "/plugins/terminal:/usr/share/ansible/plugins/terminal" }}' description: Colon separated paths in which Ansible will search for Terminal Plugins. env: [{name: ANSIBLE_TERMINAL_PLUGINS}] ini: @@ -1082,7 +1094,7 @@ DEFAULT_TERMINAL_PLUGIN_PATH: DEFAULT_TEST_PLUGIN_PATH: name: Jinja2 Test Plugins Path description: Colon separated paths in which Ansible will search for Jinja2 Test Plugins. - default: ~/.ansible/plugins/test:/usr/share/ansible/plugins/test + default: '{{ ANSIBLE_HOME ~ "/plugins/test:/usr/share/ansible/plugins/test" }}' env: [{name: ANSIBLE_TEST_PLUGINS}] ini: - {key: test_plugins, section: defaults} @@ -1116,7 +1128,7 @@ DEFAULT_UNDEFINED_VAR_BEHAVIOR: type: boolean DEFAULT_VARS_PLUGIN_PATH: name: Vars Plugins Path - default: ~/.ansible/plugins/vars:/usr/share/ansible/plugins/vars + default: '{{ ANSIBLE_HOME ~ "/plugins/vars:/usr/share/ansible/plugins/vars" }}' description: Colon separated paths in which Ansible will search for Vars Plugins. env: [{name: ANSIBLE_VARS_PLUGINS}] ini: @@ -1384,7 +1396,7 @@ GALAXY_SERVER_LIST: type: list version_added: "2.9" GALAXY_TOKEN_PATH: - default: ~/.ansible/galaxy_token + default: '{{ ANSIBLE_HOME ~ "/galaxy_token" }}' description: "Local path to galaxy access token file" env: [{name: ANSIBLE_GALAXY_TOKEN_PATH}] ini: @@ -1404,7 +1416,7 @@ GALAXY_DISPLAY_PROGRESS: type: bool version_added: "2.10" GALAXY_CACHE_DIR: - default: ~/.ansible/galaxy_cache + default: '{{ ANSIBLE_HOME ~ "/galaxy_cache" }}' description: - The directory that stores cached responses from a Galaxy server. - This is only used by the ``ansible-galaxy collection install`` and ``download`` commands. @@ -1767,7 +1779,7 @@ PARAMIKO_LOOK_FOR_KEYS: type: boolean PERSISTENT_CONTROL_PATH_DIR: name: Persistence socket path - default: ~/.ansible/pc + default: '{{ ANSIBLE_HOME ~ "/pc" }}' description: Path to socket to be used by the connection persistence system. env: [{name: ANSIBLE_PERSISTENT_CONTROL_PATH_DIR}] ini: diff --git a/lib/ansible/galaxy/api.py b/lib/ansible/galaxy/api.py index 796c88519ea..55434f6d941 100644 --- a/lib/ansible/galaxy/api.py +++ b/lib/ansible/galaxy/api.py @@ -270,7 +270,7 @@ class GalaxyAPI: self._available_api_versions = available_api_versions or {} self._priority = priority - b_cache_dir = to_bytes(C.config.get_config_value('GALAXY_CACHE_DIR'), errors='surrogate_or_strict') + b_cache_dir = to_bytes(C.GALAXY_CACHE_DIR, errors='surrogate_or_strict') makedirs_safe(b_cache_dir, mode=0o700) self._b_cache_path = os.path.join(b_cache_dir, b'api.json') diff --git a/lib/ansible/plugins/loader.py b/lib/ansible/plugins/loader.py index 019a5bcf24f..610d7ab4064 100644 --- a/lib/ansible/plugins/loader.py +++ b/lib/ansible/plugins/loader.py @@ -1145,7 +1145,7 @@ def _configure_collection_loader(): warnings.warn('AnsibleCollectionFinder has already been configured') return - finder = _AnsibleCollectionFinder(C.config.get_config_value('COLLECTIONS_PATHS'), C.config.get_config_value('COLLECTIONS_SCAN_SYS_PATH')) + finder = _AnsibleCollectionFinder(C.COLLECTIONS_PATHS, C.COLLECTIONS_SCAN_SYS_PATH) finder._install() # this should succeed now diff --git a/test/units/galaxy/test_api.py b/test/units/galaxy/test_api.py index 733f99b5718..a2965012b1f 100644 --- a/test/units/galaxy/test_api.py +++ b/test/units/galaxy/test_api.py @@ -58,7 +58,7 @@ def collection_artifact(tmp_path_factory): @pytest.fixture() def cache_dir(tmp_path_factory, monkeypatch): cache_dir = to_text(tmp_path_factory.mktemp('Test ÅÑŚÌβŁÈ Galaxy Cache')) - monkeypatch.setitem(C.config._base_defs, 'GALAXY_CACHE_DIR', {'default': cache_dir}) + monkeypatch.setattr(C, 'GALAXY_CACHE_DIR', cache_dir) yield cache_dir