Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ansible-core for openSUSE:Factory checked in at 2023-11-07 21:27:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ansible-core (Old) and /work/SRC/openSUSE:Factory/.ansible-core.new.17445 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ansible-core" Tue Nov 7 21:27:42 2023 rev:20 rq:1123920 version:2.15.6 Changes: -------- --- /work/SRC/openSUSE:Factory/ansible-core/ansible-core.changes 2023-10-10 21:01:51.501472468 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core.new.17445/ansible-core.changes 2023-11-07 21:28:30.068247661 +0100 @@ -1,0 +2,24 @@ +Tue Nov 7 07:46:07 UTC 2023 - Johannes Kastl <[email protected]> + +- update to 2.15.6: + * Minor Changes + - ansible-test - Windows 2012 and 2012-R2 instances are now + requested from Azure instead of AWS. + * Bugfixes + - Fix run_once being incorrectly interpreted on handlers + (#81666) + - Plugin loader does not dedupe nor cache filter/test plugins + by file basename, but full path name. + - Properly template tags in parent blocks (#81053) + - Restoring the ability of filters/tests can have same file + base name but different tests/filters defined inside. + - import_role reverts to previous behavior of exporting vars at + compile time. + - ansible-galaxy - Provide a better error message when using a + requirements file with an invalid format - #81901 + - ansible-inventory - index available_hosts for major + performance boost when dumping large inventories + - ansible-test - Fix parsing of cgroup entries which contain a + : in the path (#81977). + +------------------------------------------------------------------- @@ -9 +33 @@ - * - Security Fixes + * Security Fixes @@ -13 +37 @@ - * - Bugfixes + * Bugfixes Old: ---- ansible-core-2.15.5.tar.gz New: ---- ansible-core-2.15.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ansible-core.spec ++++++ --- /var/tmp/diff_new_pack.d5iyIN/_old 2023-11-07 21:28:30.764273292 +0100 +++ /var/tmp/diff_new_pack.d5iyIN/_new 2023-11-07 21:28:30.768273439 +0100 @@ -38,7 +38,7 @@ %endif Name: ansible-core -Version: 2.15.5 +Version: 2.15.6 Release: 0 Summary: Radically simple IT automation License: GPL-3.0-or-later ++++++ ansible-core-2.15.5.tar.gz -> ansible-core-2.15.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/PKG-INFO new/ansible-core-2.15.6/PKG-INFO --- old/ansible-core-2.15.5/PKG-INFO 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/PKG-INFO 2023-11-06 18:56:48.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ansible-core -Version: 2.15.5 +Version: 2.15.6 Summary: Radically simple IT automation Home-page: https://ansible.com/ Author: Ansible, Inc. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/bin/ansible-galaxy new/ansible-core-2.15.6/bin/ansible-galaxy --- old/ansible-core-2.15.5/bin/ansible-galaxy 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/bin/ansible-galaxy 2023-11-06 18:56:48.000000000 +0100 @@ -805,7 +805,7 @@ for role_req in file_requirements: requirements['roles'] += parse_role_req(role_req) - else: + elif isinstance(file_requirements, dict): # Newer format with a collections and/or roles key extra_keys = set(file_requirements.keys()).difference(set(['roles', 'collections'])) if extra_keys: @@ -824,6 +824,9 @@ for collection_req in file_requirements.get('collections') or [] ] + else: + raise AnsibleError(f"Expecting requirements yaml to be a list or dictionary but got {type(file_requirements).__name__}") + return requirements def _init_coll_req_dict(self, coll_req): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/bin/ansible-inventory new/ansible-core-2.15.6/bin/ansible-inventory --- old/ansible-core-2.15.5/bin/ansible-inventory 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/bin/ansible-inventory 2023-11-06 18:56:48.000000000 +0100 @@ -325,7 +325,7 @@ return results hosts = self.inventory.get_hosts(top.name) - results = format_group(top, [h.name for h in hosts]) + results = format_group(top, frozenset(h.name for h in hosts)) # populate meta results['_meta'] = {'hostvars': {}} @@ -381,7 +381,7 @@ return results - available_hosts = [h.name for h in self.inventory.get_hosts(top.name)] + available_hosts = frozenset(h.name for h in self.inventory.get_hosts(top.name)) return format_group(top, available_hosts) def toml_inventory(self, top): @@ -425,7 +425,7 @@ return results - available_hosts = [h.name for h in self.inventory.get_hosts(top.name)] + available_hosts = frozenset(h.name for h in self.inventory.get_hosts(top.name)) results = format_group(top, available_hosts) return results diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/changelogs/CHANGELOG-v2.15.rst new/ansible-core-2.15.6/changelogs/CHANGELOG-v2.15.rst --- old/ansible-core-2.15.5/changelogs/CHANGELOG-v2.15.rst 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/changelogs/CHANGELOG-v2.15.rst 2023-11-06 18:56:48.000000000 +0100 @@ -5,6 +5,33 @@ .. contents:: Topics +v2.15.6 +======= + +Release Summary +--------------- + +| Release Date: 2023-11-06 +| `Porting Guide <https://docs.ansible.com/ansible-core/2.15/porting_guides/porting_guide_core_2.15.html>`__ + + +Minor Changes +------------- + +- ansible-test - Windows 2012 and 2012-R2 instances are now requested from Azure instead of AWS. + +Bugfixes +-------- + +- Fix ``run_once`` being incorrectly interpreted on handlers (https://github.com/ansible/ansible/issues/81666) +- Plugin loader does not dedupe nor cache filter/test plugins by file basename, but full path name. +- Properly template tags in parent blocks (https://github.com/ansible/ansible/issues/81053) +- Restoring the ability of filters/tests can have same file base name but different tests/filters defined inside. +- ``import_role`` reverts to previous behavior of exporting vars at compile time. +- ansible-galaxy - Provide a better error message when using a requirements file with an invalid format - https://github.com/ansible/ansible/issues/81901 +- ansible-inventory - index available_hosts for major performance boost when dumping large inventories +- ansible-test - Fix parsing of cgroup entries which contain a ``:`` in the path (https://github.com/ansible/ansible/issues/81977). + v2.15.5 ======= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/changelogs/changelog.yaml new/ansible-core-2.15.6/changelogs/changelog.yaml --- old/ansible-core-2.15.5/changelogs/changelog.yaml 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/changelogs/changelog.yaml 2023-11-06 18:56:48.000000000 +0100 @@ -1180,3 +1180,51 @@ - role-deduplication-condition.yml - winrm-send-input.yml release_date: '2023-10-03' + 2.15.6: + changes: + release_summary: '| Release Date: 2023-11-06 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.15/porting_guides/porting_guide_core_2.15.html>`__ + + ' + codename: Ten Years Gone + fragments: + - 2.15.6_summary.yaml + release_date: '2023-11-06' + 2.15.6rc1: + changes: + bugfixes: + - Fix ``run_once`` being incorrectly interpreted on handlers (https://github.com/ansible/ansible/issues/81666) + - Plugin loader does not dedupe nor cache filter/test plugins by file basename, + but full path name. + - Properly template tags in parent blocks (https://github.com/ansible/ansible/issues/81053) + - Restoring the ability of filters/tests can have same file base name but different + tests/filters defined inside. + - '``import_role`` reverts to previous behavior of exporting vars at compile + time.' + - ansible-galaxy - Provide a better error message when using a requirements + file with an invalid format - https://github.com/ansible/ansible/issues/81901 + - ansible-inventory - index available_hosts for major performance boost when + dumping large inventories + - ansible-test - Fix parsing of cgroup entries which contain a ``:`` in the + path (https://github.com/ansible/ansible/issues/81977). + minor_changes: + - ansible-test - Windows 2012 and 2012-R2 instances are now requested from Azure + instead of AWS. + release_summary: '| Release Date: 2023-10-30 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.15/porting_guides/porting_guide_core_2.15.html>`__ + + ' + codename: Ten Years Gone + fragments: + - 2.15.6rc1_summary.yaml + - 81053-templated-tags-inheritance.yml + - 81666-handlers-run_once.yml + - 81901-galaxy-requirements-format.yml + - ansible-test-cgroup-split.yml + - ansible-test-windows-2012-and-2012-R2.yml + - import_role_goes_public.yml + - inv_available_hosts_to_frozenset.yml + - j2_load_fix.yml + release_date: '2023-10-30' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/cli/galaxy.py new/ansible-core-2.15.6/lib/ansible/cli/galaxy.py --- old/ansible-core-2.15.5/lib/ansible/cli/galaxy.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/cli/galaxy.py 2023-11-06 18:56:48.000000000 +0100 @@ -805,7 +805,7 @@ for role_req in file_requirements: requirements['roles'] += parse_role_req(role_req) - else: + elif isinstance(file_requirements, dict): # Newer format with a collections and/or roles key extra_keys = set(file_requirements.keys()).difference(set(['roles', 'collections'])) if extra_keys: @@ -824,6 +824,9 @@ for collection_req in file_requirements.get('collections') or [] ] + else: + raise AnsibleError(f"Expecting requirements yaml to be a list or dictionary but got {type(file_requirements).__name__}") + return requirements def _init_coll_req_dict(self, coll_req): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/cli/inventory.py new/ansible-core-2.15.6/lib/ansible/cli/inventory.py --- old/ansible-core-2.15.5/lib/ansible/cli/inventory.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/cli/inventory.py 2023-11-06 18:56:48.000000000 +0100 @@ -325,7 +325,7 @@ return results hosts = self.inventory.get_hosts(top.name) - results = format_group(top, [h.name for h in hosts]) + results = format_group(top, frozenset(h.name for h in hosts)) # populate meta results['_meta'] = {'hostvars': {}} @@ -381,7 +381,7 @@ return results - available_hosts = [h.name for h in self.inventory.get_hosts(top.name)] + available_hosts = frozenset(h.name for h in self.inventory.get_hosts(top.name)) return format_group(top, available_hosts) def toml_inventory(self, top): @@ -425,7 +425,7 @@ return results - available_hosts = [h.name for h in self.inventory.get_hosts(top.name)] + available_hosts = frozenset(h.name for h in self.inventory.get_hosts(top.name)) results = format_group(top, available_hosts) return results diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/module_utils/ansible_release.py new/ansible-core-2.15.6/lib/ansible/module_utils/ansible_release.py --- old/ansible-core-2.15.5/lib/ansible/module_utils/ansible_release.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/module_utils/ansible_release.py 2023-11-06 18:56:48.000000000 +0100 @@ -19,6 +19,6 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -__version__ = '2.15.5' +__version__ = '2.15.6' __author__ = 'Ansible, Inc.' __codename__ = "Ten Years Gone" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/playbook/handler.py new/ansible-core-2.15.6/lib/ansible/playbook/handler.py --- old/ansible-core-2.15.5/lib/ansible/playbook/handler.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/playbook/handler.py 2023-11-06 18:56:48.000000000 +0100 @@ -53,6 +53,9 @@ def remove_host(self, host): self.notified_hosts = [h for h in self.notified_hosts if h != host] + def clear_hosts(self): + self.notified_hosts = [] + def is_host_notified(self, host): return host in self.notified_hosts diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/playbook/role_include.py new/ansible-core-2.15.6/lib/ansible/playbook/role_include.py --- old/ansible-core-2.15.5/lib/ansible/playbook/role_include.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/playbook/role_include.py 2023-11-06 18:56:48.000000000 +0100 @@ -49,10 +49,10 @@ # ================================================================================= # ATTRIBUTES + public = NonInheritableFieldAttribute(isa='bool', default=None, private=False, always_post_validate=True) # private as this is a 'module options' vs a task property allow_duplicates = NonInheritableFieldAttribute(isa='bool', default=True, private=True, always_post_validate=True) - public = NonInheritableFieldAttribute(isa='bool', default=False, private=True, always_post_validate=True) rolespec_validate = NonInheritableFieldAttribute(isa='bool', default=True, private=True, always_post_validate=True) def __init__(self, block=None, role=None, task_include=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/playbook/taggable.py new/ansible-core-2.15.6/lib/ansible/playbook/taggable.py --- old/ansible-core-2.15.5/lib/ansible/playbook/taggable.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/playbook/taggable.py 2023-11-06 18:56:48.000000000 +0100 @@ -23,6 +23,17 @@ from ansible.module_utils.six import string_types from ansible.playbook.attribute import FieldAttribute from ansible.template import Templar +from ansible.utils.sentinel import Sentinel + + +def _flatten_tags(tags: list) -> list: + rv = set() + for tag in tags: + if isinstance(tag, list): + rv.update(tag) + else: + rv.add(tag) + return list(rv) class Taggable: @@ -34,11 +45,7 @@ if isinstance(ds, list): return ds elif isinstance(ds, string_types): - value = ds.split(',') - if isinstance(value, list): - return [x.strip() for x in value] - else: - return [ds] + return [x.strip() for x in ds.split(',')] else: raise AnsibleError('tags must be specified as a list', obj=ds) @@ -47,16 +54,12 @@ if self.tags: templar = Templar(loader=self._loader, variables=all_vars) - tags = templar.template(self.tags) - - _temp_tags = set() - for tag in tags: - if isinstance(tag, list): - _temp_tags.update(tag) - else: - _temp_tags.add(tag) - tags = _temp_tags - self.tags = list(tags) + obj = self + while obj is not None: + if (_tags := getattr(obj, "_tags", Sentinel)) is not Sentinel: + obj._tags = _flatten_tags(templar.template(_tags)) + obj = obj._parent + tags = set(self.tags) else: # this makes isdisjoint work for untagged tags = self.untagged diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/plugins/loader.py new/ansible-core-2.15.6/lib/ansible/plugins/loader.py --- old/ansible-core-2.15.5/lib/ansible/plugins/loader.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/plugins/loader.py 2023-11-06 18:56:48.000000000 +0100 @@ -986,23 +986,32 @@ loaded_modules = set() for path in all_matches: + name = os.path.splitext(path)[0] basename = os.path.basename(name) + is_j2 = isinstance(self, Jinja2Loader) + + if is_j2: + ref_name = path + else: + ref_name = basename - if basename in _PLUGIN_FILTERS[self.package]: + if not is_j2 and basename in _PLUGIN_FILTERS[self.package]: + # j2 plugins get processed in own class, here they would just be container files display.debug("'%s' skipped due to a defined plugin filter" % basename) continue if basename == '__init__' or (basename == 'base' and self.package == 'ansible.plugins.cache'): # cache has legacy 'base.py' file, which is wrapper for __init__.py - display.debug("'%s' skipped due to reserved name" % basename) + display.debug("'%s' skipped due to reserved name" % name) continue - if dedupe and basename in loaded_modules: - display.debug("'%s' skipped as duplicate" % basename) + if dedupe and ref_name in loaded_modules: + # for j2 this is 'same file', other plugins it is basename + display.debug("'%s' skipped as duplicate" % ref_name) continue - loaded_modules.add(basename) + loaded_modules.add(ref_name) if path_only: yield path diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/plugins/strategy/__init__.py new/ansible-core-2.15.6/lib/ansible/plugins/strategy/__init__.py --- old/ansible-core-2.15.5/lib/ansible/plugins/strategy/__init__.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/plugins/strategy/__init__.py 2023-11-06 18:56:48.000000000 +0100 @@ -803,10 +803,6 @@ ret_results.append(task_result) - if isinstance(original_task, Handler): - for handler in (h for b in iterator._play.handlers for h in b.block if h._uuid == original_task._uuid): - handler.remove_host(original_host) - if one_pass or max_passes is not None and (cur_pass + 1) >= max_passes: break @@ -1094,9 +1090,6 @@ header = skip_reason if skipped else msg display.vv(f"META: {header}") - if isinstance(task, Handler): - task.remove_host(target_host) - res = TaskResult(target_host, task, result) if skipped: self._tqm.send_callback('v2_runner_on_skipped', res) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/plugins/strategy/free.py new/ansible-core-2.15.6/lib/ansible/plugins/strategy/free.py --- old/ansible-core-2.15.5/lib/ansible/plugins/strategy/free.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/plugins/strategy/free.py 2023-11-06 18:56:48.000000000 +0100 @@ -146,6 +146,8 @@ # advance the host, mark the host blocked, and queue it self._blocked_hosts[host_name] = True iterator.set_state_for_host(host.name, state) + if isinstance(task, Handler): + task.remove_host(host) try: action = action_loader.get(task.action, class_only=True, collection_list=task.collections) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/plugins/strategy/linear.py new/ansible-core-2.15.6/lib/ansible/plugins/strategy/linear.py --- old/ansible-core-2.15.5/lib/ansible/plugins/strategy/linear.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/plugins/strategy/linear.py 2023-11-06 18:56:48.000000000 +0100 @@ -242,6 +242,12 @@ self._queue_task(host, task, task_vars, play_context) del task_vars + if isinstance(task, Handler): + if run_once: + task.clear_hosts() + else: + task.remove_host(host) + # if we're bypassing the host loop, break out now if run_once: break diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/release.py new/ansible-core-2.15.6/lib/ansible/release.py --- old/ansible-core-2.15.5/lib/ansible/release.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/release.py 2023-11-06 18:56:48.000000000 +0100 @@ -19,6 +19,6 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -__version__ = '2.15.5' +__version__ = '2.15.6' __author__ = 'Ansible, Inc.' __codename__ = "Ten Years Gone" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible/vars/manager.py new/ansible-core-2.15.6/lib/ansible/vars/manager.py --- old/ansible-core-2.15.5/lib/ansible/vars/manager.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible/vars/manager.py 2023-11-06 18:56:48.000000000 +0100 @@ -197,13 +197,13 @@ if play: if not C.DEFAULT_PRIVATE_ROLE_VARS: - # first we compile any vars specified in defaults/main.yml - # for all roles within the specified play for role in play.get_roles(): - # role from roles or include_role+public or import_role and completed - if not role.from_include or role.public or (role.static and role._completed.get(to_text(host), False)): + # role is public and + # either static or dynamic and completed + # role is not set + # use config option as default + if role.static or role.public and role._completed.get(host.name, False): all_vars = _combine_and_track(all_vars, role.get_default_vars(), "role '%s' defaults" % role.name) - if task: # set basedirs if C.PLAYBOOK_VARS_ROOT == 'all': # should be default @@ -386,11 +386,15 @@ raise AnsibleParserError("Error while reading vars files - please supply a list of file names. " "Got '%s' of type %s" % (vars_files, type(vars_files))) - # By default, we now merge in all exported vars from all roles in the play, - # unless the user has disabled this via a config option + # We now merge in all exported vars from all roles in the play, + # unless the user has disabled this + # role is public and + # either static or dynamic and completed + # role is not set + # use config option as default if not C.DEFAULT_PRIVATE_ROLE_VARS: for role in play.get_roles(): - if not role.from_include or role.public or (role.static and role._completed.get(to_text(host), False)): + if role.static or role.public and role._completed.get(host.name, False): all_vars = _combine_and_track(all_vars, role.get_vars(include_params=False, only_exports=True), "role '%s' exported vars" % role.name) # next, we merge in the vars from the role, which will specifically diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible_core.egg-info/PKG-INFO new/ansible-core-2.15.6/lib/ansible_core.egg-info/PKG-INFO --- old/ansible-core-2.15.5/lib/ansible_core.egg-info/PKG-INFO 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible_core.egg-info/PKG-INFO 2023-11-06 18:56:48.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ansible-core -Version: 2.15.5 +Version: 2.15.6 Summary: Radically simple IT automation Home-page: https://ansible.com/ Author: Ansible, Inc. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/lib/ansible_core.egg-info/SOURCES.txt new/ansible-core-2.15.6/lib/ansible_core.egg-info/SOURCES.txt --- old/ansible-core-2.15.5/lib/ansible_core.egg-info/SOURCES.txt 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/lib/ansible_core.egg-info/SOURCES.txt 2023-11-06 18:56:48.000000000 +0100 @@ -2018,6 +2018,7 @@ test/integration/targets/handlers/test_notify_included.yml test/integration/targets/handlers/test_role_as_handler.yml test/integration/targets/handlers/test_role_handlers_including_tasks.yml +test/integration/targets/handlers/test_run_once.yml test/integration/targets/handlers/test_skip_flush.yml test/integration/targets/handlers/test_templating_in_handlers.yml test/integration/targets/handlers/roles/import_template_handler_names/tasks/main.yml @@ -3052,6 +3053,12 @@ test/integration/targets/plugin_loader/aliases test/integration/targets/plugin_loader/runme.sh test/integration/targets/plugin_loader/use_coll_name.yml +test/integration/targets/plugin_loader/file_collision/play.yml +test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/custom.py +test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter1.yml +test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter3.yml +test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/custom.py +test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/filter2.yml test/integration/targets/plugin_loader/normal/filters.yml test/integration/targets/plugin_loader/normal/self_referential.yml test/integration/targets/plugin_loader/normal/underscore.yml @@ -3421,6 +3428,7 @@ test/integration/targets/tags/ansible_run_tags.yml test/integration/targets/tags/runme.sh test/integration/targets/tags/test_tags.yml +test/integration/targets/tags/test_template_parent_tags.yml test/integration/targets/task_ordering/aliases test/integration/targets/task_ordering/meta/main.yml test/integration/targets/task_ordering/tasks/main.yml @@ -4200,7 +4208,6 @@ test/sanity/code-smell/update-bundled.requirements.txt test/support/README.md test/support/integration/plugins/filter/json_query.py -test/support/integration/plugins/modules/htpasswd.py test/support/integration/plugins/modules/pkgng.py test/support/integration/plugins/modules/sefcontext.py test/support/integration/plugins/modules/timezone.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/handlers/runme.sh new/ansible-core-2.15.6/test/integration/targets/handlers/runme.sh --- old/ansible-core-2.15.5/test/integration/targets/handlers/runme.sh 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/handlers/runme.sh 2023-11-06 18:56:48.000000000 +0100 @@ -195,3 +195,6 @@ ansible-playbook test_include_tasks_in_include_role.yml "$@" 2>&1 | tee out.txt [ "$(grep out.txt -ce 'handler ran')" = "1" ] + +ansible-playbook test_run_once.yml -i inventory.handlers "$@" 2>&1 | tee out.txt +[ "$(grep out.txt -ce 'handler ran once')" = "1" ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/handlers/test_run_once.yml new/ansible-core-2.15.6/test/integration/targets/handlers/test_run_once.yml --- old/ansible-core-2.15.5/test/integration/targets/handlers/test_run_once.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible-core-2.15.6/test/integration/targets/handlers/test_run_once.yml 2023-11-06 18:56:48.000000000 +0100 @@ -0,0 +1,10 @@ +- hosts: A,B,C + gather_facts: false + tasks: + - command: echo + notify: handler + handlers: + - name: handler + run_once: true + debug: + msg: handler ran once diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/include_import/public_exposure/no_bleeding.yml new/ansible-core-2.15.6/test/integration/targets/include_import/public_exposure/no_bleeding.yml --- old/ansible-core-2.15.5/test/integration/targets/include_import/public_exposure/no_bleeding.yml 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/include_import/public_exposure/no_bleeding.yml 2023-11-06 18:56:48.000000000 +0100 @@ -5,8 +5,8 @@ - name: Static imports should expose vars at parse time, not at execution time assert: that: - - static_defaults_var is not defined - - static_vars_var is not defined + - static_defaults_var == 'static_defaults' + - static_vars_var == 'static_vars' - import_role: name: static - assert: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/include_import/public_exposure/playbook.yml new/ansible-core-2.15.6/test/integration/targets/include_import/public_exposure/playbook.yml --- old/ansible-core-2.15.5/test/integration/targets/include_import/public_exposure/playbook.yml 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/include_import/public_exposure/playbook.yml 2023-11-06 18:56:48.000000000 +0100 @@ -10,16 +10,12 @@ - name: Static imports should expose vars at parse time, not at execution time assert: that: - - static_defaults_var is not defined - - static_vars_var is not defined - - static_task_var is not defined + - static_defaults_var == 'static_defaults' + - static_vars_var == 'static_vars' - import_role: name: static - assert: that: - - static_vars_var is defined - - static_tasks_var is defined - - static_defaults_var is defined - static_tasks_var == 'static_tasks' - static_defaults_var == 'static_defaults' - static_vars_var == 'static_vars' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/play.yml new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/play.yml --- old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/play.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/play.yml 2023-11-06 18:56:48.000000000 +0100 @@ -0,0 +1,7 @@ +- hosts: localhost + gather_facts: false + roles: + - r1 + - r2 + tasks: + - debug: msg={{'a'|filter1|filter2|filter3}} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/custom.py new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/custom.py --- old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/custom.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/custom.py 2023-11-06 18:56:48.000000000 +0100 @@ -0,0 +1,15 @@ +from __future__ import annotations + + +def do_nothing(myval): + return myval + + +class FilterModule(object): + ''' Ansible core jinja2 filters ''' + + def filters(self): + return { + 'filter1': do_nothing, + 'filter3': do_nothing, + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter1.yml new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter1.yml --- old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter1.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter1.yml 2023-11-06 18:56:48.000000000 +0100 @@ -0,0 +1,18 @@ +DOCUMENTATION: + name: filter1 + version_added: "1.9" + short_description: Does nothing + description: + - Really, does nothing + notes: + - This is a test filter + positional: _input + options: + _input: + description: the input + required: true + +EXAMPLES: '' +RETURN: + _value: + description: The input diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter3.yml new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter3.yml --- old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter3.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter3.yml 2023-11-06 18:56:48.000000000 +0100 @@ -0,0 +1,18 @@ +DOCUMENTATION: + name: filter3 + version_added: "1.9" + short_description: Does nothing + description: + - Really, does nothing + notes: + - This is a test filter + positional: _input + options: + _input: + description: the input + required: true + +EXAMPLES: '' +RETURN: + _value: + description: The input diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/custom.py new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/custom.py --- old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/custom.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/custom.py 2023-11-06 18:56:48.000000000 +0100 @@ -0,0 +1,14 @@ +from __future__ import annotations + + +def do_nothing(myval): + return myval + + +class FilterModule(object): + ''' Ansible core jinja2 filters ''' + + def filters(self): + return { + 'filter2': do_nothing, + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/filter2.yml new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/filter2.yml --- old/ansible-core-2.15.5/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/filter2.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible-core-2.15.6/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/filter2.yml 2023-11-06 18:56:48.000000000 +0100 @@ -0,0 +1,18 @@ +DOCUMENTATION: + name: filter2 + version_added: "1.9" + short_description: Does nothing + description: + - Really, does nothing + notes: + - This is a test filter + positional: _input + options: + _input: + description: the input + required: true + +EXAMPLES: '' +RETURN: + _value: + description: The input diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/plugin_loader/runme.sh new/ansible-core-2.15.6/test/integration/targets/plugin_loader/runme.sh --- old/ansible-core-2.15.5/test/integration/targets/plugin_loader/runme.sh 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/plugin_loader/runme.sh 2023-11-06 18:56:48.000000000 +0100 @@ -34,3 +34,6 @@ # test config loading ansible-playbook use_coll_name.yml -i ../../inventory -e 'ansible_connection=ansible.builtin.ssh' "$@" + +# test filter loading ignoring duplicate file basename +ansible-playbook file_collision/play.yml "$@" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/roles/vars_scope.yml new/ansible-core-2.15.6/test/integration/targets/roles/vars_scope.yml --- old/ansible-core-2.15.5/test/integration/targets/roles/vars_scope.yml 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/roles/vars_scope.yml 2023-11-06 18:56:48.000000000 +0100 @@ -63,8 +63,7 @@ play_only: play roles_and_role_vars: role_vars role_vars_only: role_vars - -- name: play and import +- name: play baseline (no roles) hosts: localhost gather_facts: false vars_files: @@ -82,6 +81,30 @@ play_and_role_vars: play play_only: play +- name: play and import + hosts: localhost + gather_facts: false + vars_files: + - vars/play.yml + tasks: + - include_tasks: roles/vars_scope/tasks/check_vars.yml + vars: + defined: + play_and_import: play + play_and_include: play + play_and_roles: play + play_only: play + default_only: default + import_and_role_vars: role_vars + include_and_role_vars: role_vars + play_and_import_and_role_vars: role_vars + play_and_role_vars: role_vars + play_and_role_vars_and_role_vars: role_vars + play_and_include_and_role_vars: role_vars + play_and_roles_and_role_vars: role_vars + roles_and_role_vars: role_vars + role_vars_only: role_vars + - name: static import import_role: name: vars_scope diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/subversion/aliases new/ansible-core-2.15.6/test/integration/targets/subversion/aliases --- old/ansible-core-2.15.5/test/integration/targets/subversion/aliases 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/subversion/aliases 2023-11-06 18:56:48.000000000 +0100 @@ -1,4 +1,3 @@ -setup/always/setup_passlib shippable/posix/group2 skip/macos destructive diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/subversion/roles/subversion/tasks/cleanup.yml new/ansible-core-2.15.6/test/integration/targets/subversion/roles/subversion/tasks/cleanup.yml --- old/ansible-core-2.15.5/test/integration/targets/subversion/roles/subversion/tasks/cleanup.yml 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/subversion/roles/subversion/tasks/cleanup.yml 2023-11-06 18:56:48.000000000 +0100 @@ -1,8 +1,18 @@ --- -- name: stop apache after tests - shell: "kill -9 $(cat '{{ subversion_server_dir }}/apache.pid')" +- name: stop apache after tests - non Red Hat + shell: apachectl -k stop -f {{ subversion_server_dir }}/subversion.conf + when: ansible_os_family not in ['RedHat'] + +- name: stop apache after tests - Red Hat + shell: "kill $(cat '{{ subversion_server_dir }}/apache.pid')" + when: ansible_os_family in ['RedHat'] - name: remove tmp subversion server dir file: path: '{{ subversion_server_dir }}' state: absent + +- name: remove tmp subversion checkout dir + file: + path: '{{ subversion_test_dir }}' + state: absent diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/subversion/roles/subversion/tasks/setup.yml new/ansible-core-2.15.6/test/integration/targets/subversion/roles/subversion/tasks/setup.yml --- old/ansible-core-2.15.5/test/integration/targets/subversion/roles/subversion/tasks/setup.yml 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/subversion/roles/subversion/tasks/setup.yml 2023-11-06 18:56:48.000000000 +0100 @@ -45,11 +45,7 @@ creates: '{{ subversion_server_dir }}/{{ subversion_repo_name }}' - name: add test user to htpasswd for Subversion site - htpasswd: - path: '{{ subversion_server_dir }}/svn-auth-users' - name: '{{ subversion_username }}' - password: '{{ subversion_password }}' - state: present + command: htpasswd -bc {{ subversion_server_dir + '/svn-auth-users' | quote }} {{ subversion_username | quote }} {{ subversion_password | quote }} - name: apply ownership for all SVN directories file: @@ -62,11 +58,11 @@ command: apachectl -k start -f {{ subversion_server_dir }}/subversion.conf async: 3600 # We kill apache manually in the clean up phase poll: 0 - when: ansible_os_family not in ['RedHat', 'Alpine'] + when: ansible_os_family not in ['RedHat'] # On Red Hat based OS', we can't use apachectl to start up own instance, just use the raw httpd - name: start test Apache SVN site - Red Hat command: httpd -k start -f {{ subversion_server_dir }}/subversion.conf async: 3600 # We kill apache manually in the clean up phase poll: 0 - when: ansible_os_family in ['RedHat', 'Alpine'] + when: ansible_os_family in ['RedHat'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/subversion/vars/Alpine.yml new/ansible-core-2.15.6/test/integration/targets/subversion/vars/Alpine.yml --- old/ansible-core-2.15.5/test/integration/targets/subversion/vars/Alpine.yml 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/subversion/vars/Alpine.yml 2023-11-06 18:56:48.000000000 +0100 @@ -3,5 +3,7 @@ - subversion - mod_dav_svn - apache2-webdav +- apache2-utils +- apache2-ctl apache_user: apache apache_group: apache diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/tags/runme.sh new/ansible-core-2.15.6/test/integration/targets/tags/runme.sh --- old/ansible-core-2.15.5/test/integration/targets/tags/runme.sh 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/integration/targets/tags/runme.sh 2023-11-06 18:56:48.000000000 +0100 @@ -73,3 +73,12 @@ ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=untagged --tags untagged "$@" ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=untagged_list --tags untagged,tag3 "$@" ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=tagged --tags tagged "$@" + +ansible-playbook test_template_parent_tags.yml "$@" 2>&1 | tee out.txt +[ "$(grep out.txt -ce 'Tagged_task')" = "1" ]; rm out.txt + +ansible-playbook test_template_parent_tags.yml --tags tag1 "$@" 2>&1 | tee out.txt +[ "$(grep out.txt -ce 'Tagged_task')" = "1" ]; rm out.txt + +ansible-playbook test_template_parent_tags.yml --skip-tags tag1 "$@" 2>&1 | tee out.txt +[ "$(grep out.txt -ce 'Tagged_task')" = "0" ]; rm out.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/integration/targets/tags/test_template_parent_tags.yml new/ansible-core-2.15.6/test/integration/targets/tags/test_template_parent_tags.yml --- old/ansible-core-2.15.5/test/integration/targets/tags/test_template_parent_tags.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible-core-2.15.6/test/integration/targets/tags/test_template_parent_tags.yml 2023-11-06 18:56:48.000000000 +0100 @@ -0,0 +1,10 @@ +- hosts: localhost + gather_facts: false + vars: + tags_in_var: + - tag1 + tasks: + - block: + - name: Tagged_task + debug: + tags: "{{ tags_in_var }}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/lib/ansible_test/_data/completion/windows.txt new/ansible-core-2.15.6/test/lib/ansible_test/_data/completion/windows.txt --- old/ansible-core-2.15.5/test/lib/ansible_test/_data/completion/windows.txt 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/lib/ansible_test/_data/completion/windows.txt 2023-11-06 18:56:48.000000000 +0100 @@ -1,5 +1,5 @@ -windows/2012 provider=aws arch=x86_64 -windows/2012-R2 provider=aws arch=x86_64 +windows/2012 provider=azure arch=x86_64 +windows/2012-R2 provider=azure arch=x86_64 windows/2016 provider=aws arch=x86_64 windows/2019 provider=aws arch=x86_64 windows/2022 provider=aws arch=x86_64 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/lib/ansible_test/_internal/cgroup.py new/ansible-core-2.15.6/test/lib/ansible_test/_internal/cgroup.py --- old/ansible-core-2.15.5/test/lib/ansible_test/_internal/cgroup.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/lib/ansible_test/_internal/cgroup.py 2023-11-06 18:56:48.000000000 +0100 @@ -44,7 +44,7 @@ @classmethod def parse(cls, value: str) -> CGroupEntry: """Parse the given cgroup line from the proc filesystem and return a cgroup entry.""" - cid, subsystem, path = value.split(':') + cid, subsystem, path = value.split(':', maxsplit=2) return cls( id=int(cid), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/support/integration/plugins/modules/htpasswd.py new/ansible-core-2.15.6/test/support/integration/plugins/modules/htpasswd.py --- old/ansible-core-2.15.5/test/support/integration/plugins/modules/htpasswd.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/support/integration/plugins/modules/htpasswd.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,275 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2013, Nimbis Services, Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = """ -module: htpasswd -version_added: "1.3" -short_description: manage user files for basic authentication -description: - - Add and remove username/password entries in a password file using htpasswd. - - This is used by web servers such as Apache and Nginx for basic authentication. -options: - path: - required: true - aliases: [ dest, destfile ] - description: - - Path to the file that contains the usernames and passwords - name: - required: true - aliases: [ username ] - description: - - User name to add or remove - password: - required: false - description: - - Password associated with user. - - Must be specified if user does not exist yet. - crypt_scheme: - required: false - choices: ["apr_md5_crypt", "des_crypt", "ldap_sha1", "plaintext"] - default: "apr_md5_crypt" - description: - - Encryption scheme to be used. As well as the four choices listed - here, you can also use any other hash supported by passlib, such as - md5_crypt and sha256_crypt, which are linux passwd hashes. If you - do so the password file will not be compatible with Apache or Nginx - state: - required: false - choices: [ present, absent ] - default: "present" - description: - - Whether the user entry should be present or not - create: - required: false - type: bool - default: "yes" - description: - - Used with C(state=present). If specified, the file will be created - if it does not already exist. If set to "no", will fail if the - file does not exist -notes: - - "This module depends on the I(passlib) Python library, which needs to be installed on all target systems." - - "On Debian, Ubuntu, or Fedora: install I(python-passlib)." - - "On RHEL or CentOS: Enable EPEL, then install I(python-passlib)." -requirements: [ passlib>=1.6 ] -author: "Ansible Core Team" -extends_documentation_fragment: files -""" - -EXAMPLES = """ -# Add a user to a password file and ensure permissions are set -- htpasswd: - path: /etc/nginx/passwdfile - name: janedoe - password: '9s36?;fyNp' - owner: root - group: www-data - mode: 0640 - -# Remove a user from a password file -- htpasswd: - path: /etc/apache2/passwdfile - name: foobar - state: absent - -# Add a user to a password file suitable for use by libpam-pwdfile -- htpasswd: - path: /etc/mail/passwords - name: alex - password: oedu2eGh - crypt_scheme: md5_crypt -""" - - -import os -import tempfile -import traceback -from ansible.module_utils.compat.version import LooseVersion -from ansible.module_utils.basic import AnsibleModule, missing_required_lib -from ansible.module_utils._text import to_native - -PASSLIB_IMP_ERR = None -try: - from passlib.apache import HtpasswdFile, htpasswd_context - from passlib.context import CryptContext - import passlib -except ImportError: - PASSLIB_IMP_ERR = traceback.format_exc() - passlib_installed = False -else: - passlib_installed = True - -apache_hashes = ["apr_md5_crypt", "des_crypt", "ldap_sha1", "plaintext"] - - -def create_missing_directories(dest): - destpath = os.path.dirname(dest) - if not os.path.exists(destpath): - os.makedirs(destpath) - - -def present(dest, username, password, crypt_scheme, create, check_mode): - """ Ensures user is present - - Returns (msg, changed) """ - if crypt_scheme in apache_hashes: - context = htpasswd_context - else: - context = CryptContext(schemes=[crypt_scheme] + apache_hashes) - if not os.path.exists(dest): - if not create: - raise ValueError('Destination %s does not exist' % dest) - if check_mode: - return ("Create %s" % dest, True) - create_missing_directories(dest) - if LooseVersion(passlib.__version__) >= LooseVersion('1.6'): - ht = HtpasswdFile(dest, new=True, default_scheme=crypt_scheme, context=context) - else: - ht = HtpasswdFile(dest, autoload=False, default=crypt_scheme, context=context) - if getattr(ht, 'set_password', None): - ht.set_password(username, password) - else: - ht.update(username, password) - ht.save() - return ("Created %s and added %s" % (dest, username), True) - else: - if LooseVersion(passlib.__version__) >= LooseVersion('1.6'): - ht = HtpasswdFile(dest, new=False, default_scheme=crypt_scheme, context=context) - else: - ht = HtpasswdFile(dest, default=crypt_scheme, context=context) - - found = None - if getattr(ht, 'check_password', None): - found = ht.check_password(username, password) - else: - found = ht.verify(username, password) - - if found: - return ("%s already present" % username, False) - else: - if not check_mode: - if getattr(ht, 'set_password', None): - ht.set_password(username, password) - else: - ht.update(username, password) - ht.save() - return ("Add/update %s" % username, True) - - -def absent(dest, username, check_mode): - """ Ensures user is absent - - Returns (msg, changed) """ - if LooseVersion(passlib.__version__) >= LooseVersion('1.6'): - ht = HtpasswdFile(dest, new=False) - else: - ht = HtpasswdFile(dest) - - if username not in ht.users(): - return ("%s not present" % username, False) - else: - if not check_mode: - ht.delete(username) - ht.save() - return ("Remove %s" % username, True) - - -def check_file_attrs(module, changed, message): - - file_args = module.load_file_common_arguments(module.params) - if module.set_fs_attributes_if_different(file_args, False): - - if changed: - message += " and " - changed = True - message += "ownership, perms or SE linux context changed" - - return message, changed - - -def main(): - arg_spec = dict( - path=dict(required=True, aliases=["dest", "destfile"]), - name=dict(required=True, aliases=["username"]), - password=dict(required=False, default=None, no_log=True), - crypt_scheme=dict(required=False, default="apr_md5_crypt"), - state=dict(required=False, default="present"), - create=dict(type='bool', default='yes'), - - ) - module = AnsibleModule(argument_spec=arg_spec, - add_file_common_args=True, - supports_check_mode=True) - - path = module.params['path'] - username = module.params['name'] - password = module.params['password'] - crypt_scheme = module.params['crypt_scheme'] - state = module.params['state'] - create = module.params['create'] - check_mode = module.check_mode - - if not passlib_installed: - module.fail_json(msg=missing_required_lib("passlib"), exception=PASSLIB_IMP_ERR) - - # Check file for blank lines in effort to avoid "need more than 1 value to unpack" error. - try: - f = open(path, "r") - except IOError: - # No preexisting file to remove blank lines from - f = None - else: - try: - lines = f.readlines() - finally: - f.close() - - # If the file gets edited, it returns true, so only edit the file if it has blank lines - strip = False - for line in lines: - if not line.strip(): - strip = True - break - - if strip: - # If check mode, create a temporary file - if check_mode: - temp = tempfile.NamedTemporaryFile() - path = temp.name - f = open(path, "w") - try: - [f.write(line) for line in lines if line.strip()] - finally: - f.close() - - try: - if state == 'present': - (msg, changed) = present(path, username, password, crypt_scheme, create, check_mode) - elif state == 'absent': - if not os.path.exists(path): - module.exit_json(msg="%s not present" % username, - warnings="%s does not exist" % path, changed=False) - (msg, changed) = absent(path, username, check_mode) - else: - module.fail_json(msg="Invalid state: %s" % state) - - check_file_attrs(module, changed, msg) - module.exit_json(msg=msg, changed=changed) - except Exception as e: - module.fail_json(msg=to_native(e)) - - -if __name__ == '__main__': - main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/units/cli/test_galaxy.py new/ansible-core-2.15.6/test/units/cli/test_galaxy.py --- old/ansible-core-2.15.5/test/units/cli/test_galaxy.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/units/cli/test_galaxy.py 2023-11-06 18:56:48.000000000 +0100 @@ -753,6 +753,20 @@ assert mock_install.call_args[0][6] is False # force_deps +def test_collection_install_with_invalid_requirements_format(collection_install): + output_dir = collection_install[2] + + requirements_file = os.path.join(output_dir, 'requirements.yml') + with open(requirements_file, 'wb') as req_obj: + req_obj.write(b'"invalid"') + + galaxy_args = ['ansible-galaxy', 'collection', 'install', '--requirements-file', requirements_file, + '--collections-path', output_dir] + + with pytest.raises(AnsibleError, match="Expecting requirements yaml to be a list or dictionary but got str"): + GalaxyCLI(args=galaxy_args).run() + + def test_collection_install_with_requirements_file(collection_install): mock_install, mock_warning, output_dir = collection_install diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/units/playbook/test_taggable.py new/ansible-core-2.15.6/test/units/playbook/test_taggable.py --- old/ansible-core-2.15.5/test/units/playbook/test_taggable.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/units/playbook/test_taggable.py 2023-11-06 18:56:48.000000000 +0100 @@ -29,6 +29,7 @@ def __init__(self): self._loader = DictDataLoader({}) self.tags = [] + self._parent = None class TestTaggable(unittest.TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-core-2.15.5/test/units/plugins/connection/test_winrm.py new/ansible-core-2.15.6/test/units/plugins/connection/test_winrm.py --- old/ansible-core-2.15.5/test/units/plugins/connection/test_winrm.py 2023-10-09 17:51:22.000000000 +0200 +++ new/ansible-core-2.15.6/test/units/plugins/connection/test_winrm.py 2023-11-06 18:56:48.000000000 +0100 @@ -13,8 +13,8 @@ from io import StringIO from unittest.mock import MagicMock -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_bytes +from ansible.errors import AnsibleConnectionFailure, AnsibleError +from ansible.module_utils.common.text.converters import to_bytes from ansible.playbook.play_context import PlayContext from ansible.plugins.loader import connection_loader from ansible.plugins.connection import winrm @@ -441,3 +441,123 @@ assert str(err.value) == \ "Kerberos auth failure for principal username with pexpect: " \ "Error with kinit\n<redacted>" + + def test_exec_command_with_timeout(self, monkeypatch): + requests_exc = pytest.importorskip("requests.exceptions") + + pc = PlayContext() + new_stdin = StringIO() + conn = connection_loader.get('winrm', pc, new_stdin) + + mock_proto = MagicMock() + mock_proto.run_command.side_effect = requests_exc.Timeout("msg") + + conn._connected = True + conn._winrm_host = 'hostname' + + monkeypatch.setattr(conn, "_winrm_connect", lambda: mock_proto) + + with pytest.raises(AnsibleConnectionFailure) as e: + conn.exec_command('cmd', in_data=None, sudoable=True) + + assert str(e.value) == "winrm connection error: msg" + + def test_exec_command_get_output_timeout(self, monkeypatch): + requests_exc = pytest.importorskip("requests.exceptions") + + pc = PlayContext() + new_stdin = StringIO() + conn = connection_loader.get('winrm', pc, new_stdin) + + mock_proto = MagicMock() + mock_proto.run_command.return_value = "command_id" + mock_proto.get_command_output.side_effect = requests_exc.Timeout("msg") + + conn._connected = True + conn._winrm_host = 'hostname' + + monkeypatch.setattr(conn, "_winrm_connect", lambda: mock_proto) + + with pytest.raises(AnsibleConnectionFailure) as e: + conn.exec_command('cmd', in_data=None, sudoable=True) + + assert str(e.value) == "winrm connection error: msg" + + def test_exec_command_with_timeout(self, monkeypatch): + requests_exc = pytest.importorskip("requests.exceptions") + + pc = PlayContext() + new_stdin = StringIO() + conn = connection_loader.get('winrm', pc, new_stdin) + + mock_proto = MagicMock() + mock_proto.run_command.side_effect = requests_exc.Timeout("msg") + + conn._connected = True + conn._winrm_host = 'hostname' + + monkeypatch.setattr(conn, "_winrm_connect", lambda: mock_proto) + + with pytest.raises(AnsibleConnectionFailure) as e: + conn.exec_command('cmd', in_data=None, sudoable=True) + + assert str(e.value) == "winrm connection error: msg" + + def test_connect_failure_auth_401(self, monkeypatch): + pc = PlayContext() + new_stdin = StringIO() + conn = connection_loader.get('winrm', pc, new_stdin) + conn.set_options(var_options={"ansible_winrm_transport": "basic", "_extras": {}}) + + mock_proto = MagicMock() + mock_proto.open_shell.side_effect = ValueError("Custom exc Code 401") + + mock_proto_init = MagicMock() + mock_proto_init.return_value = mock_proto + monkeypatch.setattr(winrm, "Protocol", mock_proto_init) + + with pytest.raises(AnsibleConnectionFailure, match="the specified credentials were rejected by the server"): + conn.exec_command('cmd', in_data=None, sudoable=True) + + def test_connect_failure_other_exception(self, monkeypatch): + pc = PlayContext() + new_stdin = StringIO() + conn = connection_loader.get('winrm', pc, new_stdin) + conn.set_options(var_options={"ansible_winrm_transport": "basic", "_extras": {}}) + + mock_proto = MagicMock() + mock_proto.open_shell.side_effect = ValueError("Custom exc") + + mock_proto_init = MagicMock() + mock_proto_init.return_value = mock_proto + monkeypatch.setattr(winrm, "Protocol", mock_proto_init) + + with pytest.raises(AnsibleConnectionFailure, match="basic: Custom exc"): + conn.exec_command('cmd', in_data=None, sudoable=True) + + def test_connect_failure_operation_timed_out(self, monkeypatch): + pc = PlayContext() + new_stdin = StringIO() + conn = connection_loader.get('winrm', pc, new_stdin) + conn.set_options(var_options={"ansible_winrm_transport": "basic", "_extras": {}}) + + mock_proto = MagicMock() + mock_proto.open_shell.side_effect = ValueError("Custom exc Operation timed out") + + mock_proto_init = MagicMock() + mock_proto_init.return_value = mock_proto + monkeypatch.setattr(winrm, "Protocol", mock_proto_init) + + with pytest.raises(AnsibleError, match="the connection attempt timed out"): + conn.exec_command('cmd', in_data=None, sudoable=True) + + def test_connect_no_transport(self): + pc = PlayContext() + new_stdin = StringIO() + conn = connection_loader.get('winrm', pc, new_stdin) + conn.set_options(var_options={"_extras": {}}) + conn._build_winrm_kwargs() + conn._winrm_transport = [] + + with pytest.raises(AnsibleError, match="No transport found for WinRM connection"): + conn._winrm_connect()
